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/tupconvert.h"
29 : #include "access/xact.h"
30 : #include "access/xlog.h"
31 : #include "access/xloginsert.h"
32 : #include "catalog/catalog.h"
33 : #include "catalog/heap.h"
34 : #include "catalog/index.h"
35 : #include "catalog/namespace.h"
36 : #include "catalog/objectaccess.h"
37 : #include "catalog/partition.h"
38 : #include "catalog/pg_am.h"
39 : #include "catalog/pg_attrdef.h"
40 : #include "catalog/pg_collation.h"
41 : #include "catalog/pg_constraint.h"
42 : #include "catalog/pg_depend.h"
43 : #include "catalog/pg_foreign_table.h"
44 : #include "catalog/pg_inherits.h"
45 : #include "catalog/pg_largeobject.h"
46 : #include "catalog/pg_largeobject_metadata.h"
47 : #include "catalog/pg_namespace.h"
48 : #include "catalog/pg_opclass.h"
49 : #include "catalog/pg_policy.h"
50 : #include "catalog/pg_proc.h"
51 : #include "catalog/pg_publication_rel.h"
52 : #include "catalog/pg_rewrite.h"
53 : #include "catalog/pg_statistic_ext.h"
54 : #include "catalog/pg_tablespace.h"
55 : #include "catalog/pg_trigger.h"
56 : #include "catalog/pg_type.h"
57 : #include "catalog/storage.h"
58 : #include "catalog/storage_xlog.h"
59 : #include "catalog/toasting.h"
60 : #include "commands/comment.h"
61 : #include "commands/defrem.h"
62 : #include "commands/event_trigger.h"
63 : #include "commands/repack.h"
64 : #include "commands/sequence.h"
65 : #include "commands/tablecmds.h"
66 : #include "commands/tablespace.h"
67 : #include "commands/trigger.h"
68 : #include "commands/typecmds.h"
69 : #include "commands/user.h"
70 : #include "commands/vacuum.h"
71 : #include "common/int.h"
72 : #include "executor/executor.h"
73 : #include "foreign/fdwapi.h"
74 : #include "foreign/foreign.h"
75 : #include "miscadmin.h"
76 : #include "nodes/makefuncs.h"
77 : #include "nodes/nodeFuncs.h"
78 : #include "nodes/parsenodes.h"
79 : #include "optimizer/optimizer.h"
80 : #include "parser/parse_coerce.h"
81 : #include "parser/parse_collate.h"
82 : #include "parser/parse_expr.h"
83 : #include "parser/parse_relation.h"
84 : #include "parser/parse_type.h"
85 : #include "parser/parse_utilcmd.h"
86 : #include "parser/parser.h"
87 : #include "partitioning/partbounds.h"
88 : #include "partitioning/partdesc.h"
89 : #include "pgstat.h"
90 : #include "rewrite/rewriteDefine.h"
91 : #include "rewrite/rewriteHandler.h"
92 : #include "rewrite/rewriteManip.h"
93 : #include "storage/bufmgr.h"
94 : #include "storage/lmgr.h"
95 : #include "storage/lock.h"
96 : #include "storage/predicate.h"
97 : #include "storage/smgr.h"
98 : #include "tcop/utility.h"
99 : #include "utils/acl.h"
100 : #include "utils/builtins.h"
101 : #include "utils/fmgroids.h"
102 : #include "utils/inval.h"
103 : #include "utils/lsyscache.h"
104 : #include "utils/memutils.h"
105 : #include "utils/partcache.h"
106 : #include "utils/relcache.h"
107 : #include "utils/ruleutils.h"
108 : #include "utils/snapmgr.h"
109 : #include "utils/syscache.h"
110 : #include "utils/timestamp.h"
111 : #include "utils/typcache.h"
112 : #include "utils/usercontext.h"
113 :
114 : /*
115 : * ON COMMIT action list
116 : */
117 : typedef struct OnCommitItem
118 : {
119 : Oid relid; /* relid of relation */
120 : OnCommitAction oncommit; /* what to do at end of xact */
121 :
122 : /*
123 : * If this entry was created during the current transaction,
124 : * creating_subid is the ID of the creating subxact; if created in a prior
125 : * transaction, creating_subid is zero. If deleted during the current
126 : * transaction, deleting_subid is the ID of the deleting subxact; if no
127 : * deletion request is pending, deleting_subid is zero.
128 : */
129 : SubTransactionId creating_subid;
130 : SubTransactionId deleting_subid;
131 : } OnCommitItem;
132 :
133 : static List *on_commits = NIL;
134 :
135 :
136 : /*
137 : * State information for ALTER TABLE
138 : *
139 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
140 : * structs, one for each table modified by the operation (the named table
141 : * plus any child tables that are affected). We save lists of subcommands
142 : * to apply to this table (possibly modified by parse transformation steps);
143 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
144 : * necessary information is stored in the constraints and newvals lists.
145 : *
146 : * Phase 2 is divided into multiple passes; subcommands are executed in
147 : * a pass determined by subcommand type.
148 : */
149 :
150 : typedef enum AlterTablePass
151 : {
152 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
153 : AT_PASS_DROP, /* DROP (all flavors) */
154 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
155 : AT_PASS_ADD_COL, /* ADD COLUMN */
156 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
157 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
158 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
159 : /* We could support a RENAME COLUMN pass here, but not currently used */
160 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
161 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
162 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
163 : AT_PASS_ADD_INDEX, /* ADD indexes */
164 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
165 : AT_PASS_MISC, /* other stuff */
166 : } AlterTablePass;
167 :
168 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
169 :
170 : typedef struct AlteredTableInfo
171 : {
172 : /* Information saved before any work commences: */
173 : Oid relid; /* Relation to work on */
174 : char relkind; /* Its relkind */
175 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
176 :
177 : /*
178 : * Transiently set during Phase 2, normally set to NULL.
179 : *
180 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
181 : * returns control. This can be exploited by ATExecCmd subroutines to
182 : * close/reopen across transaction boundaries.
183 : */
184 : Relation rel;
185 :
186 : /* Information saved by Phase 1 for Phase 2: */
187 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
188 : /* Information saved by Phases 1/2 for Phase 3: */
189 : List *constraints; /* List of NewConstraint */
190 : List *newvals; /* List of NewColumnValue */
191 : List *afterStmts; /* List of utility command parsetrees */
192 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
193 : int rewrite; /* Reason for forced rewrite, if any */
194 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
195 : Oid newAccessMethod; /* new access method; 0 means no change,
196 : * if above is true */
197 : Oid newTableSpace; /* new tablespace; 0 means no change */
198 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
199 : char newrelpersistence; /* if above is true */
200 : Expr *partition_constraint; /* for attach partition validation */
201 : /* true, if validating default due to some other attach/detach */
202 : bool validate_default;
203 : /* Objects to rebuild after completing ALTER TYPE operations */
204 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
205 : List *changedConstraintDefs; /* string definitions of same */
206 : List *changedIndexOids; /* OIDs of indexes to rebuild */
207 : List *changedIndexDefs; /* string definitions of same */
208 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
209 : char *clusterOnIndex; /* index to use for CLUSTER */
210 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
211 : List *changedStatisticsDefs; /* string definitions of same */
212 : } AlteredTableInfo;
213 :
214 : /* Struct describing one new constraint to check in Phase 3 scan */
215 : /* Note: new not-null constraints are handled elsewhere */
216 : typedef struct NewConstraint
217 : {
218 : char *name; /* Constraint name, or NULL if none */
219 : ConstrType contype; /* CHECK or FOREIGN */
220 : Oid refrelid; /* PK rel, if FOREIGN */
221 : Oid refindid; /* OID of PK's index, if FOREIGN */
222 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
223 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
224 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
225 : ExprState *qualstate; /* Execution state for CHECK expr */
226 : } NewConstraint;
227 :
228 : /*
229 : * Struct describing one new column value that needs to be computed during
230 : * Phase 3 copy (this could be either a new column with a non-null default, or
231 : * a column that we're changing the type of). Columns without such an entry
232 : * are just copied from the old table during ATRewriteTable. Note that the
233 : * expr is an expression over *old* table values, except when is_generated
234 : * is true; then it is an expression over columns of the *new* tuple.
235 : */
236 : typedef struct NewColumnValue
237 : {
238 : AttrNumber attnum; /* which column */
239 : Expr *expr; /* expression to compute */
240 : ExprState *exprstate; /* execution state */
241 : bool is_generated; /* is it a GENERATED expression? */
242 : } NewColumnValue;
243 :
244 : /*
245 : * Error-reporting support for RemoveRelations
246 : */
247 : struct dropmsgstrings
248 : {
249 : char kind;
250 : int nonexistent_code;
251 : const char *nonexistent_msg;
252 : const char *skipping_msg;
253 : const char *nota_msg;
254 : const char *drophint_msg;
255 : };
256 :
257 : static const struct dropmsgstrings dropmsgstringarray[] = {
258 : {RELKIND_RELATION,
259 : ERRCODE_UNDEFINED_TABLE,
260 : gettext_noop("table \"%s\" does not exist"),
261 : gettext_noop("table \"%s\" does not exist, skipping"),
262 : gettext_noop("\"%s\" is not a table"),
263 : gettext_noop("Use DROP TABLE to remove a table.")},
264 : {RELKIND_SEQUENCE,
265 : ERRCODE_UNDEFINED_TABLE,
266 : gettext_noop("sequence \"%s\" does not exist"),
267 : gettext_noop("sequence \"%s\" does not exist, skipping"),
268 : gettext_noop("\"%s\" is not a sequence"),
269 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
270 : {RELKIND_VIEW,
271 : ERRCODE_UNDEFINED_TABLE,
272 : gettext_noop("view \"%s\" does not exist"),
273 : gettext_noop("view \"%s\" does not exist, skipping"),
274 : gettext_noop("\"%s\" is not a view"),
275 : gettext_noop("Use DROP VIEW to remove a view.")},
276 : {RELKIND_MATVIEW,
277 : ERRCODE_UNDEFINED_TABLE,
278 : gettext_noop("materialized view \"%s\" does not exist"),
279 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
280 : gettext_noop("\"%s\" is not a materialized view"),
281 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
282 : {RELKIND_INDEX,
283 : ERRCODE_UNDEFINED_OBJECT,
284 : gettext_noop("index \"%s\" does not exist"),
285 : gettext_noop("index \"%s\" does not exist, skipping"),
286 : gettext_noop("\"%s\" is not an index"),
287 : gettext_noop("Use DROP INDEX to remove an index.")},
288 : {RELKIND_COMPOSITE_TYPE,
289 : ERRCODE_UNDEFINED_OBJECT,
290 : gettext_noop("type \"%s\" does not exist"),
291 : gettext_noop("type \"%s\" does not exist, skipping"),
292 : gettext_noop("\"%s\" is not a type"),
293 : gettext_noop("Use DROP TYPE to remove a type.")},
294 : {RELKIND_FOREIGN_TABLE,
295 : ERRCODE_UNDEFINED_OBJECT,
296 : gettext_noop("foreign table \"%s\" does not exist"),
297 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
298 : gettext_noop("\"%s\" is not a foreign table"),
299 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
300 : {RELKIND_PARTITIONED_TABLE,
301 : ERRCODE_UNDEFINED_TABLE,
302 : gettext_noop("table \"%s\" does not exist"),
303 : gettext_noop("table \"%s\" does not exist, skipping"),
304 : gettext_noop("\"%s\" is not a table"),
305 : gettext_noop("Use DROP TABLE to remove a table.")},
306 : {RELKIND_PARTITIONED_INDEX,
307 : ERRCODE_UNDEFINED_OBJECT,
308 : gettext_noop("index \"%s\" does not exist"),
309 : gettext_noop("index \"%s\" does not exist, skipping"),
310 : gettext_noop("\"%s\" is not an index"),
311 : gettext_noop("Use DROP INDEX to remove an index.")},
312 : {RELKIND_PROPGRAPH,
313 : ERRCODE_UNDEFINED_OBJECT,
314 : gettext_noop("property graph \"%s\" does not exist"),
315 : gettext_noop("property graph \"%s\" does not exist, skipping"),
316 : gettext_noop("\"%s\" is not a property graph"),
317 : gettext_noop("Use DROP PROPERTY GRAPH to remove a property graph.")},
318 : {'\0', 0, NULL, NULL, NULL, NULL}
319 : };
320 :
321 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
322 : struct DropRelationCallbackState
323 : {
324 : /* These fields are set by RemoveRelations: */
325 : char expected_relkind;
326 : LOCKMODE heap_lockmode;
327 : /* These fields are state to track which subsidiary locks are held: */
328 : Oid heapOid;
329 : Oid partParentOid;
330 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
331 : char actual_relkind;
332 : char actual_relpersistence;
333 : };
334 :
335 : /* Alter table target-type flags for ATSimplePermissions */
336 : #define ATT_TABLE 0x0001
337 : #define ATT_VIEW 0x0002
338 : #define ATT_MATVIEW 0x0004
339 : #define ATT_INDEX 0x0008
340 : #define ATT_COMPOSITE_TYPE 0x0010
341 : #define ATT_FOREIGN_TABLE 0x0020
342 : #define ATT_PARTITIONED_INDEX 0x0040
343 : #define ATT_SEQUENCE 0x0080
344 : #define ATT_PARTITIONED_TABLE 0x0100
345 :
346 : /*
347 : * ForeignTruncateInfo
348 : *
349 : * Information related to truncation of foreign tables. This is used for
350 : * the elements in a hash table. It uses the server OID as lookup key,
351 : * and includes a per-server list of all foreign tables involved in the
352 : * truncation.
353 : */
354 : typedef struct ForeignTruncateInfo
355 : {
356 : Oid serverid;
357 : List *rels;
358 : } ForeignTruncateInfo;
359 :
360 : /* Partial or complete FK creation in addFkConstraint() */
361 : typedef enum addFkConstraintSides
362 : {
363 : addFkReferencedSide,
364 : addFkReferencingSide,
365 : addFkBothSides,
366 : } addFkConstraintSides;
367 :
368 : /*
369 : * Partition tables are expected to be dropped when the parent partitioned
370 : * table gets dropped. Hence for partitioning we use AUTO dependency.
371 : * Otherwise, for regular inheritance use NORMAL dependency.
372 : */
373 : #define child_dependency_type(child_is_partition) \
374 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
375 :
376 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
377 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
378 : static void truncate_check_activity(Relation rel);
379 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
380 : Oid relId, Oid oldRelId, void *arg);
381 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
382 : bool is_partition, List **supconstr,
383 : List **supnotnulls);
384 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
385 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
386 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
387 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
388 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
389 : static void StoreCatalogInheritance(Oid relationId, List *supers,
390 : bool child_is_partition);
391 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
392 : int32 seqNumber, Relation inhRelation,
393 : bool child_is_partition);
394 : static int findAttrByName(const char *attributeName, const List *columns);
395 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
396 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
397 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
398 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
399 : LOCKMODE lockmode);
400 : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
401 : ATAlterConstraint *cmdcon,
402 : bool recurse, LOCKMODE lockmode);
403 : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
404 : Relation tgrel, Relation rel, HeapTuple contuple,
405 : bool recurse, LOCKMODE lockmode);
406 : static bool ATExecAlterFKConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
407 : Relation conrel, Relation tgrel,
408 : Oid fkrelid, Oid pkrelid,
409 : HeapTuple contuple, LOCKMODE lockmode,
410 : Oid ReferencedParentDelTrigger,
411 : Oid ReferencedParentUpdTrigger,
412 : Oid ReferencingParentInsTrigger,
413 : Oid ReferencingParentUpdTrigger);
414 : static bool ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
415 : Relation conrel, HeapTuple contuple,
416 : bool recurse, bool recursing,
417 : LOCKMODE lockmode);
418 : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
419 : Relation conrel, Relation tgrel, Relation rel,
420 : HeapTuple contuple, bool recurse,
421 : List **otherrelids, LOCKMODE lockmode);
422 : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
423 : Relation conrel, Relation rel,
424 : HeapTuple contuple, LOCKMODE lockmode);
425 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
426 : bool deferrable, bool initdeferred,
427 : List **otherrelids);
428 : static void AlterFKConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
429 : Relation conrel, Relation tgrel,
430 : Oid fkrelid, Oid pkrelid,
431 : HeapTuple contuple, LOCKMODE lockmode,
432 : Oid ReferencedParentDelTrigger,
433 : Oid ReferencedParentUpdTrigger,
434 : Oid ReferencingParentInsTrigger,
435 : Oid ReferencingParentUpdTrigger);
436 : static void AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
437 : Relation conrel, Oid conrelid,
438 : bool recurse, bool recursing,
439 : LOCKMODE lockmode);
440 : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
441 : Relation conrel, Relation tgrel, Relation rel,
442 : HeapTuple contuple, bool recurse,
443 : List **otherrelids, LOCKMODE lockmode);
444 : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
445 : HeapTuple contuple);
446 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
447 : Relation rel, char *constrName,
448 : bool recurse, bool recursing, LOCKMODE lockmode);
449 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
450 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
451 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
452 : char *constrName, HeapTuple contuple,
453 : bool recurse, bool recursing, LOCKMODE lockmode);
454 : static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
455 : HeapTuple contuple, bool recurse, bool recursing,
456 : LOCKMODE lockmode);
457 : static int transformColumnNameList(Oid relId, List *colList,
458 : int16 *attnums, Oid *atttypids, Oid *attcollids);
459 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
460 : List **attnamelist,
461 : int16 *attnums, Oid *atttypids, Oid *attcollids,
462 : Oid *opclasses, bool *pk_has_without_overlaps);
463 : static Oid transformFkeyCheckAttrs(Relation pkrel,
464 : int numattrs, int16 *attnums,
465 : bool with_period, Oid *opclasses,
466 : bool *pk_has_without_overlaps);
467 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
468 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
469 : Oid *funcid);
470 : static void validateForeignKeyConstraint(char *conname,
471 : Relation rel, Relation pkrel,
472 : Oid pkindOid, Oid constraintOid, bool hasperiod);
473 : static void CheckAlterTableIsSafe(Relation rel);
474 : static void ATController(AlterTableStmt *parsetree,
475 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
476 : AlterTableUtilityContext *context);
477 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
478 : bool recurse, bool recursing, LOCKMODE lockmode,
479 : AlterTableUtilityContext *context);
480 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
481 : AlterTableUtilityContext *context);
482 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
483 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
484 : AlterTableUtilityContext *context);
485 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
486 : Relation rel, AlterTableCmd *cmd,
487 : bool recurse, LOCKMODE lockmode,
488 : AlterTablePass cur_pass,
489 : AlterTableUtilityContext *context);
490 : static void ATRewriteTables(AlterTableStmt *parsetree,
491 : List **wqueue, LOCKMODE lockmode,
492 : AlterTableUtilityContext *context);
493 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
494 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
495 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
496 : static void ATSimpleRecursion(List **wqueue, Relation rel,
497 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
498 : AlterTableUtilityContext *context);
499 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
500 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
501 : LOCKMODE lockmode,
502 : AlterTableUtilityContext *context);
503 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
504 : DropBehavior behavior);
505 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
506 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
507 : AlterTableUtilityContext *context);
508 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
509 : Relation rel, AlterTableCmd **cmd,
510 : bool recurse, bool recursing,
511 : LOCKMODE lockmode, AlterTablePass cur_pass,
512 : AlterTableUtilityContext *context);
513 : static bool check_for_column_name_collision(Relation rel, const char *colname,
514 : bool if_not_exists);
515 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
516 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
517 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
518 : LOCKMODE lockmode);
519 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
520 : bool is_valid, bool queue_validation);
521 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
522 : char *conName, char *colName,
523 : bool recurse, bool recursing,
524 : LOCKMODE lockmode);
525 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
526 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
527 : List *testConstraint, List *provenConstraint);
528 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
529 : Node *newDefault, LOCKMODE lockmode);
530 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
531 : Node *newDefault);
532 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
533 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
534 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
535 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
536 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
537 : bool recurse, bool recursing);
538 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
539 : Node *newExpr, LOCKMODE lockmode);
540 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
541 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
542 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
543 : Node *newValue, LOCKMODE lockmode);
544 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
545 : Node *options, bool isReset, LOCKMODE lockmode);
546 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
547 : Node *newValue, LOCKMODE lockmode);
548 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
549 : AlterTableCmd *cmd, LOCKMODE lockmode,
550 : AlterTableUtilityContext *context);
551 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
552 : DropBehavior behavior,
553 : bool recurse, bool recursing,
554 : bool missing_ok, LOCKMODE lockmode,
555 : ObjectAddresses *addrs);
556 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
557 : bool recurse, LOCKMODE lockmode,
558 : AlterTableUtilityContext *context);
559 : static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
560 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
561 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
562 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
563 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
564 : static ObjectAddress ATExecAddConstraint(List **wqueue,
565 : AlteredTableInfo *tab, Relation rel,
566 : Constraint *newConstraint, bool recurse, bool is_readd,
567 : LOCKMODE lockmode);
568 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
569 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
570 : IndexStmt *stmt, LOCKMODE lockmode);
571 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
572 : AlteredTableInfo *tab, Relation rel,
573 : Constraint *constr,
574 : bool recurse, bool recursing, bool is_readd,
575 : LOCKMODE lockmode);
576 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
577 : Relation rel, Constraint *fkconstraint,
578 : bool recurse, bool recursing,
579 : LOCKMODE lockmode);
580 : static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
581 : int numfksetcols, int16 *fksetcolsattnums,
582 : List *fksetcols);
583 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
584 : char *constraintname,
585 : Constraint *fkconstraint, Relation rel,
586 : Relation pkrel, Oid indexOid,
587 : Oid parentConstr,
588 : int numfks, int16 *pkattnum, int16 *fkattnum,
589 : Oid *pfeqoperators, Oid *ppeqoperators,
590 : Oid *ffeqoperators, int numfkdelsetcols,
591 : int16 *fkdelsetcols, bool is_internal,
592 : bool with_period);
593 : static void addFkRecurseReferenced(Constraint *fkconstraint,
594 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
595 : int numfks, int16 *pkattnum, int16 *fkattnum,
596 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
597 : int numfkdelsetcols, int16 *fkdelsetcols,
598 : bool old_check_ok,
599 : Oid parentDelTrigger, Oid parentUpdTrigger,
600 : bool with_period);
601 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
602 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
603 : int numfks, int16 *pkattnum, int16 *fkattnum,
604 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
605 : int numfkdelsetcols, int16 *fkdelsetcols,
606 : bool old_check_ok, LOCKMODE lockmode,
607 : Oid parentInsTrigger, Oid parentUpdTrigger,
608 : bool with_period);
609 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
610 : Relation partitionRel);
611 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
612 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
613 : Relation partRel);
614 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
615 : Constraint *fkconstraint, Oid constraintOid,
616 : Oid indexOid,
617 : Oid parentInsTrigger, Oid parentUpdTrigger,
618 : Oid *insertTrigOid, Oid *updateTrigOid);
619 : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
620 : Constraint *fkconstraint, Oid constraintOid,
621 : Oid indexOid,
622 : Oid parentDelTrigger, Oid parentUpdTrigger,
623 : Oid *deleteTrigOid, Oid *updateTrigOid);
624 : static bool tryAttachPartitionForeignKey(List **wqueue,
625 : ForeignKeyCacheInfo *fk,
626 : Relation partition,
627 : Oid parentConstrOid, int numfks,
628 : AttrNumber *mapped_conkey, AttrNumber *confkey,
629 : Oid *conpfeqop,
630 : Oid parentInsTrigger,
631 : Oid parentUpdTrigger,
632 : Relation trigrel);
633 : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
634 : Oid partConstrOid, Oid parentConstrOid,
635 : Oid parentInsTrigger, Oid parentUpdTrigger,
636 : Relation trigrel);
637 : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
638 : Oid conoid, Oid conrelid);
639 : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
640 : Oid confrelid, Oid conrelid);
641 : static void GetForeignKeyActionTriggers(Relation trigrel,
642 : Oid conoid, Oid confrelid, Oid conrelid,
643 : Oid *deleteTriggerOid,
644 : Oid *updateTriggerOid);
645 : static void GetForeignKeyCheckTriggers(Relation trigrel,
646 : Oid conoid, Oid confrelid, Oid conrelid,
647 : Oid *insertTriggerOid,
648 : Oid *updateTriggerOid);
649 : static void ATExecDropConstraint(Relation rel, const char *constrName,
650 : DropBehavior behavior, bool recurse,
651 : bool missing_ok, LOCKMODE lockmode);
652 : static ObjectAddress dropconstraint_internal(Relation rel,
653 : HeapTuple constraintTup, DropBehavior behavior,
654 : bool recurse, bool recursing,
655 : bool missing_ok, LOCKMODE lockmode);
656 : static void ATPrepAlterColumnType(List **wqueue,
657 : AlteredTableInfo *tab, Relation rel,
658 : bool recurse, bool recursing,
659 : AlterTableCmd *cmd, LOCKMODE lockmode,
660 : AlterTableUtilityContext *context);
661 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
662 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
663 : AlterTableCmd *cmd, LOCKMODE lockmode);
664 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
665 : Relation rel, AttrNumber attnum, const char *colName);
666 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
667 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
668 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
669 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
670 : LOCKMODE lockmode);
671 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
672 : char *cmd, List **wqueue, LOCKMODE lockmode,
673 : bool rewrite);
674 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
675 : Oid objid, Relation rel, List *domname,
676 : const char *conname);
677 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
678 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
679 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
680 : List *options, LOCKMODE lockmode);
681 : static void change_owner_fix_column_acls(Oid relationOid,
682 : Oid oldOwnerId, Oid newOwnerId);
683 : static void change_owner_recurse_to_sequences(Oid relationOid,
684 : Oid newOwnerId, LOCKMODE lockmode);
685 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
686 : LOCKMODE lockmode);
687 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
688 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
689 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
690 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
691 : bool toLogged);
692 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
693 : const char *tablespacename, LOCKMODE lockmode);
694 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
695 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
696 : static void ATExecSetRelOptions(Relation rel, List *defList,
697 : AlterTableType operation,
698 : LOCKMODE lockmode);
699 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
700 : char fires_when, bool skip_system, bool recurse,
701 : LOCKMODE lockmode);
702 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
703 : char fires_when, LOCKMODE lockmode);
704 : static void ATPrepChangeInherit(Relation child_rel);
705 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
706 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
707 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
708 : DependencyType deptype);
709 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
710 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
711 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
712 : static void ATExecGenericOptions(Relation rel, List *options);
713 : static void ATExecSetRowSecurity(Relation rel, bool rls);
714 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
715 : static ObjectAddress ATExecSetCompression(Relation rel,
716 : const char *column, Node *newValue, LOCKMODE lockmode);
717 :
718 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
719 : static const char *storage_name(char c);
720 :
721 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
722 : Oid oldRelOid, void *arg);
723 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
724 : Oid oldrelid, void *arg);
725 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
726 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
727 : List **partexprs, Oid *partopclass, Oid *partcollation,
728 : PartitionStrategy strategy);
729 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
730 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
731 : bool expect_detached);
732 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
733 : PartitionCmd *cmd,
734 : AlterTableUtilityContext *context);
735 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
736 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
737 : List *partConstraint,
738 : bool validate_default);
739 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
740 : static void DropClonedTriggersFromPartition(Oid partitionId);
741 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
742 : Relation rel, RangeVar *name,
743 : bool concurrent);
744 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
745 : bool concurrent, Oid defaultPartOid);
746 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
747 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
748 : RangeVar *name);
749 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
750 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
751 : Relation partitionTbl);
752 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
753 : static List *GetParentedForeignKeyRefs(Relation partition);
754 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
755 : static char GetAttributeCompression(Oid atttypid, const char *compression);
756 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
757 :
758 : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
759 : PartitionCmd *cmd, AlterTableUtilityContext *context);
760 : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
761 : Relation rel, PartitionCmd *cmd,
762 : AlterTableUtilityContext *context);
763 :
764 : /* ----------------------------------------------------------------
765 : * DefineRelation
766 : * Creates a new relation.
767 : *
768 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
769 : * The other arguments are used to extend the behavior for other cases:
770 : * relkind: relkind to assign to the new relation
771 : * ownerId: if not InvalidOid, use this as the new relation's owner.
772 : * typaddress: if not null, it's set to the pg_type entry's address.
773 : * queryString: for error reporting
774 : *
775 : * Note that permissions checks are done against current user regardless of
776 : * ownerId. A nonzero ownerId is used when someone is creating a relation
777 : * "on behalf of" someone else, so we still want to see that the current user
778 : * has permissions to do it.
779 : *
780 : * If successful, returns the address of the new relation.
781 : * ----------------------------------------------------------------
782 : */
783 : ObjectAddress
784 41627 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
785 : ObjectAddress *typaddress, const char *queryString)
786 : {
787 : char relname[NAMEDATALEN];
788 : Oid namespaceId;
789 : Oid relationId;
790 : Oid tablespaceId;
791 : Relation rel;
792 : TupleDesc descriptor;
793 : List *inheritOids;
794 : List *old_constraints;
795 : List *old_notnulls;
796 : List *rawDefaults;
797 : List *cookedDefaults;
798 : List *nncols;
799 41627 : List *connames = NIL;
800 : Datum reloptions;
801 : ListCell *listptr;
802 : AttrNumber attnum;
803 : bool partitioned;
804 41627 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
805 : Oid ofTypeId;
806 : ObjectAddress address;
807 : LOCKMODE parentLockmode;
808 41627 : Oid accessMethodId = InvalidOid;
809 :
810 : /*
811 : * Truncate relname to appropriate length (probably a waste of time, as
812 : * parser should have done this already).
813 : */
814 41627 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
815 :
816 : /*
817 : * Check consistency of arguments
818 : */
819 41627 : if (stmt->oncommit != ONCOMMIT_NOOP
820 128 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
821 8 : ereport(ERROR,
822 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
823 : errmsg("ON COMMIT can only be used on temporary tables")));
824 :
825 41619 : if (stmt->partspec != NULL)
826 : {
827 3571 : if (relkind != RELKIND_RELATION)
828 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
829 :
830 3571 : relkind = RELKIND_PARTITIONED_TABLE;
831 3571 : partitioned = true;
832 : }
833 : else
834 38048 : partitioned = false;
835 :
836 41619 : if (relkind == RELKIND_PARTITIONED_TABLE &&
837 3571 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
838 4 : ereport(ERROR,
839 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
840 : errmsg("partitioned tables cannot be unlogged")));
841 :
842 : /*
843 : * Look up the namespace in which we are supposed to create the relation,
844 : * check we have permission to create there, lock it against concurrent
845 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
846 : * namespace is selected.
847 : */
848 : namespaceId =
849 41615 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
850 :
851 : /*
852 : * Security check: disallow creating temp tables from security-restricted
853 : * code. This is needed because calling code might not expect untrusted
854 : * tables to appear in pg_temp at the front of its search path.
855 : */
856 41615 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
857 2300 : && InSecurityRestrictedOperation())
858 0 : ereport(ERROR,
859 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
860 : errmsg("cannot create temporary table within security-restricted operation")));
861 :
862 : /*
863 : * Determine the lockmode to use when scanning parents. A self-exclusive
864 : * lock is needed here.
865 : *
866 : * For regular inheritance, if two backends attempt to add children to the
867 : * same parent simultaneously, and that parent has no pre-existing
868 : * children, then both will attempt to update the parent's relhassubclass
869 : * field, leading to a "tuple concurrently updated" error. Also, this
870 : * interlocks against a concurrent ANALYZE on the parent table, which
871 : * might otherwise be attempting to clear the parent's relhassubclass
872 : * field, if its previous children were recently dropped.
873 : *
874 : * If the child table is a partition, then we instead grab an exclusive
875 : * lock on the parent because its partition descriptor will be changed by
876 : * addition of the new partition.
877 : */
878 41615 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
879 : ShareUpdateExclusiveLock);
880 :
881 : /* Determine the list of OIDs of the parents. */
882 41615 : inheritOids = NIL;
883 49667 : foreach(listptr, stmt->inhRelations)
884 : {
885 8052 : RangeVar *rv = (RangeVar *) lfirst(listptr);
886 : Oid parentOid;
887 :
888 8052 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
889 :
890 : /*
891 : * Reject duplications in the list of parents.
892 : */
893 8052 : if (list_member_oid(inheritOids, parentOid))
894 0 : ereport(ERROR,
895 : (errcode(ERRCODE_DUPLICATE_TABLE),
896 : errmsg("relation \"%s\" would be inherited from more than once",
897 : get_rel_name(parentOid))));
898 :
899 8052 : inheritOids = lappend_oid(inheritOids, parentOid);
900 : }
901 :
902 : /*
903 : * Select tablespace to use: an explicitly indicated one, or (in the case
904 : * of a partitioned table) the parent's, if it has one.
905 : */
906 41615 : if (stmt->tablespacename)
907 : {
908 84 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
909 :
910 80 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
911 4 : ereport(ERROR,
912 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
913 : errmsg("cannot specify default tablespace for partitioned relations")));
914 : }
915 41531 : else if (stmt->partbound)
916 : {
917 : Assert(list_length(inheritOids) == 1);
918 6415 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
919 : }
920 : else
921 35116 : tablespaceId = InvalidOid;
922 :
923 : /* still nothing? use the default */
924 41607 : if (!OidIsValid(tablespaceId))
925 41505 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
926 : partitioned);
927 :
928 : /* Check permissions except when using database's default */
929 41603 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
930 : {
931 : AclResult aclresult;
932 :
933 118 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
934 : ACL_CREATE);
935 118 : if (aclresult != ACLCHECK_OK)
936 3 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
937 3 : get_tablespace_name(tablespaceId));
938 : }
939 :
940 : /* In all cases disallow placing user relations in pg_global */
941 41600 : if (tablespaceId == GLOBALTABLESPACE_OID)
942 12 : ereport(ERROR,
943 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
944 : errmsg("only shared relations can be placed in pg_global tablespace")));
945 :
946 : /* Identify user ID that will own the table */
947 41588 : if (!OidIsValid(ownerId))
948 41433 : ownerId = GetUserId();
949 :
950 : /*
951 : * Parse and validate reloptions, if any.
952 : */
953 41588 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
954 : true, false);
955 :
956 41576 : switch (relkind)
957 : {
958 10728 : case RELKIND_VIEW:
959 10728 : (void) view_reloptions(reloptions, true);
960 10716 : break;
961 3555 : case RELKIND_PARTITIONED_TABLE:
962 3555 : (void) partitioned_table_reloptions(reloptions, true);
963 3551 : break;
964 27293 : default:
965 27293 : (void) heap_reloptions(relkind, reloptions, true);
966 : }
967 :
968 41496 : if (stmt->ofTypename)
969 : {
970 : AclResult aclresult;
971 :
972 57 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
973 :
974 57 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
975 57 : if (aclresult != ACLCHECK_OK)
976 4 : aclcheck_error_type(aclresult, ofTypeId);
977 : }
978 : else
979 41439 : ofTypeId = InvalidOid;
980 :
981 : /*
982 : * Look up inheritance ancestors and generate relation schema, including
983 : * inherited attributes. (Note that stmt->tableElts is destructively
984 : * modified by MergeAttributes.)
985 : */
986 41332 : stmt->tableElts =
987 41492 : MergeAttributes(stmt->tableElts, inheritOids,
988 41492 : stmt->relation->relpersistence,
989 41492 : stmt->partbound != NULL,
990 : &old_constraints, &old_notnulls);
991 :
992 : /*
993 : * Create a tuple descriptor from the relation schema. Note that this
994 : * deals with column names, types, and in-descriptor NOT NULL flags, but
995 : * not default values, NOT NULL or CHECK constraints; we handle those
996 : * below.
997 : */
998 41332 : descriptor = BuildDescForRelation(stmt->tableElts);
999 :
1000 : /*
1001 : * Find columns with default values and prepare for insertion of the
1002 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
1003 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
1004 : * while raw defaults go into a list of RawColumnDefault structs that will
1005 : * be processed by AddRelationNewConstraints. (We can't deal with raw
1006 : * expressions until we can do transformExpr.)
1007 : */
1008 41300 : rawDefaults = NIL;
1009 41300 : cookedDefaults = NIL;
1010 41300 : attnum = 0;
1011 :
1012 207196 : foreach(listptr, stmt->tableElts)
1013 : {
1014 165896 : ColumnDef *colDef = lfirst(listptr);
1015 :
1016 165896 : attnum++;
1017 165896 : if (colDef->raw_default != NULL)
1018 : {
1019 : RawColumnDefault *rawEnt;
1020 :
1021 : Assert(colDef->cooked_default == NULL);
1022 :
1023 2159 : rawEnt = palloc_object(RawColumnDefault);
1024 2159 : rawEnt->attnum = attnum;
1025 2159 : rawEnt->raw_default = colDef->raw_default;
1026 2159 : rawEnt->generated = colDef->generated;
1027 2159 : rawDefaults = lappend(rawDefaults, rawEnt);
1028 : }
1029 163737 : else if (colDef->cooked_default != NULL)
1030 : {
1031 : CookedConstraint *cooked;
1032 :
1033 346 : cooked = palloc_object(CookedConstraint);
1034 346 : cooked->contype = CONSTR_DEFAULT;
1035 346 : cooked->conoid = InvalidOid; /* until created */
1036 346 : cooked->name = NULL;
1037 346 : cooked->attnum = attnum;
1038 346 : cooked->expr = colDef->cooked_default;
1039 346 : cooked->is_enforced = true;
1040 346 : cooked->skip_validation = false;
1041 346 : cooked->is_local = true; /* not used for defaults */
1042 346 : cooked->inhcount = 0; /* ditto */
1043 346 : cooked->is_no_inherit = false;
1044 346 : cookedDefaults = lappend(cookedDefaults, cooked);
1045 : }
1046 : }
1047 :
1048 41300 : TupleDescFinalize(descriptor);
1049 :
1050 : /*
1051 : * For relations with table AM and partitioned tables, select access
1052 : * method to use: an explicitly indicated one, or (in the case of a
1053 : * partitioned table) the parent's, if it has one.
1054 : */
1055 41300 : if (stmt->accessMethod != NULL)
1056 : {
1057 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1058 87 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1059 : }
1060 41213 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1061 : {
1062 26484 : if (stmt->partbound)
1063 : {
1064 : Assert(list_length(inheritOids) == 1);
1065 6301 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1066 : }
1067 :
1068 26484 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1069 22920 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1070 : }
1071 :
1072 : /*
1073 : * Create the relation. Inherited defaults and CHECK constraints are
1074 : * passed in for immediate handling --- since they don't need parsing,
1075 : * they can be stored immediately.
1076 : */
1077 41288 : relationId = heap_create_with_catalog(relname,
1078 : namespaceId,
1079 : tablespaceId,
1080 : InvalidOid,
1081 : InvalidOid,
1082 : ofTypeId,
1083 : ownerId,
1084 : accessMethodId,
1085 : descriptor,
1086 : list_concat(cookedDefaults,
1087 : old_constraints),
1088 : relkind,
1089 41288 : stmt->relation->relpersistence,
1090 : false,
1091 : false,
1092 : stmt->oncommit,
1093 : reloptions,
1094 : true,
1095 : allowSystemTableMods,
1096 : false,
1097 : InvalidOid,
1098 : typaddress);
1099 :
1100 : /*
1101 : * We must bump the command counter to make the newly-created relation
1102 : * tuple visible for opening.
1103 : */
1104 41253 : CommandCounterIncrement();
1105 :
1106 : /*
1107 : * Open the new relation and acquire exclusive lock on it. This isn't
1108 : * really necessary for locking out other backends (since they can't see
1109 : * the new rel anyway until we commit), but it keeps the lock manager from
1110 : * complaining about deadlock risks.
1111 : */
1112 41253 : rel = relation_open(relationId, AccessExclusiveLock);
1113 :
1114 : /*
1115 : * Now add any newly specified column default and generation expressions
1116 : * to the new relation. These are passed to us in the form of raw
1117 : * parsetrees; we need to transform them to executable expression trees
1118 : * before they can be added. The most convenient way to do that is to
1119 : * apply the parser's transformExpr routine, but transformExpr doesn't
1120 : * work unless we have a pre-existing relation. So, the transformation has
1121 : * to be postponed to this final step of CREATE TABLE.
1122 : *
1123 : * This needs to be before processing the partitioning clauses because
1124 : * those could refer to generated columns.
1125 : */
1126 41253 : if (rawDefaults)
1127 1818 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1128 : true, true, false, queryString);
1129 :
1130 : /*
1131 : * Make column generation expressions visible for use by partitioning.
1132 : */
1133 41125 : CommandCounterIncrement();
1134 :
1135 : /* Process and store partition bound, if any. */
1136 41125 : if (stmt->partbound)
1137 : {
1138 : PartitionBoundSpec *bound;
1139 : ParseState *pstate;
1140 6363 : Oid parentId = linitial_oid(inheritOids),
1141 : defaultPartOid;
1142 : Relation parent,
1143 6363 : defaultRel = NULL;
1144 : ParseNamespaceItem *nsitem;
1145 :
1146 : /* Already have strong enough lock on the parent */
1147 6363 : parent = table_open(parentId, NoLock);
1148 :
1149 : /*
1150 : * We are going to try to validate the partition bound specification
1151 : * against the partition key of parentRel, so it better have one.
1152 : */
1153 6363 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1154 12 : ereport(ERROR,
1155 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1156 : errmsg("\"%s\" is not partitioned",
1157 : RelationGetRelationName(parent))));
1158 :
1159 : /*
1160 : * The partition constraint of the default partition depends on the
1161 : * partition bounds of every other partition. It is possible that
1162 : * another backend might be about to execute a query on the default
1163 : * partition table, and that the query relies on previously cached
1164 : * default partition constraints. We must therefore take a table lock
1165 : * strong enough to prevent all queries on the default partition from
1166 : * proceeding until we commit and send out a shared-cache-inval notice
1167 : * that will make them update their index lists.
1168 : *
1169 : * Order of locking: The relation being added won't be visible to
1170 : * other backends until it is committed, hence here in
1171 : * DefineRelation() the order of locking the default partition and the
1172 : * relation being added does not matter. But at all other places we
1173 : * need to lock the default relation before we lock the relation being
1174 : * added or removed i.e. we should take the lock in same order at all
1175 : * the places such that lock parent, lock default partition and then
1176 : * lock the partition so as to avoid a deadlock.
1177 : */
1178 : defaultPartOid =
1179 6351 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1180 : true));
1181 6351 : if (OidIsValid(defaultPartOid))
1182 251 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1183 :
1184 : /* Transform the bound values */
1185 6351 : pstate = make_parsestate(NULL);
1186 6351 : pstate->p_sourcetext = queryString;
1187 :
1188 : /*
1189 : * Add an nsitem containing this relation, so that transformExpr
1190 : * called on partition bound expressions is able to report errors
1191 : * using a proper context.
1192 : */
1193 6351 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1194 : NULL, false, false);
1195 6351 : addNSItemToQuery(pstate, nsitem, false, true, true);
1196 :
1197 6351 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1198 :
1199 : /*
1200 : * Check first that the new partition's bound is valid and does not
1201 : * overlap with any of existing partitions of the parent.
1202 : */
1203 6215 : check_new_partition_bound(relname, parent, bound, pstate);
1204 :
1205 : /*
1206 : * If the default partition exists, its partition constraints will
1207 : * change after the addition of this new partition such that it won't
1208 : * allow any row that qualifies for this new partition. So, check that
1209 : * the existing data in the default partition satisfies the constraint
1210 : * as it will exist after adding this partition.
1211 : */
1212 6139 : if (OidIsValid(defaultPartOid))
1213 : {
1214 231 : check_default_partition_contents(parent, defaultRel, bound);
1215 : /* Keep the lock until commit. */
1216 219 : table_close(defaultRel, NoLock);
1217 : }
1218 :
1219 : /* Update the pg_class entry. */
1220 6127 : StorePartitionBound(rel, parent, bound);
1221 :
1222 6127 : table_close(parent, NoLock);
1223 : }
1224 :
1225 : /* Store inheritance information for new rel. */
1226 40889 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1227 :
1228 : /*
1229 : * Process the partitioning specification (if any) and store the partition
1230 : * key information into the catalog.
1231 : */
1232 40889 : if (partitioned)
1233 : {
1234 : ParseState *pstate;
1235 : int partnatts;
1236 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1237 : Oid partopclass[PARTITION_MAX_KEYS];
1238 : Oid partcollation[PARTITION_MAX_KEYS];
1239 3551 : List *partexprs = NIL;
1240 :
1241 3551 : pstate = make_parsestate(NULL);
1242 3551 : pstate->p_sourcetext = queryString;
1243 :
1244 3551 : partnatts = list_length(stmt->partspec->partParams);
1245 :
1246 : /* Protect fixed-size arrays here and in executor */
1247 3551 : if (partnatts > PARTITION_MAX_KEYS)
1248 0 : ereport(ERROR,
1249 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1250 : errmsg("cannot partition using more than %d columns",
1251 : PARTITION_MAX_KEYS)));
1252 :
1253 : /*
1254 : * We need to transform the raw parsetrees corresponding to partition
1255 : * expressions into executable expression trees. Like column defaults
1256 : * and CHECK constraints, we could not have done the transformation
1257 : * earlier.
1258 : */
1259 3551 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1260 :
1261 3531 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1262 : partattrs, &partexprs, partopclass,
1263 3531 : partcollation, stmt->partspec->strategy);
1264 :
1265 3443 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1266 : partexprs,
1267 : partopclass, partcollation);
1268 :
1269 : /* make it all visible */
1270 3443 : CommandCounterIncrement();
1271 : }
1272 :
1273 : /*
1274 : * If we're creating a partition, create now all the indexes, triggers,
1275 : * FKs defined in the parent.
1276 : *
1277 : * We can't do it earlier, because DefineIndex wants to know the partition
1278 : * key which we just stored.
1279 : */
1280 40781 : if (stmt->partbound)
1281 : {
1282 6123 : Oid parentId = linitial_oid(inheritOids);
1283 : Relation parent;
1284 : List *idxlist;
1285 : ListCell *cell;
1286 :
1287 : /* Already have strong enough lock on the parent */
1288 6123 : parent = table_open(parentId, NoLock);
1289 6123 : idxlist = RelationGetIndexList(parent);
1290 :
1291 : /*
1292 : * For each index in the parent table, create one in the partition
1293 : */
1294 7205 : foreach(cell, idxlist)
1295 : {
1296 1094 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1297 : AttrMap *attmap;
1298 : IndexStmt *idxstmt;
1299 : Oid constraintOid;
1300 :
1301 1094 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1302 : {
1303 24 : if (idxRel->rd_index->indisunique)
1304 8 : ereport(ERROR,
1305 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1306 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1307 : RelationGetRelationName(parent)),
1308 : errdetail("Table \"%s\" contains indexes that are unique.",
1309 : RelationGetRelationName(parent))));
1310 : else
1311 : {
1312 16 : index_close(idxRel, AccessShareLock);
1313 16 : continue;
1314 : }
1315 : }
1316 :
1317 1070 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1318 : RelationGetDescr(parent),
1319 : false);
1320 : idxstmt =
1321 1070 : generateClonedIndexStmt(NULL, idxRel,
1322 : attmap, &constraintOid);
1323 1070 : DefineIndex(NULL,
1324 : RelationGetRelid(rel),
1325 : idxstmt,
1326 : InvalidOid,
1327 : RelationGetRelid(idxRel),
1328 : constraintOid,
1329 : -1,
1330 : false, false, false, false, false);
1331 :
1332 1066 : index_close(idxRel, AccessShareLock);
1333 : }
1334 :
1335 6111 : list_free(idxlist);
1336 :
1337 : /*
1338 : * If there are any row-level triggers, clone them to the new
1339 : * partition.
1340 : */
1341 6111 : if (parent->trigdesc != NULL)
1342 260 : CloneRowTriggersToPartition(parent, rel);
1343 :
1344 : /*
1345 : * And foreign keys too. Note that because we're freshly creating the
1346 : * table, there is no need to verify these new constraints.
1347 : */
1348 6111 : CloneForeignKeyConstraints(NULL, parent, rel);
1349 :
1350 6111 : table_close(parent, NoLock);
1351 : }
1352 :
1353 : /*
1354 : * Now add any newly specified CHECK constraints to the new relation. Same
1355 : * as for defaults above, but these need to come after partitioning is set
1356 : * up. We save the constraint names that were used, to avoid dupes below.
1357 : */
1358 40769 : if (stmt->constraints)
1359 : {
1360 : List *conlist;
1361 :
1362 498 : conlist = AddRelationNewConstraints(rel, NIL, stmt->constraints,
1363 : true, true, false, queryString);
1364 1509 : foreach_ptr(CookedConstraint, cons, conlist)
1365 : {
1366 553 : if (cons->name != NULL)
1367 553 : connames = lappend(connames, cons->name);
1368 : }
1369 : }
1370 :
1371 : /*
1372 : * Finally, merge the not-null constraints that are declared directly with
1373 : * those that come from parent relations (making sure to count inheritance
1374 : * appropriately for each), create them, and set the attnotnull flag on
1375 : * columns that don't yet have it.
1376 : */
1377 40749 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1378 : old_notnulls, connames);
1379 91537 : foreach_int(attrnum, nncols)
1380 10143 : set_attnotnull(NULL, rel, attrnum, true, false);
1381 :
1382 40697 : ObjectAddressSet(address, RelationRelationId, relationId);
1383 :
1384 : /*
1385 : * Clean up. We keep lock on new relation (although it shouldn't be
1386 : * visible to anyone else anyway, until commit).
1387 : */
1388 40697 : relation_close(rel, NoLock);
1389 :
1390 40697 : return address;
1391 : }
1392 :
1393 : /*
1394 : * BuildDescForRelation
1395 : *
1396 : * Given a list of ColumnDef nodes, build a TupleDesc.
1397 : *
1398 : * Note: This is only for the limited purpose of table and view creation. Not
1399 : * everything is filled in. A real tuple descriptor should be obtained from
1400 : * the relcache.
1401 : */
1402 : TupleDesc
1403 43924 : BuildDescForRelation(const List *columns)
1404 : {
1405 : int natts;
1406 : AttrNumber attnum;
1407 : ListCell *l;
1408 : TupleDesc desc;
1409 : char *attname;
1410 : Oid atttypid;
1411 : int32 atttypmod;
1412 : Oid attcollation;
1413 : int attdim;
1414 :
1415 : /*
1416 : * allocate a new tuple descriptor
1417 : */
1418 43924 : natts = list_length(columns);
1419 43924 : desc = CreateTemplateTupleDesc(natts);
1420 :
1421 43924 : attnum = 0;
1422 :
1423 213337 : foreach(l, columns)
1424 : {
1425 169453 : ColumnDef *entry = lfirst(l);
1426 : AclResult aclresult;
1427 : Form_pg_attribute att;
1428 :
1429 : /*
1430 : * for each entry in the list, get the name and type information from
1431 : * the list and have TupleDescInitEntry fill in the attribute
1432 : * information we need.
1433 : */
1434 169453 : attnum++;
1435 :
1436 169453 : attname = entry->colname;
1437 169453 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1438 :
1439 169453 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1440 169453 : if (aclresult != ACLCHECK_OK)
1441 28 : aclcheck_error_type(aclresult, atttypid);
1442 :
1443 169425 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1444 169425 : attdim = list_length(entry->typeName->arrayBounds);
1445 169425 : if (attdim > PG_INT16_MAX)
1446 0 : ereport(ERROR,
1447 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1448 : errmsg("too many array dimensions"));
1449 :
1450 169425 : if (entry->typeName->setof)
1451 0 : ereport(ERROR,
1452 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1453 : errmsg("column \"%s\" cannot be declared SETOF",
1454 : attname)));
1455 :
1456 169425 : TupleDescInitEntry(desc, attnum, attname,
1457 : atttypid, atttypmod, attdim);
1458 169425 : att = TupleDescAttr(desc, attnum - 1);
1459 :
1460 : /* Override TupleDescInitEntry's settings as requested */
1461 169425 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1462 :
1463 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1464 169425 : att->attnotnull = entry->is_not_null;
1465 169425 : att->attislocal = entry->is_local;
1466 169425 : att->attinhcount = entry->inhcount;
1467 169425 : att->attidentity = entry->identity;
1468 169425 : att->attgenerated = entry->generated;
1469 169425 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1470 169417 : if (entry->storage)
1471 16593 : att->attstorage = entry->storage;
1472 152824 : else if (entry->storage_name)
1473 39 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1474 :
1475 169413 : populate_compact_attribute(desc, attnum - 1);
1476 : }
1477 :
1478 43884 : TupleDescFinalize(desc);
1479 :
1480 43884 : return desc;
1481 : }
1482 :
1483 : /*
1484 : * Emit the right error or warning message for a "DROP" command issued on a
1485 : * non-existent relation
1486 : */
1487 : static void
1488 625 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1489 : {
1490 : const struct dropmsgstrings *rentry;
1491 :
1492 705 : if (rel->schemaname != NULL &&
1493 80 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1494 : {
1495 28 : if (!missing_ok)
1496 : {
1497 0 : ereport(ERROR,
1498 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1499 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1500 : }
1501 : else
1502 : {
1503 28 : ereport(NOTICE,
1504 : (errmsg("schema \"%s\" does not exist, skipping",
1505 : rel->schemaname)));
1506 : }
1507 28 : return;
1508 : }
1509 :
1510 882 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1511 : {
1512 882 : if (rentry->kind == rightkind)
1513 : {
1514 597 : if (!missing_ok)
1515 : {
1516 90 : ereport(ERROR,
1517 : (errcode(rentry->nonexistent_code),
1518 : errmsg(rentry->nonexistent_msg, rel->relname)));
1519 : }
1520 : else
1521 : {
1522 507 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1523 507 : break;
1524 : }
1525 : }
1526 : }
1527 :
1528 : Assert(rentry->kind != '\0'); /* Should be impossible */
1529 : }
1530 :
1531 : /*
1532 : * Emit the right error message for a "DROP" command issued on a
1533 : * relation of the wrong type
1534 : */
1535 : static void
1536 4 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1537 : {
1538 : const struct dropmsgstrings *rentry;
1539 : const struct dropmsgstrings *wentry;
1540 :
1541 4 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1542 4 : if (rentry->kind == rightkind)
1543 4 : break;
1544 : Assert(rentry->kind != '\0');
1545 :
1546 40 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1547 40 : if (wentry->kind == wrongkind)
1548 4 : break;
1549 : /* wrongkind could be something we don't have in our table... */
1550 :
1551 4 : ereport(ERROR,
1552 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1553 : errmsg(rentry->nota_msg, relname),
1554 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1555 : }
1556 :
1557 : /*
1558 : * RemoveRelations
1559 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1560 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE, DROP PROPERTY GRAPH
1561 : */
1562 : void
1563 11519 : RemoveRelations(DropStmt *drop)
1564 : {
1565 : ObjectAddresses *objects;
1566 : char relkind;
1567 : ListCell *cell;
1568 11519 : int flags = 0;
1569 11519 : LOCKMODE lockmode = AccessExclusiveLock;
1570 :
1571 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1572 11519 : if (drop->concurrent)
1573 : {
1574 : /*
1575 : * Note that for temporary relations this lock may get upgraded later
1576 : * on, but as no other session can access a temporary relation, this
1577 : * is actually fine.
1578 : */
1579 95 : lockmode = ShareUpdateExclusiveLock;
1580 : Assert(drop->removeType == OBJECT_INDEX);
1581 95 : if (list_length(drop->objects) != 1)
1582 4 : ereport(ERROR,
1583 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1584 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1585 91 : if (drop->behavior == DROP_CASCADE)
1586 0 : ereport(ERROR,
1587 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1588 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1589 : }
1590 :
1591 : /*
1592 : * First we identify all the relations, then we delete them in a single
1593 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1594 : * RESTRICT errors if one of the relations depends on another.
1595 : */
1596 :
1597 : /* Determine required relkind */
1598 11515 : switch (drop->removeType)
1599 : {
1600 9969 : case OBJECT_TABLE:
1601 9969 : relkind = RELKIND_RELATION;
1602 9969 : break;
1603 :
1604 541 : case OBJECT_INDEX:
1605 541 : relkind = RELKIND_INDEX;
1606 541 : break;
1607 :
1608 119 : case OBJECT_SEQUENCE:
1609 119 : relkind = RELKIND_SEQUENCE;
1610 119 : break;
1611 :
1612 658 : case OBJECT_VIEW:
1613 658 : relkind = RELKIND_VIEW;
1614 658 : break;
1615 :
1616 79 : case OBJECT_MATVIEW:
1617 79 : relkind = RELKIND_MATVIEW;
1618 79 : break;
1619 :
1620 103 : case OBJECT_FOREIGN_TABLE:
1621 103 : relkind = RELKIND_FOREIGN_TABLE;
1622 103 : break;
1623 :
1624 46 : case OBJECT_PROPGRAPH:
1625 46 : relkind = RELKIND_PROPGRAPH;
1626 46 : break;
1627 :
1628 0 : default:
1629 0 : elog(ERROR, "unrecognized drop object type: %d",
1630 : (int) drop->removeType);
1631 : relkind = 0; /* keep compiler quiet */
1632 : break;
1633 : }
1634 :
1635 : /* Lock and validate each relation; build a list of object addresses */
1636 11515 : objects = new_object_addresses();
1637 :
1638 25405 : foreach(cell, drop->objects)
1639 : {
1640 14001 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1641 : Oid relOid;
1642 : ObjectAddress obj;
1643 : struct DropRelationCallbackState state;
1644 :
1645 : /*
1646 : * These next few steps are a great deal like relation_openrv, but we
1647 : * don't bother building a relcache entry since we don't need it.
1648 : *
1649 : * Check for shared-cache-inval messages before trying to access the
1650 : * relation. This is needed to cover the case where the name
1651 : * identifies a rel that has been dropped and recreated since the
1652 : * start of our transaction: if we don't flush the old syscache entry,
1653 : * then we'll latch onto that entry and suffer an error later.
1654 : */
1655 14001 : AcceptInvalidationMessages();
1656 :
1657 : /* Look up the appropriate relation using namespace search. */
1658 14001 : state.expected_relkind = relkind;
1659 28002 : state.heap_lockmode = drop->concurrent ?
1660 14001 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1661 : /* We must initialize these fields to show that no locks are held: */
1662 14001 : state.heapOid = InvalidOid;
1663 14001 : state.partParentOid = InvalidOid;
1664 :
1665 14001 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1666 : RangeVarCallbackForDropRelation,
1667 : &state);
1668 :
1669 : /* Not there? */
1670 13984 : if (!OidIsValid(relOid))
1671 : {
1672 625 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1673 535 : continue;
1674 : }
1675 :
1676 : /*
1677 : * Decide if concurrent mode needs to be used here or not. The
1678 : * callback retrieved the rel's persistence for us.
1679 : */
1680 13359 : if (drop->concurrent &&
1681 87 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1682 : {
1683 : Assert(list_length(drop->objects) == 1 &&
1684 : drop->removeType == OBJECT_INDEX);
1685 75 : flags |= PERFORM_DELETION_CONCURRENTLY;
1686 : }
1687 :
1688 : /*
1689 : * Concurrent index drop cannot be used with partitioned indexes,
1690 : * either.
1691 : */
1692 13359 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1693 75 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1694 4 : ereport(ERROR,
1695 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1696 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1697 : rel->relname)));
1698 :
1699 : /*
1700 : * If we're told to drop a partitioned index, we must acquire lock on
1701 : * all the children of its parent partitioned table before proceeding.
1702 : * Otherwise we'd try to lock the child index partitions before their
1703 : * tables, leading to potential deadlock against other sessions that
1704 : * will lock those objects in the other order.
1705 : */
1706 13355 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1707 50 : (void) find_all_inheritors(state.heapOid,
1708 : state.heap_lockmode,
1709 : NULL);
1710 :
1711 : /* OK, we're ready to delete this one */
1712 13355 : obj.classId = RelationRelationId;
1713 13355 : obj.objectId = relOid;
1714 13355 : obj.objectSubId = 0;
1715 :
1716 13355 : add_exact_object_address(&obj, objects);
1717 : }
1718 :
1719 11404 : performMultipleDeletions(objects, drop->behavior, flags);
1720 :
1721 11302 : free_object_addresses(objects);
1722 11302 : }
1723 :
1724 : /*
1725 : * Before acquiring a table lock, check whether we have sufficient rights.
1726 : * In the case of DROP INDEX, also try to lock the table before the index.
1727 : * Also, if the table to be dropped is a partition, we try to lock the parent
1728 : * first.
1729 : */
1730 : static void
1731 14245 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1732 : void *arg)
1733 : {
1734 : HeapTuple tuple;
1735 : struct DropRelationCallbackState *state;
1736 : char expected_relkind;
1737 : bool is_partition;
1738 : Form_pg_class classform;
1739 : LOCKMODE heap_lockmode;
1740 14245 : bool invalid_system_index = false;
1741 :
1742 14245 : state = (struct DropRelationCallbackState *) arg;
1743 14245 : heap_lockmode = state->heap_lockmode;
1744 :
1745 : /*
1746 : * If we previously locked some other index's heap, and the name we're
1747 : * looking up no longer refers to that relation, release the now-useless
1748 : * lock.
1749 : */
1750 14245 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1751 : {
1752 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1753 0 : state->heapOid = InvalidOid;
1754 : }
1755 :
1756 : /*
1757 : * Similarly, if we previously locked some other partition's heap, and the
1758 : * name we're looking up no longer refers to that relation, release the
1759 : * now-useless lock.
1760 : */
1761 14245 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1762 : {
1763 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1764 0 : state->partParentOid = InvalidOid;
1765 : }
1766 :
1767 : /* Didn't find a relation, so no need for locking or permission checks. */
1768 14245 : if (!OidIsValid(relOid))
1769 629 : return;
1770 :
1771 13616 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1772 13616 : if (!HeapTupleIsValid(tuple))
1773 0 : return; /* concurrently dropped, so nothing to do */
1774 13616 : classform = (Form_pg_class) GETSTRUCT(tuple);
1775 13616 : is_partition = classform->relispartition;
1776 :
1777 : /* Pass back some data to save lookups in RemoveRelations */
1778 13616 : state->actual_relkind = classform->relkind;
1779 13616 : state->actual_relpersistence = classform->relpersistence;
1780 :
1781 : /*
1782 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1783 : * but RemoveRelations() can only pass one relkind for a given relation.
1784 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1785 : * That means we must be careful before giving the wrong type error when
1786 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1787 : * exists with indexes.
1788 : */
1789 13616 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1790 2173 : expected_relkind = RELKIND_RELATION;
1791 11443 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1792 55 : expected_relkind = RELKIND_INDEX;
1793 : else
1794 11388 : expected_relkind = classform->relkind;
1795 :
1796 13616 : if (state->expected_relkind != expected_relkind)
1797 4 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1798 4 : state->expected_relkind);
1799 :
1800 : /* Allow DROP to either table owner or schema owner */
1801 13612 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1802 12 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1803 12 : aclcheck_error(ACLCHECK_NOT_OWNER,
1804 12 : get_relkind_objtype(classform->relkind),
1805 12 : rel->relname);
1806 :
1807 : /*
1808 : * Check the case of a system index that might have been invalidated by a
1809 : * failed concurrent process and allow its drop. For the time being, this
1810 : * only concerns indexes of toast relations that became invalid during a
1811 : * REINDEX CONCURRENTLY process.
1812 : */
1813 13600 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1814 : {
1815 : HeapTuple locTuple;
1816 : Form_pg_index indexform;
1817 : bool indisvalid;
1818 :
1819 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1820 0 : if (!HeapTupleIsValid(locTuple))
1821 : {
1822 0 : ReleaseSysCache(tuple);
1823 0 : return;
1824 : }
1825 :
1826 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1827 0 : indisvalid = indexform->indisvalid;
1828 0 : ReleaseSysCache(locTuple);
1829 :
1830 : /* Mark object as being an invalid index of system catalogs */
1831 0 : if (!indisvalid)
1832 0 : invalid_system_index = true;
1833 : }
1834 :
1835 : /* In the case of an invalid index, it is fine to bypass this check */
1836 13600 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1837 1 : ereport(ERROR,
1838 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1839 : errmsg("permission denied: \"%s\" is a system catalog",
1840 : rel->relname)));
1841 :
1842 13599 : ReleaseSysCache(tuple);
1843 :
1844 : /*
1845 : * In DROP INDEX, attempt to acquire lock on the parent table before
1846 : * locking the index. index_drop() will need this anyway, and since
1847 : * regular queries lock tables before their indexes, we risk deadlock if
1848 : * we do it the other way around. No error if we don't find a pg_index
1849 : * entry, though --- the relation may have been dropped. Note that this
1850 : * code will execute for either plain or partitioned indexes.
1851 : */
1852 13599 : if (expected_relkind == RELKIND_INDEX &&
1853 : relOid != oldRelOid)
1854 : {
1855 533 : state->heapOid = IndexGetRelation(relOid, true);
1856 533 : if (OidIsValid(state->heapOid))
1857 533 : LockRelationOid(state->heapOid, heap_lockmode);
1858 : }
1859 :
1860 : /*
1861 : * Similarly, if the relation is a partition, we must acquire lock on its
1862 : * parent before locking the partition. That's because queries lock the
1863 : * parent before its partitions, so we risk deadlock if we do it the other
1864 : * way around.
1865 : */
1866 13599 : if (is_partition && relOid != oldRelOid)
1867 : {
1868 397 : state->partParentOid = get_partition_parent(relOid, true);
1869 397 : if (OidIsValid(state->partParentOid))
1870 397 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1871 : }
1872 : }
1873 :
1874 : /*
1875 : * ExecuteTruncate
1876 : * Executes a TRUNCATE command.
1877 : *
1878 : * This is a multi-relation truncate. We first open and grab exclusive
1879 : * lock on all relations involved, checking permissions and otherwise
1880 : * verifying that the relation is OK for truncation. Note that if relations
1881 : * are foreign tables, at this stage, we have not yet checked that their
1882 : * foreign data in external data sources are OK for truncation. These are
1883 : * checked when foreign data are actually truncated later. In CASCADE mode,
1884 : * relations having FK references to the targeted relations are automatically
1885 : * added to the group; in RESTRICT mode, we check that all FK references are
1886 : * internal to the group that's being truncated. Finally all the relations
1887 : * are truncated and reindexed.
1888 : */
1889 : void
1890 1147 : ExecuteTruncate(TruncateStmt *stmt)
1891 : {
1892 1147 : List *rels = NIL;
1893 1147 : List *relids = NIL;
1894 1147 : List *relids_logged = NIL;
1895 : ListCell *cell;
1896 :
1897 : /*
1898 : * Open, exclusive-lock, and check all the explicitly-specified relations
1899 : */
1900 2443 : foreach(cell, stmt->relations)
1901 : {
1902 1331 : RangeVar *rv = lfirst(cell);
1903 : Relation rel;
1904 1331 : bool recurse = rv->inh;
1905 : Oid myrelid;
1906 1331 : LOCKMODE lockmode = AccessExclusiveLock;
1907 :
1908 1331 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1909 : 0, RangeVarCallbackForTruncate,
1910 : NULL);
1911 :
1912 : /* don't throw error for "TRUNCATE foo, foo" */
1913 1308 : if (list_member_oid(relids, myrelid))
1914 1 : continue;
1915 :
1916 : /* open the relation, we already hold a lock on it */
1917 1307 : rel = table_open(myrelid, NoLock);
1918 :
1919 : /*
1920 : * RangeVarGetRelidExtended() has done most checks with its callback,
1921 : * but other checks with the now-opened Relation remain.
1922 : */
1923 1307 : truncate_check_activity(rel);
1924 :
1925 1303 : rels = lappend(rels, rel);
1926 1303 : relids = lappend_oid(relids, myrelid);
1927 :
1928 : /* Log this relation only if needed for logical decoding */
1929 1303 : if (RelationIsLogicallyLogged(rel))
1930 39 : relids_logged = lappend_oid(relids_logged, myrelid);
1931 :
1932 1303 : if (recurse)
1933 : {
1934 : ListCell *child;
1935 : List *children;
1936 :
1937 1270 : children = find_all_inheritors(myrelid, lockmode, NULL);
1938 :
1939 3760 : foreach(child, children)
1940 : {
1941 2490 : Oid childrelid = lfirst_oid(child);
1942 :
1943 2490 : if (list_member_oid(relids, childrelid))
1944 1270 : continue;
1945 :
1946 : /* find_all_inheritors already got lock */
1947 1220 : rel = table_open(childrelid, NoLock);
1948 :
1949 : /*
1950 : * It is possible that the parent table has children that are
1951 : * temp tables of other backends. We cannot safely access
1952 : * such tables (because of buffering issues), and the best
1953 : * thing to do is to silently ignore them. Note that this
1954 : * check is the same as one of the checks done in
1955 : * truncate_check_activity() called below, still it is kept
1956 : * here for simplicity.
1957 : */
1958 1220 : if (RELATION_IS_OTHER_TEMP(rel))
1959 : {
1960 4 : table_close(rel, lockmode);
1961 4 : continue;
1962 : }
1963 :
1964 : /*
1965 : * Inherited TRUNCATE commands perform access permission
1966 : * checks on the parent table only. So we skip checking the
1967 : * children's permissions and don't call
1968 : * truncate_check_perms() here.
1969 : */
1970 1216 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1971 1216 : truncate_check_activity(rel);
1972 :
1973 1216 : rels = lappend(rels, rel);
1974 1216 : relids = lappend_oid(relids, childrelid);
1975 :
1976 : /* Log this relation only if needed for logical decoding */
1977 1216 : if (RelationIsLogicallyLogged(rel))
1978 11 : relids_logged = lappend_oid(relids_logged, childrelid);
1979 : }
1980 : }
1981 33 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1982 8 : ereport(ERROR,
1983 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1984 : errmsg("cannot truncate only a partitioned table"),
1985 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1986 : }
1987 :
1988 1112 : ExecuteTruncateGuts(rels, relids, relids_logged,
1989 1112 : stmt->behavior, stmt->restart_seqs, false);
1990 :
1991 : /* And close the rels */
1992 3469 : foreach(cell, rels)
1993 : {
1994 2410 : Relation rel = (Relation) lfirst(cell);
1995 :
1996 2410 : table_close(rel, NoLock);
1997 : }
1998 1059 : }
1999 :
2000 : /*
2001 : * ExecuteTruncateGuts
2002 : *
2003 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
2004 : * command (see above) as well as replication subscribers that execute a
2005 : * replicated TRUNCATE action.
2006 : *
2007 : * explicit_rels is the list of Relations to truncate that the command
2008 : * specified. relids is the list of Oids corresponding to explicit_rels.
2009 : * relids_logged is the list of Oids (a subset of relids) that require
2010 : * WAL-logging. This is all a bit redundant, but the existing callers have
2011 : * this information handy in this form.
2012 : */
2013 : void
2014 1132 : ExecuteTruncateGuts(List *explicit_rels,
2015 : List *relids,
2016 : List *relids_logged,
2017 : DropBehavior behavior, bool restart_seqs,
2018 : bool run_as_table_owner)
2019 : {
2020 : List *rels;
2021 1132 : List *seq_relids = NIL;
2022 1132 : HTAB *ft_htab = NULL;
2023 : EState *estate;
2024 : ResultRelInfo *resultRelInfos;
2025 : ResultRelInfo *resultRelInfo;
2026 : SubTransactionId mySubid;
2027 : ListCell *cell;
2028 : Oid *logrelids;
2029 :
2030 : /*
2031 : * Check the explicitly-specified relations.
2032 : *
2033 : * In CASCADE mode, suck in all referencing relations as well. This
2034 : * requires multiple iterations to find indirectly-dependent relations. At
2035 : * each phase, we need to exclusive-lock new rels before looking for their
2036 : * dependencies, else we might miss something. Also, we check each rel as
2037 : * soon as we open it, to avoid a faux pas such as holding lock for a long
2038 : * time on a rel we have no permissions for.
2039 : */
2040 1132 : rels = list_copy(explicit_rels);
2041 1132 : if (behavior == DROP_CASCADE)
2042 : {
2043 : for (;;)
2044 26 : {
2045 : List *newrelids;
2046 :
2047 51 : newrelids = heap_truncate_find_FKs(relids);
2048 51 : if (newrelids == NIL)
2049 25 : break; /* nothing else to add */
2050 :
2051 88 : foreach(cell, newrelids)
2052 : {
2053 62 : Oid relid = lfirst_oid(cell);
2054 : Relation rel;
2055 :
2056 62 : rel = table_open(relid, AccessExclusiveLock);
2057 62 : ereport(NOTICE,
2058 : (errmsg("truncate cascades to table \"%s\"",
2059 : RelationGetRelationName(rel))));
2060 62 : truncate_check_rel(relid, rel->rd_rel);
2061 62 : truncate_check_perms(relid, rel->rd_rel);
2062 62 : truncate_check_activity(rel);
2063 62 : rels = lappend(rels, rel);
2064 62 : relids = lappend_oid(relids, relid);
2065 :
2066 : /* Log this relation only if needed for logical decoding */
2067 62 : if (RelationIsLogicallyLogged(rel))
2068 0 : relids_logged = lappend_oid(relids_logged, relid);
2069 : }
2070 : }
2071 : }
2072 :
2073 : /*
2074 : * Check foreign key references. In CASCADE mode, this should be
2075 : * unnecessary since we just pulled in all the references; but as a
2076 : * cross-check, do it anyway if in an Assert-enabled build.
2077 : */
2078 : #ifdef USE_ASSERT_CHECKING
2079 : heap_truncate_check_FKs(rels, false);
2080 : #else
2081 1132 : if (behavior == DROP_RESTRICT)
2082 1107 : heap_truncate_check_FKs(rels, false);
2083 : #endif
2084 :
2085 : /*
2086 : * If we are asked to restart sequences, find all the sequences, lock them
2087 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2088 : * We want to do this early since it's pointless to do all the truncation
2089 : * work only to fail on sequence permissions.
2090 : */
2091 1083 : if (restart_seqs)
2092 : {
2093 32 : foreach(cell, rels)
2094 : {
2095 16 : Relation rel = (Relation) lfirst(cell);
2096 16 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2097 : ListCell *seqcell;
2098 :
2099 39 : foreach(seqcell, seqlist)
2100 : {
2101 23 : Oid seq_relid = lfirst_oid(seqcell);
2102 : Relation seq_rel;
2103 :
2104 23 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2105 :
2106 : /* This check must match AlterSequence! */
2107 23 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2108 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2109 0 : RelationGetRelationName(seq_rel));
2110 :
2111 23 : seq_relids = lappend_oid(seq_relids, seq_relid);
2112 :
2113 23 : relation_close(seq_rel, NoLock);
2114 : }
2115 : }
2116 : }
2117 :
2118 : /* Prepare to catch AFTER triggers. */
2119 1083 : AfterTriggerBeginQuery();
2120 :
2121 : /*
2122 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2123 : * each relation. We don't need to call ExecOpenIndices, though.
2124 : *
2125 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2126 : * though we don't have a range table and don't populate the
2127 : * es_result_relations array. That's a bit bogus, but it's enough to make
2128 : * ExecGetTriggerResultRel() find them.
2129 : */
2130 1083 : estate = CreateExecutorState();
2131 : resultRelInfos = (ResultRelInfo *)
2132 1083 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2133 1083 : resultRelInfo = resultRelInfos;
2134 3595 : foreach(cell, rels)
2135 : {
2136 2512 : Relation rel = (Relation) lfirst(cell);
2137 :
2138 2512 : InitResultRelInfo(resultRelInfo,
2139 : rel,
2140 : 0, /* dummy rangetable index */
2141 : NULL,
2142 : 0);
2143 2512 : estate->es_opened_result_relations =
2144 2512 : lappend(estate->es_opened_result_relations, resultRelInfo);
2145 2512 : resultRelInfo++;
2146 : }
2147 :
2148 : /*
2149 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2150 : * truncating (this is because one of them might throw an error). Also, if
2151 : * we were to allow them to prevent statement execution, that would need
2152 : * to be handled here.
2153 : */
2154 1083 : resultRelInfo = resultRelInfos;
2155 3595 : foreach(cell, rels)
2156 : {
2157 : UserContext ucxt;
2158 :
2159 2512 : if (run_as_table_owner)
2160 36 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2161 : &ucxt);
2162 2512 : ExecBSTruncateTriggers(estate, resultRelInfo);
2163 2512 : if (run_as_table_owner)
2164 36 : RestoreUserContext(&ucxt);
2165 2512 : resultRelInfo++;
2166 : }
2167 :
2168 : /*
2169 : * OK, truncate each table.
2170 : */
2171 1083 : mySubid = GetCurrentSubTransactionId();
2172 :
2173 3595 : foreach(cell, rels)
2174 : {
2175 2512 : Relation rel = (Relation) lfirst(cell);
2176 :
2177 : /* Skip partitioned tables as there is nothing to do */
2178 2512 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2179 477 : continue;
2180 :
2181 : /*
2182 : * Build the lists of foreign tables belonging to each foreign server
2183 : * and pass each list to the foreign data wrapper's callback function,
2184 : * so that each server can truncate its all foreign tables in bulk.
2185 : * Each list is saved as a single entry in a hash table that uses the
2186 : * server OID as lookup key.
2187 : */
2188 2035 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2189 17 : {
2190 17 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2191 : bool found;
2192 : ForeignTruncateInfo *ft_info;
2193 :
2194 : /* First time through, initialize hashtable for foreign tables */
2195 17 : if (!ft_htab)
2196 : {
2197 : HASHCTL hctl;
2198 :
2199 15 : memset(&hctl, 0, sizeof(HASHCTL));
2200 15 : hctl.keysize = sizeof(Oid);
2201 15 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2202 15 : hctl.hcxt = CurrentMemoryContext;
2203 :
2204 15 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2205 : 32, /* start small and extend */
2206 : &hctl,
2207 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2208 : }
2209 :
2210 : /* Find or create cached entry for the foreign table */
2211 17 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2212 17 : if (!found)
2213 15 : ft_info->rels = NIL;
2214 :
2215 : /*
2216 : * Save the foreign table in the entry of the server that the
2217 : * foreign table belongs to.
2218 : */
2219 17 : ft_info->rels = lappend(ft_info->rels, rel);
2220 17 : continue;
2221 : }
2222 :
2223 : /*
2224 : * Normally, we need a transaction-safe truncation here. However, if
2225 : * the table was either created in the current (sub)transaction or has
2226 : * a new relfilenumber in the current (sub)transaction, then we can
2227 : * just truncate it in-place, because a rollback would cause the whole
2228 : * table or the current physical file to be thrown away anyway.
2229 : */
2230 2018 : if (rel->rd_createSubid == mySubid ||
2231 2003 : rel->rd_newRelfilelocatorSubid == mySubid)
2232 : {
2233 : /* Immediate, non-rollbackable truncation is OK */
2234 47 : heap_truncate_one_rel(rel);
2235 : }
2236 : else
2237 : {
2238 : Oid heap_relid;
2239 : Oid toast_relid;
2240 1971 : ReindexParams reindex_params = {0};
2241 :
2242 : /*
2243 : * This effectively deletes all rows in the table, and may be done
2244 : * in a serializable transaction. In that case we must record a
2245 : * rw-conflict in to this transaction from each transaction
2246 : * holding a predicate lock on the table.
2247 : */
2248 1971 : CheckTableForSerializableConflictIn(rel);
2249 :
2250 : /*
2251 : * Need the full transaction-safe pushups.
2252 : *
2253 : * Create a new empty storage file for the relation, and assign it
2254 : * as the relfilenumber value. The old storage file is scheduled
2255 : * for deletion at commit.
2256 : */
2257 1971 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2258 :
2259 1971 : heap_relid = RelationGetRelid(rel);
2260 :
2261 : /*
2262 : * The same for the toast table, if any.
2263 : */
2264 1971 : toast_relid = rel->rd_rel->reltoastrelid;
2265 1971 : if (OidIsValid(toast_relid))
2266 : {
2267 1193 : Relation toastrel = relation_open(toast_relid,
2268 : AccessExclusiveLock);
2269 :
2270 1193 : RelationSetNewRelfilenumber(toastrel,
2271 1193 : toastrel->rd_rel->relpersistence);
2272 1193 : table_close(toastrel, NoLock);
2273 : }
2274 :
2275 : /*
2276 : * Reconstruct the indexes to match, and we're done.
2277 : */
2278 1971 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2279 : &reindex_params);
2280 : }
2281 :
2282 2018 : pgstat_count_truncate(rel);
2283 : }
2284 :
2285 : /* Now go through the hash table, and truncate foreign tables */
2286 1083 : if (ft_htab)
2287 : {
2288 : ForeignTruncateInfo *ft_info;
2289 : HASH_SEQ_STATUS seq;
2290 :
2291 15 : hash_seq_init(&seq, ft_htab);
2292 :
2293 15 : PG_TRY();
2294 : {
2295 26 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2296 : {
2297 15 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2298 :
2299 : /* truncate_check_rel() has checked that already */
2300 : Assert(routine->ExecForeignTruncate != NULL);
2301 :
2302 15 : routine->ExecForeignTruncate(ft_info->rels,
2303 : behavior,
2304 : restart_seqs);
2305 : }
2306 : }
2307 4 : PG_FINALLY();
2308 : {
2309 15 : hash_destroy(ft_htab);
2310 : }
2311 15 : PG_END_TRY();
2312 : }
2313 :
2314 : /*
2315 : * Restart owned sequences if we were asked to.
2316 : */
2317 1102 : foreach(cell, seq_relids)
2318 : {
2319 23 : Oid seq_relid = lfirst_oid(cell);
2320 :
2321 23 : ResetSequence(seq_relid);
2322 : }
2323 :
2324 : /*
2325 : * Write a WAL record to allow this set of actions to be logically
2326 : * decoded.
2327 : *
2328 : * Assemble an array of relids so we can write a single WAL record for the
2329 : * whole action.
2330 : */
2331 1079 : if (relids_logged != NIL)
2332 : {
2333 : xl_heap_truncate xlrec;
2334 33 : int i = 0;
2335 :
2336 : /* should only get here if effective_wal_level is 'logical' */
2337 : Assert(XLogLogicalInfoActive());
2338 :
2339 33 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2340 84 : foreach(cell, relids_logged)
2341 51 : logrelids[i++] = lfirst_oid(cell);
2342 :
2343 33 : xlrec.dbId = MyDatabaseId;
2344 33 : xlrec.nrelids = list_length(relids_logged);
2345 33 : xlrec.flags = 0;
2346 33 : if (behavior == DROP_CASCADE)
2347 1 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2348 33 : if (restart_seqs)
2349 2 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2350 :
2351 33 : XLogBeginInsert();
2352 33 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2353 33 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2354 :
2355 33 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2356 :
2357 33 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2358 : }
2359 :
2360 : /*
2361 : * Process all AFTER STATEMENT TRUNCATE triggers.
2362 : */
2363 1079 : resultRelInfo = resultRelInfos;
2364 3587 : foreach(cell, rels)
2365 : {
2366 : UserContext ucxt;
2367 :
2368 2508 : if (run_as_table_owner)
2369 36 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2370 : &ucxt);
2371 2508 : ExecASTruncateTriggers(estate, resultRelInfo);
2372 2508 : if (run_as_table_owner)
2373 36 : RestoreUserContext(&ucxt);
2374 2508 : resultRelInfo++;
2375 : }
2376 :
2377 : /* Handle queued AFTER triggers */
2378 1079 : AfterTriggerEndQuery(estate);
2379 :
2380 : /* We can clean up the EState now */
2381 1079 : FreeExecutorState(estate);
2382 :
2383 : /*
2384 : * Close any rels opened by CASCADE (can't do this while EState still
2385 : * holds refs)
2386 : */
2387 1079 : rels = list_difference_ptr(rels, explicit_rels);
2388 1141 : foreach(cell, rels)
2389 : {
2390 62 : Relation rel = (Relation) lfirst(cell);
2391 :
2392 62 : table_close(rel, NoLock);
2393 : }
2394 1079 : }
2395 :
2396 : /*
2397 : * Check that a given relation is safe to truncate. Subroutine for
2398 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2399 : */
2400 : static void
2401 2698 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2402 : {
2403 2698 : char *relname = NameStr(reltuple->relname);
2404 :
2405 : /*
2406 : * Only allow truncate on regular tables, foreign tables using foreign
2407 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2408 : * latter are only being included here for the following checks; no
2409 : * physical truncation will occur in their case.).
2410 : */
2411 2698 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2412 : {
2413 19 : Oid serverid = GetForeignServerIdByRelId(relid);
2414 19 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2415 :
2416 18 : if (!fdwroutine->ExecForeignTruncate)
2417 1 : ereport(ERROR,
2418 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2419 : errmsg("cannot truncate foreign table \"%s\"",
2420 : relname)));
2421 : }
2422 2679 : else if (reltuple->relkind != RELKIND_RELATION &&
2423 495 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2424 0 : ereport(ERROR,
2425 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2426 : errmsg("\"%s\" is not a table", relname)));
2427 :
2428 : /*
2429 : * Most system catalogs can't be truncated at all, or at least not unless
2430 : * allow_system_table_mods=on. As an exception, however, we allow
2431 : * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2432 : * pg_upgrade, because we need to change its relfilenode to match the old
2433 : * cluster, and allowing a TRUNCATE command to be executed is the easiest
2434 : * way of doing that.
2435 : */
2436 2696 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2437 65 : && (!IsBinaryUpgrade ||
2438 32 : (relid != LargeObjectRelationId &&
2439 : relid != LargeObjectMetadataRelationId)))
2440 1 : ereport(ERROR,
2441 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2442 : errmsg("permission denied: \"%s\" is a system catalog",
2443 : relname)));
2444 :
2445 2695 : InvokeObjectTruncateHook(relid);
2446 2695 : }
2447 :
2448 : /*
2449 : * Check that current user has the permission to truncate given relation.
2450 : */
2451 : static void
2452 1479 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2453 : {
2454 1479 : char *relname = NameStr(reltuple->relname);
2455 : AclResult aclresult;
2456 :
2457 : /* Permissions checks */
2458 1479 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2459 1479 : if (aclresult != ACLCHECK_OK)
2460 20 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2461 : relname);
2462 1459 : }
2463 :
2464 : /*
2465 : * Set of extra sanity checks to check if a given relation is safe to
2466 : * truncate. This is split with truncate_check_rel() as
2467 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2468 : */
2469 : static void
2470 2585 : truncate_check_activity(Relation rel)
2471 : {
2472 : /*
2473 : * Don't allow truncate on temp tables of other backends ... their local
2474 : * buffer manager is not going to cope.
2475 : */
2476 2585 : if (RELATION_IS_OTHER_TEMP(rel))
2477 0 : ereport(ERROR,
2478 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2479 : errmsg("cannot truncate temporary tables of other sessions")));
2480 :
2481 : /*
2482 : * Also check for active uses of the relation in the current transaction,
2483 : * including open scans and pending AFTER trigger events.
2484 : */
2485 2585 : CheckTableNotInUse(rel, "TRUNCATE");
2486 2581 : }
2487 :
2488 : /*
2489 : * storage_name
2490 : * returns the name corresponding to a typstorage/attstorage enum value
2491 : */
2492 : static const char *
2493 16 : storage_name(char c)
2494 : {
2495 16 : switch (c)
2496 : {
2497 0 : case TYPSTORAGE_PLAIN:
2498 0 : return "PLAIN";
2499 0 : case TYPSTORAGE_EXTERNAL:
2500 0 : return "EXTERNAL";
2501 8 : case TYPSTORAGE_EXTENDED:
2502 8 : return "EXTENDED";
2503 8 : case TYPSTORAGE_MAIN:
2504 8 : return "MAIN";
2505 0 : default:
2506 0 : return "???";
2507 : }
2508 : }
2509 :
2510 : /*----------
2511 : * MergeAttributes
2512 : * Returns new schema given initial schema and superclasses.
2513 : *
2514 : * Input arguments:
2515 : * 'columns' is the column/attribute definition for the table. (It's a list
2516 : * of ColumnDef's.) It is destructively changed.
2517 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2518 : * 'relpersistence' is the persistence type of the table.
2519 : * 'is_partition' tells if the table is a partition.
2520 : *
2521 : * Output arguments:
2522 : * 'supconstr' receives a list of CookedConstraint representing
2523 : * CHECK constraints belonging to parent relations, updated as
2524 : * necessary to be valid for the child.
2525 : * 'supnotnulls' receives a list of CookedConstraint representing
2526 : * not-null constraints based on those from parent relations.
2527 : *
2528 : * Return value:
2529 : * Completed schema list.
2530 : *
2531 : * Notes:
2532 : * The order in which the attributes are inherited is very important.
2533 : * Intuitively, the inherited attributes should come first. If a table
2534 : * inherits from multiple parents, the order of those attributes are
2535 : * according to the order of the parents specified in CREATE TABLE.
2536 : *
2537 : * Here's an example:
2538 : *
2539 : * create table person (name text, age int4, location point);
2540 : * create table emp (salary int4, manager text) inherits(person);
2541 : * create table student (gpa float8) inherits (person);
2542 : * create table stud_emp (percent int4) inherits (emp, student);
2543 : *
2544 : * The order of the attributes of stud_emp is:
2545 : *
2546 : * person {1:name, 2:age, 3:location}
2547 : * / \
2548 : * {6:gpa} student emp {4:salary, 5:manager}
2549 : * \ /
2550 : * stud_emp {7:percent}
2551 : *
2552 : * If the same attribute name appears multiple times, then it appears
2553 : * in the result table in the proper location for its first appearance.
2554 : *
2555 : * Constraints (including not-null constraints) for the child table
2556 : * are the union of all relevant constraints, from both the child schema
2557 : * and parent tables. In addition, in legacy inheritance, each column that
2558 : * appears in a primary key in any of the parents also gets a NOT NULL
2559 : * constraint (partitioning doesn't need this, because the PK itself gets
2560 : * inherited.)
2561 : *
2562 : * The default value for a child column is defined as:
2563 : * (1) If the child schema specifies a default, that value is used.
2564 : * (2) If neither the child nor any parent specifies a default, then
2565 : * the column will not have a default.
2566 : * (3) If conflicting defaults are inherited from different parents
2567 : * (and not overridden by the child), an error is raised.
2568 : * (4) Otherwise the inherited default is used.
2569 : *
2570 : * Note that the default-value infrastructure is used for generated
2571 : * columns' expressions too, so most of the preceding paragraph applies
2572 : * to generation expressions too. We insist that a child column be
2573 : * generated if and only if its parent(s) are, but it need not have
2574 : * the same generation expression.
2575 : *----------
2576 : */
2577 : static List *
2578 41492 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2579 : bool is_partition, List **supconstr, List **supnotnulls)
2580 : {
2581 41492 : List *inh_columns = NIL;
2582 41492 : List *constraints = NIL;
2583 41492 : List *nnconstraints = NIL;
2584 41492 : bool have_bogus_defaults = false;
2585 : int child_attno;
2586 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2587 41492 : List *saved_columns = NIL;
2588 : ListCell *lc;
2589 :
2590 : /*
2591 : * Check for and reject tables with too many columns. We perform this
2592 : * check relatively early for two reasons: (a) we don't run the risk of
2593 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2594 : * okay if we're processing <= 1600 columns, but could take minutes to
2595 : * execute if the user attempts to create a table with hundreds of
2596 : * thousands of columns.
2597 : *
2598 : * Note that we also need to check that we do not exceed this figure after
2599 : * including columns from inherited relations.
2600 : */
2601 41492 : if (list_length(columns) > MaxHeapAttributeNumber)
2602 0 : ereport(ERROR,
2603 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2604 : errmsg("tables can have at most %d columns",
2605 : MaxHeapAttributeNumber)));
2606 :
2607 : /*
2608 : * Check for duplicate names in the explicit list of attributes.
2609 : *
2610 : * Although we might consider merging such entries in the same way that we
2611 : * handle name conflicts for inherited attributes, it seems to make more
2612 : * sense to assume such conflicts are errors.
2613 : *
2614 : * We don't use foreach() here because we have two nested loops over the
2615 : * columns list, with possible element deletions in the inner one. If we
2616 : * used foreach_delete_current() it could only fix up the state of one of
2617 : * the loops, so it seems cleaner to use looping over list indexes for
2618 : * both loops. Note that any deletion will happen beyond where the outer
2619 : * loop is, so its index never needs adjustment.
2620 : */
2621 192524 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2622 : {
2623 151048 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2624 :
2625 151048 : if (!is_partition && coldef->typeName == NULL)
2626 : {
2627 : /*
2628 : * Typed table column option that does not belong to a column from
2629 : * the type. This works because the columns from the type come
2630 : * first in the list. (We omit this check for partition column
2631 : * lists; those are processed separately below.)
2632 : */
2633 4 : ereport(ERROR,
2634 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2635 : errmsg("column \"%s\" does not exist",
2636 : coldef->colname)));
2637 : }
2638 :
2639 : /* restpos scans all entries beyond coldef; incr is in loop body */
2640 4106900 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2641 : {
2642 3955868 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2643 :
2644 3955868 : if (strcmp(coldef->colname, restdef->colname) == 0)
2645 : {
2646 33 : if (coldef->is_from_type)
2647 : {
2648 : /*
2649 : * merge the column options into the column from the type
2650 : */
2651 21 : coldef->is_not_null = restdef->is_not_null;
2652 21 : coldef->raw_default = restdef->raw_default;
2653 21 : coldef->cooked_default = restdef->cooked_default;
2654 21 : coldef->constraints = restdef->constraints;
2655 21 : coldef->is_from_type = false;
2656 21 : columns = list_delete_nth_cell(columns, restpos);
2657 : }
2658 : else
2659 12 : ereport(ERROR,
2660 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2661 : errmsg("column \"%s\" specified more than once",
2662 : coldef->colname)));
2663 : }
2664 : else
2665 3955835 : restpos++;
2666 : }
2667 : }
2668 :
2669 : /*
2670 : * In case of a partition, there are no new column definitions, only dummy
2671 : * ColumnDefs created for column constraints. Set them aside for now and
2672 : * process them at the end.
2673 : */
2674 41476 : if (is_partition)
2675 : {
2676 6407 : saved_columns = columns;
2677 6407 : columns = NIL;
2678 : }
2679 :
2680 : /*
2681 : * Scan the parents left-to-right, and merge their attributes to form a
2682 : * list of inherited columns (inh_columns).
2683 : */
2684 41476 : child_attno = 0;
2685 49456 : foreach(lc, supers)
2686 : {
2687 8036 : Oid parent = lfirst_oid(lc);
2688 : Relation relation;
2689 : TupleDesc tupleDesc;
2690 : TupleConstr *constr;
2691 : AttrMap *newattmap;
2692 : List *inherited_defaults;
2693 : List *cols_with_defaults;
2694 : List *nnconstrs;
2695 : ListCell *lc1;
2696 : ListCell *lc2;
2697 8036 : Bitmapset *nncols = NULL;
2698 :
2699 : /* caller already got lock */
2700 8036 : relation = table_open(parent, NoLock);
2701 :
2702 : /*
2703 : * Check for active uses of the parent partitioned table in the
2704 : * current transaction, such as being used in some manner by an
2705 : * enclosing command.
2706 : */
2707 8036 : if (is_partition)
2708 6407 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2709 :
2710 : /*
2711 : * We do not allow partitioned tables and partitions to participate in
2712 : * regular inheritance.
2713 : */
2714 8032 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2715 4 : ereport(ERROR,
2716 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2717 : errmsg("cannot inherit from partitioned table \"%s\"",
2718 : RelationGetRelationName(relation))));
2719 8028 : if (relation->rd_rel->relispartition && !is_partition)
2720 4 : ereport(ERROR,
2721 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2722 : errmsg("cannot inherit from partition \"%s\"",
2723 : RelationGetRelationName(relation))));
2724 :
2725 8024 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2726 6403 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2727 6391 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2728 0 : ereport(ERROR,
2729 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2730 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2731 : RelationGetRelationName(relation))));
2732 :
2733 : /*
2734 : * If the parent is permanent, so must be all of its partitions. Note
2735 : * that inheritance allows that case.
2736 : */
2737 8024 : if (is_partition &&
2738 6403 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2739 : relpersistence == RELPERSISTENCE_TEMP)
2740 4 : ereport(ERROR,
2741 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2742 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2743 : RelationGetRelationName(relation))));
2744 :
2745 : /* Permanent rels cannot inherit from temporary ones */
2746 8020 : if (relpersistence != RELPERSISTENCE_TEMP &&
2747 7750 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2748 16 : ereport(ERROR,
2749 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2750 : errmsg(!is_partition
2751 : ? "cannot inherit from temporary relation \"%s\""
2752 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2753 : RelationGetRelationName(relation))));
2754 :
2755 : /* If existing rel is temp, it must belong to this session */
2756 8004 : if (RELATION_IS_OTHER_TEMP(relation))
2757 0 : ereport(ERROR,
2758 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2759 : errmsg(!is_partition
2760 : ? "cannot inherit from temporary relation of another session"
2761 : : "cannot create as partition of temporary relation of another session")));
2762 :
2763 : /*
2764 : * We should have an UNDER permission flag for this, but for now,
2765 : * demand that creator of a child table own the parent.
2766 : */
2767 8004 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2768 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2769 0 : RelationGetRelationName(relation));
2770 :
2771 8004 : tupleDesc = RelationGetDescr(relation);
2772 8004 : constr = tupleDesc->constr;
2773 :
2774 : /*
2775 : * newattmap->attnums[] will contain the child-table attribute numbers
2776 : * for the attributes of this parent table. (They are not the same
2777 : * for parents after the first one, nor if we have dropped columns.)
2778 : */
2779 8004 : newattmap = make_attrmap(tupleDesc->natts);
2780 :
2781 : /* We can't process inherited defaults until newattmap is complete. */
2782 8004 : inherited_defaults = cols_with_defaults = NIL;
2783 :
2784 : /*
2785 : * Request attnotnull on columns that have a not-null constraint
2786 : * that's not marked NO INHERIT (even if not valid).
2787 : */
2788 8004 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2789 : true, false);
2790 17730 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2791 1722 : nncols = bms_add_member(nncols, cc->attnum);
2792 :
2793 23983 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2794 15979 : parent_attno++)
2795 : {
2796 16003 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2797 : parent_attno - 1);
2798 16003 : char *attributeName = NameStr(attribute->attname);
2799 : int exist_attno;
2800 : ColumnDef *newdef;
2801 : ColumnDef *mergeddef;
2802 :
2803 : /*
2804 : * Ignore dropped columns in the parent.
2805 : */
2806 16003 : if (attribute->attisdropped)
2807 132 : continue; /* leave newattmap->attnums entry as zero */
2808 :
2809 : /*
2810 : * Create new column definition
2811 : */
2812 15871 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2813 : attribute->atttypmod, attribute->attcollation);
2814 15871 : newdef->storage = attribute->attstorage;
2815 15871 : newdef->generated = attribute->attgenerated;
2816 15871 : if (CompressionMethodIsValid(attribute->attcompression))
2817 24 : newdef->compression =
2818 24 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2819 :
2820 : /*
2821 : * Regular inheritance children are independent enough not to
2822 : * inherit identity columns. But partitions are integral part of
2823 : * a partitioned table and inherit identity column.
2824 : */
2825 15871 : if (is_partition)
2826 12925 : newdef->identity = attribute->attidentity;
2827 :
2828 : /*
2829 : * Does it match some previously considered column from another
2830 : * parent?
2831 : */
2832 15871 : exist_attno = findAttrByName(attributeName, inh_columns);
2833 15871 : if (exist_attno > 0)
2834 : {
2835 : /*
2836 : * Yes, try to merge the two column definitions.
2837 : */
2838 245 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2839 :
2840 221 : newattmap->attnums[parent_attno - 1] = exist_attno;
2841 :
2842 : /*
2843 : * Partitions have only one parent, so conflict should never
2844 : * occur.
2845 : */
2846 : Assert(!is_partition);
2847 : }
2848 : else
2849 : {
2850 : /*
2851 : * No, create a new inherited column
2852 : */
2853 15626 : newdef->inhcount = 1;
2854 15626 : newdef->is_local = false;
2855 15626 : inh_columns = lappend(inh_columns, newdef);
2856 :
2857 15626 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2858 15626 : mergeddef = newdef;
2859 : }
2860 :
2861 : /*
2862 : * mark attnotnull if parent has it
2863 : */
2864 15847 : if (bms_is_member(parent_attno, nncols))
2865 1722 : mergeddef->is_not_null = true;
2866 :
2867 : /*
2868 : * Locate default/generation expression if any
2869 : */
2870 15847 : if (attribute->atthasdef)
2871 : {
2872 : Node *this_default;
2873 :
2874 538 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2875 538 : if (this_default == NULL)
2876 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2877 : parent_attno, RelationGetRelationName(relation));
2878 :
2879 : /*
2880 : * If it's a GENERATED default, it might contain Vars that
2881 : * need to be mapped to the inherited column(s)' new numbers.
2882 : * We can't do that till newattmap is ready, so just remember
2883 : * all the inherited default expressions for the moment.
2884 : */
2885 538 : inherited_defaults = lappend(inherited_defaults, this_default);
2886 538 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2887 : }
2888 : }
2889 :
2890 : /*
2891 : * Now process any inherited default expressions, adjusting attnos
2892 : * using the completed newattmap map.
2893 : */
2894 8518 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2895 : {
2896 538 : Node *this_default = (Node *) lfirst(lc1);
2897 538 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2898 : bool found_whole_row;
2899 :
2900 : /* Adjust Vars to match new table's column numbering */
2901 538 : this_default = map_variable_attnos(this_default,
2902 : 1, 0,
2903 : newattmap,
2904 : InvalidOid, &found_whole_row);
2905 :
2906 : /*
2907 : * For the moment we have to reject whole-row variables. We could
2908 : * convert them, if we knew the new table's rowtype OID, but that
2909 : * hasn't been assigned yet. (A variable could only appear in a
2910 : * generation expression, so the error message is correct.)
2911 : */
2912 538 : if (found_whole_row)
2913 0 : ereport(ERROR,
2914 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2915 : errmsg("cannot convert whole-row table reference"),
2916 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2917 : def->colname,
2918 : RelationGetRelationName(relation))));
2919 :
2920 : /*
2921 : * If we already had a default from some prior parent, check to
2922 : * see if they are the same. If so, no problem; if not, mark the
2923 : * column as having a bogus default. Below, we will complain if
2924 : * the bogus default isn't overridden by the child columns.
2925 : */
2926 : Assert(def->raw_default == NULL);
2927 538 : if (def->cooked_default == NULL)
2928 510 : def->cooked_default = this_default;
2929 28 : else if (!equal(def->cooked_default, this_default))
2930 : {
2931 24 : def->cooked_default = &bogus_marker;
2932 24 : have_bogus_defaults = true;
2933 : }
2934 : }
2935 :
2936 : /*
2937 : * Now copy the CHECK constraints of this parent, adjusting attnos
2938 : * using the completed newattmap map. Identically named constraints
2939 : * are merged if possible, else we throw error.
2940 : */
2941 7980 : if (constr && constr->num_check > 0)
2942 : {
2943 245 : ConstrCheck *check = constr->check;
2944 :
2945 770 : for (int i = 0; i < constr->num_check; i++)
2946 : {
2947 525 : char *name = check[i].ccname;
2948 : Node *expr;
2949 : bool found_whole_row;
2950 :
2951 : /* ignore if the constraint is non-inheritable */
2952 525 : if (check[i].ccnoinherit)
2953 32 : continue;
2954 :
2955 : /* Adjust Vars to match new table's column numbering */
2956 493 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2957 : 1, 0,
2958 : newattmap,
2959 : InvalidOid, &found_whole_row);
2960 :
2961 : /*
2962 : * For the moment we have to reject whole-row variables. We
2963 : * could convert them, if we knew the new table's rowtype OID,
2964 : * but that hasn't been assigned yet.
2965 : */
2966 493 : if (found_whole_row)
2967 0 : ereport(ERROR,
2968 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2969 : errmsg("cannot convert whole-row table reference"),
2970 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2971 : name,
2972 : RelationGetRelationName(relation))));
2973 :
2974 493 : constraints = MergeCheckConstraint(constraints, name, expr,
2975 493 : check[i].ccenforced);
2976 : }
2977 : }
2978 :
2979 : /*
2980 : * Also copy the not-null constraints from this parent. The
2981 : * attnotnull markings were already installed above.
2982 : */
2983 17682 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2984 : {
2985 : Assert(nn->contype == CONSTR_NOTNULL);
2986 :
2987 1722 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2988 :
2989 1722 : nnconstraints = lappend(nnconstraints, nn);
2990 : }
2991 :
2992 7980 : free_attrmap(newattmap);
2993 :
2994 : /*
2995 : * Close the parent rel, but keep our lock on it until xact commit.
2996 : * That will prevent someone else from deleting or ALTERing the parent
2997 : * before the child is committed.
2998 : */
2999 7980 : table_close(relation, NoLock);
3000 : }
3001 :
3002 : /*
3003 : * If we had no inherited attributes, the result columns are just the
3004 : * explicitly declared columns. Otherwise, we need to merge the declared
3005 : * columns into the inherited column list. Although, we never have any
3006 : * explicitly declared columns if the table is a partition.
3007 : */
3008 41420 : if (inh_columns != NIL)
3009 : {
3010 7690 : int newcol_attno = 0;
3011 :
3012 8338 : foreach(lc, columns)
3013 : {
3014 700 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
3015 700 : char *attributeName = newdef->colname;
3016 : int exist_attno;
3017 :
3018 : /*
3019 : * Partitions have only one parent and have no column definitions
3020 : * of their own, so conflict should never occur.
3021 : */
3022 : Assert(!is_partition);
3023 :
3024 700 : newcol_attno++;
3025 :
3026 : /*
3027 : * Does it match some inherited column?
3028 : */
3029 700 : exist_attno = findAttrByName(attributeName, inh_columns);
3030 700 : if (exist_attno > 0)
3031 : {
3032 : /*
3033 : * Yes, try to merge the two column definitions.
3034 : */
3035 251 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
3036 : }
3037 : else
3038 : {
3039 : /*
3040 : * No, attach new column unchanged to result columns.
3041 : */
3042 449 : inh_columns = lappend(inh_columns, newdef);
3043 : }
3044 : }
3045 :
3046 7638 : columns = inh_columns;
3047 :
3048 : /*
3049 : * Check that we haven't exceeded the legal # of columns after merging
3050 : * in inherited columns.
3051 : */
3052 7638 : if (list_length(columns) > MaxHeapAttributeNumber)
3053 0 : ereport(ERROR,
3054 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3055 : errmsg("tables can have at most %d columns",
3056 : MaxHeapAttributeNumber)));
3057 : }
3058 :
3059 : /*
3060 : * Now that we have the column definition list for a partition, we can
3061 : * check whether the columns referenced in the column constraint specs
3062 : * actually exist. Also, merge column defaults.
3063 : */
3064 41368 : if (is_partition)
3065 : {
3066 6514 : foreach(lc, saved_columns)
3067 : {
3068 151 : ColumnDef *restdef = lfirst(lc);
3069 151 : bool found = false;
3070 : ListCell *l;
3071 :
3072 549 : foreach(l, columns)
3073 : {
3074 422 : ColumnDef *coldef = lfirst(l);
3075 :
3076 422 : if (strcmp(coldef->colname, restdef->colname) == 0)
3077 : {
3078 151 : found = true;
3079 :
3080 : /*
3081 : * Check for conflicts related to generated columns.
3082 : *
3083 : * Same rules as above: generated-ness has to match the
3084 : * parent, but the contents of the generation expression
3085 : * can be different.
3086 : */
3087 151 : if (coldef->generated)
3088 : {
3089 80 : if (restdef->raw_default && !restdef->generated)
3090 8 : ereport(ERROR,
3091 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3092 : errmsg("column \"%s\" inherits from generated column but specifies default",
3093 : restdef->colname)));
3094 72 : if (restdef->identity)
3095 0 : ereport(ERROR,
3096 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3097 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3098 : restdef->colname)));
3099 : }
3100 : else
3101 : {
3102 71 : if (restdef->generated)
3103 8 : ereport(ERROR,
3104 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3105 : errmsg("child column \"%s\" specifies generation expression",
3106 : restdef->colname),
3107 : errhint("A child table column cannot be generated unless its parent column is.")));
3108 : }
3109 :
3110 135 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3111 8 : ereport(ERROR,
3112 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3113 : errmsg("column \"%s\" inherits from generated column of different kind",
3114 : restdef->colname),
3115 : errdetail("Parent column is %s, child column is %s.",
3116 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3117 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3118 :
3119 : /*
3120 : * Override the parent's default value for this column
3121 : * (coldef->cooked_default) with the partition's local
3122 : * definition (restdef->raw_default), if there's one. It
3123 : * should be physically impossible to get a cooked default
3124 : * in the local definition or a raw default in the
3125 : * inherited definition, but make sure they're nulls, for
3126 : * future-proofing.
3127 : */
3128 : Assert(restdef->cooked_default == NULL);
3129 : Assert(coldef->raw_default == NULL);
3130 127 : if (restdef->raw_default)
3131 : {
3132 79 : coldef->raw_default = restdef->raw_default;
3133 79 : coldef->cooked_default = NULL;
3134 : }
3135 : }
3136 : }
3137 :
3138 : /* complain for constraints on columns not in parent */
3139 127 : if (!found)
3140 0 : ereport(ERROR,
3141 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3142 : errmsg("column \"%s\" does not exist",
3143 : restdef->colname)));
3144 : }
3145 : }
3146 :
3147 : /*
3148 : * If we found any conflicting parent default values, check to make sure
3149 : * they were overridden by the child.
3150 : */
3151 41344 : if (have_bogus_defaults)
3152 : {
3153 60 : foreach(lc, columns)
3154 : {
3155 48 : ColumnDef *def = lfirst(lc);
3156 :
3157 48 : if (def->cooked_default == &bogus_marker)
3158 : {
3159 12 : if (def->generated)
3160 8 : ereport(ERROR,
3161 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3162 : errmsg("column \"%s\" inherits conflicting generation expressions",
3163 : def->colname),
3164 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3165 : else
3166 4 : ereport(ERROR,
3167 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3168 : errmsg("column \"%s\" inherits conflicting default values",
3169 : def->colname),
3170 : errhint("To resolve the conflict, specify a default explicitly.")));
3171 : }
3172 : }
3173 : }
3174 :
3175 41332 : *supconstr = constraints;
3176 41332 : *supnotnulls = nnconstraints;
3177 :
3178 41332 : return columns;
3179 : }
3180 :
3181 :
3182 : /*
3183 : * MergeCheckConstraint
3184 : * Try to merge an inherited CHECK constraint with previous ones
3185 : *
3186 : * If we inherit identically-named constraints from multiple parents, we must
3187 : * merge them, or throw an error if they don't have identical definitions.
3188 : *
3189 : * constraints is a list of CookedConstraint structs for previous constraints.
3190 : *
3191 : * If the new constraint matches an existing one, then the existing
3192 : * constraint's inheritance count is updated. If there is a conflict (same
3193 : * name but different expression), throw an error. If the constraint neither
3194 : * matches nor conflicts with an existing one, a new constraint is appended to
3195 : * the list.
3196 : */
3197 : static List *
3198 493 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3199 : {
3200 : ListCell *lc;
3201 : CookedConstraint *newcon;
3202 :
3203 1521 : foreach(lc, constraints)
3204 : {
3205 1128 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3206 :
3207 : Assert(ccon->contype == CONSTR_CHECK);
3208 :
3209 : /* Non-matching names never conflict */
3210 1128 : if (strcmp(ccon->name, name) != 0)
3211 1028 : continue;
3212 :
3213 100 : if (equal(expr, ccon->expr))
3214 : {
3215 : /* OK to merge constraint with existing */
3216 100 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3217 : &ccon->inhcount))
3218 0 : ereport(ERROR,
3219 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3220 : errmsg("too many inheritance parents"));
3221 :
3222 : /*
3223 : * When enforceability differs, the merged constraint should be
3224 : * marked as ENFORCED because one of the parents is ENFORCED.
3225 : */
3226 100 : if (!ccon->is_enforced && is_enforced)
3227 : {
3228 32 : ccon->is_enforced = true;
3229 32 : ccon->skip_validation = false;
3230 : }
3231 :
3232 100 : return constraints;
3233 : }
3234 :
3235 0 : ereport(ERROR,
3236 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3237 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3238 : name)));
3239 : }
3240 :
3241 : /*
3242 : * Constraint couldn't be merged with an existing one and also didn't
3243 : * conflict with an existing one, so add it as a new one to the list.
3244 : */
3245 393 : newcon = palloc0_object(CookedConstraint);
3246 393 : newcon->contype = CONSTR_CHECK;
3247 393 : newcon->name = pstrdup(name);
3248 393 : newcon->expr = expr;
3249 393 : newcon->inhcount = 1;
3250 393 : newcon->is_enforced = is_enforced;
3251 393 : newcon->skip_validation = !is_enforced;
3252 393 : return lappend(constraints, newcon);
3253 : }
3254 :
3255 : /*
3256 : * MergeChildAttribute
3257 : * Merge given child attribute definition into given inherited attribute.
3258 : *
3259 : * Input arguments:
3260 : * 'inh_columns' is the list of inherited ColumnDefs.
3261 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3262 : * 'newcol_attno' is the attribute number in child table's schema definition
3263 : * 'newdef' is the column/attribute definition from the child table.
3264 : *
3265 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3266 : * ColumnDef remains unchanged.
3267 : *
3268 : * Notes:
3269 : * - The attribute is merged according to the rules laid out in the prologue
3270 : * of MergeAttributes().
3271 : * - If matching inherited attribute exists but the child attribute can not be
3272 : * merged into it, the function throws respective errors.
3273 : * - A partition can not have its own column definitions. Hence this function
3274 : * is applicable only to a regular inheritance child.
3275 : */
3276 : static void
3277 251 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3278 : {
3279 251 : char *attributeName = newdef->colname;
3280 : ColumnDef *inhdef;
3281 : Oid inhtypeid,
3282 : newtypeid;
3283 : int32 inhtypmod,
3284 : newtypmod;
3285 : Oid inhcollid,
3286 : newcollid;
3287 :
3288 251 : if (exist_attno == newcol_attno)
3289 229 : ereport(NOTICE,
3290 : (errmsg("merging column \"%s\" with inherited definition",
3291 : attributeName)));
3292 : else
3293 22 : ereport(NOTICE,
3294 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3295 : errdetail("User-specified column moved to the position of the inherited column.")));
3296 :
3297 251 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3298 :
3299 : /*
3300 : * Must have the same type and typmod
3301 : */
3302 251 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3303 251 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3304 251 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3305 8 : ereport(ERROR,
3306 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3307 : errmsg("column \"%s\" has a type conflict",
3308 : attributeName),
3309 : errdetail("%s versus %s",
3310 : format_type_with_typemod(inhtypeid, inhtypmod),
3311 : format_type_with_typemod(newtypeid, newtypmod))));
3312 :
3313 : /*
3314 : * Must have the same collation
3315 : */
3316 243 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3317 243 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3318 243 : if (inhcollid != newcollid)
3319 4 : ereport(ERROR,
3320 : (errcode(ERRCODE_COLLATION_MISMATCH),
3321 : errmsg("column \"%s\" has a collation conflict",
3322 : attributeName),
3323 : errdetail("\"%s\" versus \"%s\"",
3324 : get_collation_name(inhcollid),
3325 : get_collation_name(newcollid))));
3326 :
3327 : /*
3328 : * Identity is never inherited by a regular inheritance child. Pick
3329 : * child's identity definition if there's one.
3330 : */
3331 239 : inhdef->identity = newdef->identity;
3332 :
3333 : /*
3334 : * Copy storage parameter
3335 : */
3336 239 : if (inhdef->storage == 0)
3337 0 : inhdef->storage = newdef->storage;
3338 239 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3339 4 : ereport(ERROR,
3340 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3341 : errmsg("column \"%s\" has a storage parameter conflict",
3342 : attributeName),
3343 : errdetail("%s versus %s",
3344 : storage_name(inhdef->storage),
3345 : storage_name(newdef->storage))));
3346 :
3347 : /*
3348 : * Copy compression parameter
3349 : */
3350 235 : if (inhdef->compression == NULL)
3351 231 : inhdef->compression = newdef->compression;
3352 4 : else if (newdef->compression != NULL)
3353 : {
3354 4 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3355 4 : ereport(ERROR,
3356 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3357 : errmsg("column \"%s\" has a compression method conflict",
3358 : attributeName),
3359 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3360 : }
3361 :
3362 : /*
3363 : * Merge of not-null constraints = OR 'em together
3364 : */
3365 231 : inhdef->is_not_null |= newdef->is_not_null;
3366 :
3367 : /*
3368 : * Check for conflicts related to generated columns.
3369 : *
3370 : * If the parent column is generated, the child column will be made a
3371 : * generated column if it isn't already. If it is a generated column,
3372 : * we'll take its generation expression in preference to the parent's. We
3373 : * must check that the child column doesn't specify a default value or
3374 : * identity, which matches the rules for a single column in
3375 : * parse_utilcmd.c.
3376 : *
3377 : * Conversely, if the parent column is not generated, the child column
3378 : * can't be either. (We used to allow that, but it results in being able
3379 : * to override the generation expression via UPDATEs through the parent.)
3380 : */
3381 231 : if (inhdef->generated)
3382 : {
3383 41 : if (newdef->raw_default && !newdef->generated)
3384 8 : ereport(ERROR,
3385 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3386 : errmsg("column \"%s\" inherits from generated column but specifies default",
3387 : inhdef->colname)));
3388 33 : if (newdef->identity)
3389 8 : ereport(ERROR,
3390 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3391 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3392 : inhdef->colname)));
3393 : }
3394 : else
3395 : {
3396 190 : if (newdef->generated)
3397 8 : ereport(ERROR,
3398 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3399 : errmsg("child column \"%s\" specifies generation expression",
3400 : inhdef->colname),
3401 : errhint("A child table column cannot be generated unless its parent column is.")));
3402 : }
3403 :
3404 207 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3405 8 : ereport(ERROR,
3406 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3407 : errmsg("column \"%s\" inherits from generated column of different kind",
3408 : inhdef->colname),
3409 : errdetail("Parent column is %s, child column is %s.",
3410 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3411 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3412 :
3413 : /*
3414 : * If new def has a default, override previous default
3415 : */
3416 199 : if (newdef->raw_default != NULL)
3417 : {
3418 20 : inhdef->raw_default = newdef->raw_default;
3419 20 : inhdef->cooked_default = newdef->cooked_default;
3420 : }
3421 :
3422 : /* Mark the column as locally defined */
3423 199 : inhdef->is_local = true;
3424 199 : }
3425 :
3426 : /*
3427 : * MergeInheritedAttribute
3428 : * Merge given parent attribute definition into specified attribute
3429 : * inherited from the previous parents.
3430 : *
3431 : * Input arguments:
3432 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3433 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3434 : * 'newdef' is the new parent column/attribute definition to be merged.
3435 : *
3436 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3437 : *
3438 : * Notes:
3439 : * - The attribute is merged according to the rules laid out in the prologue
3440 : * of MergeAttributes().
3441 : * - If matching inherited attribute exists but the new attribute can not be
3442 : * merged into it, the function throws respective errors.
3443 : * - A partition inherits from only a single parent. Hence this function is
3444 : * applicable only to a regular inheritance.
3445 : */
3446 : static ColumnDef *
3447 245 : MergeInheritedAttribute(List *inh_columns,
3448 : int exist_attno,
3449 : const ColumnDef *newdef)
3450 : {
3451 245 : char *attributeName = newdef->colname;
3452 : ColumnDef *prevdef;
3453 : Oid prevtypeid,
3454 : newtypeid;
3455 : int32 prevtypmod,
3456 : newtypmod;
3457 : Oid prevcollid,
3458 : newcollid;
3459 :
3460 245 : ereport(NOTICE,
3461 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3462 : attributeName)));
3463 245 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3464 :
3465 : /*
3466 : * Must have the same type and typmod
3467 : */
3468 245 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3469 245 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3470 245 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3471 0 : ereport(ERROR,
3472 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3473 : errmsg("inherited column \"%s\" has a type conflict",
3474 : attributeName),
3475 : errdetail("%s versus %s",
3476 : format_type_with_typemod(prevtypeid, prevtypmod),
3477 : format_type_with_typemod(newtypeid, newtypmod))));
3478 :
3479 : /*
3480 : * Must have the same collation
3481 : */
3482 245 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3483 245 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3484 245 : if (prevcollid != newcollid)
3485 0 : ereport(ERROR,
3486 : (errcode(ERRCODE_COLLATION_MISMATCH),
3487 : errmsg("inherited column \"%s\" has a collation conflict",
3488 : attributeName),
3489 : errdetail("\"%s\" versus \"%s\"",
3490 : get_collation_name(prevcollid),
3491 : get_collation_name(newcollid))));
3492 :
3493 : /*
3494 : * Copy/check storage parameter
3495 : */
3496 245 : if (prevdef->storage == 0)
3497 0 : prevdef->storage = newdef->storage;
3498 245 : else if (prevdef->storage != newdef->storage)
3499 4 : ereport(ERROR,
3500 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3501 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3502 : attributeName),
3503 : errdetail("%s versus %s",
3504 : storage_name(prevdef->storage),
3505 : storage_name(newdef->storage))));
3506 :
3507 : /*
3508 : * Copy/check compression parameter
3509 : */
3510 241 : if (prevdef->compression == NULL)
3511 229 : prevdef->compression = newdef->compression;
3512 12 : else if (newdef->compression != NULL)
3513 : {
3514 4 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3515 4 : ereport(ERROR,
3516 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3517 : errmsg("column \"%s\" has a compression method conflict",
3518 : attributeName),
3519 : errdetail("%s versus %s",
3520 : prevdef->compression, newdef->compression)));
3521 : }
3522 :
3523 : /*
3524 : * Check for GENERATED conflicts
3525 : */
3526 237 : if (prevdef->generated != newdef->generated)
3527 16 : ereport(ERROR,
3528 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3529 : errmsg("inherited column \"%s\" has a generation conflict",
3530 : attributeName)));
3531 :
3532 : /*
3533 : * Default and other constraints are handled by the caller.
3534 : */
3535 :
3536 221 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3537 : &prevdef->inhcount))
3538 0 : ereport(ERROR,
3539 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3540 : errmsg("too many inheritance parents"));
3541 :
3542 221 : return prevdef;
3543 : }
3544 :
3545 : /*
3546 : * StoreCatalogInheritance
3547 : * Updates the system catalogs with proper inheritance information.
3548 : *
3549 : * supers is a list of the OIDs of the new relation's direct ancestors.
3550 : */
3551 : static void
3552 40889 : StoreCatalogInheritance(Oid relationId, List *supers,
3553 : bool child_is_partition)
3554 : {
3555 : Relation relation;
3556 : int32 seqNumber;
3557 : ListCell *entry;
3558 :
3559 : /*
3560 : * sanity checks
3561 : */
3562 : Assert(OidIsValid(relationId));
3563 :
3564 40889 : if (supers == NIL)
3565 33491 : return;
3566 :
3567 : /*
3568 : * Store INHERITS information in pg_inherits using direct ancestors only.
3569 : * Also enter dependencies on the direct ancestors, and make sure they are
3570 : * marked with relhassubclass = true.
3571 : *
3572 : * (Once upon a time, both direct and indirect ancestors were found here
3573 : * and then entered into pg_ipl. Since that catalog doesn't exist
3574 : * anymore, there's no need to look for indirect ancestors.)
3575 : */
3576 7398 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3577 :
3578 7398 : seqNumber = 1;
3579 15018 : foreach(entry, supers)
3580 : {
3581 7620 : Oid parentOid = lfirst_oid(entry);
3582 :
3583 7620 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3584 : child_is_partition);
3585 7620 : seqNumber++;
3586 : }
3587 :
3588 7398 : table_close(relation, RowExclusiveLock);
3589 : }
3590 :
3591 : /*
3592 : * Make catalog entries showing relationId as being an inheritance child
3593 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3594 : */
3595 : static void
3596 9673 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3597 : int32 seqNumber, Relation inhRelation,
3598 : bool child_is_partition)
3599 : {
3600 : ObjectAddress childobject,
3601 : parentobject;
3602 :
3603 : /* store the pg_inherits row */
3604 9673 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3605 :
3606 : /*
3607 : * Store a dependency too
3608 : */
3609 9673 : parentobject.classId = RelationRelationId;
3610 9673 : parentobject.objectId = parentOid;
3611 9673 : parentobject.objectSubId = 0;
3612 9673 : childobject.classId = RelationRelationId;
3613 9673 : childobject.objectId = relationId;
3614 9673 : childobject.objectSubId = 0;
3615 :
3616 9673 : recordDependencyOn(&childobject, &parentobject,
3617 : child_dependency_type(child_is_partition));
3618 :
3619 : /*
3620 : * Post creation hook of this inheritance. Since object_access_hook
3621 : * doesn't take multiple object identifiers, we relay oid of parent
3622 : * relation using auxiliary_id argument.
3623 : */
3624 9673 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3625 : relationId, 0,
3626 : parentOid, false);
3627 :
3628 : /*
3629 : * Mark the parent as having subclasses.
3630 : */
3631 9673 : SetRelationHasSubclass(parentOid, true);
3632 9673 : }
3633 :
3634 : /*
3635 : * Look for an existing column entry with the given name.
3636 : *
3637 : * Returns the index (starting with 1) if attribute already exists in columns,
3638 : * 0 if it doesn't.
3639 : */
3640 : static int
3641 16571 : findAttrByName(const char *attributeName, const List *columns)
3642 : {
3643 : ListCell *lc;
3644 16571 : int i = 1;
3645 :
3646 30059 : foreach(lc, columns)
3647 : {
3648 13984 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3649 496 : return i;
3650 :
3651 13488 : i++;
3652 : }
3653 16075 : return 0;
3654 : }
3655 :
3656 :
3657 : /*
3658 : * SetRelationHasSubclass
3659 : * Set the value of the relation's relhassubclass field in pg_class.
3660 : *
3661 : * It's always safe to set this field to true, because all SQL commands are
3662 : * ready to see true and then find no children. On the other hand, commands
3663 : * generally assume zero children if this is false.
3664 : *
3665 : * Caller must hold any self-exclusive lock until end of transaction. If the
3666 : * new value is false, caller must have acquired that lock before reading the
3667 : * evidence that justified the false value. That way, it properly waits if
3668 : * another backend is simultaneously concluding no need to change the tuple
3669 : * (new and old values are true).
3670 : *
3671 : * NOTE: an important side-effect of this operation is that an SI invalidation
3672 : * message is sent out to all backends --- including me --- causing plans
3673 : * referencing the relation to be rebuilt with the new list of children.
3674 : * This must happen even if we find that no change is needed in the pg_class
3675 : * row.
3676 : */
3677 : void
3678 12120 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3679 : {
3680 : Relation relationRelation;
3681 : HeapTuple tuple;
3682 : Form_pg_class classtuple;
3683 :
3684 : Assert(CheckRelationOidLockedByMe(relationId,
3685 : ShareUpdateExclusiveLock, false) ||
3686 : CheckRelationOidLockedByMe(relationId,
3687 : ShareRowExclusiveLock, true));
3688 :
3689 : /*
3690 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3691 : */
3692 12120 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3693 12120 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3694 12120 : if (!HeapTupleIsValid(tuple))
3695 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3696 12120 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3697 :
3698 12120 : if (classtuple->relhassubclass != relhassubclass)
3699 : {
3700 5462 : classtuple->relhassubclass = relhassubclass;
3701 5462 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3702 : }
3703 : else
3704 : {
3705 : /* no need to change tuple, but force relcache rebuild anyway */
3706 6658 : CacheInvalidateRelcacheByTuple(tuple);
3707 : }
3708 :
3709 12120 : heap_freetuple(tuple);
3710 12120 : table_close(relationRelation, RowExclusiveLock);
3711 12120 : }
3712 :
3713 : /*
3714 : * CheckRelationTableSpaceMove
3715 : * Check if relation can be moved to new tablespace.
3716 : *
3717 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3718 : *
3719 : * Returns true if the relation can be moved to the new tablespace; raises
3720 : * an error if it is not possible to do the move; returns false if the move
3721 : * would have no effect.
3722 : */
3723 : bool
3724 142 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3725 : {
3726 : Oid oldTableSpaceId;
3727 :
3728 : /*
3729 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3730 : * stored as 0.
3731 : */
3732 142 : oldTableSpaceId = rel->rd_rel->reltablespace;
3733 142 : if (newTableSpaceId == oldTableSpaceId ||
3734 137 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3735 10 : return false;
3736 :
3737 : /*
3738 : * We cannot support moving mapped relations into different tablespaces.
3739 : * (In particular this eliminates all shared catalogs.)
3740 : */
3741 132 : if (RelationIsMapped(rel))
3742 0 : ereport(ERROR,
3743 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3744 : errmsg("cannot move system relation \"%s\"",
3745 : RelationGetRelationName(rel))));
3746 :
3747 : /* Cannot move a non-shared relation into pg_global */
3748 132 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3749 8 : ereport(ERROR,
3750 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3751 : errmsg("only shared relations can be placed in pg_global tablespace")));
3752 :
3753 : /*
3754 : * Do not allow moving temp tables of other backends ... their local
3755 : * buffer manager is not going to cope.
3756 : */
3757 124 : if (RELATION_IS_OTHER_TEMP(rel))
3758 0 : ereport(ERROR,
3759 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3760 : errmsg("cannot move temporary tables of other sessions")));
3761 :
3762 124 : return true;
3763 : }
3764 :
3765 : /*
3766 : * SetRelationTableSpace
3767 : * Set new reltablespace and relfilenumber in pg_class entry.
3768 : *
3769 : * newTableSpaceId is the new tablespace for the relation, and
3770 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3771 : * InvalidRelFileNumber, this field is not updated.
3772 : *
3773 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3774 : *
3775 : * The caller of this routine had better check if a relation can be
3776 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3777 : * first, and is responsible for making the change visible with
3778 : * CommandCounterIncrement().
3779 : */
3780 : void
3781 124 : SetRelationTableSpace(Relation rel,
3782 : Oid newTableSpaceId,
3783 : RelFileNumber newRelFilenumber)
3784 : {
3785 : Relation pg_class;
3786 : HeapTuple tuple;
3787 : ItemPointerData otid;
3788 : Form_pg_class rd_rel;
3789 124 : Oid reloid = RelationGetRelid(rel);
3790 :
3791 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3792 :
3793 : /* Get a modifiable copy of the relation's pg_class row. */
3794 124 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3795 :
3796 124 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3797 124 : if (!HeapTupleIsValid(tuple))
3798 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3799 124 : otid = tuple->t_self;
3800 124 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3801 :
3802 : /* Update the pg_class row. */
3803 248 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3804 124 : InvalidOid : newTableSpaceId;
3805 124 : if (RelFileNumberIsValid(newRelFilenumber))
3806 95 : rd_rel->relfilenode = newRelFilenumber;
3807 124 : CatalogTupleUpdate(pg_class, &otid, tuple);
3808 124 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3809 :
3810 : /*
3811 : * Record dependency on tablespace. This is only required for relations
3812 : * that have no physical storage.
3813 : */
3814 124 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3815 20 : changeDependencyOnTablespace(RelationRelationId, reloid,
3816 : rd_rel->reltablespace);
3817 :
3818 124 : heap_freetuple(tuple);
3819 124 : table_close(pg_class, RowExclusiveLock);
3820 124 : }
3821 :
3822 : /*
3823 : * renameatt_check - basic sanity checks before attribute rename
3824 : */
3825 : static void
3826 663 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3827 : {
3828 663 : char relkind = classform->relkind;
3829 :
3830 663 : if (classform->reloftype && !recursing)
3831 4 : ereport(ERROR,
3832 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3833 : errmsg("cannot rename column of typed table")));
3834 :
3835 : /*
3836 : * Renaming the columns of sequences or toast tables doesn't actually
3837 : * break anything from the system's point of view, since internal
3838 : * references are by attnum. But it doesn't seem right to allow users to
3839 : * change names that are hardcoded into the system, hence the following
3840 : * restriction.
3841 : */
3842 659 : if (relkind != RELKIND_RELATION &&
3843 56 : relkind != RELKIND_VIEW &&
3844 56 : relkind != RELKIND_MATVIEW &&
3845 24 : relkind != RELKIND_COMPOSITE_TYPE &&
3846 24 : relkind != RELKIND_INDEX &&
3847 24 : relkind != RELKIND_PARTITIONED_INDEX &&
3848 0 : relkind != RELKIND_FOREIGN_TABLE &&
3849 : relkind != RELKIND_PARTITIONED_TABLE)
3850 0 : ereport(ERROR,
3851 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3852 : errmsg("cannot rename columns of relation \"%s\"",
3853 : NameStr(classform->relname)),
3854 : errdetail_relkind_not_supported(relkind)));
3855 :
3856 : /*
3857 : * permissions checking. only the owner of a class can change its schema.
3858 : */
3859 659 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3860 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3861 0 : NameStr(classform->relname));
3862 659 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3863 1 : ereport(ERROR,
3864 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3865 : errmsg("permission denied: \"%s\" is a system catalog",
3866 : NameStr(classform->relname))));
3867 658 : }
3868 :
3869 : /*
3870 : * renameatt_internal - workhorse for renameatt
3871 : *
3872 : * Return value is the attribute number in the 'myrelid' relation.
3873 : */
3874 : static AttrNumber
3875 366 : renameatt_internal(Oid myrelid,
3876 : const char *oldattname,
3877 : const char *newattname,
3878 : bool recurse,
3879 : bool recursing,
3880 : int expected_parents,
3881 : DropBehavior behavior)
3882 : {
3883 : Relation targetrelation;
3884 : Relation attrelation;
3885 : HeapTuple atttup;
3886 : Form_pg_attribute attform;
3887 : AttrNumber attnum;
3888 :
3889 : /*
3890 : * Grab an exclusive lock on the target table, which we will NOT release
3891 : * until end of transaction.
3892 : */
3893 366 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3894 366 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3895 :
3896 : /*
3897 : * if the 'recurse' flag is set then we are supposed to rename this
3898 : * attribute in all classes that inherit from 'relname' (as well as in
3899 : * 'relname').
3900 : *
3901 : * any permissions or problems with duplicate attributes will cause the
3902 : * whole transaction to abort, which is what we want -- all or nothing.
3903 : */
3904 366 : if (recurse)
3905 : {
3906 : List *child_oids,
3907 : *child_numparents;
3908 : ListCell *lo,
3909 : *li;
3910 :
3911 : /*
3912 : * we need the number of parents for each child so that the recursive
3913 : * calls to renameatt() can determine whether there are any parents
3914 : * outside the inheritance hierarchy being processed.
3915 : */
3916 164 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3917 : &child_numparents);
3918 :
3919 : /*
3920 : * find_all_inheritors does the recursive search of the inheritance
3921 : * hierarchy, so all we have to do is process all of the relids in the
3922 : * list that it returns.
3923 : */
3924 486 : forboth(lo, child_oids, li, child_numparents)
3925 : {
3926 342 : Oid childrelid = lfirst_oid(lo);
3927 342 : int numparents = lfirst_int(li);
3928 :
3929 342 : if (childrelid == myrelid)
3930 164 : continue;
3931 : /* note we need not recurse again */
3932 178 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3933 : }
3934 : }
3935 : else
3936 : {
3937 : /*
3938 : * If we are told not to recurse, there had better not be any child
3939 : * tables; else the rename would put them out of step.
3940 : *
3941 : * expected_parents will only be 0 if we are not already recursing.
3942 : */
3943 226 : if (expected_parents == 0 &&
3944 24 : find_inheritance_children(myrelid, NoLock) != NIL)
3945 8 : ereport(ERROR,
3946 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3947 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3948 : oldattname)));
3949 : }
3950 :
3951 : /* rename attributes in typed tables of composite type */
3952 338 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3953 : {
3954 : List *child_oids;
3955 : ListCell *lo;
3956 :
3957 16 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3958 16 : RelationGetRelationName(targetrelation),
3959 : behavior);
3960 :
3961 16 : foreach(lo, child_oids)
3962 4 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3963 : }
3964 :
3965 334 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3966 :
3967 334 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3968 334 : if (!HeapTupleIsValid(atttup))
3969 16 : ereport(ERROR,
3970 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3971 : errmsg("column \"%s\" does not exist",
3972 : oldattname)));
3973 318 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3974 :
3975 318 : attnum = attform->attnum;
3976 318 : if (attnum <= 0)
3977 0 : ereport(ERROR,
3978 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3979 : errmsg("cannot rename system column \"%s\"",
3980 : oldattname)));
3981 :
3982 : /*
3983 : * if the attribute is inherited, forbid the renaming. if this is a
3984 : * top-level call to renameatt(), then expected_parents will be 0, so the
3985 : * effect of this code will be to prohibit the renaming if the attribute
3986 : * is inherited at all. if this is a recursive call to renameatt(),
3987 : * expected_parents will be the number of parents the current relation has
3988 : * within the inheritance hierarchy being processed, so we'll prohibit the
3989 : * renaming only if there are additional parents from elsewhere.
3990 : */
3991 318 : if (attform->attinhcount > expected_parents)
3992 20 : ereport(ERROR,
3993 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3994 : errmsg("cannot rename inherited column \"%s\"",
3995 : oldattname)));
3996 :
3997 : /* new name should not already exist */
3998 298 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3999 :
4000 : /* apply the update */
4001 290 : namestrcpy(&(attform->attname), newattname);
4002 :
4003 290 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
4004 :
4005 290 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
4006 :
4007 290 : heap_freetuple(atttup);
4008 :
4009 290 : table_close(attrelation, RowExclusiveLock);
4010 :
4011 290 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4012 :
4013 290 : return attnum;
4014 : }
4015 :
4016 : /*
4017 : * Perform permissions and integrity checks before acquiring a relation lock.
4018 : */
4019 : static void
4020 265 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
4021 : void *arg)
4022 : {
4023 : HeapTuple tuple;
4024 : Form_pg_class form;
4025 :
4026 265 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
4027 265 : if (!HeapTupleIsValid(tuple))
4028 24 : return; /* concurrently dropped */
4029 241 : form = (Form_pg_class) GETSTRUCT(tuple);
4030 241 : renameatt_check(relid, form, false);
4031 236 : ReleaseSysCache(tuple);
4032 : }
4033 :
4034 : /*
4035 : * renameatt - changes the name of an attribute in a relation
4036 : *
4037 : * The returned ObjectAddress is that of the renamed column.
4038 : */
4039 : ObjectAddress
4040 209 : renameatt(RenameStmt *stmt)
4041 : {
4042 : Oid relid;
4043 : AttrNumber attnum;
4044 : ObjectAddress address;
4045 :
4046 : /* lock level taken here should match renameatt_internal */
4047 209 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4048 209 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4049 : RangeVarCallbackForRenameAttribute,
4050 : NULL);
4051 :
4052 200 : if (!OidIsValid(relid))
4053 : {
4054 16 : ereport(NOTICE,
4055 : (errmsg("relation \"%s\" does not exist, skipping",
4056 : stmt->relation->relname)));
4057 16 : return InvalidObjectAddress;
4058 : }
4059 :
4060 : attnum =
4061 184 : renameatt_internal(relid,
4062 184 : stmt->subname, /* old att name */
4063 184 : stmt->newname, /* new att name */
4064 184 : stmt->relation->inh, /* recursive? */
4065 : false, /* recursing? */
4066 : 0, /* expected inhcount */
4067 : stmt->behavior);
4068 :
4069 128 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4070 :
4071 128 : return address;
4072 : }
4073 :
4074 : /*
4075 : * same logic as renameatt_internal
4076 : */
4077 : static ObjectAddress
4078 60 : rename_constraint_internal(Oid myrelid,
4079 : Oid mytypid,
4080 : const char *oldconname,
4081 : const char *newconname,
4082 : bool recurse,
4083 : bool recursing,
4084 : int expected_parents)
4085 : {
4086 60 : Relation targetrelation = NULL;
4087 : Oid constraintOid;
4088 : HeapTuple tuple;
4089 : Form_pg_constraint con;
4090 : ObjectAddress address;
4091 :
4092 : Assert(!myrelid || !mytypid);
4093 :
4094 60 : if (mytypid)
4095 : {
4096 4 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4097 : }
4098 : else
4099 : {
4100 56 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4101 :
4102 : /*
4103 : * don't tell it whether we're recursing; we allow changing typed
4104 : * tables here
4105 : */
4106 56 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4107 :
4108 56 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4109 : }
4110 :
4111 60 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4112 60 : if (!HeapTupleIsValid(tuple))
4113 0 : elog(ERROR, "cache lookup failed for constraint %u",
4114 : constraintOid);
4115 60 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4116 :
4117 60 : if (myrelid &&
4118 56 : (con->contype == CONSTRAINT_CHECK ||
4119 16 : con->contype == CONSTRAINT_NOTNULL) &&
4120 44 : !con->connoinherit)
4121 : {
4122 36 : if (recurse)
4123 : {
4124 : List *child_oids,
4125 : *child_numparents;
4126 : ListCell *lo,
4127 : *li;
4128 :
4129 24 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4130 : &child_numparents);
4131 :
4132 56 : forboth(lo, child_oids, li, child_numparents)
4133 : {
4134 32 : Oid childrelid = lfirst_oid(lo);
4135 32 : int numparents = lfirst_int(li);
4136 :
4137 32 : if (childrelid == myrelid)
4138 24 : continue;
4139 :
4140 8 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4141 : }
4142 : }
4143 : else
4144 : {
4145 16 : if (expected_parents == 0 &&
4146 4 : find_inheritance_children(myrelid, NoLock) != NIL)
4147 4 : ereport(ERROR,
4148 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4149 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4150 : oldconname)));
4151 : }
4152 :
4153 32 : if (con->coninhcount > expected_parents)
4154 4 : ereport(ERROR,
4155 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4156 : errmsg("cannot rename inherited constraint \"%s\"",
4157 : oldconname)));
4158 : }
4159 :
4160 52 : if (con->conindid
4161 12 : && (con->contype == CONSTRAINT_PRIMARY
4162 4 : || con->contype == CONSTRAINT_UNIQUE
4163 0 : || con->contype == CONSTRAINT_EXCLUSION))
4164 : /* rename the index; this renames the constraint as well */
4165 12 : RenameRelationInternal(con->conindid, newconname, false, true);
4166 : else
4167 40 : RenameConstraintById(constraintOid, newconname);
4168 :
4169 52 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4170 :
4171 52 : ReleaseSysCache(tuple);
4172 :
4173 52 : if (targetrelation)
4174 : {
4175 : /*
4176 : * Invalidate relcache so as others can see the new constraint name.
4177 : */
4178 48 : CacheInvalidateRelcache(targetrelation);
4179 :
4180 48 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4181 : }
4182 :
4183 52 : return address;
4184 : }
4185 :
4186 : ObjectAddress
4187 56 : RenameConstraint(RenameStmt *stmt)
4188 : {
4189 56 : Oid relid = InvalidOid;
4190 56 : Oid typid = InvalidOid;
4191 :
4192 56 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4193 : {
4194 : Relation rel;
4195 : HeapTuple tup;
4196 :
4197 4 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4198 4 : rel = table_open(TypeRelationId, RowExclusiveLock);
4199 4 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4200 4 : if (!HeapTupleIsValid(tup))
4201 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4202 4 : checkDomainOwner(tup);
4203 4 : ReleaseSysCache(tup);
4204 4 : table_close(rel, NoLock);
4205 : }
4206 : else
4207 : {
4208 : /* lock level taken here should match rename_constraint_internal */
4209 52 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4210 52 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4211 : RangeVarCallbackForRenameAttribute,
4212 : NULL);
4213 52 : if (!OidIsValid(relid))
4214 : {
4215 4 : ereport(NOTICE,
4216 : (errmsg("relation \"%s\" does not exist, skipping",
4217 : stmt->relation->relname)));
4218 4 : return InvalidObjectAddress;
4219 : }
4220 : }
4221 :
4222 : return
4223 52 : rename_constraint_internal(relid, typid,
4224 52 : stmt->subname,
4225 52 : stmt->newname,
4226 100 : (stmt->relation &&
4227 48 : stmt->relation->inh), /* recursive? */
4228 : false, /* recursing? */
4229 52 : 0 /* expected inhcount */ );
4230 : }
4231 :
4232 : /*
4233 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE/PROPERTY GRAPH
4234 : * RENAME
4235 : */
4236 : ObjectAddress
4237 327 : RenameRelation(RenameStmt *stmt)
4238 : {
4239 327 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4240 : Oid relid;
4241 : ObjectAddress address;
4242 :
4243 : /*
4244 : * Grab an exclusive lock on the target table, index, sequence, view,
4245 : * materialized view, or foreign table, which we will NOT release until
4246 : * end of transaction.
4247 : *
4248 : * Lock level used here should match RenameRelationInternal, to avoid lock
4249 : * escalation. However, because ALTER INDEX can be used with any relation
4250 : * type, we mustn't believe without verification.
4251 : */
4252 : for (;;)
4253 8 : {
4254 : LOCKMODE lockmode;
4255 : char relkind;
4256 : bool obj_is_index;
4257 :
4258 335 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4259 :
4260 335 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4261 335 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4262 : RangeVarCallbackForAlterRelation,
4263 : stmt);
4264 :
4265 298 : if (!OidIsValid(relid))
4266 : {
4267 12 : ereport(NOTICE,
4268 : (errmsg("relation \"%s\" does not exist, skipping",
4269 : stmt->relation->relname)));
4270 12 : return InvalidObjectAddress;
4271 : }
4272 :
4273 : /*
4274 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4275 : * to rename a table), but we might've used the wrong lock level. If
4276 : * that happens, retry with the correct lock level. We don't bother
4277 : * if we already acquired AccessExclusiveLock with an index, however.
4278 : */
4279 286 : relkind = get_rel_relkind(relid);
4280 286 : obj_is_index = (relkind == RELKIND_INDEX ||
4281 : relkind == RELKIND_PARTITIONED_INDEX);
4282 286 : if (obj_is_index || is_index_stmt == obj_is_index)
4283 : break;
4284 :
4285 8 : UnlockRelationOid(relid, lockmode);
4286 8 : is_index_stmt = obj_is_index;
4287 : }
4288 :
4289 : /* Do the work */
4290 278 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4291 :
4292 266 : ObjectAddressSet(address, RelationRelationId, relid);
4293 :
4294 266 : return address;
4295 : }
4296 :
4297 : /*
4298 : * RenameRelationInternal - change the name of a relation
4299 : */
4300 : void
4301 1104 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4302 : {
4303 : Relation targetrelation;
4304 : Relation relrelation; /* for RELATION relation */
4305 : ItemPointerData otid;
4306 : HeapTuple reltup;
4307 : Form_pg_class relform;
4308 : Oid namespaceId;
4309 :
4310 : /*
4311 : * Grab a lock on the target relation, which we will NOT release until end
4312 : * of transaction. We need at least a self-exclusive lock so that
4313 : * concurrent DDL doesn't overwrite the rename if they start updating
4314 : * while still seeing the old version. The lock also guards against
4315 : * triggering relcache reloads in concurrent sessions, which might not
4316 : * handle this information changing under them. For indexes, we can use a
4317 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4318 : * specially.
4319 : */
4320 1104 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4321 1104 : namespaceId = RelationGetNamespace(targetrelation);
4322 :
4323 : /*
4324 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4325 : */
4326 1104 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4327 :
4328 1104 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4329 1104 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4330 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4331 1104 : otid = reltup->t_self;
4332 1104 : relform = (Form_pg_class) GETSTRUCT(reltup);
4333 :
4334 1104 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4335 12 : ereport(ERROR,
4336 : (errcode(ERRCODE_DUPLICATE_TABLE),
4337 : errmsg("relation \"%s\" already exists",
4338 : newrelname)));
4339 :
4340 : /*
4341 : * RenameRelation is careful not to believe the caller's idea of the
4342 : * relation kind being handled. We don't have to worry about this, but
4343 : * let's not be totally oblivious to it. We can process an index as
4344 : * not-an-index, but not the other way around.
4345 : */
4346 : Assert(!is_index ||
4347 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4348 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4349 :
4350 : /*
4351 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4352 : * because it's a copy...)
4353 : */
4354 1092 : namestrcpy(&(relform->relname), newrelname);
4355 :
4356 1092 : CatalogTupleUpdate(relrelation, &otid, reltup);
4357 1092 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4358 :
4359 1092 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4360 : InvalidOid, is_internal);
4361 :
4362 1092 : heap_freetuple(reltup);
4363 1092 : table_close(relrelation, RowExclusiveLock);
4364 :
4365 : /*
4366 : * Also rename the associated type, if any.
4367 : */
4368 1092 : if (OidIsValid(targetrelation->rd_rel->reltype))
4369 144 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4370 : newrelname, namespaceId);
4371 :
4372 : /*
4373 : * Also rename the associated constraint, if any.
4374 : */
4375 1092 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4376 604 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4377 : {
4378 500 : Oid constraintId = get_index_constraint(myrelid);
4379 :
4380 500 : if (OidIsValid(constraintId))
4381 24 : RenameConstraintById(constraintId, newrelname);
4382 : }
4383 :
4384 : /*
4385 : * Close rel, but keep lock!
4386 : */
4387 1092 : relation_close(targetrelation, NoLock);
4388 1092 : }
4389 :
4390 : /*
4391 : * ResetRelRewrite - reset relrewrite
4392 : */
4393 : void
4394 379 : ResetRelRewrite(Oid myrelid)
4395 : {
4396 : Relation relrelation; /* for RELATION relation */
4397 : HeapTuple reltup;
4398 : Form_pg_class relform;
4399 :
4400 : /*
4401 : * Find relation's pg_class tuple.
4402 : */
4403 379 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4404 :
4405 379 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4406 379 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4407 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4408 379 : relform = (Form_pg_class) GETSTRUCT(reltup);
4409 :
4410 : /*
4411 : * Update pg_class tuple.
4412 : */
4413 379 : relform->relrewrite = InvalidOid;
4414 :
4415 379 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4416 :
4417 379 : heap_freetuple(reltup);
4418 379 : table_close(relrelation, RowExclusiveLock);
4419 379 : }
4420 :
4421 : /*
4422 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4423 : * any open reference to the target table besides the one just acquired by
4424 : * the calling command; this implies there's an open cursor or active plan.
4425 : * We need this check because our lock doesn't protect us against stomping
4426 : * on our own foot, only other people's feet!
4427 : *
4428 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4429 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4430 : * possibly be relaxed to only error out for certain types of alterations.
4431 : * But the use-case for allowing any of these things is not obvious, so we
4432 : * won't work hard at it for now.
4433 : *
4434 : * We also reject these commands if there are any pending AFTER trigger events
4435 : * for the rel. This is certainly necessary for the rewriting variants of
4436 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4437 : * events would try to fetch the wrong tuples. It might be overly cautious
4438 : * in other cases, but again it seems better to err on the side of paranoia.
4439 : *
4440 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4441 : * we are worried about active indexscans on the index. The trigger-event
4442 : * check can be skipped, since we are doing no damage to the parent table.
4443 : *
4444 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4445 : */
4446 : void
4447 114319 : CheckTableNotInUse(Relation rel, const char *stmt)
4448 : {
4449 : int expected_refcnt;
4450 :
4451 114319 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4452 114319 : if (rel->rd_refcnt != expected_refcnt)
4453 28 : ereport(ERROR,
4454 : (errcode(ERRCODE_OBJECT_IN_USE),
4455 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4456 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4457 : stmt, RelationGetRelationName(rel))));
4458 :
4459 114291 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4460 187293 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4461 92925 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4462 12 : ereport(ERROR,
4463 : (errcode(ERRCODE_OBJECT_IN_USE),
4464 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4465 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4466 : stmt, RelationGetRelationName(rel))));
4467 114279 : }
4468 :
4469 : /*
4470 : * CheckAlterTableIsSafe
4471 : * Verify that it's safe to allow ALTER TABLE on this relation.
4472 : *
4473 : * This consists of CheckTableNotInUse() plus a check that the relation
4474 : * isn't another session's temp table. We must split out the temp-table
4475 : * check because there are callers of CheckTableNotInUse() that don't want
4476 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4477 : * an orphaned temp schema.) Compare truncate_check_activity().
4478 : */
4479 : static void
4480 40693 : CheckAlterTableIsSafe(Relation rel)
4481 : {
4482 : /*
4483 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4484 : * manager is not going to cope if we need to change the table's contents.
4485 : * Even if we don't, there may be optimizations that assume temp tables
4486 : * aren't subject to such interference.
4487 : */
4488 40693 : if (RELATION_IS_OTHER_TEMP(rel))
4489 0 : ereport(ERROR,
4490 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4491 : errmsg("cannot alter temporary tables of other sessions")));
4492 :
4493 : /*
4494 : * Also check for active uses of the relation in the current transaction,
4495 : * including open scans and pending AFTER trigger events.
4496 : */
4497 40693 : CheckTableNotInUse(rel, "ALTER TABLE");
4498 40669 : }
4499 :
4500 : /*
4501 : * AlterTableLookupRelation
4502 : * Look up, and lock, the OID for the relation named by an alter table
4503 : * statement.
4504 : */
4505 : Oid
4506 21588 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4507 : {
4508 43111 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4509 21588 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4510 : RangeVarCallbackForAlterRelation,
4511 : stmt);
4512 : }
4513 :
4514 : /*
4515 : * AlterTable
4516 : * Execute ALTER TABLE, which can be a list of subcommands
4517 : *
4518 : * ALTER TABLE is performed in three phases:
4519 : * 1. Examine subcommands and perform pre-transformation checking.
4520 : * 2. Validate and transform subcommands, and update system catalogs.
4521 : * 3. Scan table(s) to check new constraints, and optionally recopy
4522 : * the data into new table(s).
4523 : * Phase 3 is not performed unless one or more of the subcommands requires
4524 : * it. The intention of this design is to allow multiple independent
4525 : * updates of the table schema to be performed with only one pass over the
4526 : * data.
4527 : *
4528 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4529 : * each table to be affected (there may be multiple affected tables if the
4530 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4531 : * validation of the subcommands. Because earlier subcommands may change
4532 : * the catalog state seen by later commands, there are limits to what can
4533 : * be done in this phase. Generally, this phase acquires table locks,
4534 : * checks permissions and relkind, and recurses to find child tables.
4535 : *
4536 : * ATRewriteCatalogs performs phase 2 for each affected table.
4537 : * Certain subcommands need to be performed before others to avoid
4538 : * unnecessary conflicts; for example, DROP COLUMN should come before
4539 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4540 : * lists, one for each logical "pass" of phase 2.
4541 : *
4542 : * ATRewriteTables performs phase 3 for those tables that need it.
4543 : *
4544 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4545 : * since phase 1 already does it. However, for certain subcommand types
4546 : * it is only possible to determine how to recurse at phase 2 time; for
4547 : * those cases, phase 1 sets the cmd->recurse flag.
4548 : *
4549 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4550 : * the whole operation; we don't have to do anything special to clean up.
4551 : *
4552 : * The caller must lock the relation, with an appropriate lock level
4553 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4554 : * or higher. We pass the lock level down
4555 : * so that we can apply it recursively to inherited tables. Note that the
4556 : * lock level we want as we recurse might well be higher than required for
4557 : * that specific subcommand. So we pass down the overall lock requirement,
4558 : * rather than reassess it at lower levels.
4559 : *
4560 : * The caller also provides a "context" which is to be passed back to
4561 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4562 : * Some of the fields therein, such as the relid, are used here as well.
4563 : */
4564 : void
4565 21415 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4566 : AlterTableUtilityContext *context)
4567 : {
4568 : Relation rel;
4569 :
4570 : /* Caller is required to provide an adequate lock. */
4571 21415 : rel = relation_open(context->relid, NoLock);
4572 :
4573 21415 : CheckAlterTableIsSafe(rel);
4574 :
4575 21403 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4576 18532 : }
4577 :
4578 : /*
4579 : * AlterTableInternal
4580 : *
4581 : * ALTER TABLE with target specified by OID
4582 : *
4583 : * We do not reject if the relation is already open, because it's quite
4584 : * likely that one or more layers of caller have it open. That means it
4585 : * is unsafe to use this entry point for alterations that could break
4586 : * existing query plans. On the assumption it's not used for such, we
4587 : * don't have to reject pending AFTER triggers, either.
4588 : *
4589 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4590 : * used for any subcommand types that require parse transformation or
4591 : * could generate subcommands that have to be passed to ProcessUtility.
4592 : */
4593 : void
4594 184 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4595 : {
4596 : Relation rel;
4597 184 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4598 :
4599 184 : rel = relation_open(relid, lockmode);
4600 :
4601 184 : EventTriggerAlterTableRelid(relid);
4602 :
4603 184 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4604 184 : }
4605 :
4606 : /*
4607 : * AlterTableGetLockLevel
4608 : *
4609 : * Sets the overall lock level required for the supplied list of subcommands.
4610 : * Policy for doing this set according to needs of AlterTable(), see
4611 : * comments there for overall explanation.
4612 : *
4613 : * Function is called before and after parsing, so it must give same
4614 : * answer each time it is called. Some subcommands are transformed
4615 : * into other subcommand types, so the transform must never be made to a
4616 : * lower lock level than previously assigned. All transforms are noted below.
4617 : *
4618 : * Since this is called before we lock the table we cannot use table metadata
4619 : * to influence the type of lock we acquire.
4620 : *
4621 : * There should be no lockmodes hardcoded into the subcommand functions. All
4622 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4623 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4624 : * and does not travel through this section of code and cannot be combined with
4625 : * any of the subcommands given here.
4626 : *
4627 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4628 : * so any changes that might affect SELECTs running on standbys need to use
4629 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4630 : * have a solution for that also.
4631 : *
4632 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4633 : * that takes a lock less than AccessExclusiveLock can change object definitions
4634 : * while pg_dump is running. Be careful to check that the appropriate data is
4635 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4636 : * otherwise we might end up with an inconsistent dump that can't restore.
4637 : */
4638 : LOCKMODE
4639 21772 : AlterTableGetLockLevel(List *cmds)
4640 : {
4641 : /*
4642 : * This only works if we read catalog tables using MVCC snapshots.
4643 : */
4644 : ListCell *lcmd;
4645 21772 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4646 :
4647 44405 : foreach(lcmd, cmds)
4648 : {
4649 22633 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4650 22633 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4651 :
4652 22633 : switch (cmd->subtype)
4653 : {
4654 : /*
4655 : * These subcommands rewrite the heap, so require full locks.
4656 : */
4657 2624 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4658 : * to SELECT */
4659 : case AT_SetAccessMethod: /* must rewrite heap */
4660 : case AT_SetTableSpace: /* must rewrite heap */
4661 : case AT_AlterColumnType: /* must rewrite heap */
4662 2624 : cmd_lockmode = AccessExclusiveLock;
4663 2624 : break;
4664 :
4665 : /*
4666 : * These subcommands may require addition of toast tables. If
4667 : * we add a toast table to a table currently being scanned, we
4668 : * might miss data added to the new toast table by concurrent
4669 : * insert transactions.
4670 : */
4671 159 : case AT_SetStorage: /* may add toast tables, see
4672 : * ATRewriteCatalogs() */
4673 159 : cmd_lockmode = AccessExclusiveLock;
4674 159 : break;
4675 :
4676 : /*
4677 : * Removing constraints can affect SELECTs that have been
4678 : * optimized assuming the constraint holds true. See also
4679 : * CloneFkReferenced.
4680 : */
4681 783 : case AT_DropConstraint: /* as DROP INDEX */
4682 : case AT_DropNotNull: /* may change some SQL plans */
4683 783 : cmd_lockmode = AccessExclusiveLock;
4684 783 : break;
4685 :
4686 : /*
4687 : * Subcommands that may be visible to concurrent SELECTs
4688 : */
4689 1211 : case AT_DropColumn: /* change visible to SELECT */
4690 : case AT_AddColumnToView: /* CREATE VIEW */
4691 : case AT_DropOids: /* used to equiv to DropColumn */
4692 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4693 : case AT_EnableReplicaRule: /* may change SELECT rules */
4694 : case AT_EnableRule: /* may change SELECT rules */
4695 : case AT_DisableRule: /* may change SELECT rules */
4696 1211 : cmd_lockmode = AccessExclusiveLock;
4697 1211 : break;
4698 :
4699 : /*
4700 : * Changing owner may remove implicit SELECT privileges
4701 : */
4702 1155 : case AT_ChangeOwner: /* change visible to SELECT */
4703 1155 : cmd_lockmode = AccessExclusiveLock;
4704 1155 : break;
4705 :
4706 : /*
4707 : * Changing foreign table options may affect optimization.
4708 : */
4709 140 : case AT_GenericOptions:
4710 : case AT_AlterColumnGenericOptions:
4711 140 : cmd_lockmode = AccessExclusiveLock;
4712 140 : break;
4713 :
4714 : /*
4715 : * These subcommands affect write operations only.
4716 : */
4717 191 : case AT_EnableTrig:
4718 : case AT_EnableAlwaysTrig:
4719 : case AT_EnableReplicaTrig:
4720 : case AT_EnableTrigAll:
4721 : case AT_EnableTrigUser:
4722 : case AT_DisableTrig:
4723 : case AT_DisableTrigAll:
4724 : case AT_DisableTrigUser:
4725 191 : cmd_lockmode = ShareRowExclusiveLock;
4726 191 : break;
4727 :
4728 : /*
4729 : * These subcommands affect write operations only. XXX
4730 : * Theoretically, these could be ShareRowExclusiveLock.
4731 : */
4732 2046 : case AT_ColumnDefault:
4733 : case AT_CookedColumnDefault:
4734 : case AT_AlterConstraint:
4735 : case AT_AddIndex: /* from ADD CONSTRAINT */
4736 : case AT_AddIndexConstraint:
4737 : case AT_ReplicaIdentity:
4738 : case AT_SetNotNull:
4739 : case AT_EnableRowSecurity:
4740 : case AT_DisableRowSecurity:
4741 : case AT_ForceRowSecurity:
4742 : case AT_NoForceRowSecurity:
4743 : case AT_AddIdentity:
4744 : case AT_DropIdentity:
4745 : case AT_SetIdentity:
4746 : case AT_SetExpression:
4747 : case AT_DropExpression:
4748 : case AT_SetCompression:
4749 2046 : cmd_lockmode = AccessExclusiveLock;
4750 2046 : break;
4751 :
4752 10059 : case AT_AddConstraint:
4753 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4754 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4755 10059 : if (IsA(cmd->def, Constraint))
4756 : {
4757 10059 : Constraint *con = (Constraint *) cmd->def;
4758 :
4759 10059 : switch (con->contype)
4760 : {
4761 7455 : case CONSTR_EXCLUSION:
4762 : case CONSTR_PRIMARY:
4763 : case CONSTR_UNIQUE:
4764 :
4765 : /*
4766 : * Cases essentially the same as CREATE INDEX. We
4767 : * could reduce the lock strength to ShareLock if
4768 : * we can work out how to allow concurrent catalog
4769 : * updates. XXX Might be set down to
4770 : * ShareRowExclusiveLock but requires further
4771 : * analysis.
4772 : */
4773 7455 : cmd_lockmode = AccessExclusiveLock;
4774 7455 : break;
4775 1773 : case CONSTR_FOREIGN:
4776 :
4777 : /*
4778 : * We add triggers to both tables when we add a
4779 : * Foreign Key, so the lock level must be at least
4780 : * as strong as CREATE TRIGGER.
4781 : */
4782 1773 : cmd_lockmode = ShareRowExclusiveLock;
4783 1773 : break;
4784 :
4785 831 : default:
4786 831 : cmd_lockmode = AccessExclusiveLock;
4787 : }
4788 : }
4789 10059 : break;
4790 :
4791 : /*
4792 : * These subcommands affect inheritance behaviour. Queries
4793 : * started before us will continue to see the old inheritance
4794 : * behaviour, while queries started after we commit will see
4795 : * new behaviour. No need to prevent reads or writes to the
4796 : * subtable while we hook it up though. Changing the TupDesc
4797 : * may be a problem, so keep highest lock.
4798 : */
4799 386 : case AT_AddInherit:
4800 : case AT_DropInherit:
4801 386 : cmd_lockmode = AccessExclusiveLock;
4802 386 : break;
4803 :
4804 : /*
4805 : * These subcommands affect implicit row type conversion. They
4806 : * have affects similar to CREATE/DROP CAST on queries. don't
4807 : * provide for invalidating parse trees as a result of such
4808 : * changes, so we keep these at AccessExclusiveLock.
4809 : */
4810 46 : case AT_AddOf:
4811 : case AT_DropOf:
4812 46 : cmd_lockmode = AccessExclusiveLock;
4813 46 : break;
4814 :
4815 : /*
4816 : * Only used by CREATE OR REPLACE VIEW which must conflict
4817 : * with an SELECTs currently using the view.
4818 : */
4819 133 : case AT_ReplaceRelOptions:
4820 133 : cmd_lockmode = AccessExclusiveLock;
4821 133 : break;
4822 :
4823 : /*
4824 : * These subcommands affect general strategies for performance
4825 : * and maintenance, though don't change the semantic results
4826 : * from normal data reads and writes. Delaying an ALTER TABLE
4827 : * behind currently active writes only delays the point where
4828 : * the new strategy begins to take effect, so there is no
4829 : * benefit in waiting. In this case the minimum restriction
4830 : * applies: we don't currently allow concurrent catalog
4831 : * updates.
4832 : */
4833 154 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4834 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4835 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4836 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4837 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4838 154 : cmd_lockmode = ShareUpdateExclusiveLock;
4839 154 : break;
4840 :
4841 74 : case AT_SetLogged:
4842 : case AT_SetUnLogged:
4843 74 : cmd_lockmode = AccessExclusiveLock;
4844 74 : break;
4845 :
4846 275 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4847 275 : cmd_lockmode = ShareUpdateExclusiveLock;
4848 275 : break;
4849 :
4850 : /*
4851 : * Rel options are more complex than first appears. Options
4852 : * are set here for tables, views and indexes; for historical
4853 : * reasons these can all be used with ALTER TABLE, so we can't
4854 : * decide between them using the basic grammar.
4855 : */
4856 496 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4857 : * getTables() */
4858 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4859 : * getTables() */
4860 496 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4861 496 : break;
4862 :
4863 1873 : case AT_AttachPartition:
4864 1873 : cmd_lockmode = ShareUpdateExclusiveLock;
4865 1873 : break;
4866 :
4867 385 : case AT_DetachPartition:
4868 385 : if (((PartitionCmd *) cmd->def)->concurrent)
4869 87 : cmd_lockmode = ShareUpdateExclusiveLock;
4870 : else
4871 298 : cmd_lockmode = AccessExclusiveLock;
4872 385 : break;
4873 :
4874 11 : case AT_DetachPartitionFinalize:
4875 11 : cmd_lockmode = ShareUpdateExclusiveLock;
4876 11 : break;
4877 :
4878 432 : case AT_MergePartitions:
4879 : case AT_SplitPartition:
4880 432 : cmd_lockmode = AccessExclusiveLock;
4881 432 : break;
4882 :
4883 0 : default: /* oops */
4884 0 : elog(ERROR, "unrecognized alter table type: %d",
4885 : (int) cmd->subtype);
4886 : break;
4887 : }
4888 :
4889 : /*
4890 : * Take the greatest lockmode from any subcommand
4891 : */
4892 22633 : if (cmd_lockmode > lockmode)
4893 18994 : lockmode = cmd_lockmode;
4894 : }
4895 :
4896 21772 : return lockmode;
4897 : }
4898 :
4899 : /*
4900 : * ATController provides top level control over the phases.
4901 : *
4902 : * parsetree is passed in to allow it to be passed to event triggers
4903 : * when requested.
4904 : */
4905 : static void
4906 21587 : ATController(AlterTableStmt *parsetree,
4907 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4908 : AlterTableUtilityContext *context)
4909 : {
4910 21587 : List *wqueue = NIL;
4911 : ListCell *lcmd;
4912 :
4913 : /* Phase 1: preliminary examination of commands, create work queue */
4914 43734 : foreach(lcmd, cmds)
4915 : {
4916 22444 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4917 :
4918 22444 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4919 : }
4920 :
4921 : /* Close the relation, but keep lock until commit */
4922 21290 : relation_close(rel, NoLock);
4923 :
4924 : /* Phase 2: update system catalogs */
4925 21290 : ATRewriteCatalogs(&wqueue, lockmode, context);
4926 :
4927 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4928 19121 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4929 18716 : }
4930 :
4931 : /*
4932 : * ATPrepCmd
4933 : *
4934 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4935 : * recursion and permission checks.
4936 : *
4937 : * Caller must have acquired appropriate lock type on relation already.
4938 : * This lock should be held until commit.
4939 : */
4940 : static void
4941 23063 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4942 : bool recurse, bool recursing, LOCKMODE lockmode,
4943 : AlterTableUtilityContext *context)
4944 : {
4945 : AlteredTableInfo *tab;
4946 23063 : AlterTablePass pass = AT_PASS_UNSET;
4947 :
4948 : /* Find or create work queue entry for this table */
4949 23063 : tab = ATGetQueueEntry(wqueue, rel);
4950 :
4951 : /*
4952 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4953 : * partitions that are pending detach.
4954 : */
4955 23063 : if (rel->rd_rel->relispartition &&
4956 1794 : cmd->subtype != AT_DetachPartitionFinalize &&
4957 897 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4958 1 : ereport(ERROR,
4959 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4960 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4961 : RelationGetRelationName(rel)),
4962 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4963 :
4964 : /*
4965 : * Copy the original subcommand for each table, so we can scribble on it.
4966 : * This avoids conflicts when different child tables need to make
4967 : * different parse transformations (for example, the same column may have
4968 : * different column numbers in different children).
4969 : */
4970 23062 : cmd = copyObject(cmd);
4971 :
4972 : /*
4973 : * Do permissions and relkind checking, recursion to child tables if
4974 : * needed, and any additional phase-1 processing needed. (But beware of
4975 : * adding any processing that looks at table details that another
4976 : * subcommand could change. In some cases we reject multiple subcommands
4977 : * that could try to change the same state in contrary ways.)
4978 : */
4979 23062 : switch (cmd->subtype)
4980 : {
4981 1596 : case AT_AddColumn: /* ADD COLUMN */
4982 1596 : ATSimplePermissions(cmd->subtype, rel,
4983 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4984 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4985 1596 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4986 : lockmode, context);
4987 : /* Recursion occurs during execution phase */
4988 1588 : pass = AT_PASS_ADD_COL;
4989 1588 : break;
4990 21 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4991 21 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4992 21 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4993 : lockmode, context);
4994 : /* Recursion occurs during execution phase */
4995 21 : pass = AT_PASS_ADD_COL;
4996 21 : break;
4997 407 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4998 :
4999 : /*
5000 : * We allow defaults on views so that INSERT into a view can have
5001 : * default-ish behavior. This works because the rewriter
5002 : * substitutes default values into INSERTs before it expands
5003 : * rules.
5004 : */
5005 407 : ATSimplePermissions(cmd->subtype, rel,
5006 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5007 : ATT_FOREIGN_TABLE);
5008 407 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5009 : /* No command-specific prep needed */
5010 407 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
5011 407 : break;
5012 53 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5013 : /* This is currently used only in CREATE TABLE */
5014 : /* (so the permission check really isn't necessary) */
5015 53 : ATSimplePermissions(cmd->subtype, rel,
5016 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5017 : /* This command never recurses */
5018 53 : pass = AT_PASS_ADD_OTHERCONSTR;
5019 53 : break;
5020 107 : case AT_AddIdentity:
5021 107 : ATSimplePermissions(cmd->subtype, rel,
5022 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5023 : ATT_FOREIGN_TABLE);
5024 : /* Set up recursion for phase 2; no other prep needed */
5025 107 : if (recurse)
5026 103 : cmd->recurse = true;
5027 107 : pass = AT_PASS_ADD_OTHERCONSTR;
5028 107 : break;
5029 41 : case AT_SetIdentity:
5030 41 : ATSimplePermissions(cmd->subtype, rel,
5031 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5032 : ATT_FOREIGN_TABLE);
5033 : /* Set up recursion for phase 2; no other prep needed */
5034 41 : if (recurse)
5035 37 : cmd->recurse = true;
5036 : /* This should run after AddIdentity, so do it in MISC pass */
5037 41 : pass = AT_PASS_MISC;
5038 41 : break;
5039 37 : case AT_DropIdentity:
5040 37 : ATSimplePermissions(cmd->subtype, rel,
5041 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5042 : ATT_FOREIGN_TABLE);
5043 : /* Set up recursion for phase 2; no other prep needed */
5044 37 : if (recurse)
5045 33 : cmd->recurse = true;
5046 37 : pass = AT_PASS_DROP;
5047 37 : break;
5048 181 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5049 181 : ATSimplePermissions(cmd->subtype, rel,
5050 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5051 : /* Set up recursion for phase 2; no other prep needed */
5052 177 : if (recurse)
5053 165 : cmd->recurse = true;
5054 177 : pass = AT_PASS_DROP;
5055 177 : break;
5056 276 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5057 276 : ATSimplePermissions(cmd->subtype, rel,
5058 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5059 : /* Set up recursion for phase 2; no other prep needed */
5060 272 : if (recurse)
5061 252 : cmd->recurse = true;
5062 272 : pass = AT_PASS_COL_ATTRS;
5063 272 : break;
5064 169 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5065 169 : ATSimplePermissions(cmd->subtype, rel,
5066 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5067 169 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5068 169 : pass = AT_PASS_SET_EXPRESSION;
5069 169 : break;
5070 57 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5071 57 : ATSimplePermissions(cmd->subtype, rel,
5072 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5073 57 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5074 57 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5075 41 : pass = AT_PASS_DROP;
5076 41 : break;
5077 107 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5078 107 : ATSimplePermissions(cmd->subtype, rel,
5079 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5080 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5081 107 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5082 : /* No command-specific prep needed */
5083 107 : pass = AT_PASS_MISC;
5084 107 : break;
5085 29 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5086 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5087 29 : ATSimplePermissions(cmd->subtype, rel,
5088 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5089 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5090 : /* This command never recurses */
5091 21 : pass = AT_PASS_MISC;
5092 21 : break;
5093 173 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5094 173 : ATSimplePermissions(cmd->subtype, rel,
5095 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5096 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5097 173 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5098 : /* No command-specific prep needed */
5099 173 : pass = AT_PASS_MISC;
5100 173 : break;
5101 47 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5102 47 : ATSimplePermissions(cmd->subtype, rel,
5103 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5104 : /* This command never recurses */
5105 : /* No command-specific prep needed */
5106 47 : pass = AT_PASS_MISC;
5107 47 : break;
5108 1137 : case AT_DropColumn: /* DROP COLUMN */
5109 1137 : ATSimplePermissions(cmd->subtype, rel,
5110 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5111 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5112 1133 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5113 : lockmode, context);
5114 : /* Recursion occurs during execution phase */
5115 1125 : pass = AT_PASS_DROP;
5116 1125 : break;
5117 0 : case AT_AddIndex: /* ADD INDEX */
5118 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5119 : /* This command never recurses */
5120 : /* No command-specific prep needed */
5121 0 : pass = AT_PASS_ADD_INDEX;
5122 0 : break;
5123 10356 : case AT_AddConstraint: /* ADD CONSTRAINT */
5124 10356 : ATSimplePermissions(cmd->subtype, rel,
5125 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5126 10356 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5127 10336 : if (recurse)
5128 : {
5129 : /* recurses at exec time; lock descendants and set flag */
5130 10102 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5131 10102 : cmd->recurse = true;
5132 : }
5133 10336 : pass = AT_PASS_ADD_CONSTR;
5134 10336 : break;
5135 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5136 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5137 : /* This command never recurses */
5138 : /* No command-specific prep needed */
5139 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5140 0 : break;
5141 577 : case AT_DropConstraint: /* DROP CONSTRAINT */
5142 577 : ATSimplePermissions(cmd->subtype, rel,
5143 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5144 577 : ATCheckPartitionsNotInUse(rel, lockmode);
5145 : /* Other recursion occurs during execution phase */
5146 : /* No command-specific prep needed except saving recurse flag */
5147 573 : if (recurse)
5148 549 : cmd->recurse = true;
5149 573 : pass = AT_PASS_DROP;
5150 573 : break;
5151 951 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5152 951 : ATSimplePermissions(cmd->subtype, rel,
5153 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5154 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5155 : /* See comments for ATPrepAlterColumnType */
5156 951 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5157 : AT_PASS_UNSET, context);
5158 : Assert(cmd != NULL);
5159 : /* Performs own recursion */
5160 947 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5161 : lockmode, context);
5162 816 : pass = AT_PASS_ALTER_TYPE;
5163 816 : break;
5164 93 : case AT_AlterColumnGenericOptions:
5165 93 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5166 : /* This command never recurses */
5167 : /* No command-specific prep needed */
5168 93 : pass = AT_PASS_MISC;
5169 93 : break;
5170 1139 : case AT_ChangeOwner: /* ALTER OWNER */
5171 : /* This command never recurses */
5172 : /* No command-specific prep needed */
5173 1139 : pass = AT_PASS_MISC;
5174 1139 : break;
5175 43 : case AT_ClusterOn: /* CLUSTER ON */
5176 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5177 43 : ATSimplePermissions(cmd->subtype, rel,
5178 : ATT_TABLE | ATT_MATVIEW);
5179 : /* These commands never recurse */
5180 : /* No command-specific prep needed */
5181 35 : pass = AT_PASS_MISC;
5182 35 : break;
5183 74 : case AT_SetLogged: /* SET LOGGED */
5184 : case AT_SetUnLogged: /* SET UNLOGGED */
5185 74 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5186 66 : if (tab->chgPersistence)
5187 0 : ereport(ERROR,
5188 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5189 : errmsg("cannot change persistence setting twice")));
5190 66 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5191 58 : pass = AT_PASS_MISC;
5192 58 : break;
5193 4 : case AT_DropOids: /* SET WITHOUT OIDS */
5194 4 : ATSimplePermissions(cmd->subtype, rel,
5195 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5196 4 : pass = AT_PASS_DROP;
5197 4 : break;
5198 85 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5199 85 : ATSimplePermissions(cmd->subtype, rel,
5200 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5201 :
5202 : /* check if another access method change was already requested */
5203 85 : if (tab->chgAccessMethod)
5204 12 : ereport(ERROR,
5205 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5206 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5207 :
5208 73 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5209 73 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5210 73 : break;
5211 98 : case AT_SetTableSpace: /* SET TABLESPACE */
5212 98 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5213 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5214 : /* This command never recurses */
5215 98 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5216 98 : pass = AT_PASS_MISC; /* doesn't actually matter */
5217 98 : break;
5218 628 : case AT_SetRelOptions: /* SET (...) */
5219 : case AT_ResetRelOptions: /* RESET (...) */
5220 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5221 628 : ATSimplePermissions(cmd->subtype, rel,
5222 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5223 : ATT_MATVIEW | ATT_INDEX);
5224 : /* This command never recurses */
5225 : /* No command-specific prep needed */
5226 627 : pass = AT_PASS_MISC;
5227 627 : break;
5228 305 : case AT_AddInherit: /* INHERIT */
5229 305 : ATSimplePermissions(cmd->subtype, rel,
5230 : ATT_TABLE | ATT_FOREIGN_TABLE);
5231 : /* This command never recurses */
5232 301 : ATPrepChangeInherit(rel);
5233 289 : pass = AT_PASS_MISC;
5234 289 : break;
5235 81 : case AT_DropInherit: /* NO INHERIT */
5236 81 : ATSimplePermissions(cmd->subtype, rel,
5237 : ATT_TABLE | ATT_FOREIGN_TABLE);
5238 : /* This command never recurses */
5239 77 : ATPrepChangeInherit(rel);
5240 69 : pass = AT_PASS_MISC;
5241 69 : break;
5242 296 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5243 296 : ATSimplePermissions(cmd->subtype, rel,
5244 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5245 : /* Recursion occurs during execution phase */
5246 292 : if (recurse)
5247 284 : cmd->recurse = true;
5248 292 : pass = AT_PASS_MISC;
5249 292 : break;
5250 275 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5251 275 : ATSimplePermissions(cmd->subtype, rel,
5252 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5253 : /* Recursion occurs during execution phase */
5254 : /* No command-specific prep needed except saving recurse flag */
5255 275 : if (recurse)
5256 275 : cmd->recurse = true;
5257 275 : pass = AT_PASS_MISC;
5258 275 : break;
5259 301 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5260 301 : ATSimplePermissions(cmd->subtype, rel,
5261 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5262 301 : pass = AT_PASS_MISC;
5263 : /* This command never recurses */
5264 : /* No command-specific prep needed */
5265 301 : break;
5266 191 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5267 : case AT_EnableAlwaysTrig:
5268 : case AT_EnableReplicaTrig:
5269 : case AT_EnableTrigAll:
5270 : case AT_EnableTrigUser:
5271 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5272 : case AT_DisableTrigAll:
5273 : case AT_DisableTrigUser:
5274 191 : ATSimplePermissions(cmd->subtype, rel,
5275 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5276 : /* Set up recursion for phase 2; no other prep needed */
5277 191 : if (recurse)
5278 174 : cmd->recurse = true;
5279 191 : pass = AT_PASS_MISC;
5280 191 : break;
5281 411 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5282 : case AT_EnableAlwaysRule:
5283 : case AT_EnableReplicaRule:
5284 : case AT_DisableRule:
5285 : case AT_AddOf: /* OF */
5286 : case AT_DropOf: /* NOT OF */
5287 : case AT_EnableRowSecurity:
5288 : case AT_DisableRowSecurity:
5289 : case AT_ForceRowSecurity:
5290 : case AT_NoForceRowSecurity:
5291 411 : ATSimplePermissions(cmd->subtype, rel,
5292 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5293 : /* These commands never recurse */
5294 : /* No command-specific prep needed */
5295 411 : pass = AT_PASS_MISC;
5296 411 : break;
5297 31 : case AT_GenericOptions:
5298 31 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5299 : /* No command-specific prep needed */
5300 31 : pass = AT_PASS_MISC;
5301 31 : break;
5302 1865 : case AT_AttachPartition:
5303 1865 : ATSimplePermissions(cmd->subtype, rel,
5304 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5305 : /* No command-specific prep needed */
5306 1861 : pass = AT_PASS_MISC;
5307 1861 : break;
5308 385 : case AT_DetachPartition:
5309 385 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5310 : /* No command-specific prep needed */
5311 373 : pass = AT_PASS_MISC;
5312 373 : break;
5313 11 : case AT_DetachPartitionFinalize:
5314 11 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5315 : /* No command-specific prep needed */
5316 7 : pass = AT_PASS_MISC;
5317 7 : break;
5318 424 : case AT_MergePartitions:
5319 : case AT_SplitPartition:
5320 424 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5321 : /* No command-specific prep needed */
5322 420 : pass = AT_PASS_MISC;
5323 420 : break;
5324 0 : default: /* oops */
5325 0 : elog(ERROR, "unrecognized alter table type: %d",
5326 : (int) cmd->subtype);
5327 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5328 : break;
5329 : }
5330 : Assert(pass > AT_PASS_UNSET);
5331 :
5332 : /* Add the subcommand to the appropriate list for phase 2 */
5333 22758 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5334 22758 : }
5335 :
5336 : /*
5337 : * ATRewriteCatalogs
5338 : *
5339 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5340 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5341 : * conflicts).
5342 : */
5343 : static void
5344 21290 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5345 : AlterTableUtilityContext *context)
5346 : {
5347 : ListCell *ltab;
5348 :
5349 : /*
5350 : * We process all the tables "in parallel", one pass at a time. This is
5351 : * needed because we may have to propagate work from one table to another
5352 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5353 : * re-adding of the foreign key constraint to the other table). Work can
5354 : * only be propagated into later passes, however.
5355 : */
5356 267358 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5357 : {
5358 : /* Go through each table that needs to be processed */
5359 505764 : foreach(ltab, *wqueue)
5360 : {
5361 259696 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5362 259696 : List *subcmds = tab->subcmds[pass];
5363 : ListCell *lcmd;
5364 :
5365 259696 : if (subcmds == NIL)
5366 223265 : continue;
5367 :
5368 : /*
5369 : * Open the relation and store it in tab. This allows subroutines
5370 : * close and reopen, if necessary. Appropriate lock was obtained
5371 : * by phase 1, needn't get it again.
5372 : */
5373 36431 : tab->rel = relation_open(tab->relid, NoLock);
5374 :
5375 73082 : foreach(lcmd, subcmds)
5376 38820 : ATExecCmd(wqueue, tab,
5377 38820 : lfirst_node(AlterTableCmd, lcmd),
5378 : lockmode, pass, context);
5379 :
5380 : /*
5381 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5382 : * (this is not done in ATExecAlterColumnType since it should be
5383 : * done only once if multiple columns of a table are altered).
5384 : */
5385 34262 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5386 889 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5387 :
5388 34262 : if (tab->rel)
5389 : {
5390 34262 : relation_close(tab->rel, NoLock);
5391 34262 : tab->rel = NULL;
5392 : }
5393 : }
5394 : }
5395 :
5396 : /* Check to see if a toast table must be added. */
5397 41288 : foreach(ltab, *wqueue)
5398 : {
5399 22167 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5400 :
5401 : /*
5402 : * If the table is source table of ATTACH PARTITION command, we did
5403 : * not modify anything about it that will change its toasting
5404 : * requirement, so no need to check.
5405 : */
5406 22167 : if (((tab->relkind == RELKIND_RELATION ||
5407 4289 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5408 20974 : tab->partition_constraint == NULL) ||
5409 2596 : tab->relkind == RELKIND_MATVIEW)
5410 19600 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5411 : }
5412 19121 : }
5413 :
5414 : /*
5415 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5416 : */
5417 : static void
5418 38820 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5419 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5420 : AlterTableUtilityContext *context)
5421 : {
5422 38820 : ObjectAddress address = InvalidObjectAddress;
5423 38820 : Relation rel = tab->rel;
5424 :
5425 38820 : switch (cmd->subtype)
5426 : {
5427 1605 : case AT_AddColumn: /* ADD COLUMN */
5428 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5429 1605 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5430 1605 : cmd->recurse, false,
5431 : lockmode, cur_pass, context);
5432 1437 : break;
5433 383 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5434 383 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5435 339 : break;
5436 53 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5437 53 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5438 53 : break;
5439 107 : case AT_AddIdentity:
5440 107 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5441 : cur_pass, context);
5442 : Assert(cmd != NULL);
5443 99 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5444 63 : break;
5445 41 : case AT_SetIdentity:
5446 41 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5447 : cur_pass, context);
5448 : Assert(cmd != NULL);
5449 41 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5450 25 : break;
5451 37 : case AT_DropIdentity:
5452 37 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5453 25 : break;
5454 177 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5455 177 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5456 109 : break;
5457 272 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5458 272 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5459 272 : cmd->recurse, false, lockmode);
5460 252 : break;
5461 169 : case AT_SetExpression:
5462 169 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5463 157 : break;
5464 37 : case AT_DropExpression:
5465 37 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5466 21 : break;
5467 107 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5468 107 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5469 75 : break;
5470 17 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5471 17 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5472 17 : break;
5473 4 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5474 4 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5475 4 : break;
5476 173 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5477 173 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5478 165 : break;
5479 47 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5480 47 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5481 : lockmode);
5482 43 : break;
5483 1125 : case AT_DropColumn: /* DROP COLUMN */
5484 1125 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5485 1125 : cmd->behavior, cmd->recurse, false,
5486 1125 : cmd->missing_ok, lockmode,
5487 : NULL);
5488 997 : break;
5489 751 : case AT_AddIndex: /* ADD INDEX */
5490 751 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5491 : lockmode);
5492 638 : break;
5493 306 : case AT_ReAddIndex: /* ADD INDEX */
5494 306 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5495 : lockmode);
5496 306 : break;
5497 53 : case AT_ReAddStatistics: /* ADD STATISTICS */
5498 53 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5499 : true, lockmode);
5500 53 : break;
5501 18361 : case AT_AddConstraint: /* ADD CONSTRAINT */
5502 : /* Transform the command only during initial examination */
5503 18361 : if (cur_pass == AT_PASS_ADD_CONSTR)
5504 10316 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5505 10336 : cmd->recurse, lockmode,
5506 : cur_pass, context);
5507 : /* Depending on constraint type, might be no more work to do now */
5508 18341 : if (cmd != NULL)
5509 : address =
5510 8025 : ATExecAddConstraint(wqueue, tab, rel,
5511 8025 : (Constraint *) cmd->def,
5512 8025 : cmd->recurse, false, lockmode);
5513 17884 : break;
5514 257 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5515 : address =
5516 257 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5517 : true, true, lockmode);
5518 249 : break;
5519 9 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5520 : * constraint */
5521 : address =
5522 9 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5523 9 : ((AlterDomainStmt *) cmd->def)->def,
5524 : NULL);
5525 5 : break;
5526 52 : case AT_ReAddComment: /* Re-add existing comment */
5527 52 : address = CommentObject((CommentStmt *) cmd->def);
5528 52 : break;
5529 6653 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5530 6653 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5531 : lockmode);
5532 6645 : break;
5533 292 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5534 292 : address = ATExecAlterConstraint(wqueue, rel,
5535 292 : castNode(ATAlterConstraint, cmd->def),
5536 292 : cmd->recurse, lockmode);
5537 240 : break;
5538 275 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5539 275 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5540 : false, lockmode);
5541 271 : break;
5542 573 : case AT_DropConstraint: /* DROP CONSTRAINT */
5543 573 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5544 573 : cmd->recurse,
5545 573 : cmd->missing_ok, lockmode);
5546 433 : break;
5547 792 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5548 : /* parse transformation was done earlier */
5549 792 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5550 764 : break;
5551 93 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5552 : address =
5553 93 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5554 93 : (List *) cmd->def, lockmode);
5555 89 : break;
5556 1139 : case AT_ChangeOwner: /* ALTER OWNER */
5557 1136 : ATExecChangeOwner(RelationGetRelid(rel),
5558 1139 : get_rolespec_oid(cmd->newowner, false),
5559 : false, lockmode);
5560 1128 : break;
5561 39 : case AT_ClusterOn: /* CLUSTER ON */
5562 39 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5563 39 : break;
5564 8 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5565 8 : ATExecDropCluster(rel, lockmode);
5566 8 : break;
5567 58 : case AT_SetLogged: /* SET LOGGED */
5568 : case AT_SetUnLogged: /* SET UNLOGGED */
5569 58 : break;
5570 4 : case AT_DropOids: /* SET WITHOUT OIDS */
5571 : /* nothing to do here, oid columns don't exist anymore */
5572 4 : break;
5573 61 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5574 :
5575 : /*
5576 : * Only do this for partitioned tables, for which this is just a
5577 : * catalog change. Tables with storage are handled by Phase 3.
5578 : */
5579 61 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5580 33 : tab->chgAccessMethod)
5581 29 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5582 61 : break;
5583 98 : case AT_SetTableSpace: /* SET TABLESPACE */
5584 :
5585 : /*
5586 : * Only do this for partitioned tables and indexes, for which this
5587 : * is just a catalog change. Other relation types which have
5588 : * storage are handled by Phase 3.
5589 : */
5590 98 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5591 90 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5592 24 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5593 :
5594 94 : break;
5595 627 : case AT_SetRelOptions: /* SET (...) */
5596 : case AT_ResetRelOptions: /* RESET (...) */
5597 : case AT_ReplaceRelOptions: /* replace entire option list */
5598 627 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5599 593 : break;
5600 65 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5601 65 : ATExecEnableDisableTrigger(rel, cmd->name,
5602 : TRIGGER_FIRES_ON_ORIGIN, false,
5603 65 : cmd->recurse,
5604 : lockmode);
5605 65 : break;
5606 27 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5607 27 : ATExecEnableDisableTrigger(rel, cmd->name,
5608 : TRIGGER_FIRES_ALWAYS, false,
5609 27 : cmd->recurse,
5610 : lockmode);
5611 27 : break;
5612 8 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5613 8 : ATExecEnableDisableTrigger(rel, cmd->name,
5614 : TRIGGER_FIRES_ON_REPLICA, false,
5615 8 : cmd->recurse,
5616 : lockmode);
5617 8 : break;
5618 75 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5619 75 : ATExecEnableDisableTrigger(rel, cmd->name,
5620 : TRIGGER_DISABLED, false,
5621 75 : cmd->recurse,
5622 : lockmode);
5623 75 : break;
5624 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5625 0 : ATExecEnableDisableTrigger(rel, NULL,
5626 : TRIGGER_FIRES_ON_ORIGIN, false,
5627 0 : cmd->recurse,
5628 : lockmode);
5629 0 : break;
5630 8 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5631 8 : ATExecEnableDisableTrigger(rel, NULL,
5632 : TRIGGER_DISABLED, false,
5633 8 : cmd->recurse,
5634 : lockmode);
5635 8 : break;
5636 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5637 0 : ATExecEnableDisableTrigger(rel, NULL,
5638 : TRIGGER_FIRES_ON_ORIGIN, true,
5639 0 : cmd->recurse,
5640 : lockmode);
5641 0 : break;
5642 8 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5643 8 : ATExecEnableDisableTrigger(rel, NULL,
5644 : TRIGGER_DISABLED, true,
5645 8 : cmd->recurse,
5646 : lockmode);
5647 8 : break;
5648 :
5649 5 : case AT_EnableRule: /* ENABLE RULE name */
5650 5 : ATExecEnableDisableRule(rel, cmd->name,
5651 : RULE_FIRES_ON_ORIGIN, lockmode);
5652 5 : break;
5653 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5654 0 : ATExecEnableDisableRule(rel, cmd->name,
5655 : RULE_FIRES_ALWAYS, lockmode);
5656 0 : break;
5657 4 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5658 4 : ATExecEnableDisableRule(rel, cmd->name,
5659 : RULE_FIRES_ON_REPLICA, lockmode);
5660 4 : break;
5661 20 : case AT_DisableRule: /* DISABLE RULE name */
5662 20 : ATExecEnableDisableRule(rel, cmd->name,
5663 : RULE_DISABLED, lockmode);
5664 20 : break;
5665 :
5666 289 : case AT_AddInherit:
5667 289 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5668 209 : break;
5669 69 : case AT_DropInherit:
5670 69 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5671 65 : break;
5672 42 : case AT_AddOf:
5673 42 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5674 18 : break;
5675 4 : case AT_DropOf:
5676 4 : ATExecDropOf(rel, lockmode);
5677 4 : break;
5678 313 : case AT_ReplicaIdentity:
5679 313 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5680 281 : break;
5681 240 : case AT_EnableRowSecurity:
5682 240 : ATExecSetRowSecurity(rel, true);
5683 240 : break;
5684 6 : case AT_DisableRowSecurity:
5685 6 : ATExecSetRowSecurity(rel, false);
5686 6 : break;
5687 70 : case AT_ForceRowSecurity:
5688 70 : ATExecForceNoForceRowSecurity(rel, true);
5689 70 : break;
5690 20 : case AT_NoForceRowSecurity:
5691 20 : ATExecForceNoForceRowSecurity(rel, false);
5692 20 : break;
5693 31 : case AT_GenericOptions:
5694 31 : ATExecGenericOptions(rel, (List *) cmd->def);
5695 30 : break;
5696 1861 : case AT_AttachPartition:
5697 1861 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5698 : cur_pass, context);
5699 : Assert(cmd != NULL);
5700 1845 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5701 1604 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5702 : context);
5703 : else
5704 241 : address = ATExecAttachPartitionIdx(wqueue, rel,
5705 241 : ((PartitionCmd *) cmd->def)->name);
5706 1581 : break;
5707 373 : case AT_DetachPartition:
5708 373 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5709 : cur_pass, context);
5710 : Assert(cmd != NULL);
5711 : /* ATPrepCmd ensures it must be a table */
5712 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5713 373 : address = ATExecDetachPartition(wqueue, tab, rel,
5714 373 : ((PartitionCmd *) cmd->def)->name,
5715 373 : ((PartitionCmd *) cmd->def)->concurrent);
5716 296 : break;
5717 7 : case AT_DetachPartitionFinalize:
5718 7 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5719 7 : break;
5720 172 : case AT_MergePartitions:
5721 172 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5722 : cur_pass, context);
5723 : Assert(cmd != NULL);
5724 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5725 116 : ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5726 : context);
5727 88 : break;
5728 248 : case AT_SplitPartition:
5729 248 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5730 : cur_pass, context);
5731 : Assert(cmd != NULL);
5732 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5733 128 : ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5734 : context);
5735 120 : break;
5736 0 : default: /* oops */
5737 0 : elog(ERROR, "unrecognized alter table type: %d",
5738 : (int) cmd->subtype);
5739 : break;
5740 : }
5741 :
5742 : /*
5743 : * Report the subcommand to interested event triggers.
5744 : */
5745 36651 : if (cmd)
5746 26335 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5747 :
5748 : /*
5749 : * Bump the command counter to ensure the next subcommand in the sequence
5750 : * can see the changes so far
5751 : */
5752 36651 : CommandCounterIncrement();
5753 36651 : }
5754 :
5755 : /*
5756 : * ATParseTransformCmd: perform parse transformation for one subcommand
5757 : *
5758 : * Returns the transformed subcommand tree, if there is one, else NULL.
5759 : *
5760 : * The parser may hand back additional AlterTableCmd(s) and/or other
5761 : * utility statements, either before or after the original subcommand.
5762 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5763 : * AlteredTableInfo (they had better be for later passes than the current one).
5764 : * Utility statements that are supposed to happen before the AlterTableCmd
5765 : * are executed immediately. Those that are supposed to happen afterwards
5766 : * are added to the tab->afterStmts list to be done at the very end.
5767 : */
5768 : static AlterTableCmd *
5769 15601 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5770 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5771 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5772 : {
5773 15601 : AlterTableCmd *newcmd = NULL;
5774 15601 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5775 : List *beforeStmts;
5776 : List *afterStmts;
5777 : ListCell *lc;
5778 :
5779 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5780 15601 : atstmt->relation =
5781 15601 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5782 15601 : pstrdup(RelationGetRelationName(rel)),
5783 : -1);
5784 15601 : atstmt->relation->inh = recurse;
5785 15601 : atstmt->cmds = list_make1(cmd);
5786 15601 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5787 15601 : atstmt->missing_ok = false;
5788 :
5789 : /* Transform the AlterTableStmt */
5790 15601 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5791 : atstmt,
5792 : context->queryString,
5793 : &beforeStmts,
5794 : &afterStmts);
5795 :
5796 : /* Execute any statements that should happen before these subcommand(s) */
5797 15688 : foreach(lc, beforeStmts)
5798 : {
5799 315 : Node *stmt = (Node *) lfirst(lc);
5800 :
5801 315 : ProcessUtilityForAlterTable(stmt, context);
5802 307 : CommandCounterIncrement();
5803 : }
5804 :
5805 : /* Examine the transformed subcommands and schedule them appropriately */
5806 35883 : foreach(lc, atstmt->cmds)
5807 : {
5808 20510 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5809 : AlterTablePass pass;
5810 :
5811 : /*
5812 : * This switch need only cover the subcommand types that can be added
5813 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5814 : * executing the subcommand immediately, as a substitute for the
5815 : * original subcommand. (Note, however, that this does cause
5816 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5817 : * which is important for index and foreign key constraints.)
5818 : *
5819 : * We assume we needn't do any phase-1 checks for added subcommands.
5820 : */
5821 20510 : switch (cmd2->subtype)
5822 : {
5823 767 : case AT_AddIndex:
5824 767 : pass = AT_PASS_ADD_INDEX;
5825 767 : break;
5826 6653 : case AT_AddIndexConstraint:
5827 6653 : pass = AT_PASS_ADD_INDEXCONSTR;
5828 6653 : break;
5829 8033 : case AT_AddConstraint:
5830 : /* Recursion occurs during execution phase */
5831 8033 : if (recurse)
5832 7996 : cmd2->recurse = true;
5833 8033 : switch (castNode(Constraint, cmd2->def)->contype)
5834 : {
5835 5560 : case CONSTR_NOTNULL:
5836 5560 : pass = AT_PASS_COL_ATTRS;
5837 5560 : break;
5838 0 : case CONSTR_PRIMARY:
5839 : case CONSTR_UNIQUE:
5840 : case CONSTR_EXCLUSION:
5841 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5842 0 : break;
5843 2473 : default:
5844 2473 : pass = AT_PASS_ADD_OTHERCONSTR;
5845 2473 : break;
5846 : }
5847 8033 : break;
5848 0 : case AT_AlterColumnGenericOptions:
5849 : /* This command never recurses */
5850 : /* No command-specific prep needed */
5851 0 : pass = AT_PASS_MISC;
5852 0 : break;
5853 5057 : default:
5854 5057 : pass = cur_pass;
5855 5057 : break;
5856 : }
5857 :
5858 20510 : if (pass < cur_pass)
5859 : {
5860 : /* Cannot schedule into a pass we already finished */
5861 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5862 : pass);
5863 : }
5864 20510 : else if (pass > cur_pass)
5865 : {
5866 : /* OK, queue it up for later */
5867 15453 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5868 : }
5869 : else
5870 : {
5871 : /*
5872 : * We should see at most one subcommand for the current pass,
5873 : * which is the transformed version of the original subcommand.
5874 : */
5875 5057 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5876 : {
5877 : /* Found the transformed version of our subcommand */
5878 5057 : newcmd = cmd2;
5879 : }
5880 : else
5881 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5882 : pass);
5883 : }
5884 : }
5885 :
5886 : /* Queue up any after-statements to happen at the end */
5887 15373 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5888 :
5889 15373 : return newcmd;
5890 : }
5891 :
5892 : /*
5893 : * ATRewriteTables: ALTER TABLE phase 3
5894 : */
5895 : static void
5896 19121 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5897 : AlterTableUtilityContext *context)
5898 : {
5899 : ListCell *ltab;
5900 :
5901 : /* Go through each table that needs to be checked or rewritten */
5902 40870 : foreach(ltab, *wqueue)
5903 : {
5904 22083 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5905 :
5906 : /* Relations without storage may be ignored here */
5907 22083 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5908 4111 : continue;
5909 :
5910 : /*
5911 : * If we change column data types, the operation has to be propagated
5912 : * to tables that use this table's rowtype as a column type.
5913 : * tab->newvals will also be non-NULL in the case where we're adding a
5914 : * column with a default. We choose to forbid that case as well,
5915 : * since composite types might eventually support defaults.
5916 : *
5917 : * (Eventually we'll probably need to check for composite type
5918 : * dependencies even when we're just scanning the table without a
5919 : * rewrite, but at the moment a composite type does not enforce any
5920 : * constraints, so it's not necessary/appropriate to enforce them just
5921 : * during ALTER.)
5922 : */
5923 17972 : if (tab->newvals != NIL || tab->rewrite > 0)
5924 : {
5925 : Relation rel;
5926 :
5927 1311 : rel = table_open(tab->relid, NoLock);
5928 1311 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5929 1275 : table_close(rel, NoLock);
5930 : }
5931 :
5932 : /*
5933 : * We only need to rewrite the table if at least one column needs to
5934 : * be recomputed, or we are changing its persistence or access method.
5935 : *
5936 : * There are two reasons for requiring a rewrite when changing
5937 : * persistence: on one hand, we need to ensure that the buffers
5938 : * belonging to each of the two relations are marked with or without
5939 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5940 : * and assigns a new relfilenumber, we automatically create or drop an
5941 : * init fork for the relation as appropriate.
5942 : */
5943 17936 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5944 727 : {
5945 : /* Build a temporary relation and copy data */
5946 : Relation OldHeap;
5947 : Oid OIDNewHeap;
5948 : Oid NewAccessMethod;
5949 : Oid NewTableSpace;
5950 : char persistence;
5951 :
5952 796 : OldHeap = table_open(tab->relid, NoLock);
5953 :
5954 : /*
5955 : * We don't support rewriting of system catalogs; there are too
5956 : * many corner cases and too little benefit. In particular this
5957 : * is certainly not going to work for mapped catalogs.
5958 : */
5959 796 : if (IsSystemRelation(OldHeap))
5960 0 : ereport(ERROR,
5961 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5962 : errmsg("cannot rewrite system relation \"%s\"",
5963 : RelationGetRelationName(OldHeap))));
5964 :
5965 796 : if (RelationIsUsedAsCatalogTable(OldHeap))
5966 1 : ereport(ERROR,
5967 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5968 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5969 : RelationGetRelationName(OldHeap))));
5970 :
5971 : /*
5972 : * Don't allow rewrite on temp tables of other backends ... their
5973 : * local buffer manager is not going to cope. (This is redundant
5974 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5975 : * check here too.)
5976 : */
5977 795 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5978 0 : ereport(ERROR,
5979 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5980 : errmsg("cannot rewrite temporary tables of other sessions")));
5981 :
5982 : /*
5983 : * Select destination tablespace (same as original unless user
5984 : * requested a change)
5985 : */
5986 795 : if (tab->newTableSpace)
5987 0 : NewTableSpace = tab->newTableSpace;
5988 : else
5989 795 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5990 :
5991 : /*
5992 : * Select destination access method (same as original unless user
5993 : * requested a change)
5994 : */
5995 795 : if (tab->chgAccessMethod)
5996 24 : NewAccessMethod = tab->newAccessMethod;
5997 : else
5998 771 : NewAccessMethod = OldHeap->rd_rel->relam;
5999 :
6000 : /*
6001 : * Select persistence of transient table (same as original unless
6002 : * user requested a change)
6003 : */
6004 795 : persistence = tab->chgPersistence ?
6005 761 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
6006 :
6007 795 : table_close(OldHeap, NoLock);
6008 :
6009 : /*
6010 : * Fire off an Event Trigger now, before actually rewriting the
6011 : * table.
6012 : *
6013 : * We don't support Event Trigger for nested commands anywhere,
6014 : * here included, and parsetree is given NULL when coming from
6015 : * AlterTableInternal.
6016 : *
6017 : * And fire it only once.
6018 : */
6019 795 : if (parsetree)
6020 795 : EventTriggerTableRewrite((Node *) parsetree,
6021 : tab->relid,
6022 : tab->rewrite);
6023 :
6024 : /*
6025 : * Create transient table that will receive the modified data.
6026 : *
6027 : * Ensure it is marked correctly as logged or unlogged. We have
6028 : * to do this here so that buffers for the new relfilenumber will
6029 : * have the right persistence set, and at the same time ensure
6030 : * that the original filenumbers's buffers will get read in with
6031 : * the correct setting (i.e. the original one). Otherwise a
6032 : * rollback after the rewrite would possibly result with buffers
6033 : * for the original filenumbers having the wrong persistence
6034 : * setting.
6035 : *
6036 : * NB: This relies on swap_relation_files() also swapping the
6037 : * persistence. That wouldn't work for pg_class, but that can't be
6038 : * unlogged anyway.
6039 : */
6040 791 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
6041 : persistence, lockmode);
6042 :
6043 : /*
6044 : * Copy the heap data into the new table with the desired
6045 : * modifications, and test the current data within the table
6046 : * against new constraints generated by ALTER TABLE commands.
6047 : */
6048 791 : ATRewriteTable(tab, OIDNewHeap);
6049 :
6050 : /*
6051 : * Swap the physical files of the old and new heaps, then rebuild
6052 : * indexes and discard the old heap. We can use RecentXmin for
6053 : * the table's new relfrozenxid because we rewrote all the tuples
6054 : * in ATRewriteTable, so no older Xid remains in the table. Also,
6055 : * we never try to swap toast tables by content, since we have no
6056 : * interest in letting this code work on system catalogs.
6057 : */
6058 731 : finish_heap_swap(tab->relid, OIDNewHeap,
6059 : false, false, true,
6060 731 : !OidIsValid(tab->newTableSpace),
6061 : true, /* reindex */
6062 : RecentXmin,
6063 : ReadNextMultiXactId(),
6064 : persistence);
6065 :
6066 727 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6067 : }
6068 17140 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6069 : {
6070 16 : if (tab->chgPersistence)
6071 16 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
6072 : }
6073 : else
6074 : {
6075 : /*
6076 : * If required, test the current data within the table against new
6077 : * constraints generated by ALTER TABLE commands, but don't
6078 : * rebuild data.
6079 : */
6080 17124 : if (tab->constraints != NIL || tab->verify_new_notnull ||
6081 14942 : tab->partition_constraint != NULL)
6082 3487 : ATRewriteTable(tab, InvalidOid);
6083 :
6084 : /*
6085 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
6086 : * just do a block-by-block copy.
6087 : */
6088 16895 : if (tab->newTableSpace)
6089 74 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6090 : }
6091 :
6092 : /*
6093 : * Also change persistence of owned sequences, so that it matches the
6094 : * table persistence.
6095 : */
6096 17638 : if (tab->chgPersistence)
6097 : {
6098 50 : List *seqlist = getOwnedSequences(tab->relid);
6099 : ListCell *lc;
6100 :
6101 82 : foreach(lc, seqlist)
6102 : {
6103 32 : Oid seq_relid = lfirst_oid(lc);
6104 :
6105 32 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6106 : }
6107 : }
6108 : }
6109 :
6110 : /*
6111 : * Foreign key constraints are checked in a final pass, since (a) it's
6112 : * generally best to examine each one separately, and (b) it's at least
6113 : * theoretically possible that we have changed both relations of the
6114 : * foreign key, and we'd better have finished both rewrites before we try
6115 : * to read the tables.
6116 : */
6117 40287 : foreach(ltab, *wqueue)
6118 : {
6119 21571 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6120 21571 : Relation rel = NULL;
6121 : ListCell *lcon;
6122 :
6123 : /* Relations without storage may be ignored here too */
6124 21571 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6125 4006 : continue;
6126 :
6127 18940 : foreach(lcon, tab->constraints)
6128 : {
6129 1446 : NewConstraint *con = lfirst(lcon);
6130 :
6131 1446 : if (con->contype == CONSTR_FOREIGN)
6132 : {
6133 828 : Constraint *fkconstraint = (Constraint *) con->qual;
6134 : Relation refrel;
6135 :
6136 828 : if (rel == NULL)
6137 : {
6138 : /* Long since locked, no need for another */
6139 820 : rel = table_open(tab->relid, NoLock);
6140 : }
6141 :
6142 828 : refrel = table_open(con->refrelid, RowShareLock);
6143 :
6144 828 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6145 : con->refindid,
6146 : con->conid,
6147 828 : con->conwithperiod);
6148 :
6149 : /*
6150 : * No need to mark the constraint row as validated, we did
6151 : * that when we inserted the row earlier.
6152 : */
6153 :
6154 757 : table_close(refrel, NoLock);
6155 : }
6156 : }
6157 :
6158 17494 : if (rel)
6159 749 : table_close(rel, NoLock);
6160 : }
6161 :
6162 : /* Finally, run any afterStmts that were queued up */
6163 40189 : foreach(ltab, *wqueue)
6164 : {
6165 21473 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6166 : ListCell *lc;
6167 :
6168 21529 : foreach(lc, tab->afterStmts)
6169 : {
6170 56 : Node *stmt = (Node *) lfirst(lc);
6171 :
6172 56 : ProcessUtilityForAlterTable(stmt, context);
6173 56 : CommandCounterIncrement();
6174 : }
6175 : }
6176 18716 : }
6177 :
6178 : /*
6179 : * ATRewriteTable: scan or rewrite one table
6180 : *
6181 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6182 : * must already hold AccessExclusiveLock on it.
6183 : */
6184 : static void
6185 4278 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6186 : {
6187 : Relation oldrel;
6188 : Relation newrel;
6189 : TupleDesc oldTupDesc;
6190 : TupleDesc newTupDesc;
6191 4278 : bool needscan = false;
6192 : List *notnull_attrs;
6193 : List *notnull_virtual_attrs;
6194 : int i;
6195 : ListCell *l;
6196 : EState *estate;
6197 : CommandId mycid;
6198 : BulkInsertState bistate;
6199 : uint32 ti_options;
6200 4278 : ExprState *partqualstate = NULL;
6201 :
6202 : /*
6203 : * Open the relation(s). We have surely already locked the existing
6204 : * table.
6205 : */
6206 4278 : oldrel = table_open(tab->relid, NoLock);
6207 4278 : oldTupDesc = tab->oldDesc;
6208 4278 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6209 :
6210 4278 : if (OidIsValid(OIDNewHeap))
6211 : {
6212 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6213 : false));
6214 791 : newrel = table_open(OIDNewHeap, NoLock);
6215 : }
6216 : else
6217 3487 : newrel = NULL;
6218 :
6219 : /*
6220 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6221 : * is empty, so don't bother using it.
6222 : */
6223 4278 : if (newrel)
6224 : {
6225 791 : mycid = GetCurrentCommandId(true);
6226 791 : bistate = GetBulkInsertState();
6227 791 : ti_options = TABLE_INSERT_SKIP_FSM;
6228 : }
6229 : else
6230 : {
6231 : /* keep compiler quiet about using these uninitialized */
6232 3487 : mycid = 0;
6233 3487 : bistate = NULL;
6234 3487 : ti_options = 0;
6235 : }
6236 :
6237 : /*
6238 : * Generate the constraint and default execution states
6239 : */
6240 :
6241 4278 : estate = CreateExecutorState();
6242 :
6243 : /* Build the needed expression execution states */
6244 5880 : foreach(l, tab->constraints)
6245 : {
6246 1602 : NewConstraint *con = lfirst(l);
6247 :
6248 1602 : switch (con->contype)
6249 : {
6250 770 : case CONSTR_CHECK:
6251 770 : needscan = true;
6252 770 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6253 770 : break;
6254 832 : case CONSTR_FOREIGN:
6255 : /* Nothing to do here */
6256 832 : break;
6257 0 : default:
6258 0 : elog(ERROR, "unrecognized constraint type: %d",
6259 : (int) con->contype);
6260 : }
6261 : }
6262 :
6263 : /* Build expression execution states for partition check quals */
6264 4278 : if (tab->partition_constraint)
6265 : {
6266 1399 : needscan = true;
6267 1399 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6268 : }
6269 :
6270 5123 : foreach(l, tab->newvals)
6271 : {
6272 845 : NewColumnValue *ex = lfirst(l);
6273 :
6274 : /* expr already planned */
6275 845 : ex->exprstate = ExecInitExpr(ex->expr, NULL);
6276 : }
6277 :
6278 4278 : notnull_attrs = notnull_virtual_attrs = NIL;
6279 4278 : if (newrel || tab->verify_new_notnull)
6280 : {
6281 : /*
6282 : * If we are rebuilding the tuples OR if we added any new but not
6283 : * verified not-null constraints, check all *valid* not-null
6284 : * constraints. This is a bit of overkill but it minimizes risk of
6285 : * bugs.
6286 : *
6287 : * notnull_attrs does *not* collect attribute numbers for valid
6288 : * not-null constraints over virtual generated columns; instead, they
6289 : * are collected in notnull_virtual_attrs for verification elsewhere.
6290 : */
6291 5548 : for (i = 0; i < newTupDesc->natts; i++)
6292 : {
6293 4064 : CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6294 :
6295 4064 : if (attr->attnullability == ATTNULLABLE_VALID &&
6296 1476 : !attr->attisdropped)
6297 : {
6298 1476 : Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6299 :
6300 1476 : if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6301 1416 : notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6302 : else
6303 60 : notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6304 60 : wholeatt->attnum);
6305 : }
6306 : }
6307 1484 : if (notnull_attrs || notnull_virtual_attrs)
6308 1075 : needscan = true;
6309 : }
6310 :
6311 4278 : if (newrel || needscan)
6312 : {
6313 : ExprContext *econtext;
6314 : TupleTableSlot *oldslot;
6315 : TupleTableSlot *newslot;
6316 : TableScanDesc scan;
6317 : MemoryContext oldCxt;
6318 3576 : List *dropped_attrs = NIL;
6319 : ListCell *lc;
6320 : Snapshot snapshot;
6321 3576 : ResultRelInfo *rInfo = NULL;
6322 :
6323 : /*
6324 : * When adding or changing a virtual generated column with a not-null
6325 : * constraint, we need to evaluate whether the generation expression
6326 : * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6327 : * prepare a dummy ResultRelInfo.
6328 : */
6329 3576 : if (notnull_virtual_attrs != NIL)
6330 : {
6331 : MemoryContext oldcontext;
6332 :
6333 : Assert(newTupDesc->constr->has_generated_virtual);
6334 : Assert(newTupDesc->constr->has_not_null);
6335 40 : oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6336 40 : rInfo = makeNode(ResultRelInfo);
6337 40 : InitResultRelInfo(rInfo,
6338 : oldrel,
6339 : 0, /* dummy rangetable index */
6340 : NULL,
6341 : estate->es_instrument);
6342 40 : MemoryContextSwitchTo(oldcontext);
6343 : }
6344 :
6345 3576 : if (newrel)
6346 791 : ereport(DEBUG1,
6347 : (errmsg_internal("rewriting table \"%s\"",
6348 : RelationGetRelationName(oldrel))));
6349 : else
6350 2785 : ereport(DEBUG1,
6351 : (errmsg_internal("verifying table \"%s\"",
6352 : RelationGetRelationName(oldrel))));
6353 :
6354 3576 : if (newrel)
6355 : {
6356 : /*
6357 : * All predicate locks on the tuples or pages are about to be made
6358 : * invalid, because we move tuples around. Promote them to
6359 : * relation locks.
6360 : */
6361 791 : TransferPredicateLocksToHeapRelation(oldrel);
6362 : }
6363 :
6364 3576 : econtext = GetPerTupleExprContext(estate);
6365 :
6366 : /*
6367 : * Create necessary tuple slots. When rewriting, two slots are needed,
6368 : * otherwise one suffices. In the case where one slot suffices, we
6369 : * need to use the new tuple descriptor, otherwise some constraints
6370 : * can't be evaluated. Note that even when the tuple layout is the
6371 : * same and no rewrite is required, the tupDescs might not be
6372 : * (consider ADD COLUMN without a default).
6373 : */
6374 3576 : if (tab->rewrite)
6375 : {
6376 : Assert(newrel != NULL);
6377 791 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6378 : table_slot_callbacks(oldrel));
6379 791 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6380 : table_slot_callbacks(newrel));
6381 :
6382 : /*
6383 : * Set all columns in the new slot to NULL initially, to ensure
6384 : * columns added as part of the rewrite are initialized to NULL.
6385 : * That is necessary as tab->newvals will not contain an
6386 : * expression for columns with a NULL default, e.g. when adding a
6387 : * column without a default together with a column with a default
6388 : * requiring an actual rewrite.
6389 : */
6390 791 : ExecStoreAllNullTuple(newslot);
6391 : }
6392 : else
6393 : {
6394 2785 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6395 : table_slot_callbacks(oldrel));
6396 2785 : newslot = NULL;
6397 : }
6398 :
6399 : /*
6400 : * Any attributes that are dropped according to the new tuple
6401 : * descriptor can be set to NULL. We precompute the list of dropped
6402 : * attributes to avoid needing to do so in the per-tuple loop.
6403 : */
6404 12700 : for (i = 0; i < newTupDesc->natts; i++)
6405 : {
6406 9124 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6407 600 : dropped_attrs = lappend_int(dropped_attrs, i);
6408 : }
6409 :
6410 : /*
6411 : * Scan through the rows, generating a new row if needed and then
6412 : * checking all the constraints.
6413 : */
6414 3576 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6415 3576 : scan = table_beginscan(oldrel, snapshot, 0, NULL,
6416 : SO_NONE);
6417 :
6418 : /*
6419 : * Switch to per-tuple memory context and reset it for each tuple
6420 : * produced, so we don't leak memory.
6421 : */
6422 3576 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6423 :
6424 490239 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6425 : {
6426 : TupleTableSlot *insertslot;
6427 :
6428 483376 : if (tab->rewrite > 0)
6429 : {
6430 : /* Extract data from old tuple */
6431 66503 : slot_getallattrs(oldslot);
6432 66503 : ExecClearTuple(newslot);
6433 :
6434 : /* copy attributes */
6435 66503 : memcpy(newslot->tts_values, oldslot->tts_values,
6436 66503 : sizeof(Datum) * oldslot->tts_nvalid);
6437 66503 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6438 66503 : sizeof(bool) * oldslot->tts_nvalid);
6439 :
6440 : /* Set dropped attributes to null in new tuple */
6441 66632 : foreach(lc, dropped_attrs)
6442 129 : newslot->tts_isnull[lfirst_int(lc)] = true;
6443 :
6444 : /*
6445 : * Constraints and GENERATED expressions might reference the
6446 : * tableoid column, so fill tts_tableOid with the desired
6447 : * value. (We must do this each time, because it gets
6448 : * overwritten with newrel's OID during storing.)
6449 : */
6450 66503 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6451 :
6452 : /*
6453 : * Process supplied expressions to replace selected columns.
6454 : *
6455 : * First, evaluate expressions whose inputs come from the old
6456 : * tuple.
6457 : */
6458 66503 : econtext->ecxt_scantuple = oldslot;
6459 :
6460 136986 : foreach(l, tab->newvals)
6461 : {
6462 70503 : NewColumnValue *ex = lfirst(l);
6463 :
6464 70503 : if (ex->is_generated)
6465 264 : continue;
6466 :
6467 70239 : newslot->tts_values[ex->attnum - 1]
6468 70219 : = ExecEvalExpr(ex->exprstate,
6469 : econtext,
6470 70239 : &newslot->tts_isnull[ex->attnum - 1]);
6471 : }
6472 :
6473 66483 : ExecStoreVirtualTuple(newslot);
6474 :
6475 : /*
6476 : * Now, evaluate any expressions whose inputs come from the
6477 : * new tuple. We assume these columns won't reference each
6478 : * other, so that there's no ordering dependency.
6479 : */
6480 66483 : econtext->ecxt_scantuple = newslot;
6481 :
6482 136966 : foreach(l, tab->newvals)
6483 : {
6484 70483 : NewColumnValue *ex = lfirst(l);
6485 :
6486 70483 : if (!ex->is_generated)
6487 70219 : continue;
6488 :
6489 264 : newslot->tts_values[ex->attnum - 1]
6490 264 : = ExecEvalExpr(ex->exprstate,
6491 : econtext,
6492 264 : &newslot->tts_isnull[ex->attnum - 1]);
6493 : }
6494 :
6495 66483 : insertslot = newslot;
6496 : }
6497 : else
6498 : {
6499 : /*
6500 : * If there's no rewrite, old and new table are guaranteed to
6501 : * have the same AM, so we can just use the old slot to verify
6502 : * new constraints etc.
6503 : */
6504 416873 : insertslot = oldslot;
6505 : }
6506 :
6507 : /* Now check any constraints on the possibly-changed tuple */
6508 483356 : econtext->ecxt_scantuple = insertslot;
6509 :
6510 2577570 : foreach_int(attn, notnull_attrs)
6511 : {
6512 1611034 : if (slot_attisnull(insertslot, attn))
6513 : {
6514 88 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6515 :
6516 88 : ereport(ERROR,
6517 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6518 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6519 : NameStr(attr->attname),
6520 : RelationGetRelationName(oldrel)),
6521 : errtablecol(oldrel, attn)));
6522 : }
6523 : }
6524 :
6525 483268 : if (notnull_virtual_attrs != NIL)
6526 : {
6527 : AttrNumber attnum;
6528 :
6529 56 : attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6530 : estate,
6531 : notnull_virtual_attrs);
6532 56 : if (attnum != InvalidAttrNumber)
6533 : {
6534 20 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6535 :
6536 20 : ereport(ERROR,
6537 : errcode(ERRCODE_NOT_NULL_VIOLATION),
6538 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6539 : NameStr(attr->attname),
6540 : RelationGetRelationName(oldrel)),
6541 : errtablecol(oldrel, attnum));
6542 : }
6543 : }
6544 :
6545 488577 : foreach(l, tab->constraints)
6546 : {
6547 5441 : NewConstraint *con = lfirst(l);
6548 :
6549 5441 : switch (con->contype)
6550 : {
6551 5375 : case CONSTR_CHECK:
6552 5375 : if (!ExecCheck(con->qualstate, econtext))
6553 112 : ereport(ERROR,
6554 : (errcode(ERRCODE_CHECK_VIOLATION),
6555 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6556 : con->name,
6557 : RelationGetRelationName(oldrel)),
6558 : errtableconstraint(oldrel, con->name)));
6559 5263 : break;
6560 66 : case CONSTR_NOTNULL:
6561 : case CONSTR_FOREIGN:
6562 : /* Nothing to do here */
6563 66 : break;
6564 0 : default:
6565 0 : elog(ERROR, "unrecognized constraint type: %d",
6566 : (int) con->contype);
6567 : }
6568 : }
6569 :
6570 483136 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6571 : {
6572 49 : if (tab->validate_default)
6573 17 : ereport(ERROR,
6574 : (errcode(ERRCODE_CHECK_VIOLATION),
6575 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6576 : RelationGetRelationName(oldrel)),
6577 : errtable(oldrel)));
6578 : else
6579 32 : ereport(ERROR,
6580 : (errcode(ERRCODE_CHECK_VIOLATION),
6581 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6582 : RelationGetRelationName(oldrel)),
6583 : errtable(oldrel)));
6584 : }
6585 :
6586 : /* Write the tuple out to the new relation */
6587 483087 : if (newrel)
6588 66443 : table_tuple_insert(newrel, insertslot, mycid,
6589 : ti_options, bistate);
6590 :
6591 483087 : ResetExprContext(econtext);
6592 :
6593 483087 : CHECK_FOR_INTERRUPTS();
6594 : }
6595 :
6596 3287 : MemoryContextSwitchTo(oldCxt);
6597 3287 : table_endscan(scan);
6598 3287 : UnregisterSnapshot(snapshot);
6599 :
6600 3287 : ExecDropSingleTupleTableSlot(oldslot);
6601 3287 : if (newslot)
6602 731 : ExecDropSingleTupleTableSlot(newslot);
6603 : }
6604 :
6605 3989 : FreeExecutorState(estate);
6606 :
6607 3989 : table_close(oldrel, NoLock);
6608 3989 : if (newrel)
6609 : {
6610 731 : FreeBulkInsertState(bistate);
6611 :
6612 731 : table_finish_bulk_insert(newrel, ti_options);
6613 :
6614 731 : table_close(newrel, NoLock);
6615 : }
6616 3989 : }
6617 :
6618 : /*
6619 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6620 : */
6621 : static AlteredTableInfo *
6622 29018 : ATGetQueueEntry(List **wqueue, Relation rel)
6623 : {
6624 29018 : Oid relid = RelationGetRelid(rel);
6625 : AlteredTableInfo *tab;
6626 : ListCell *ltab;
6627 :
6628 37444 : foreach(ltab, *wqueue)
6629 : {
6630 12295 : tab = (AlteredTableInfo *) lfirst(ltab);
6631 12295 : if (tab->relid == relid)
6632 3869 : return tab;
6633 : }
6634 :
6635 : /*
6636 : * Not there, so add it. Note that we make a copy of the relation's
6637 : * existing descriptor before anything interesting can happen to it.
6638 : */
6639 25149 : tab = palloc0_object(AlteredTableInfo);
6640 25149 : tab->relid = relid;
6641 25149 : tab->rel = NULL; /* set later */
6642 25149 : tab->relkind = rel->rd_rel->relkind;
6643 25149 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6644 25149 : tab->newAccessMethod = InvalidOid;
6645 25149 : tab->chgAccessMethod = false;
6646 25149 : tab->newTableSpace = InvalidOid;
6647 25149 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6648 25149 : tab->chgPersistence = false;
6649 :
6650 25149 : *wqueue = lappend(*wqueue, tab);
6651 :
6652 25149 : return tab;
6653 : }
6654 :
6655 : static const char *
6656 73 : alter_table_type_to_string(AlterTableType cmdtype)
6657 : {
6658 73 : switch (cmdtype)
6659 : {
6660 0 : case AT_AddColumn:
6661 : case AT_AddColumnToView:
6662 0 : return "ADD COLUMN";
6663 0 : case AT_ColumnDefault:
6664 : case AT_CookedColumnDefault:
6665 0 : return "ALTER COLUMN ... SET DEFAULT";
6666 4 : case AT_DropNotNull:
6667 4 : return "ALTER COLUMN ... DROP NOT NULL";
6668 4 : case AT_SetNotNull:
6669 4 : return "ALTER COLUMN ... SET NOT NULL";
6670 0 : case AT_SetExpression:
6671 0 : return "ALTER COLUMN ... SET EXPRESSION";
6672 0 : case AT_DropExpression:
6673 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6674 0 : case AT_SetStatistics:
6675 0 : return "ALTER COLUMN ... SET STATISTICS";
6676 8 : case AT_SetOptions:
6677 8 : return "ALTER COLUMN ... SET";
6678 0 : case AT_ResetOptions:
6679 0 : return "ALTER COLUMN ... RESET";
6680 0 : case AT_SetStorage:
6681 0 : return "ALTER COLUMN ... SET STORAGE";
6682 0 : case AT_SetCompression:
6683 0 : return "ALTER COLUMN ... SET COMPRESSION";
6684 4 : case AT_DropColumn:
6685 4 : return "DROP COLUMN";
6686 0 : case AT_AddIndex:
6687 : case AT_ReAddIndex:
6688 0 : return NULL; /* not real grammar */
6689 0 : case AT_AddConstraint:
6690 : case AT_ReAddConstraint:
6691 : case AT_ReAddDomainConstraint:
6692 : case AT_AddIndexConstraint:
6693 0 : return "ADD CONSTRAINT";
6694 4 : case AT_AlterConstraint:
6695 4 : return "ALTER CONSTRAINT";
6696 0 : case AT_ValidateConstraint:
6697 0 : return "VALIDATE CONSTRAINT";
6698 0 : case AT_DropConstraint:
6699 0 : return "DROP CONSTRAINT";
6700 0 : case AT_ReAddComment:
6701 0 : return NULL; /* not real grammar */
6702 0 : case AT_AlterColumnType:
6703 0 : return "ALTER COLUMN ... SET DATA TYPE";
6704 0 : case AT_AlterColumnGenericOptions:
6705 0 : return "ALTER COLUMN ... OPTIONS";
6706 0 : case AT_ChangeOwner:
6707 0 : return "OWNER TO";
6708 4 : case AT_ClusterOn:
6709 4 : return "CLUSTER ON";
6710 4 : case AT_DropCluster:
6711 4 : return "SET WITHOUT CLUSTER";
6712 0 : case AT_SetAccessMethod:
6713 0 : return "SET ACCESS METHOD";
6714 4 : case AT_SetLogged:
6715 4 : return "SET LOGGED";
6716 4 : case AT_SetUnLogged:
6717 4 : return "SET UNLOGGED";
6718 0 : case AT_DropOids:
6719 0 : return "SET WITHOUT OIDS";
6720 0 : case AT_SetTableSpace:
6721 0 : return "SET TABLESPACE";
6722 1 : case AT_SetRelOptions:
6723 1 : return "SET";
6724 0 : case AT_ResetRelOptions:
6725 0 : return "RESET";
6726 0 : case AT_ReplaceRelOptions:
6727 0 : return NULL; /* not real grammar */
6728 0 : case AT_EnableTrig:
6729 0 : return "ENABLE TRIGGER";
6730 0 : case AT_EnableAlwaysTrig:
6731 0 : return "ENABLE ALWAYS TRIGGER";
6732 0 : case AT_EnableReplicaTrig:
6733 0 : return "ENABLE REPLICA TRIGGER";
6734 0 : case AT_DisableTrig:
6735 0 : return "DISABLE TRIGGER";
6736 0 : case AT_EnableTrigAll:
6737 0 : return "ENABLE TRIGGER ALL";
6738 0 : case AT_DisableTrigAll:
6739 0 : return "DISABLE TRIGGER ALL";
6740 0 : case AT_EnableTrigUser:
6741 0 : return "ENABLE TRIGGER USER";
6742 0 : case AT_DisableTrigUser:
6743 0 : return "DISABLE TRIGGER USER";
6744 0 : case AT_EnableRule:
6745 0 : return "ENABLE RULE";
6746 0 : case AT_EnableAlwaysRule:
6747 0 : return "ENABLE ALWAYS RULE";
6748 0 : case AT_EnableReplicaRule:
6749 0 : return "ENABLE REPLICA RULE";
6750 0 : case AT_DisableRule:
6751 0 : return "DISABLE RULE";
6752 4 : case AT_AddInherit:
6753 4 : return "INHERIT";
6754 4 : case AT_DropInherit:
6755 4 : return "NO INHERIT";
6756 0 : case AT_AddOf:
6757 0 : return "OF";
6758 0 : case AT_DropOf:
6759 0 : return "NOT OF";
6760 0 : case AT_ReplicaIdentity:
6761 0 : return "REPLICA IDENTITY";
6762 0 : case AT_EnableRowSecurity:
6763 0 : return "ENABLE ROW SECURITY";
6764 0 : case AT_DisableRowSecurity:
6765 0 : return "DISABLE ROW SECURITY";
6766 0 : case AT_ForceRowSecurity:
6767 0 : return "FORCE ROW SECURITY";
6768 0 : case AT_NoForceRowSecurity:
6769 0 : return "NO FORCE ROW SECURITY";
6770 0 : case AT_GenericOptions:
6771 0 : return "OPTIONS";
6772 4 : case AT_AttachPartition:
6773 4 : return "ATTACH PARTITION";
6774 12 : case AT_DetachPartition:
6775 12 : return "DETACH PARTITION";
6776 4 : case AT_DetachPartitionFinalize:
6777 4 : return "DETACH PARTITION ... FINALIZE";
6778 0 : case AT_MergePartitions:
6779 0 : return "MERGE PARTITIONS";
6780 4 : case AT_SplitPartition:
6781 4 : return "SPLIT PARTITION";
6782 0 : case AT_AddIdentity:
6783 0 : return "ALTER COLUMN ... ADD IDENTITY";
6784 0 : case AT_SetIdentity:
6785 0 : return "ALTER COLUMN ... SET";
6786 0 : case AT_DropIdentity:
6787 0 : return "ALTER COLUMN ... DROP IDENTITY";
6788 0 : case AT_ReAddStatistics:
6789 0 : return NULL; /* not real grammar */
6790 : }
6791 :
6792 0 : return NULL;
6793 : }
6794 :
6795 : /*
6796 : * ATSimplePermissions
6797 : *
6798 : * - Ensure that it is a relation (or possibly a view)
6799 : * - Ensure this user is the owner
6800 : * - Ensure that it is not a system table
6801 : */
6802 : static void
6803 25738 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6804 : {
6805 : int actual_target;
6806 :
6807 25738 : switch (rel->rd_rel->relkind)
6808 : {
6809 19989 : case RELKIND_RELATION:
6810 19989 : actual_target = ATT_TABLE;
6811 19989 : break;
6812 4285 : case RELKIND_PARTITIONED_TABLE:
6813 4285 : actual_target = ATT_PARTITIONED_TABLE;
6814 4285 : break;
6815 276 : case RELKIND_VIEW:
6816 276 : actual_target = ATT_VIEW;
6817 276 : break;
6818 29 : case RELKIND_MATVIEW:
6819 29 : actual_target = ATT_MATVIEW;
6820 29 : break;
6821 140 : case RELKIND_INDEX:
6822 140 : actual_target = ATT_INDEX;
6823 140 : break;
6824 269 : case RELKIND_PARTITIONED_INDEX:
6825 269 : actual_target = ATT_PARTITIONED_INDEX;
6826 269 : break;
6827 141 : case RELKIND_COMPOSITE_TYPE:
6828 141 : actual_target = ATT_COMPOSITE_TYPE;
6829 141 : break;
6830 592 : case RELKIND_FOREIGN_TABLE:
6831 592 : actual_target = ATT_FOREIGN_TABLE;
6832 592 : break;
6833 16 : case RELKIND_SEQUENCE:
6834 16 : actual_target = ATT_SEQUENCE;
6835 16 : break;
6836 1 : default:
6837 1 : actual_target = 0;
6838 1 : break;
6839 : }
6840 :
6841 : /* Wrong target type? */
6842 25738 : if ((actual_target & allowed_targets) == 0)
6843 : {
6844 73 : const char *action_str = alter_table_type_to_string(cmdtype);
6845 :
6846 73 : if (action_str)
6847 73 : ereport(ERROR,
6848 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6849 : /* translator: %s is a group of some SQL keywords */
6850 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6851 : action_str, RelationGetRelationName(rel)),
6852 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6853 : else
6854 : /* internal error? */
6855 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6856 : RelationGetRelationName(rel));
6857 : }
6858 :
6859 : /* Permissions checks */
6860 25665 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6861 8 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6862 8 : RelationGetRelationName(rel));
6863 :
6864 25657 : if (!allowSystemTableMods && IsSystemRelation(rel))
6865 0 : ereport(ERROR,
6866 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6867 : errmsg("permission denied: \"%s\" is a system catalog",
6868 : RelationGetRelationName(rel))));
6869 25657 : }
6870 :
6871 : /*
6872 : * ATSimpleRecursion
6873 : *
6874 : * Simple table recursion sufficient for most ALTER TABLE operations.
6875 : * All direct and indirect children are processed in an unspecified order.
6876 : * Note that if a child inherits from the original table via multiple
6877 : * inheritance paths, it will be visited just once.
6878 : */
6879 : static void
6880 913 : ATSimpleRecursion(List **wqueue, Relation rel,
6881 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6882 : AlterTableUtilityContext *context)
6883 : {
6884 : /*
6885 : * Propagate to children, if desired and if there are (or might be) any
6886 : * children.
6887 : */
6888 913 : if (recurse && rel->rd_rel->relhassubclass)
6889 : {
6890 59 : Oid relid = RelationGetRelid(rel);
6891 : ListCell *child;
6892 : List *children;
6893 :
6894 59 : children = find_all_inheritors(relid, lockmode, NULL);
6895 :
6896 : /*
6897 : * find_all_inheritors does the recursive search of the inheritance
6898 : * hierarchy, so all we have to do is process all of the relids in the
6899 : * list that it returns.
6900 : */
6901 256 : foreach(child, children)
6902 : {
6903 197 : Oid childrelid = lfirst_oid(child);
6904 : Relation childrel;
6905 :
6906 197 : if (childrelid == relid)
6907 59 : continue;
6908 : /* find_all_inheritors already got lock */
6909 138 : childrel = relation_open(childrelid, NoLock);
6910 138 : CheckAlterTableIsSafe(childrel);
6911 138 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6912 138 : relation_close(childrel, NoLock);
6913 : }
6914 : }
6915 913 : }
6916 :
6917 : /*
6918 : * Obtain list of partitions of the given table, locking them all at the given
6919 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6920 : *
6921 : * This function is a no-op if the given relation is not a partitioned table;
6922 : * in particular, nothing is done if it's a legacy inheritance parent.
6923 : */
6924 : static void
6925 577 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6926 : {
6927 577 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6928 : {
6929 : List *inh;
6930 : ListCell *cell;
6931 :
6932 121 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6933 : /* first element is the parent rel; must ignore it */
6934 398 : for_each_from(cell, inh, 1)
6935 : {
6936 : Relation childrel;
6937 :
6938 : /* find_all_inheritors already got lock */
6939 281 : childrel = table_open(lfirst_oid(cell), NoLock);
6940 281 : CheckAlterTableIsSafe(childrel);
6941 277 : table_close(childrel, NoLock);
6942 : }
6943 117 : list_free(inh);
6944 : }
6945 573 : }
6946 :
6947 : /*
6948 : * ATTypedTableRecursion
6949 : *
6950 : * Propagate ALTER TYPE operations to the typed tables of that type.
6951 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6952 : * recursion to inheritance children of the typed tables.
6953 : */
6954 : static void
6955 125 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6956 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6957 : {
6958 : ListCell *child;
6959 : List *children;
6960 :
6961 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6962 :
6963 125 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6964 125 : RelationGetRelationName(rel),
6965 : cmd->behavior);
6966 :
6967 133 : foreach(child, children)
6968 : {
6969 20 : Oid childrelid = lfirst_oid(child);
6970 : Relation childrel;
6971 :
6972 20 : childrel = relation_open(childrelid, lockmode);
6973 20 : CheckAlterTableIsSafe(childrel);
6974 20 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6975 20 : relation_close(childrel, NoLock);
6976 : }
6977 113 : }
6978 :
6979 :
6980 : /*
6981 : * find_composite_type_dependencies
6982 : *
6983 : * Check to see if the type "typeOid" is being used as a column in some table
6984 : * (possibly nested several levels deep in composite types, arrays, etc!).
6985 : * Eventually, we'd like to propagate the check or rewrite operation
6986 : * into such tables, but for now, just error out if we find any.
6987 : *
6988 : * Caller should provide either the associated relation of a rowtype,
6989 : * or a type name (not both) for use in the error message, if any.
6990 : *
6991 : * Note that "typeOid" is not necessarily a composite type; it could also be
6992 : * another container type such as an array or range, or a domain over one of
6993 : * these things. The name of this function is therefore somewhat historical,
6994 : * but it's not worth changing.
6995 : *
6996 : * We assume that functions and views depending on the type are not reasons
6997 : * to reject the ALTER. (How safe is this really?)
6998 : */
6999 : void
7000 3321 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
7001 : const char *origTypeName)
7002 : {
7003 : Relation depRel;
7004 : ScanKeyData key[2];
7005 : SysScanDesc depScan;
7006 : HeapTuple depTup;
7007 :
7008 : /* since this function recurses, it could be driven to stack overflow */
7009 3321 : check_stack_depth();
7010 :
7011 : /*
7012 : * We scan pg_depend to find those things that depend on the given type.
7013 : * (We assume we can ignore refobjsubid for a type.)
7014 : */
7015 3321 : depRel = table_open(DependRelationId, AccessShareLock);
7016 :
7017 3321 : ScanKeyInit(&key[0],
7018 : Anum_pg_depend_refclassid,
7019 : BTEqualStrategyNumber, F_OIDEQ,
7020 : ObjectIdGetDatum(TypeRelationId));
7021 3321 : ScanKeyInit(&key[1],
7022 : Anum_pg_depend_refobjid,
7023 : BTEqualStrategyNumber, F_OIDEQ,
7024 : ObjectIdGetDatum(typeOid));
7025 :
7026 3321 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
7027 : NULL, 2, key);
7028 :
7029 5093 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
7030 : {
7031 1876 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
7032 : Relation rel;
7033 : TupleDesc tupleDesc;
7034 : Form_pg_attribute att;
7035 :
7036 : /* Check for directly dependent types */
7037 1876 : if (pg_depend->classid == TypeRelationId)
7038 : {
7039 : /*
7040 : * This must be an array, domain, or range containing the given
7041 : * type, so recursively check for uses of this type. Note that
7042 : * any error message will mention the original type not the
7043 : * container; this is intentional.
7044 : */
7045 1606 : find_composite_type_dependencies(pg_depend->objid,
7046 : origRelation, origTypeName);
7047 1590 : continue;
7048 : }
7049 :
7050 : /* Else, ignore dependees that aren't relations */
7051 270 : if (pg_depend->classid != RelationRelationId)
7052 81 : continue;
7053 :
7054 189 : rel = relation_open(pg_depend->objid, AccessShareLock);
7055 189 : tupleDesc = RelationGetDescr(rel);
7056 :
7057 : /*
7058 : * If objsubid identifies a specific column, refer to that in error
7059 : * messages. Otherwise, search to see if there's a user column of the
7060 : * type. (We assume system columns are never of interesting types.)
7061 : * The search is needed because an index containing an expression
7062 : * column of the target type will just be recorded as a whole-relation
7063 : * dependency. If we do not find a column of the type, the dependency
7064 : * must indicate that the type is transiently referenced in an index
7065 : * expression but not stored on disk, which we assume is OK, just as
7066 : * we do for references in views. (It could also be that the target
7067 : * type is embedded in some container type that is stored in an index
7068 : * column, but the previous recursion should catch such cases.)
7069 : */
7070 189 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7071 84 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7072 : else
7073 : {
7074 105 : att = NULL;
7075 270 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
7076 : {
7077 169 : att = TupleDescAttr(tupleDesc, attno - 1);
7078 169 : if (att->atttypid == typeOid && !att->attisdropped)
7079 4 : break;
7080 165 : att = NULL;
7081 : }
7082 105 : if (att == NULL)
7083 : {
7084 : /* No such column, so assume OK */
7085 101 : relation_close(rel, AccessShareLock);
7086 101 : continue;
7087 : }
7088 : }
7089 :
7090 : /*
7091 : * We definitely should reject if the relation has storage. If it's
7092 : * partitioned, then perhaps we don't have to reject: if there are
7093 : * partitions then we'll fail when we find one, else there is no
7094 : * stored data to worry about. However, it's possible that the type
7095 : * change would affect conclusions about whether the type is sortable
7096 : * or hashable and thus (if it's a partitioning column) break the
7097 : * partitioning rule. For now, reject for partitioned rels too.
7098 : */
7099 88 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7100 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7101 : {
7102 88 : if (origTypeName)
7103 20 : ereport(ERROR,
7104 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7105 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7106 : origTypeName,
7107 : RelationGetRelationName(rel),
7108 : NameStr(att->attname))));
7109 68 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7110 12 : ereport(ERROR,
7111 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7112 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7113 : RelationGetRelationName(origRelation),
7114 : RelationGetRelationName(rel),
7115 : NameStr(att->attname))));
7116 56 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7117 4 : ereport(ERROR,
7118 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7119 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7120 : RelationGetRelationName(origRelation),
7121 : RelationGetRelationName(rel),
7122 : NameStr(att->attname))));
7123 : else
7124 52 : ereport(ERROR,
7125 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7126 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7127 : RelationGetRelationName(origRelation),
7128 : RelationGetRelationName(rel),
7129 : NameStr(att->attname))));
7130 : }
7131 0 : else if (OidIsValid(rel->rd_rel->reltype))
7132 : {
7133 : /*
7134 : * A view or composite type itself isn't a problem, but we must
7135 : * recursively check for indirect dependencies via its rowtype.
7136 : */
7137 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
7138 : origRelation, origTypeName);
7139 : }
7140 :
7141 0 : relation_close(rel, AccessShareLock);
7142 : }
7143 :
7144 3217 : systable_endscan(depScan);
7145 :
7146 3217 : relation_close(depRel, AccessShareLock);
7147 3217 : }
7148 :
7149 :
7150 : /*
7151 : * find_typed_table_dependencies
7152 : *
7153 : * Check to see if a composite type is being used as the type of a
7154 : * typed table. Abort if any are found and behavior is RESTRICT.
7155 : * Else return the list of tables.
7156 : */
7157 : static List *
7158 141 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7159 : {
7160 : Relation classRel;
7161 : ScanKeyData key[1];
7162 : TableScanDesc scan;
7163 : HeapTuple tuple;
7164 141 : List *result = NIL;
7165 :
7166 141 : classRel = table_open(RelationRelationId, AccessShareLock);
7167 :
7168 141 : ScanKeyInit(&key[0],
7169 : Anum_pg_class_reloftype,
7170 : BTEqualStrategyNumber, F_OIDEQ,
7171 : ObjectIdGetDatum(typeOid));
7172 :
7173 141 : scan = table_beginscan_catalog(classRel, 1, key);
7174 :
7175 165 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7176 : {
7177 40 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7178 :
7179 40 : if (behavior == DROP_RESTRICT)
7180 16 : ereport(ERROR,
7181 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7182 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7183 : typeName),
7184 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7185 : else
7186 24 : result = lappend_oid(result, classform->oid);
7187 : }
7188 :
7189 125 : table_endscan(scan);
7190 125 : table_close(classRel, AccessShareLock);
7191 :
7192 125 : return result;
7193 : }
7194 :
7195 :
7196 : /*
7197 : * check_of_type
7198 : *
7199 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7200 : * isn't suitable, throw an error. Currently, we require that the type
7201 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7202 : * would require handling a number of extra corner cases in the DDL commands.
7203 : * (Also, allowing domain-over-composite would open up a can of worms about
7204 : * whether and how the domain's constraints should apply to derived tables.)
7205 : */
7206 : void
7207 119 : check_of_type(HeapTuple typetuple)
7208 : {
7209 119 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7210 119 : bool typeOk = false;
7211 :
7212 119 : if (typ->typtype == TYPTYPE_COMPOSITE)
7213 : {
7214 : Relation typeRelation;
7215 :
7216 : Assert(OidIsValid(typ->typrelid));
7217 115 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7218 115 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7219 :
7220 : /*
7221 : * Close the parent rel, but keep our AccessShareLock on it until xact
7222 : * commit. That will prevent someone else from deleting or ALTERing
7223 : * the type before the typed table creation/conversion commits.
7224 : */
7225 115 : relation_close(typeRelation, NoLock);
7226 :
7227 115 : if (!typeOk)
7228 4 : ereport(ERROR,
7229 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7230 : errmsg("type %s is the row type of another table",
7231 : format_type_be(typ->oid)),
7232 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7233 : }
7234 : else
7235 4 : ereport(ERROR,
7236 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7237 : errmsg("type %s is not a composite type",
7238 : format_type_be(typ->oid))));
7239 111 : }
7240 :
7241 :
7242 : /*
7243 : * ALTER TABLE ADD COLUMN
7244 : *
7245 : * Adds an additional attribute to a relation making the assumption that
7246 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7247 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7248 : * AlterTableCmd's.
7249 : *
7250 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7251 : * have to decide at runtime whether to recurse or not depending on whether we
7252 : * actually add a column or merely merge with an existing column. (We can't
7253 : * check this in a static pre-pass because it won't handle multiple inheritance
7254 : * situations correctly.)
7255 : */
7256 : static void
7257 1617 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7258 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7259 : AlterTableUtilityContext *context)
7260 : {
7261 1617 : if (rel->rd_rel->reloftype && !recursing)
7262 4 : ereport(ERROR,
7263 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7264 : errmsg("cannot add column to typed table")));
7265 :
7266 1613 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7267 38 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7268 :
7269 1609 : if (recurse && !is_view)
7270 1538 : cmd->recurse = true;
7271 1609 : }
7272 :
7273 : /*
7274 : * Add a column to a table. The return value is the address of the
7275 : * new column in the parent relation.
7276 : *
7277 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7278 : * copy (but that happens only after we check for IF NOT EXISTS).
7279 : */
7280 : static ObjectAddress
7281 2119 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7282 : AlterTableCmd **cmd, bool recurse, bool recursing,
7283 : LOCKMODE lockmode, AlterTablePass cur_pass,
7284 : AlterTableUtilityContext *context)
7285 : {
7286 2119 : Oid myrelid = RelationGetRelid(rel);
7287 2119 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7288 2119 : bool if_not_exists = (*cmd)->missing_ok;
7289 : Relation pgclass,
7290 : attrdesc;
7291 : HeapTuple reltup;
7292 : Form_pg_class relform;
7293 : Form_pg_attribute attribute;
7294 : int newattnum;
7295 : char relkind;
7296 : Expr *defval;
7297 : List *children;
7298 : ListCell *child;
7299 : AlterTableCmd *childcmd;
7300 : ObjectAddress address;
7301 : TupleDesc tupdesc;
7302 :
7303 : /* since this function recurses, it could be driven to stack overflow */
7304 2119 : check_stack_depth();
7305 :
7306 : /* At top level, permission check was done in ATPrepCmd, else do it */
7307 2119 : if (recursing)
7308 514 : ATSimplePermissions((*cmd)->subtype, rel,
7309 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7310 :
7311 2119 : if (rel->rd_rel->relispartition && !recursing)
7312 8 : ereport(ERROR,
7313 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7314 : errmsg("cannot add column to a partition")));
7315 :
7316 2111 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7317 :
7318 : /*
7319 : * Are we adding the column to a recursion child? If so, check whether to
7320 : * merge with an existing definition for the column. If we do merge, we
7321 : * must not recurse. Children will already have the column, and recursing
7322 : * into them would mess up attinhcount.
7323 : */
7324 2111 : if (colDef->inhcount > 0)
7325 : {
7326 : HeapTuple tuple;
7327 :
7328 : /* Does child already have a column by this name? */
7329 514 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7330 514 : if (HeapTupleIsValid(tuple))
7331 : {
7332 40 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7333 : Oid ctypeId;
7334 : int32 ctypmod;
7335 : Oid ccollid;
7336 :
7337 : /* Child column must match on type, typmod, and collation */
7338 40 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7339 40 : if (ctypeId != childatt->atttypid ||
7340 40 : ctypmod != childatt->atttypmod)
7341 0 : ereport(ERROR,
7342 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7343 : errmsg("child table \"%s\" has different type for column \"%s\"",
7344 : RelationGetRelationName(rel), colDef->colname)));
7345 40 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7346 40 : if (ccollid != childatt->attcollation)
7347 0 : ereport(ERROR,
7348 : (errcode(ERRCODE_COLLATION_MISMATCH),
7349 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7350 : RelationGetRelationName(rel), colDef->colname),
7351 : errdetail("\"%s\" versus \"%s\"",
7352 : get_collation_name(ccollid),
7353 : get_collation_name(childatt->attcollation))));
7354 :
7355 : /* Bump the existing child att's inhcount */
7356 40 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7357 : &childatt->attinhcount))
7358 0 : ereport(ERROR,
7359 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7360 : errmsg("too many inheritance parents"));
7361 40 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7362 :
7363 40 : heap_freetuple(tuple);
7364 :
7365 : /* Inform the user about the merge */
7366 40 : ereport(NOTICE,
7367 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7368 : colDef->colname, RelationGetRelationName(rel))));
7369 :
7370 40 : table_close(attrdesc, RowExclusiveLock);
7371 :
7372 : /* Make the child column change visible */
7373 40 : CommandCounterIncrement();
7374 :
7375 40 : return InvalidObjectAddress;
7376 : }
7377 : }
7378 :
7379 : /* skip if the name already exists and if_not_exists is true */
7380 2071 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7381 : {
7382 44 : table_close(attrdesc, RowExclusiveLock);
7383 44 : return InvalidObjectAddress;
7384 : }
7385 :
7386 : /*
7387 : * Okay, we need to add the column, so go ahead and do parse
7388 : * transformation. This can result in queueing up, or even immediately
7389 : * executing, subsidiary operations (such as creation of unique indexes);
7390 : * so we mustn't do it until we have made the if_not_exists check.
7391 : *
7392 : * When recursing, the command was already transformed and we needn't do
7393 : * so again. Also, if context isn't given we can't transform. (That
7394 : * currently happens only for AT_AddColumnToView; we expect that view.c
7395 : * passed us a ColumnDef that doesn't need work.)
7396 : */
7397 2007 : if (context != NULL && !recursing)
7398 : {
7399 1512 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7400 : cur_pass, context);
7401 : Assert(*cmd != NULL);
7402 1508 : colDef = castNode(ColumnDef, (*cmd)->def);
7403 : }
7404 :
7405 : /*
7406 : * Regular inheritance children are independent enough not to inherit the
7407 : * identity column from parent hence cannot recursively add identity
7408 : * column if the table has inheritance children.
7409 : *
7410 : * Partitions, on the other hand, are integral part of a partitioned table
7411 : * and inherit identity column. Hence propagate identity column down the
7412 : * partition hierarchy.
7413 : */
7414 2003 : if (colDef->identity &&
7415 36 : recurse &&
7416 68 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7417 32 : find_inheritance_children(myrelid, NoLock) != NIL)
7418 4 : ereport(ERROR,
7419 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7420 : errmsg("cannot recursively add identity column to table that has child tables")));
7421 :
7422 1999 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7423 :
7424 1999 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7425 1999 : if (!HeapTupleIsValid(reltup))
7426 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7427 1999 : relform = (Form_pg_class) GETSTRUCT(reltup);
7428 1999 : relkind = relform->relkind;
7429 :
7430 : /* Determine the new attribute's number */
7431 1999 : newattnum = relform->relnatts + 1;
7432 1999 : if (newattnum > MaxHeapAttributeNumber)
7433 0 : ereport(ERROR,
7434 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7435 : errmsg("tables can have at most %d columns",
7436 : MaxHeapAttributeNumber)));
7437 :
7438 : /*
7439 : * Construct new attribute's pg_attribute entry.
7440 : */
7441 1999 : tupdesc = BuildDescForRelation(list_make1(colDef));
7442 :
7443 1991 : attribute = TupleDescAttr(tupdesc, 0);
7444 :
7445 : /* Fix up attribute number */
7446 1991 : attribute->attnum = newattnum;
7447 :
7448 : /* make sure datatype is legal for a column */
7449 3982 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7450 1991 : list_make1_oid(rel->rd_rel->reltype),
7451 1991 : (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7452 :
7453 1963 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7454 :
7455 1963 : table_close(attrdesc, RowExclusiveLock);
7456 :
7457 : /*
7458 : * Update pg_class tuple as appropriate
7459 : */
7460 1963 : relform->relnatts = newattnum;
7461 :
7462 1963 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7463 :
7464 1963 : heap_freetuple(reltup);
7465 :
7466 : /* Post creation hook for new attribute */
7467 1963 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7468 :
7469 1963 : table_close(pgclass, RowExclusiveLock);
7470 :
7471 : /* Make the attribute's catalog entry visible */
7472 1963 : CommandCounterIncrement();
7473 :
7474 : /*
7475 : * Store the DEFAULT, if any, in the catalogs
7476 : */
7477 1963 : if (colDef->raw_default)
7478 : {
7479 : RawColumnDefault *rawEnt;
7480 :
7481 749 : rawEnt = palloc_object(RawColumnDefault);
7482 749 : rawEnt->attnum = attribute->attnum;
7483 749 : rawEnt->raw_default = copyObject(colDef->raw_default);
7484 749 : rawEnt->generated = colDef->generated;
7485 :
7486 : /*
7487 : * This function is intended for CREATE TABLE, so it processes a
7488 : * _list_ of defaults, but we just do one.
7489 : */
7490 749 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7491 : false, true, false, NULL);
7492 :
7493 : /* Make the additional catalog changes visible */
7494 661 : CommandCounterIncrement();
7495 : }
7496 :
7497 : /*
7498 : * Tell Phase 3 to fill in the default expression, if there is one.
7499 : *
7500 : * If there is no default, Phase 3 doesn't have to do anything, because
7501 : * that effectively means that the default is NULL. The heap tuple access
7502 : * routines always check for attnum > # of attributes in tuple, and return
7503 : * NULL if so, so without any modification of the tuple data we will get
7504 : * the effect of NULL values in the new column.
7505 : *
7506 : * Note: we use build_column_default, and not just the cooked default
7507 : * returned by AddRelationNewConstraints, so that the right thing happens
7508 : * when a datatype's default applies.
7509 : *
7510 : * Note: it might seem that this should happen at the end of Phase 2, so
7511 : * that the effects of subsequent subcommands can be taken into account.
7512 : * It's intentional that we do it now, though. The new column should be
7513 : * filled according to what is said in the ADD COLUMN subcommand, so that
7514 : * the effects are the same as if this subcommand had been run by itself
7515 : * and the later subcommands had been issued in new ALTER TABLE commands.
7516 : *
7517 : * We can skip this entirely for relations without storage, since Phase 3
7518 : * is certainly not going to touch them.
7519 : */
7520 1875 : if (RELKIND_HAS_STORAGE(relkind))
7521 : {
7522 : bool has_domain_constraints;
7523 1605 : bool has_missing = false;
7524 1605 : bool has_volatile = false;
7525 :
7526 : /*
7527 : * For an identity column, we can't use build_column_default(),
7528 : * because the sequence ownership isn't set yet. So do it manually.
7529 : */
7530 1605 : if (colDef->identity)
7531 : {
7532 28 : NextValueExpr *nve = makeNode(NextValueExpr);
7533 :
7534 28 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7535 28 : nve->typeId = attribute->atttypid;
7536 :
7537 28 : defval = (Expr *) nve;
7538 : }
7539 : else
7540 1577 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7541 :
7542 : has_domain_constraints =
7543 1605 : DomainHasConstraints(attribute->atttypid, &has_volatile);
7544 :
7545 : /*
7546 : * If the domain has volatile constraints, we must do a table rewrite
7547 : * since the constraint result could differ per row and cannot be
7548 : * evaluated once and cached as a missing value.
7549 : */
7550 1605 : if (has_volatile)
7551 12 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7552 :
7553 : /* Build CoerceToDomain(NULL) expression if needed */
7554 1605 : if (!defval && has_domain_constraints)
7555 : {
7556 : Oid baseTypeId;
7557 : int32 baseTypeMod;
7558 : Oid baseTypeColl;
7559 :
7560 12 : baseTypeMod = attribute->atttypmod;
7561 12 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7562 12 : baseTypeColl = get_typcollation(baseTypeId);
7563 12 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7564 12 : defval = (Expr *) coerce_to_target_type(NULL,
7565 : (Node *) defval,
7566 : baseTypeId,
7567 : attribute->atttypid,
7568 : attribute->atttypmod,
7569 : COERCION_ASSIGNMENT,
7570 : COERCE_IMPLICIT_CAST,
7571 : -1);
7572 12 : if (defval == NULL) /* should not happen */
7573 0 : elog(ERROR, "failed to coerce base type to domain");
7574 : }
7575 :
7576 1605 : if (defval)
7577 : {
7578 : NewColumnValue *newval;
7579 :
7580 : /* Prepare defval for execution, either here or in Phase 3 */
7581 618 : defval = expression_planner(defval);
7582 :
7583 : /* Add the new default to the newvals list */
7584 618 : newval = palloc0_object(NewColumnValue);
7585 618 : newval->attnum = attribute->attnum;
7586 618 : newval->expr = defval;
7587 618 : newval->is_generated = (colDef->generated != '\0');
7588 :
7589 618 : tab->newvals = lappend(tab->newvals, newval);
7590 :
7591 : /*
7592 : * Attempt to skip a complete table rewrite by storing the
7593 : * specified DEFAULT value outside of the heap. This is only
7594 : * allowed for plain relations and non-generated columns, and the
7595 : * default expression can't be volatile (stable is OK), and the
7596 : * domain constraint expressions can't be volatile (stable is OK).
7597 : *
7598 : * Note that contain_volatile_functions considers CoerceToDomain
7599 : * immutable, so we rely on DomainHasConstraints (called above)
7600 : * rather than checking defval alone.
7601 : *
7602 : * For domains with non-volatile constraints, we evaluate the
7603 : * default using soft error handling: if the constraint check
7604 : * fails (e.g., CHECK(value > 10) with DEFAULT 8), we fall back to
7605 : * a table rewrite. This preserves the historical behavior that
7606 : * such a failure is only raised when the table has rows.
7607 : */
7608 618 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7609 618 : !colDef->generated &&
7610 493 : !has_volatile &&
7611 481 : !contain_volatile_functions((Node *) defval))
7612 367 : {
7613 : EState *estate;
7614 : ExprState *exprState;
7615 : Datum missingval;
7616 : bool missingIsNull;
7617 367 : ErrorSaveContext escontext = {T_ErrorSaveContext};
7618 :
7619 : /* Evaluate the default expression with soft errors */
7620 367 : estate = CreateExecutorState();
7621 367 : exprState = ExecPrepareExprWithContext(defval, estate,
7622 : (Node *) &escontext);
7623 367 : missingval = ExecEvalExpr(exprState,
7624 367 : GetPerTupleExprContext(estate),
7625 : &missingIsNull);
7626 :
7627 : /*
7628 : * If the domain constraint check failed (via errsave),
7629 : * missingval is unreliable. Fall back to a table rewrite;
7630 : * Phase 3 will re-evaluate with hard errors, so the user gets
7631 : * an error only if the table has rows.
7632 : */
7633 367 : if (escontext.error_occurred)
7634 : {
7635 24 : missingIsNull = true;
7636 24 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7637 : }
7638 :
7639 : /* If it turns out NULL, nothing to do; else store it */
7640 367 : if (!missingIsNull)
7641 : {
7642 343 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7643 : /* Make the additional catalog change visible */
7644 343 : CommandCounterIncrement();
7645 343 : has_missing = true;
7646 : }
7647 367 : FreeExecutorState(estate);
7648 : }
7649 : else
7650 : {
7651 : /*
7652 : * Failed to use missing mode. We have to do a table rewrite
7653 : * to install the value --- unless it's a virtual generated
7654 : * column.
7655 : */
7656 251 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7657 182 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7658 : }
7659 : }
7660 :
7661 1605 : if (!has_missing)
7662 : {
7663 : /*
7664 : * If the new column is NOT NULL, and there is no missing value,
7665 : * tell Phase 3 it needs to check for NULLs.
7666 : */
7667 1262 : tab->verify_new_notnull |= colDef->is_not_null;
7668 : }
7669 : }
7670 :
7671 : /*
7672 : * Add needed dependency entries for the new column.
7673 : */
7674 1875 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7675 1875 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7676 :
7677 : /*
7678 : * Propagate to children as appropriate. Unlike most other ALTER
7679 : * routines, we have to do this one level of recursion at a time; we can't
7680 : * use find_all_inheritors to do it in one pass.
7681 : */
7682 : children =
7683 1875 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7684 :
7685 : /*
7686 : * If we are told not to recurse, there had better not be any child
7687 : * tables; else the addition would put them out of step.
7688 : */
7689 1875 : if (children && !recurse)
7690 8 : ereport(ERROR,
7691 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7692 : errmsg("column must be added to child tables too")));
7693 :
7694 : /* Children should see column as singly inherited */
7695 1867 : if (!recursing)
7696 : {
7697 1393 : childcmd = copyObject(*cmd);
7698 1393 : colDef = castNode(ColumnDef, childcmd->def);
7699 1393 : colDef->inhcount = 1;
7700 1393 : colDef->is_local = false;
7701 : }
7702 : else
7703 474 : childcmd = *cmd; /* no need to copy again */
7704 :
7705 2381 : foreach(child, children)
7706 : {
7707 514 : Oid childrelid = lfirst_oid(child);
7708 : Relation childrel;
7709 : AlteredTableInfo *childtab;
7710 :
7711 : /* find_inheritance_children already got lock */
7712 514 : childrel = table_open(childrelid, NoLock);
7713 514 : CheckAlterTableIsSafe(childrel);
7714 :
7715 : /* Find or create work queue entry for this table */
7716 514 : childtab = ATGetQueueEntry(wqueue, childrel);
7717 :
7718 : /* Recurse to child; return value is ignored */
7719 514 : ATExecAddColumn(wqueue, childtab, childrel,
7720 : &childcmd, recurse, true,
7721 : lockmode, cur_pass, context);
7722 :
7723 514 : table_close(childrel, NoLock);
7724 : }
7725 :
7726 1867 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7727 1867 : return address;
7728 : }
7729 :
7730 : /*
7731 : * If a new or renamed column will collide with the name of an existing
7732 : * column and if_not_exists is false then error out, else do nothing.
7733 : */
7734 : static bool
7735 2369 : check_for_column_name_collision(Relation rel, const char *colname,
7736 : bool if_not_exists)
7737 : {
7738 : HeapTuple attTuple;
7739 : int attnum;
7740 :
7741 : /*
7742 : * this test is deliberately not attisdropped-aware, since if one tries to
7743 : * add a column matching a dropped column name, it's gonna fail anyway.
7744 : */
7745 2369 : attTuple = SearchSysCache2(ATTNAME,
7746 : ObjectIdGetDatum(RelationGetRelid(rel)),
7747 : PointerGetDatum(colname));
7748 2369 : if (!HeapTupleIsValid(attTuple))
7749 2297 : return true;
7750 :
7751 72 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7752 72 : ReleaseSysCache(attTuple);
7753 :
7754 : /*
7755 : * We throw a different error message for conflicts with system column
7756 : * names, since they are normally not shown and the user might otherwise
7757 : * be confused about the reason for the conflict.
7758 : */
7759 72 : if (attnum <= 0)
7760 8 : ereport(ERROR,
7761 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7762 : errmsg("column name \"%s\" conflicts with a system column name",
7763 : colname)));
7764 : else
7765 : {
7766 64 : if (if_not_exists)
7767 : {
7768 44 : ereport(NOTICE,
7769 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7770 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7771 : colname, RelationGetRelationName(rel))));
7772 44 : return false;
7773 : }
7774 :
7775 20 : ereport(ERROR,
7776 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7777 : errmsg("column \"%s\" of relation \"%s\" already exists",
7778 : colname, RelationGetRelationName(rel))));
7779 : }
7780 :
7781 : return true;
7782 : }
7783 :
7784 : /*
7785 : * Install a column's dependency on its datatype.
7786 : */
7787 : static void
7788 2639 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7789 : {
7790 : ObjectAddress myself,
7791 : referenced;
7792 :
7793 2639 : myself.classId = RelationRelationId;
7794 2639 : myself.objectId = relid;
7795 2639 : myself.objectSubId = attnum;
7796 2639 : referenced.classId = TypeRelationId;
7797 2639 : referenced.objectId = typid;
7798 2639 : referenced.objectSubId = 0;
7799 2639 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7800 2639 : }
7801 :
7802 : /*
7803 : * Install a column's dependency on its collation.
7804 : */
7805 : static void
7806 2639 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7807 : {
7808 : ObjectAddress myself,
7809 : referenced;
7810 :
7811 : /* We know the default collation is pinned, so don't bother recording it */
7812 2639 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7813 : {
7814 12 : myself.classId = RelationRelationId;
7815 12 : myself.objectId = relid;
7816 12 : myself.objectSubId = attnum;
7817 12 : referenced.classId = CollationRelationId;
7818 12 : referenced.objectId = collid;
7819 12 : referenced.objectSubId = 0;
7820 12 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7821 : }
7822 2639 : }
7823 :
7824 : /*
7825 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7826 : *
7827 : * Return the address of the modified column. If the column was already
7828 : * nullable, InvalidObjectAddress is returned.
7829 : */
7830 : static ObjectAddress
7831 177 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7832 : LOCKMODE lockmode)
7833 : {
7834 : HeapTuple tuple;
7835 : HeapTuple conTup;
7836 : Form_pg_attribute attTup;
7837 : AttrNumber attnum;
7838 : Relation attr_rel;
7839 : ObjectAddress address;
7840 :
7841 : /*
7842 : * lookup the attribute
7843 : */
7844 177 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7845 :
7846 177 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7847 177 : if (!HeapTupleIsValid(tuple))
7848 12 : ereport(ERROR,
7849 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7850 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7851 : colName, RelationGetRelationName(rel))));
7852 165 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7853 165 : attnum = attTup->attnum;
7854 165 : ObjectAddressSubSet(address, RelationRelationId,
7855 : RelationGetRelid(rel), attnum);
7856 :
7857 : /* If the column is already nullable there's nothing to do. */
7858 165 : if (!attTup->attnotnull)
7859 : {
7860 0 : table_close(attr_rel, RowExclusiveLock);
7861 0 : return InvalidObjectAddress;
7862 : }
7863 :
7864 : /* Prevent them from altering a system attribute */
7865 165 : if (attnum <= 0)
7866 0 : ereport(ERROR,
7867 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7868 : errmsg("cannot alter system column \"%s\"",
7869 : colName)));
7870 :
7871 165 : if (attTup->attidentity)
7872 12 : ereport(ERROR,
7873 : (errcode(ERRCODE_SYNTAX_ERROR),
7874 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7875 : colName, RelationGetRelationName(rel))));
7876 :
7877 : /*
7878 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7879 : */
7880 153 : if (rel->rd_rel->relispartition)
7881 : {
7882 8 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7883 8 : Relation parent = table_open(parentId, AccessShareLock);
7884 8 : TupleDesc tupDesc = RelationGetDescr(parent);
7885 : AttrNumber parent_attnum;
7886 :
7887 8 : parent_attnum = get_attnum(parentId, colName);
7888 8 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7889 8 : ereport(ERROR,
7890 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7891 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7892 : colName)));
7893 0 : table_close(parent, AccessShareLock);
7894 : }
7895 :
7896 : /*
7897 : * Find the constraint that makes this column NOT NULL, and drop it.
7898 : * dropconstraint_internal() resets attnotnull.
7899 : */
7900 145 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7901 145 : if (conTup == NULL)
7902 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7903 : colName, RelationGetRelationName(rel));
7904 :
7905 : /* The normal case: we have a pg_constraint row, remove it */
7906 145 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7907 : false, lockmode);
7908 109 : heap_freetuple(conTup);
7909 :
7910 109 : InvokeObjectPostAlterHook(RelationRelationId,
7911 : RelationGetRelid(rel), attnum);
7912 :
7913 109 : table_close(attr_rel, RowExclusiveLock);
7914 :
7915 109 : return address;
7916 : }
7917 :
7918 : /*
7919 : * set_attnotnull
7920 : * Helper to update/validate the pg_attribute status of a not-null
7921 : * constraint
7922 : *
7923 : * pg_attribute.attnotnull is set true, if it isn't already.
7924 : * If queue_validation is true, also set up wqueue to validate the constraint.
7925 : * wqueue may be given as NULL when validation is not needed (e.g., on table
7926 : * creation).
7927 : */
7928 : static void
7929 16407 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7930 : bool is_valid, bool queue_validation)
7931 : {
7932 : Form_pg_attribute attr;
7933 : CompactAttribute *thisatt;
7934 :
7935 : Assert(!queue_validation || wqueue);
7936 :
7937 16407 : CheckAlterTableIsSafe(rel);
7938 :
7939 : /*
7940 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7941 : * attribute.
7942 : */
7943 16407 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7944 16407 : if (attr->attisdropped)
7945 0 : return;
7946 :
7947 16407 : if (!attr->attnotnull)
7948 : {
7949 : Relation attr_rel;
7950 : HeapTuple tuple;
7951 :
7952 979 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7953 :
7954 979 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7955 979 : if (!HeapTupleIsValid(tuple))
7956 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7957 : attnum, RelationGetRelid(rel));
7958 :
7959 979 : thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7960 979 : thisatt->attnullability = ATTNULLABLE_VALID;
7961 :
7962 979 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7963 :
7964 979 : attr->attnotnull = true;
7965 979 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7966 :
7967 : /*
7968 : * If the nullness isn't already proven by validated constraints, have
7969 : * ALTER TABLE phase 3 test for it.
7970 : */
7971 979 : if (queue_validation && wqueue &&
7972 816 : !NotNullImpliedByRelConstraints(rel, attr))
7973 : {
7974 : AlteredTableInfo *tab;
7975 :
7976 784 : tab = ATGetQueueEntry(wqueue, rel);
7977 784 : tab->verify_new_notnull = true;
7978 : }
7979 :
7980 979 : CommandCounterIncrement();
7981 :
7982 979 : table_close(attr_rel, RowExclusiveLock);
7983 979 : heap_freetuple(tuple);
7984 : }
7985 : else
7986 : {
7987 15428 : CacheInvalidateRelcache(rel);
7988 : }
7989 : }
7990 :
7991 : /*
7992 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7993 : *
7994 : * Add a not-null constraint to a single table and its children. Returns
7995 : * the address of the constraint added to the parent relation, if one gets
7996 : * added, or InvalidObjectAddress otherwise.
7997 : *
7998 : * We must recurse to child tables during execution, rather than using
7999 : * ALTER TABLE's normal prep-time recursion.
8000 : */
8001 : static ObjectAddress
8002 469 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
8003 : bool recurse, bool recursing, LOCKMODE lockmode)
8004 : {
8005 : HeapTuple tuple;
8006 : AttrNumber attnum;
8007 : ObjectAddress address;
8008 : Constraint *constraint;
8009 : CookedConstraint *ccon;
8010 : List *cooked;
8011 469 : bool is_no_inherit = false;
8012 :
8013 : /* Guard against stack overflow due to overly deep inheritance tree. */
8014 469 : check_stack_depth();
8015 :
8016 : /* At top level, permission check was done in ATPrepCmd, else do it */
8017 469 : if (recursing)
8018 : {
8019 197 : ATSimplePermissions(AT_AddConstraint, rel,
8020 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
8021 : Assert(conName != NULL);
8022 : }
8023 :
8024 469 : attnum = get_attnum(RelationGetRelid(rel), colName);
8025 469 : if (attnum == InvalidAttrNumber)
8026 12 : ereport(ERROR,
8027 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8028 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8029 : colName, RelationGetRelationName(rel))));
8030 :
8031 : /* Prevent them from altering a system attribute */
8032 457 : if (attnum <= 0)
8033 0 : ereport(ERROR,
8034 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8035 : errmsg("cannot alter system column \"%s\"",
8036 : colName)));
8037 :
8038 : /* See if there's already a constraint */
8039 457 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
8040 457 : if (HeapTupleIsValid(tuple))
8041 : {
8042 105 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
8043 105 : bool changed = false;
8044 :
8045 : /*
8046 : * Don't let a NO INHERIT constraint be changed into inherit.
8047 : */
8048 105 : if (conForm->connoinherit && recurse)
8049 8 : ereport(ERROR,
8050 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8051 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
8052 : NameStr(conForm->conname),
8053 : RelationGetRelationName(rel)));
8054 :
8055 : /*
8056 : * If we find an appropriate constraint, we're almost done, but just
8057 : * need to change some properties on it: if we're recursing, increment
8058 : * coninhcount; if not, set conislocal if not already set.
8059 : */
8060 97 : if (recursing)
8061 : {
8062 68 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
8063 : &conForm->coninhcount))
8064 0 : ereport(ERROR,
8065 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
8066 : errmsg("too many inheritance parents"));
8067 68 : changed = true;
8068 : }
8069 29 : else if (!conForm->conislocal)
8070 : {
8071 0 : conForm->conislocal = true;
8072 0 : changed = true;
8073 : }
8074 29 : else if (!conForm->convalidated)
8075 : {
8076 : /*
8077 : * Flip attnotnull and convalidated, and also validate the
8078 : * constraint.
8079 : */
8080 16 : return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
8081 : recurse, recursing, lockmode);
8082 : }
8083 :
8084 81 : if (changed)
8085 : {
8086 : Relation constr_rel;
8087 :
8088 68 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
8089 :
8090 68 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
8091 68 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
8092 68 : table_close(constr_rel, RowExclusiveLock);
8093 : }
8094 :
8095 81 : if (changed)
8096 68 : return address;
8097 : else
8098 13 : return InvalidObjectAddress;
8099 : }
8100 :
8101 : /*
8102 : * If we're asked not to recurse, and children exist, raise an error for
8103 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
8104 : * specified.
8105 : */
8106 372 : if (!recurse &&
8107 20 : find_inheritance_children(RelationGetRelid(rel),
8108 : NoLock) != NIL)
8109 : {
8110 12 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8111 4 : ereport(ERROR,
8112 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8113 : errmsg("constraint must be added to child tables too"),
8114 : errhint("Do not specify the ONLY keyword."));
8115 : else
8116 8 : is_no_inherit = true;
8117 : }
8118 :
8119 : /*
8120 : * No constraint exists; we must add one. First determine a name to use,
8121 : * if we haven't already.
8122 : */
8123 348 : if (!recursing)
8124 : {
8125 : Assert(conName == NULL);
8126 223 : conName = ChooseConstraintName(RelationGetRelationName(rel),
8127 : colName, "not_null",
8128 223 : RelationGetNamespace(rel),
8129 : NIL);
8130 : }
8131 :
8132 348 : constraint = makeNotNullConstraint(makeString(colName));
8133 348 : constraint->is_no_inherit = is_no_inherit;
8134 348 : constraint->conname = conName;
8135 :
8136 : /* and do it */
8137 348 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8138 348 : false, !recursing, false, NULL);
8139 348 : ccon = linitial(cooked);
8140 348 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8141 :
8142 : /* Mark pg_attribute.attnotnull for the column and queue validation */
8143 348 : set_attnotnull(wqueue, rel, attnum, true, true);
8144 :
8145 348 : InvokeObjectPostAlterHook(RelationRelationId,
8146 : RelationGetRelid(rel), attnum);
8147 :
8148 : /*
8149 : * Recurse to propagate the constraint to children that don't have one.
8150 : */
8151 348 : if (recurse)
8152 : {
8153 : List *children;
8154 :
8155 332 : children = find_inheritance_children(RelationGetRelid(rel),
8156 : lockmode);
8157 :
8158 817 : foreach_oid(childoid, children)
8159 : {
8160 161 : Relation childrel = table_open(childoid, NoLock);
8161 :
8162 161 : CommandCounterIncrement();
8163 :
8164 161 : ATExecSetNotNull(wqueue, childrel, conName, colName,
8165 : recurse, true, lockmode);
8166 157 : table_close(childrel, NoLock);
8167 : }
8168 : }
8169 :
8170 344 : return address;
8171 : }
8172 :
8173 : /*
8174 : * NotNullImpliedByRelConstraints
8175 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8176 : */
8177 : static bool
8178 816 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8179 : {
8180 816 : NullTest *nnulltest = makeNode(NullTest);
8181 :
8182 1632 : nnulltest->arg = (Expr *) makeVar(1,
8183 816 : attr->attnum,
8184 : attr->atttypid,
8185 : attr->atttypmod,
8186 : attr->attcollation,
8187 : 0);
8188 816 : nnulltest->nulltesttype = IS_NOT_NULL;
8189 :
8190 : /*
8191 : * argisrow = false is correct even for a composite column, because
8192 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8193 : * case, just IS DISTINCT FROM NULL.
8194 : */
8195 816 : nnulltest->argisrow = false;
8196 816 : nnulltest->location = -1;
8197 :
8198 816 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8199 : {
8200 32 : ereport(DEBUG1,
8201 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8202 : RelationGetRelationName(rel), NameStr(attr->attname))));
8203 32 : return true;
8204 : }
8205 :
8206 784 : return false;
8207 : }
8208 :
8209 : /*
8210 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8211 : *
8212 : * Return the address of the affected column.
8213 : */
8214 : static ObjectAddress
8215 383 : ATExecColumnDefault(Relation rel, const char *colName,
8216 : Node *newDefault, LOCKMODE lockmode)
8217 : {
8218 383 : TupleDesc tupdesc = RelationGetDescr(rel);
8219 : AttrNumber attnum;
8220 : ObjectAddress address;
8221 :
8222 : /*
8223 : * get the number of the attribute
8224 : */
8225 383 : attnum = get_attnum(RelationGetRelid(rel), colName);
8226 383 : if (attnum == InvalidAttrNumber)
8227 20 : ereport(ERROR,
8228 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8229 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8230 : colName, RelationGetRelationName(rel))));
8231 :
8232 : /* Prevent them from altering a system attribute */
8233 363 : if (attnum <= 0)
8234 0 : ereport(ERROR,
8235 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8236 : errmsg("cannot alter system column \"%s\"",
8237 : colName)));
8238 :
8239 363 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8240 12 : ereport(ERROR,
8241 : (errcode(ERRCODE_SYNTAX_ERROR),
8242 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8243 : colName, RelationGetRelationName(rel)),
8244 : /* translator: %s is an SQL ALTER command */
8245 : newDefault ? 0 : errhint("Use %s instead.",
8246 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8247 :
8248 351 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8249 8 : ereport(ERROR,
8250 : (errcode(ERRCODE_SYNTAX_ERROR),
8251 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8252 : colName, RelationGetRelationName(rel)),
8253 : newDefault ?
8254 : /* translator: %s is an SQL ALTER command */
8255 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8256 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8257 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8258 :
8259 : /*
8260 : * Remove any old default for the column. We use RESTRICT here for
8261 : * safety, but at present we do not expect anything to depend on the
8262 : * default.
8263 : *
8264 : * We treat removing the existing default as an internal operation when it
8265 : * is preparatory to adding a new default, but as a user-initiated
8266 : * operation when the user asked for a drop.
8267 : */
8268 343 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8269 : newDefault != NULL);
8270 :
8271 343 : if (newDefault)
8272 : {
8273 : /* SET DEFAULT */
8274 : RawColumnDefault *rawEnt;
8275 :
8276 227 : rawEnt = palloc_object(RawColumnDefault);
8277 227 : rawEnt->attnum = attnum;
8278 227 : rawEnt->raw_default = newDefault;
8279 227 : rawEnt->generated = '\0';
8280 :
8281 : /*
8282 : * This function is intended for CREATE TABLE, so it processes a
8283 : * _list_ of defaults, but we just do one.
8284 : */
8285 227 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8286 : false, true, false, NULL);
8287 : }
8288 :
8289 339 : ObjectAddressSubSet(address, RelationRelationId,
8290 : RelationGetRelid(rel), attnum);
8291 339 : return address;
8292 : }
8293 :
8294 : /*
8295 : * Add a pre-cooked default expression.
8296 : *
8297 : * Return the address of the affected column.
8298 : */
8299 : static ObjectAddress
8300 53 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8301 : Node *newDefault)
8302 : {
8303 : ObjectAddress address;
8304 :
8305 : /* We assume no checking is required */
8306 :
8307 : /*
8308 : * Remove any old default for the column. We use RESTRICT here for
8309 : * safety, but at present we do not expect anything to depend on the
8310 : * default. (In ordinary cases, there could not be a default in place
8311 : * anyway, but it's possible when combining LIKE with inheritance.)
8312 : */
8313 53 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8314 : true);
8315 :
8316 53 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8317 :
8318 53 : ObjectAddressSubSet(address, RelationRelationId,
8319 : RelationGetRelid(rel), attnum);
8320 53 : return address;
8321 : }
8322 :
8323 : /*
8324 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8325 : *
8326 : * Return the address of the affected column.
8327 : */
8328 : static ObjectAddress
8329 103 : ATExecAddIdentity(Relation rel, const char *colName,
8330 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8331 : {
8332 : Relation attrelation;
8333 : HeapTuple tuple;
8334 : Form_pg_attribute attTup;
8335 : AttrNumber attnum;
8336 : ObjectAddress address;
8337 103 : ColumnDef *cdef = castNode(ColumnDef, def);
8338 : bool ispartitioned;
8339 :
8340 103 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8341 103 : if (ispartitioned && !recurse)
8342 4 : ereport(ERROR,
8343 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8344 : errmsg("cannot add identity to a column of only the partitioned table"),
8345 : errhint("Do not specify the ONLY keyword.")));
8346 :
8347 99 : if (rel->rd_rel->relispartition && !recursing)
8348 8 : ereport(ERROR,
8349 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8350 : errmsg("cannot add identity to a column of a partition"));
8351 :
8352 91 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8353 :
8354 91 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8355 91 : if (!HeapTupleIsValid(tuple))
8356 0 : ereport(ERROR,
8357 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8358 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8359 : colName, RelationGetRelationName(rel))));
8360 91 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8361 91 : attnum = attTup->attnum;
8362 :
8363 : /* Can't alter a system attribute */
8364 91 : if (attnum <= 0)
8365 0 : ereport(ERROR,
8366 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8367 : errmsg("cannot alter system column \"%s\"",
8368 : colName)));
8369 :
8370 : /*
8371 : * Creating a column as identity implies NOT NULL, so adding the identity
8372 : * to an existing column that is not NOT NULL would create a state that
8373 : * cannot be reproduced without contortions.
8374 : */
8375 91 : if (!attTup->attnotnull)
8376 4 : ereport(ERROR,
8377 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8378 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8379 : colName, RelationGetRelationName(rel))));
8380 :
8381 : /*
8382 : * On the other hand, if a not-null constraint exists, then verify that
8383 : * it's compatible.
8384 : */
8385 87 : if (attTup->attnotnull)
8386 : {
8387 : HeapTuple contup;
8388 : Form_pg_constraint conForm;
8389 :
8390 87 : contup = findNotNullConstraintAttnum(RelationGetRelid(rel),
8391 : attnum);
8392 87 : if (!HeapTupleIsValid(contup))
8393 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8394 : colName, RelationGetRelationName(rel));
8395 :
8396 87 : conForm = (Form_pg_constraint) GETSTRUCT(contup);
8397 87 : if (!conForm->convalidated)
8398 4 : ereport(ERROR,
8399 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8400 : errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8401 : NameStr(conForm->conname), RelationGetRelationName(rel)),
8402 : errhint("You might need to validate it using %s.",
8403 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
8404 : }
8405 :
8406 83 : if (attTup->attidentity)
8407 12 : ereport(ERROR,
8408 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8409 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8410 : colName, RelationGetRelationName(rel))));
8411 :
8412 71 : if (attTup->atthasdef)
8413 4 : ereport(ERROR,
8414 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8415 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8416 : colName, RelationGetRelationName(rel))));
8417 :
8418 67 : attTup->attidentity = cdef->identity;
8419 67 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8420 :
8421 67 : InvokeObjectPostAlterHook(RelationRelationId,
8422 : RelationGetRelid(rel),
8423 : attTup->attnum);
8424 67 : ObjectAddressSubSet(address, RelationRelationId,
8425 : RelationGetRelid(rel), attnum);
8426 67 : heap_freetuple(tuple);
8427 :
8428 67 : table_close(attrelation, RowExclusiveLock);
8429 :
8430 : /*
8431 : * Recurse to propagate the identity column to partitions. Identity is
8432 : * not inherited in regular inheritance children.
8433 : */
8434 67 : if (recurse && ispartitioned)
8435 : {
8436 : List *children;
8437 : ListCell *lc;
8438 :
8439 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8440 :
8441 10 : foreach(lc, children)
8442 : {
8443 : Relation childrel;
8444 :
8445 4 : childrel = table_open(lfirst_oid(lc), NoLock);
8446 4 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8447 4 : table_close(childrel, NoLock);
8448 : }
8449 : }
8450 :
8451 67 : return address;
8452 : }
8453 :
8454 : /*
8455 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8456 : *
8457 : * Return the address of the affected column.
8458 : */
8459 : static ObjectAddress
8460 49 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8461 : LOCKMODE lockmode, bool recurse, bool recursing)
8462 : {
8463 : ListCell *option;
8464 49 : DefElem *generatedEl = NULL;
8465 : HeapTuple tuple;
8466 : Form_pg_attribute attTup;
8467 : AttrNumber attnum;
8468 : Relation attrelation;
8469 : ObjectAddress address;
8470 : bool ispartitioned;
8471 :
8472 49 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8473 49 : if (ispartitioned && !recurse)
8474 4 : ereport(ERROR,
8475 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8476 : errmsg("cannot change identity column of only the partitioned table"),
8477 : errhint("Do not specify the ONLY keyword.")));
8478 :
8479 45 : if (rel->rd_rel->relispartition && !recursing)
8480 8 : ereport(ERROR,
8481 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8482 : errmsg("cannot change identity column of a partition"));
8483 :
8484 66 : foreach(option, castNode(List, def))
8485 : {
8486 29 : DefElem *defel = lfirst_node(DefElem, option);
8487 :
8488 29 : if (strcmp(defel->defname, "generated") == 0)
8489 : {
8490 29 : if (generatedEl)
8491 0 : ereport(ERROR,
8492 : (errcode(ERRCODE_SYNTAX_ERROR),
8493 : errmsg("conflicting or redundant options")));
8494 29 : generatedEl = defel;
8495 : }
8496 : else
8497 0 : elog(ERROR, "option \"%s\" not recognized",
8498 : defel->defname);
8499 : }
8500 :
8501 : /*
8502 : * Even if there is nothing to change here, we run all the checks. There
8503 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8504 : * there.
8505 : */
8506 :
8507 37 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8508 37 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8509 37 : if (!HeapTupleIsValid(tuple))
8510 0 : ereport(ERROR,
8511 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8512 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8513 : colName, RelationGetRelationName(rel))));
8514 :
8515 37 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8516 37 : attnum = attTup->attnum;
8517 :
8518 37 : if (attnum <= 0)
8519 0 : ereport(ERROR,
8520 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8521 : errmsg("cannot alter system column \"%s\"",
8522 : colName)));
8523 :
8524 37 : if (!attTup->attidentity)
8525 4 : ereport(ERROR,
8526 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8527 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8528 : colName, RelationGetRelationName(rel))));
8529 :
8530 33 : if (generatedEl)
8531 : {
8532 29 : attTup->attidentity = defGetInt32(generatedEl);
8533 29 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8534 :
8535 29 : InvokeObjectPostAlterHook(RelationRelationId,
8536 : RelationGetRelid(rel),
8537 : attTup->attnum);
8538 29 : ObjectAddressSubSet(address, RelationRelationId,
8539 : RelationGetRelid(rel), attnum);
8540 : }
8541 : else
8542 4 : address = InvalidObjectAddress;
8543 :
8544 33 : heap_freetuple(tuple);
8545 33 : table_close(attrelation, RowExclusiveLock);
8546 :
8547 : /*
8548 : * Recurse to propagate the identity change to partitions. Identity is not
8549 : * inherited in regular inheritance children.
8550 : */
8551 33 : if (generatedEl && recurse && ispartitioned)
8552 : {
8553 : List *children;
8554 : ListCell *lc;
8555 :
8556 4 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8557 :
8558 12 : foreach(lc, children)
8559 : {
8560 : Relation childrel;
8561 :
8562 8 : childrel = table_open(lfirst_oid(lc), NoLock);
8563 8 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8564 8 : table_close(childrel, NoLock);
8565 : }
8566 : }
8567 :
8568 33 : return address;
8569 : }
8570 :
8571 : /*
8572 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8573 : *
8574 : * Return the address of the affected column.
8575 : */
8576 : static ObjectAddress
8577 61 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8578 : bool recurse, bool recursing)
8579 : {
8580 : HeapTuple tuple;
8581 : Form_pg_attribute attTup;
8582 : AttrNumber attnum;
8583 : Relation attrelation;
8584 : ObjectAddress address;
8585 : Oid seqid;
8586 : ObjectAddress seqaddress;
8587 : bool ispartitioned;
8588 :
8589 61 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8590 61 : if (ispartitioned && !recurse)
8591 4 : ereport(ERROR,
8592 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8593 : errmsg("cannot drop identity from a column of only the partitioned table"),
8594 : errhint("Do not specify the ONLY keyword.")));
8595 :
8596 57 : if (rel->rd_rel->relispartition && !recursing)
8597 4 : ereport(ERROR,
8598 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8599 : errmsg("cannot drop identity from a column of a partition"));
8600 :
8601 53 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8602 53 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8603 53 : if (!HeapTupleIsValid(tuple))
8604 0 : ereport(ERROR,
8605 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8606 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8607 : colName, RelationGetRelationName(rel))));
8608 :
8609 53 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8610 53 : attnum = attTup->attnum;
8611 :
8612 53 : if (attnum <= 0)
8613 0 : ereport(ERROR,
8614 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8615 : errmsg("cannot alter system column \"%s\"",
8616 : colName)));
8617 :
8618 53 : if (!attTup->attidentity)
8619 : {
8620 8 : if (!missing_ok)
8621 4 : ereport(ERROR,
8622 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8623 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8624 : colName, RelationGetRelationName(rel))));
8625 : else
8626 : {
8627 4 : ereport(NOTICE,
8628 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8629 : colName, RelationGetRelationName(rel))));
8630 4 : heap_freetuple(tuple);
8631 4 : table_close(attrelation, RowExclusiveLock);
8632 4 : return InvalidObjectAddress;
8633 : }
8634 : }
8635 :
8636 45 : attTup->attidentity = '\0';
8637 45 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8638 :
8639 45 : InvokeObjectPostAlterHook(RelationRelationId,
8640 : RelationGetRelid(rel),
8641 : attTup->attnum);
8642 45 : ObjectAddressSubSet(address, RelationRelationId,
8643 : RelationGetRelid(rel), attnum);
8644 45 : heap_freetuple(tuple);
8645 :
8646 45 : table_close(attrelation, RowExclusiveLock);
8647 :
8648 : /*
8649 : * Recurse to drop the identity from column in partitions. Identity is
8650 : * not inherited in regular inheritance children so ignore them.
8651 : */
8652 45 : if (recurse && ispartitioned)
8653 : {
8654 : List *children;
8655 : ListCell *lc;
8656 :
8657 4 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8658 :
8659 8 : foreach(lc, children)
8660 : {
8661 : Relation childrel;
8662 :
8663 4 : childrel = table_open(lfirst_oid(lc), NoLock);
8664 4 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8665 4 : table_close(childrel, NoLock);
8666 : }
8667 : }
8668 :
8669 45 : if (!recursing)
8670 : {
8671 : /* drop the internal sequence */
8672 21 : seqid = getIdentitySequence(rel, attnum, false);
8673 21 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8674 : RelationRelationId, DEPENDENCY_INTERNAL);
8675 21 : CommandCounterIncrement();
8676 21 : seqaddress.classId = RelationRelationId;
8677 21 : seqaddress.objectId = seqid;
8678 21 : seqaddress.objectSubId = 0;
8679 21 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8680 : }
8681 :
8682 45 : return address;
8683 : }
8684 :
8685 : /*
8686 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8687 : *
8688 : * Return the address of the affected column.
8689 : */
8690 : static ObjectAddress
8691 169 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8692 : Node *newExpr, LOCKMODE lockmode)
8693 : {
8694 : HeapTuple tuple;
8695 : Form_pg_attribute attTup;
8696 : AttrNumber attnum;
8697 : char attgenerated;
8698 : bool rewrite;
8699 : Oid attrdefoid;
8700 : ObjectAddress address;
8701 : Expr *defval;
8702 : NewColumnValue *newval;
8703 : RawColumnDefault *rawEnt;
8704 :
8705 169 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8706 169 : if (!HeapTupleIsValid(tuple))
8707 0 : ereport(ERROR,
8708 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8709 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8710 : colName, RelationGetRelationName(rel))));
8711 :
8712 169 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8713 :
8714 169 : attnum = attTup->attnum;
8715 169 : if (attnum <= 0)
8716 0 : ereport(ERROR,
8717 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8718 : errmsg("cannot alter system column \"%s\"",
8719 : colName)));
8720 :
8721 169 : attgenerated = attTup->attgenerated;
8722 169 : if (!attgenerated)
8723 8 : ereport(ERROR,
8724 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8725 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8726 : colName, RelationGetRelationName(rel))));
8727 :
8728 161 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8729 16 : tab->verify_new_notnull = true;
8730 :
8731 : /*
8732 : * We need to prevent this because a change of expression could affect a
8733 : * row filter and inject expressions that are not permitted in a row
8734 : * filter. XXX We could try to have a more precise check to catch only
8735 : * publications with row filters, or even re-verify the row filter
8736 : * expressions.
8737 : */
8738 237 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8739 76 : GetRelationIncludedPublications(RelationGetRelid(rel)) != NIL)
8740 4 : ereport(ERROR,
8741 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8742 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8743 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8744 : colName, RelationGetRelationName(rel))));
8745 :
8746 157 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8747 :
8748 157 : ReleaseSysCache(tuple);
8749 :
8750 157 : if (rewrite)
8751 : {
8752 : /*
8753 : * Clear all the missing values if we're rewriting the table, since
8754 : * this renders them pointless.
8755 : */
8756 85 : RelationClearMissing(rel);
8757 :
8758 : /* make sure we don't conflict with later attribute modifications */
8759 85 : CommandCounterIncrement();
8760 : }
8761 :
8762 : /*
8763 : * Find everything that depends on the column (constraints, indexes, etc),
8764 : * and record enough information to let us recreate the objects.
8765 : */
8766 157 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8767 :
8768 : /*
8769 : * Drop the dependency records of the GENERATED expression, in particular
8770 : * its INTERNAL dependency on the column, which would otherwise cause
8771 : * dependency.c to refuse to perform the deletion.
8772 : */
8773 157 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8774 157 : if (!OidIsValid(attrdefoid))
8775 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8776 : RelationGetRelid(rel), attnum);
8777 157 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8778 :
8779 : /* Make above changes visible */
8780 157 : CommandCounterIncrement();
8781 :
8782 : /*
8783 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8784 : * safety, but at present we do not expect anything to depend on the
8785 : * expression.
8786 : */
8787 157 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8788 : false, false);
8789 :
8790 : /* Prepare to store the new expression, in the catalogs */
8791 157 : rawEnt = palloc_object(RawColumnDefault);
8792 157 : rawEnt->attnum = attnum;
8793 157 : rawEnt->raw_default = newExpr;
8794 157 : rawEnt->generated = attgenerated;
8795 :
8796 : /* Store the generated expression */
8797 157 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8798 : false, true, false, NULL);
8799 :
8800 : /* Make above new expression visible */
8801 157 : CommandCounterIncrement();
8802 :
8803 157 : if (rewrite)
8804 : {
8805 : /* Prepare for table rewrite */
8806 85 : defval = (Expr *) build_column_default(rel, attnum);
8807 :
8808 85 : newval = palloc0_object(NewColumnValue);
8809 85 : newval->attnum = attnum;
8810 85 : newval->expr = expression_planner(defval);
8811 85 : newval->is_generated = true;
8812 :
8813 85 : tab->newvals = lappend(tab->newvals, newval);
8814 85 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8815 : }
8816 :
8817 : /* Drop any pg_statistic entry for the column */
8818 157 : RemoveStatistics(RelationGetRelid(rel), attnum);
8819 :
8820 157 : InvokeObjectPostAlterHook(RelationRelationId,
8821 : RelationGetRelid(rel), attnum);
8822 :
8823 157 : ObjectAddressSubSet(address, RelationRelationId,
8824 : RelationGetRelid(rel), attnum);
8825 157 : return address;
8826 : }
8827 :
8828 : /*
8829 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8830 : */
8831 : static void
8832 57 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8833 : {
8834 : /*
8835 : * Reject ONLY if there are child tables. We could implement this, but it
8836 : * is a bit complicated. GENERATED clauses must be attached to the column
8837 : * definition and cannot be added later like DEFAULT, so if a child table
8838 : * has a generation expression that the parent does not have, the child
8839 : * column will necessarily be an attislocal column. So to implement ONLY
8840 : * here, we'd need extra code to update attislocal of the direct child
8841 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8842 : * resulting state can be properly dumped and restored.
8843 : */
8844 73 : if (!recurse &&
8845 16 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8846 8 : ereport(ERROR,
8847 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8848 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8849 :
8850 : /*
8851 : * Cannot drop generation expression from inherited columns.
8852 : */
8853 49 : if (!recursing)
8854 : {
8855 : HeapTuple tuple;
8856 : Form_pg_attribute attTup;
8857 :
8858 41 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8859 41 : if (!HeapTupleIsValid(tuple))
8860 0 : ereport(ERROR,
8861 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8862 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8863 : cmd->name, RelationGetRelationName(rel))));
8864 :
8865 41 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8866 :
8867 41 : if (attTup->attinhcount > 0)
8868 8 : ereport(ERROR,
8869 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8870 : errmsg("cannot drop generation expression from inherited column")));
8871 : }
8872 41 : }
8873 :
8874 : /*
8875 : * Return the address of the affected column.
8876 : */
8877 : static ObjectAddress
8878 37 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8879 : {
8880 : HeapTuple tuple;
8881 : Form_pg_attribute attTup;
8882 : AttrNumber attnum;
8883 : Relation attrelation;
8884 : Oid attrdefoid;
8885 : ObjectAddress address;
8886 :
8887 37 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8888 37 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8889 37 : if (!HeapTupleIsValid(tuple))
8890 0 : ereport(ERROR,
8891 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8892 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8893 : colName, RelationGetRelationName(rel))));
8894 :
8895 37 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8896 37 : attnum = attTup->attnum;
8897 :
8898 37 : if (attnum <= 0)
8899 0 : ereport(ERROR,
8900 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8901 : errmsg("cannot alter system column \"%s\"",
8902 : colName)));
8903 :
8904 : /*
8905 : * TODO: This could be done, but it would need a table rewrite to
8906 : * materialize the generated values. Note that for the time being, we
8907 : * still error with missing_ok, so that we don't silently leave the column
8908 : * as generated.
8909 : */
8910 37 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8911 8 : ereport(ERROR,
8912 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8913 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8914 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8915 : colName, RelationGetRelationName(rel))));
8916 :
8917 29 : if (!attTup->attgenerated)
8918 : {
8919 16 : if (!missing_ok)
8920 8 : ereport(ERROR,
8921 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8922 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8923 : colName, RelationGetRelationName(rel))));
8924 : else
8925 : {
8926 8 : ereport(NOTICE,
8927 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8928 : colName, RelationGetRelationName(rel))));
8929 8 : heap_freetuple(tuple);
8930 8 : table_close(attrelation, RowExclusiveLock);
8931 8 : return InvalidObjectAddress;
8932 : }
8933 : }
8934 :
8935 : /*
8936 : * Mark the column as no longer generated. (The atthasdef flag needs to
8937 : * get cleared too, but RemoveAttrDefault will handle that.)
8938 : */
8939 13 : attTup->attgenerated = '\0';
8940 13 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8941 :
8942 13 : InvokeObjectPostAlterHook(RelationRelationId,
8943 : RelationGetRelid(rel),
8944 : attnum);
8945 13 : heap_freetuple(tuple);
8946 :
8947 13 : table_close(attrelation, RowExclusiveLock);
8948 :
8949 : /*
8950 : * Drop the dependency records of the GENERATED expression, in particular
8951 : * its INTERNAL dependency on the column, which would otherwise cause
8952 : * dependency.c to refuse to perform the deletion.
8953 : */
8954 13 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8955 13 : if (!OidIsValid(attrdefoid))
8956 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8957 : RelationGetRelid(rel), attnum);
8958 13 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8959 :
8960 : /* Make above changes visible */
8961 13 : CommandCounterIncrement();
8962 :
8963 : /*
8964 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8965 : * safety, but at present we do not expect anything to depend on the
8966 : * default.
8967 : */
8968 13 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8969 : false, false);
8970 :
8971 13 : ObjectAddressSubSet(address, RelationRelationId,
8972 : RelationGetRelid(rel), attnum);
8973 13 : return address;
8974 : }
8975 :
8976 : /*
8977 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8978 : *
8979 : * Return value is the address of the modified column
8980 : */
8981 : static ObjectAddress
8982 107 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8983 : {
8984 107 : int newtarget = 0;
8985 : bool newtarget_default;
8986 : Relation attrelation;
8987 : HeapTuple tuple,
8988 : newtuple;
8989 : Form_pg_attribute attrtuple;
8990 : AttrNumber attnum;
8991 : ObjectAddress address;
8992 : Datum repl_val[Natts_pg_attribute];
8993 : bool repl_null[Natts_pg_attribute];
8994 : bool repl_repl[Natts_pg_attribute];
8995 :
8996 : /*
8997 : * We allow referencing columns by numbers only for indexes, since table
8998 : * column numbers could contain gaps if columns are later dropped.
8999 : */
9000 107 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
9001 65 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
9002 : !colName)
9003 0 : ereport(ERROR,
9004 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9005 : errmsg("cannot refer to non-index column by number")));
9006 :
9007 : /* -1 was used in previous versions for the default setting */
9008 107 : if (newValue && intVal(newValue) != -1)
9009 : {
9010 78 : newtarget = intVal(newValue);
9011 78 : newtarget_default = false;
9012 : }
9013 : else
9014 29 : newtarget_default = true;
9015 :
9016 107 : if (!newtarget_default)
9017 : {
9018 : /*
9019 : * Limit target to a sane range
9020 : */
9021 78 : if (newtarget < 0)
9022 : {
9023 0 : ereport(ERROR,
9024 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
9025 : errmsg("statistics target %d is too low",
9026 : newtarget)));
9027 : }
9028 78 : else if (newtarget > MAX_STATISTICS_TARGET)
9029 : {
9030 0 : newtarget = MAX_STATISTICS_TARGET;
9031 0 : ereport(WARNING,
9032 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
9033 : errmsg("lowering statistics target to %d",
9034 : newtarget)));
9035 : }
9036 : }
9037 :
9038 107 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9039 :
9040 107 : if (colName)
9041 : {
9042 65 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9043 :
9044 65 : if (!HeapTupleIsValid(tuple))
9045 8 : ereport(ERROR,
9046 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9047 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9048 : colName, RelationGetRelationName(rel))));
9049 : }
9050 : else
9051 : {
9052 42 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
9053 :
9054 42 : if (!HeapTupleIsValid(tuple))
9055 8 : ereport(ERROR,
9056 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9057 : errmsg("column number %d of relation \"%s\" does not exist",
9058 : colNum, RelationGetRelationName(rel))));
9059 : }
9060 :
9061 91 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9062 :
9063 91 : attnum = attrtuple->attnum;
9064 91 : if (attnum <= 0)
9065 0 : ereport(ERROR,
9066 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9067 : errmsg("cannot alter system column \"%s\"",
9068 : colName)));
9069 :
9070 : /*
9071 : * Prevent this as long as the ANALYZE code skips virtual generated
9072 : * columns.
9073 : */
9074 91 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9075 0 : ereport(ERROR,
9076 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9077 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
9078 : colName)));
9079 :
9080 91 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
9081 57 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9082 : {
9083 34 : if (attnum > rel->rd_index->indnkeyatts)
9084 4 : ereport(ERROR,
9085 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9086 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9087 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9088 30 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9089 12 : ereport(ERROR,
9090 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9091 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9092 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9093 : errhint("Alter statistics on table column instead.")));
9094 : }
9095 :
9096 : /* Build new tuple. */
9097 75 : memset(repl_null, false, sizeof(repl_null));
9098 75 : memset(repl_repl, false, sizeof(repl_repl));
9099 75 : if (!newtarget_default)
9100 46 : repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
9101 : else
9102 29 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
9103 75 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
9104 75 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9105 : repl_val, repl_null, repl_repl);
9106 75 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9107 :
9108 75 : InvokeObjectPostAlterHook(RelationRelationId,
9109 : RelationGetRelid(rel),
9110 : attrtuple->attnum);
9111 75 : ObjectAddressSubSet(address, RelationRelationId,
9112 : RelationGetRelid(rel), attnum);
9113 :
9114 75 : heap_freetuple(newtuple);
9115 :
9116 75 : ReleaseSysCache(tuple);
9117 :
9118 75 : table_close(attrelation, RowExclusiveLock);
9119 :
9120 75 : return address;
9121 : }
9122 :
9123 : /*
9124 : * Return value is the address of the modified column
9125 : */
9126 : static ObjectAddress
9127 21 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
9128 : bool isReset, LOCKMODE lockmode)
9129 : {
9130 : Relation attrelation;
9131 : HeapTuple tuple,
9132 : newtuple;
9133 : Form_pg_attribute attrtuple;
9134 : AttrNumber attnum;
9135 : Datum datum,
9136 : newOptions;
9137 : bool isnull;
9138 : ObjectAddress address;
9139 : Datum repl_val[Natts_pg_attribute];
9140 : bool repl_null[Natts_pg_attribute];
9141 : bool repl_repl[Natts_pg_attribute];
9142 :
9143 21 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9144 :
9145 21 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9146 :
9147 21 : if (!HeapTupleIsValid(tuple))
9148 0 : ereport(ERROR,
9149 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9150 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9151 : colName, RelationGetRelationName(rel))));
9152 21 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9153 :
9154 21 : attnum = attrtuple->attnum;
9155 21 : if (attnum <= 0)
9156 0 : ereport(ERROR,
9157 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9158 : errmsg("cannot alter system column \"%s\"",
9159 : colName)));
9160 :
9161 : /* Generate new proposed attoptions (text array) */
9162 21 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9163 : &isnull);
9164 21 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9165 : castNode(List, options), NULL, NULL,
9166 : false, isReset);
9167 : /* Validate new options */
9168 21 : (void) attribute_reloptions(newOptions, true);
9169 :
9170 : /* Build new tuple. */
9171 21 : memset(repl_null, false, sizeof(repl_null));
9172 21 : memset(repl_repl, false, sizeof(repl_repl));
9173 21 : if (newOptions != (Datum) 0)
9174 21 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9175 : else
9176 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
9177 21 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9178 21 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9179 : repl_val, repl_null, repl_repl);
9180 :
9181 : /* Update system catalog. */
9182 21 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9183 :
9184 21 : InvokeObjectPostAlterHook(RelationRelationId,
9185 : RelationGetRelid(rel),
9186 : attrtuple->attnum);
9187 21 : ObjectAddressSubSet(address, RelationRelationId,
9188 : RelationGetRelid(rel), attnum);
9189 :
9190 21 : heap_freetuple(newtuple);
9191 :
9192 21 : ReleaseSysCache(tuple);
9193 :
9194 21 : table_close(attrelation, RowExclusiveLock);
9195 :
9196 21 : return address;
9197 : }
9198 :
9199 : /*
9200 : * Helper function for ATExecSetStorage and ATExecSetCompression
9201 : *
9202 : * Set the attstorage and/or attcompression fields for index columns
9203 : * associated with the specified table column.
9204 : */
9205 : static void
9206 208 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9207 : AttrNumber attnum,
9208 : bool setstorage, char newstorage,
9209 : bool setcompression, char newcompression,
9210 : LOCKMODE lockmode)
9211 : {
9212 : ListCell *lc;
9213 :
9214 265 : foreach(lc, RelationGetIndexList(rel))
9215 : {
9216 57 : Oid indexoid = lfirst_oid(lc);
9217 : Relation indrel;
9218 57 : AttrNumber indattnum = 0;
9219 : HeapTuple tuple;
9220 :
9221 57 : indrel = index_open(indexoid, lockmode);
9222 :
9223 95 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9224 : {
9225 61 : if (indrel->rd_index->indkey.values[i] == attnum)
9226 : {
9227 23 : indattnum = i + 1;
9228 23 : break;
9229 : }
9230 : }
9231 :
9232 57 : if (indattnum == 0)
9233 : {
9234 34 : index_close(indrel, lockmode);
9235 34 : continue;
9236 : }
9237 :
9238 23 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9239 :
9240 23 : if (HeapTupleIsValid(tuple))
9241 : {
9242 23 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9243 :
9244 23 : if (setstorage)
9245 15 : attrtuple->attstorage = newstorage;
9246 :
9247 23 : if (setcompression)
9248 8 : attrtuple->attcompression = newcompression;
9249 :
9250 23 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9251 :
9252 23 : InvokeObjectPostAlterHook(RelationRelationId,
9253 : RelationGetRelid(rel),
9254 : attrtuple->attnum);
9255 :
9256 23 : heap_freetuple(tuple);
9257 : }
9258 :
9259 23 : index_close(indrel, lockmode);
9260 : }
9261 208 : }
9262 :
9263 : /*
9264 : * ALTER TABLE ALTER COLUMN SET STORAGE
9265 : *
9266 : * Return value is the address of the modified column
9267 : */
9268 : static ObjectAddress
9269 173 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9270 : {
9271 : Relation attrelation;
9272 : HeapTuple tuple;
9273 : Form_pg_attribute attrtuple;
9274 : AttrNumber attnum;
9275 : ObjectAddress address;
9276 :
9277 173 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9278 :
9279 173 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9280 :
9281 173 : if (!HeapTupleIsValid(tuple))
9282 8 : ereport(ERROR,
9283 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9284 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9285 : colName, RelationGetRelationName(rel))));
9286 165 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9287 :
9288 165 : attnum = attrtuple->attnum;
9289 165 : if (attnum <= 0)
9290 0 : ereport(ERROR,
9291 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9292 : errmsg("cannot alter system column \"%s\"",
9293 : colName)));
9294 :
9295 165 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9296 :
9297 165 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9298 :
9299 165 : InvokeObjectPostAlterHook(RelationRelationId,
9300 : RelationGetRelid(rel),
9301 : attrtuple->attnum);
9302 :
9303 : /*
9304 : * Apply the change to indexes as well (only for simple index columns,
9305 : * matching behavior of index.c ConstructTupleDescriptor()).
9306 : */
9307 165 : SetIndexStorageProperties(rel, attrelation, attnum,
9308 165 : true, attrtuple->attstorage,
9309 : false, 0,
9310 : lockmode);
9311 :
9312 165 : heap_freetuple(tuple);
9313 :
9314 165 : table_close(attrelation, RowExclusiveLock);
9315 :
9316 165 : ObjectAddressSubSet(address, RelationRelationId,
9317 : RelationGetRelid(rel), attnum);
9318 165 : return address;
9319 : }
9320 :
9321 :
9322 : /*
9323 : * ALTER TABLE DROP COLUMN
9324 : *
9325 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9326 : * because we have to decide at runtime whether to recurse or not depending
9327 : * on whether attinhcount goes to zero or not. (We can't check this in a
9328 : * static pre-pass because it won't handle multiple inheritance situations
9329 : * correctly.)
9330 : */
9331 : static void
9332 1133 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9333 : AlterTableCmd *cmd, LOCKMODE lockmode,
9334 : AlterTableUtilityContext *context)
9335 : {
9336 1133 : if (rel->rd_rel->reloftype && !recursing)
9337 4 : ereport(ERROR,
9338 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9339 : errmsg("cannot drop column from typed table")));
9340 :
9341 1129 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9342 54 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9343 :
9344 1125 : if (recurse)
9345 967 : cmd->recurse = true;
9346 1125 : }
9347 :
9348 : /*
9349 : * Drops column 'colName' from relation 'rel' and returns the address of the
9350 : * dropped column. The column is also dropped (or marked as no longer
9351 : * inherited from relation) from the relation's inheritance children, if any.
9352 : *
9353 : * In the recursive invocations for inheritance child relations, instead of
9354 : * dropping the column directly (if to be dropped at all), its object address
9355 : * is added to 'addrs', which must be non-NULL in such invocations. All
9356 : * columns are dropped at the same time after all the children have been
9357 : * checked recursively.
9358 : */
9359 : static ObjectAddress
9360 1498 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9361 : DropBehavior behavior,
9362 : bool recurse, bool recursing,
9363 : bool missing_ok, LOCKMODE lockmode,
9364 : ObjectAddresses *addrs)
9365 : {
9366 : HeapTuple tuple;
9367 : Form_pg_attribute targetatt;
9368 : AttrNumber attnum;
9369 : List *children;
9370 : ObjectAddress object;
9371 : bool is_expr;
9372 :
9373 : /* At top level, permission check was done in ATPrepCmd, else do it */
9374 1498 : if (recursing)
9375 373 : ATSimplePermissions(AT_DropColumn, rel,
9376 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9377 :
9378 : /* Initialize addrs on the first invocation */
9379 : Assert(!recursing || addrs != NULL);
9380 :
9381 : /* since this function recurses, it could be driven to stack overflow */
9382 1498 : check_stack_depth();
9383 :
9384 1498 : if (!recursing)
9385 1125 : addrs = new_object_addresses();
9386 :
9387 : /*
9388 : * get the number of the attribute
9389 : */
9390 1498 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9391 1498 : if (!HeapTupleIsValid(tuple))
9392 : {
9393 40 : if (!missing_ok)
9394 : {
9395 24 : ereport(ERROR,
9396 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9397 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9398 : colName, RelationGetRelationName(rel))));
9399 : }
9400 : else
9401 : {
9402 16 : ereport(NOTICE,
9403 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9404 : colName, RelationGetRelationName(rel))));
9405 16 : return InvalidObjectAddress;
9406 : }
9407 : }
9408 1458 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9409 :
9410 1458 : attnum = targetatt->attnum;
9411 :
9412 : /* Can't drop a system attribute */
9413 1458 : if (attnum <= 0)
9414 4 : ereport(ERROR,
9415 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9416 : errmsg("cannot drop system column \"%s\"",
9417 : colName)));
9418 :
9419 : /*
9420 : * Don't drop inherited columns, unless recursing (presumably from a drop
9421 : * of the parent column)
9422 : */
9423 1454 : if (targetatt->attinhcount > 0 && !recursing)
9424 32 : ereport(ERROR,
9425 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9426 : errmsg("cannot drop inherited column \"%s\"",
9427 : colName)));
9428 :
9429 : /*
9430 : * Don't drop columns used in the partition key, either. (If we let this
9431 : * go through, the key column's dependencies would cause a cascaded drop
9432 : * of the whole table, which is surely not what the user expected.)
9433 : */
9434 1422 : if (has_partition_attrs(rel,
9435 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9436 : &is_expr))
9437 20 : ereport(ERROR,
9438 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9439 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9440 : colName, RelationGetRelationName(rel))));
9441 :
9442 1402 : ReleaseSysCache(tuple);
9443 :
9444 : /*
9445 : * Propagate to children as appropriate. Unlike most other ALTER
9446 : * routines, we have to do this one level of recursion at a time; we can't
9447 : * use find_all_inheritors to do it in one pass.
9448 : */
9449 : children =
9450 1402 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9451 :
9452 1402 : if (children)
9453 : {
9454 : Relation attr_rel;
9455 : ListCell *child;
9456 :
9457 : /*
9458 : * In case of a partitioned table, the column must be dropped from the
9459 : * partitions as well.
9460 : */
9461 204 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9462 4 : ereport(ERROR,
9463 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9464 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9465 : errhint("Do not specify the ONLY keyword.")));
9466 :
9467 200 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9468 593 : foreach(child, children)
9469 : {
9470 397 : Oid childrelid = lfirst_oid(child);
9471 : Relation childrel;
9472 : Form_pg_attribute childatt;
9473 :
9474 : /* find_inheritance_children already got lock */
9475 397 : childrel = table_open(childrelid, NoLock);
9476 397 : CheckAlterTableIsSafe(childrel);
9477 :
9478 397 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9479 397 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9480 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9481 : colName, childrelid);
9482 397 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9483 :
9484 397 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9485 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9486 : childrelid, colName);
9487 :
9488 397 : if (recurse)
9489 : {
9490 : /*
9491 : * If the child column has other definition sources, just
9492 : * decrement its inheritance count; if not, recurse to delete
9493 : * it.
9494 : */
9495 381 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9496 : {
9497 : /* Time to delete this child column, too */
9498 373 : ATExecDropColumn(wqueue, childrel, colName,
9499 : behavior, true, true,
9500 : false, lockmode, addrs);
9501 : }
9502 : else
9503 : {
9504 : /* Child column must survive my deletion */
9505 8 : childatt->attinhcount--;
9506 :
9507 8 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9508 :
9509 : /* Make update visible */
9510 8 : CommandCounterIncrement();
9511 : }
9512 : }
9513 : else
9514 : {
9515 : /*
9516 : * If we were told to drop ONLY in this table (no recursion),
9517 : * we need to mark the inheritors' attributes as locally
9518 : * defined rather than inherited.
9519 : */
9520 16 : childatt->attinhcount--;
9521 16 : childatt->attislocal = true;
9522 :
9523 16 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9524 :
9525 : /* Make update visible */
9526 16 : CommandCounterIncrement();
9527 : }
9528 :
9529 393 : heap_freetuple(tuple);
9530 :
9531 393 : table_close(childrel, NoLock);
9532 : }
9533 196 : table_close(attr_rel, RowExclusiveLock);
9534 : }
9535 :
9536 : /* Add object to delete */
9537 1394 : object.classId = RelationRelationId;
9538 1394 : object.objectId = RelationGetRelid(rel);
9539 1394 : object.objectSubId = attnum;
9540 1394 : add_exact_object_address(&object, addrs);
9541 :
9542 1394 : if (!recursing)
9543 : {
9544 : /* Recursion has ended, drop everything that was collected */
9545 1025 : performMultipleDeletions(addrs, behavior, 0);
9546 981 : free_object_addresses(addrs);
9547 : }
9548 :
9549 1350 : return object;
9550 : }
9551 :
9552 : /*
9553 : * Prepare to add a primary key on a table, by adding not-null constraints
9554 : * on all columns.
9555 : *
9556 : * The not-null constraints for a primary key must cover the whole inheritance
9557 : * hierarchy (failing to ensure that leads to funny corner cases). For the
9558 : * normal case where we're asked to recurse, this routine checks if the
9559 : * not-null constraints exist already, and if not queues a requirement for
9560 : * them to be created by phase 2.
9561 : *
9562 : * For the case where we're asked not to recurse, we verify that a not-null
9563 : * constraint exists on each column of each (direct) child table, throwing an
9564 : * error if not. Not throwing an error would also work, because a not-null
9565 : * constraint would be created anyway, but it'd cause a silent scan of the
9566 : * child table to verify absence of nulls. We prefer to let the user know so
9567 : * that they can add the constraint manually without having to hold
9568 : * AccessExclusiveLock while at it.
9569 : *
9570 : * However, it's also important that we do not acquire locks on children if
9571 : * the not-null constraints already exist on the parent, to avoid risking
9572 : * deadlocks during parallel pg_restore of PKs on partitioned tables.
9573 : */
9574 : static void
9575 10356 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9576 : bool recurse, LOCKMODE lockmode,
9577 : AlterTableUtilityContext *context)
9578 : {
9579 : Constraint *pkconstr;
9580 10356 : List *children = NIL;
9581 10356 : bool got_children = false;
9582 :
9583 10356 : pkconstr = castNode(Constraint, cmd->def);
9584 10356 : if (pkconstr->contype != CONSTR_PRIMARY)
9585 6100 : return;
9586 :
9587 : /* Verify that columns are not-null, or request that they be made so */
9588 9120 : foreach_node(String, column, pkconstr->keys)
9589 : {
9590 : AlterTableCmd *newcmd;
9591 : Constraint *nnconstr;
9592 : HeapTuple tuple;
9593 :
9594 : /*
9595 : * First check if a suitable constraint exists. If it does, we don't
9596 : * need to request another one. We do need to bail out if it's not
9597 : * valid, though.
9598 : */
9599 648 : tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9600 648 : if (tuple != NULL)
9601 : {
9602 321 : verifyNotNullPKCompatible(tuple, strVal(column));
9603 :
9604 : /* All good with this one; don't request another */
9605 313 : heap_freetuple(tuple);
9606 313 : continue;
9607 : }
9608 327 : else if (!recurse)
9609 : {
9610 : /*
9611 : * No constraint on this column. Asked not to recurse, we won't
9612 : * create one here, but verify that all children have one.
9613 : */
9614 24 : if (!got_children)
9615 : {
9616 24 : children = find_inheritance_children(RelationGetRelid(rel),
9617 : lockmode);
9618 : /* only search for children on the first time through */
9619 24 : got_children = true;
9620 : }
9621 :
9622 48 : foreach_oid(childrelid, children)
9623 : {
9624 : HeapTuple tup;
9625 :
9626 24 : tup = findNotNullConstraint(childrelid, strVal(column));
9627 24 : if (!tup)
9628 4 : ereport(ERROR,
9629 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9630 : strVal(column), get_rel_name(childrelid)));
9631 : /* verify it's good enough */
9632 20 : verifyNotNullPKCompatible(tup, strVal(column));
9633 : }
9634 : }
9635 :
9636 : /* This column is not already not-null, so add it to the queue */
9637 315 : nnconstr = makeNotNullConstraint(column);
9638 :
9639 315 : newcmd = makeNode(AlterTableCmd);
9640 315 : newcmd->subtype = AT_AddConstraint;
9641 : /* note we force recurse=true here; see above */
9642 315 : newcmd->recurse = true;
9643 315 : newcmd->def = (Node *) nnconstr;
9644 :
9645 315 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9646 : }
9647 : }
9648 :
9649 : /*
9650 : * Verify whether the given not-null constraint is compatible with a
9651 : * primary key. If not, an error is thrown.
9652 : */
9653 : static void
9654 341 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9655 : {
9656 341 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
9657 :
9658 341 : if (conForm->contype != CONSTRAINT_NOTNULL)
9659 0 : elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9660 :
9661 : /* a NO INHERIT constraint is no good */
9662 341 : if (conForm->connoinherit)
9663 8 : ereport(ERROR,
9664 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9665 : errmsg("cannot create primary key on column \"%s\"", colname),
9666 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9667 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9668 : NameStr(conForm->conname), colname,
9669 : get_rel_name(conForm->conrelid), "NO INHERIT"),
9670 : errhint("You might need to make the existing constraint inheritable using %s.",
9671 : "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9672 :
9673 : /* an unvalidated constraint is no good */
9674 333 : if (!conForm->convalidated)
9675 8 : ereport(ERROR,
9676 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9677 : errmsg("cannot create primary key on column \"%s\"", colname),
9678 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9679 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9680 : NameStr(conForm->conname), colname,
9681 : get_rel_name(conForm->conrelid), "NOT VALID"),
9682 : errhint("You might need to validate it using %s.",
9683 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
9684 325 : }
9685 :
9686 : /*
9687 : * ALTER TABLE ADD INDEX
9688 : *
9689 : * There is no such command in the grammar, but parse_utilcmd.c converts
9690 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9691 : * us schedule creation of the index at the appropriate time during ALTER.
9692 : *
9693 : * Return value is the address of the new index.
9694 : */
9695 : static ObjectAddress
9696 1057 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9697 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9698 : {
9699 : bool check_rights;
9700 : bool skip_build;
9701 : bool quiet;
9702 : ObjectAddress address;
9703 :
9704 : Assert(IsA(stmt, IndexStmt));
9705 : Assert(!stmt->concurrent);
9706 :
9707 : /* The IndexStmt has already been through transformIndexStmt */
9708 : Assert(stmt->transformed);
9709 :
9710 : /* suppress schema rights check when rebuilding existing index */
9711 1057 : check_rights = !is_rebuild;
9712 : /* skip index build if phase 3 will do it or we're reusing an old one */
9713 1057 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9714 : /* suppress notices when rebuilding existing index */
9715 1057 : quiet = is_rebuild;
9716 :
9717 1057 : address = DefineIndex(NULL,
9718 : RelationGetRelid(rel),
9719 : stmt,
9720 : InvalidOid, /* no predefined OID */
9721 : InvalidOid, /* no parent index */
9722 : InvalidOid, /* no parent constraint */
9723 : -1, /* total_parts unknown */
9724 : true, /* is_alter_table */
9725 : check_rights,
9726 : false, /* check_not_in_use - we did it already */
9727 : skip_build,
9728 : quiet);
9729 :
9730 : /*
9731 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9732 : * new index instead of building from scratch. Restore associated fields.
9733 : * This may store InvalidSubTransactionId in both fields, in which case
9734 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9735 : * this after the CCI that made catalog rows visible to any rebuild. The
9736 : * DROP of the old edition of this index will have scheduled the storage
9737 : * for deletion at commit, so cancel that pending deletion.
9738 : */
9739 944 : if (RelFileNumberIsValid(stmt->oldNumber))
9740 : {
9741 49 : Relation irel = index_open(address.objectId, NoLock);
9742 :
9743 49 : irel->rd_createSubid = stmt->oldCreateSubid;
9744 49 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9745 49 : RelationPreserveStorage(irel->rd_locator, true);
9746 49 : index_close(irel, NoLock);
9747 : }
9748 :
9749 944 : return address;
9750 : }
9751 :
9752 : /*
9753 : * ALTER TABLE ADD STATISTICS
9754 : *
9755 : * This is no such command in the grammar, but we use this internally to add
9756 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9757 : * column type change.
9758 : */
9759 : static ObjectAddress
9760 53 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9761 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9762 : {
9763 : ObjectAddress address;
9764 :
9765 : Assert(IsA(stmt, CreateStatsStmt));
9766 :
9767 : /* The CreateStatsStmt has already been through transformStatsStmt */
9768 : Assert(stmt->transformed);
9769 :
9770 53 : address = CreateStatistics(stmt, !is_rebuild);
9771 :
9772 53 : return address;
9773 : }
9774 :
9775 : /*
9776 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9777 : *
9778 : * Returns the address of the new constraint.
9779 : */
9780 : static ObjectAddress
9781 6653 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9782 : IndexStmt *stmt, LOCKMODE lockmode)
9783 : {
9784 6653 : Oid index_oid = stmt->indexOid;
9785 : Relation indexRel;
9786 : char *indexName;
9787 : IndexInfo *indexInfo;
9788 : char *constraintName;
9789 : char constraintType;
9790 : ObjectAddress address;
9791 : uint16 flags;
9792 :
9793 : Assert(IsA(stmt, IndexStmt));
9794 : Assert(OidIsValid(index_oid));
9795 : Assert(stmt->isconstraint);
9796 :
9797 : /*
9798 : * Doing this on partitioned tables is not a simple feature to implement,
9799 : * so let's punt for now.
9800 : */
9801 6653 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9802 4 : ereport(ERROR,
9803 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9804 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9805 :
9806 6649 : indexRel = index_open(index_oid, AccessShareLock);
9807 :
9808 6649 : indexName = pstrdup(RelationGetRelationName(indexRel));
9809 :
9810 6649 : indexInfo = BuildIndexInfo(indexRel);
9811 :
9812 : /* this should have been checked at parse time */
9813 6649 : if (!indexInfo->ii_Unique)
9814 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9815 :
9816 : /*
9817 : * Determine name to assign to constraint. We require a constraint to
9818 : * have the same name as the underlying index; therefore, use the index's
9819 : * existing name as the default constraint name, and if the user
9820 : * explicitly gives some other name for the constraint, rename the index
9821 : * to match.
9822 : */
9823 6649 : constraintName = stmt->idxname;
9824 6649 : if (constraintName == NULL)
9825 6632 : constraintName = indexName;
9826 17 : else if (strcmp(constraintName, indexName) != 0)
9827 : {
9828 13 : ereport(NOTICE,
9829 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9830 : indexName, constraintName)));
9831 13 : RenameRelationInternal(index_oid, constraintName, false, true);
9832 : }
9833 :
9834 : /* Extra checks needed if making primary key */
9835 6649 : if (stmt->primary)
9836 3721 : index_check_primary_key(rel, indexInfo, true, stmt);
9837 :
9838 : /* Note we currently don't support EXCLUSION constraints here */
9839 6645 : if (stmt->primary)
9840 3717 : constraintType = CONSTRAINT_PRIMARY;
9841 : else
9842 2928 : constraintType = CONSTRAINT_UNIQUE;
9843 :
9844 : /* Create the catalog entries for the constraint */
9845 6645 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9846 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9847 13290 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9848 6645 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9849 6645 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9850 :
9851 6645 : address = index_constraint_create(rel,
9852 : index_oid,
9853 : InvalidOid,
9854 : indexInfo,
9855 : constraintName,
9856 : constraintType,
9857 : flags,
9858 : allowSystemTableMods,
9859 : false); /* is_internal */
9860 :
9861 6645 : index_close(indexRel, NoLock);
9862 :
9863 6645 : return address;
9864 : }
9865 :
9866 : /*
9867 : * ALTER TABLE ADD CONSTRAINT
9868 : *
9869 : * Return value is the address of the new constraint; if no constraint was
9870 : * added, InvalidObjectAddress is returned.
9871 : */
9872 : static ObjectAddress
9873 8282 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9874 : Constraint *newConstraint, bool recurse, bool is_readd,
9875 : LOCKMODE lockmode)
9876 : {
9877 8282 : ObjectAddress address = InvalidObjectAddress;
9878 :
9879 : Assert(IsA(newConstraint, Constraint));
9880 :
9881 : /*
9882 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9883 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9884 : * parse_utilcmd.c).
9885 : */
9886 8282 : switch (newConstraint->contype)
9887 : {
9888 6457 : case CONSTR_CHECK:
9889 : case CONSTR_NOTNULL:
9890 : address =
9891 6457 : ATAddCheckNNConstraint(wqueue, tab, rel,
9892 : newConstraint, recurse, false, is_readd,
9893 : lockmode);
9894 6357 : break;
9895 :
9896 1825 : case CONSTR_FOREIGN:
9897 :
9898 : /*
9899 : * Assign or validate constraint name
9900 : */
9901 1825 : if (newConstraint->conname)
9902 : {
9903 757 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9904 : RelationGetRelid(rel),
9905 757 : newConstraint->conname))
9906 0 : ereport(ERROR,
9907 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9908 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9909 : newConstraint->conname,
9910 : RelationGetRelationName(rel))));
9911 : }
9912 : else
9913 1068 : newConstraint->conname =
9914 1068 : ChooseConstraintName(RelationGetRelationName(rel),
9915 1068 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9916 : "fkey",
9917 1068 : RelationGetNamespace(rel),
9918 : NIL);
9919 :
9920 1825 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9921 : newConstraint,
9922 : recurse, false,
9923 : lockmode);
9924 1460 : break;
9925 :
9926 0 : default:
9927 0 : elog(ERROR, "unrecognized constraint type: %d",
9928 : (int) newConstraint->contype);
9929 : }
9930 :
9931 7817 : return address;
9932 : }
9933 :
9934 : /*
9935 : * Generate the column-name portion of the constraint name for a new foreign
9936 : * key given the list of column names that reference the referenced
9937 : * table. This will be passed to ChooseConstraintName along with the parent
9938 : * table name and the "fkey" suffix.
9939 : *
9940 : * We know that less than NAMEDATALEN characters will actually be used, so we
9941 : * can truncate the result once we've generated that many.
9942 : *
9943 : * XXX see also ChooseExtendedStatisticNameAddition and
9944 : * ChooseIndexNameAddition.
9945 : */
9946 : static char *
9947 1068 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9948 : {
9949 : char buf[NAMEDATALEN * 2];
9950 1068 : int buflen = 0;
9951 : ListCell *lc;
9952 :
9953 1068 : buf[0] = '\0';
9954 2418 : foreach(lc, colnames)
9955 : {
9956 1350 : const char *name = strVal(lfirst(lc));
9957 :
9958 1350 : if (buflen > 0)
9959 282 : buf[buflen++] = '_'; /* insert _ between names */
9960 :
9961 : /*
9962 : * At this point we have buflen <= NAMEDATALEN. name should be less
9963 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9964 : */
9965 1350 : strlcpy(buf + buflen, name, NAMEDATALEN);
9966 1350 : buflen += strlen(buf + buflen);
9967 1350 : if (buflen >= NAMEDATALEN)
9968 0 : break;
9969 : }
9970 1068 : return pstrdup(buf);
9971 : }
9972 :
9973 : /*
9974 : * Add a check or not-null constraint to a single table and its children.
9975 : * Returns the address of the constraint added to the parent relation,
9976 : * if one gets added, or InvalidObjectAddress otherwise.
9977 : *
9978 : * Subroutine for ATExecAddConstraint.
9979 : *
9980 : * We must recurse to child tables during execution, rather than using
9981 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9982 : * constraints *must* be given the same name, else they won't be seen as
9983 : * related later. If the user didn't explicitly specify a name, then
9984 : * AddRelationNewConstraints would normally assign different names to the
9985 : * child constraints. To fix that, we must capture the name assigned at
9986 : * the parent table and pass that down.
9987 : */
9988 : static ObjectAddress
9989 7140 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9990 : Constraint *constr, bool recurse, bool recursing,
9991 : bool is_readd, LOCKMODE lockmode)
9992 : {
9993 : List *newcons;
9994 : ListCell *lcon;
9995 : List *children;
9996 : ListCell *child;
9997 7140 : ObjectAddress address = InvalidObjectAddress;
9998 :
9999 : /* Guard against stack overflow due to overly deep inheritance tree. */
10000 7140 : check_stack_depth();
10001 :
10002 : /* At top level, permission check was done in ATPrepCmd, else do it */
10003 7140 : if (recursing)
10004 683 : ATSimplePermissions(AT_AddConstraint, rel,
10005 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
10006 :
10007 : /*
10008 : * Call AddRelationNewConstraints to do the work, making sure it works on
10009 : * a copy of the Constraint so transformExpr can't modify the original. It
10010 : * returns a list of cooked constraints.
10011 : *
10012 : * If the constraint ends up getting merged with a pre-existing one, it's
10013 : * omitted from the returned list, which is what we want: we do not need
10014 : * to do any validation work. That can only happen at child tables,
10015 : * though, since we disallow merging at the top level.
10016 : */
10017 7140 : newcons = AddRelationNewConstraints(rel, NIL,
10018 : list_make1(copyObject(constr)),
10019 7140 : recursing || is_readd, /* allow_merge */
10020 : !recursing, /* is_local */
10021 : is_readd, /* is_internal */
10022 14280 : NULL); /* queryString not available
10023 : * here */
10024 :
10025 : /* we don't expect more than one constraint here */
10026 : Assert(list_length(newcons) <= 1);
10027 :
10028 : /* Add each to-be-validated constraint to Phase 3's queue */
10029 13958 : foreach(lcon, newcons)
10030 : {
10031 6914 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
10032 :
10033 6914 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
10034 : {
10035 : NewConstraint *newcon;
10036 :
10037 767 : newcon = palloc0_object(NewConstraint);
10038 767 : newcon->name = ccon->name;
10039 767 : newcon->contype = ccon->contype;
10040 767 : newcon->qual = ccon->expr;
10041 :
10042 767 : tab->constraints = lappend(tab->constraints, newcon);
10043 : }
10044 :
10045 : /* Save the actually assigned name if it was defaulted */
10046 6914 : if (constr->conname == NULL)
10047 5496 : constr->conname = ccon->name;
10048 :
10049 : /*
10050 : * If adding a valid not-null constraint, set the pg_attribute flag
10051 : * and tell phase 3 to verify existing rows, if needed. For an
10052 : * invalid constraint, just set attnotnull, without queueing
10053 : * verification.
10054 : */
10055 6914 : if (constr->contype == CONSTR_NOTNULL)
10056 5842 : set_attnotnull(wqueue, rel, ccon->attnum,
10057 5842 : !constr->skip_validation,
10058 5842 : !constr->skip_validation);
10059 :
10060 6914 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10061 : }
10062 :
10063 : /* At this point we must have a locked-down name to use */
10064 : Assert(newcons == NIL || constr->conname != NULL);
10065 :
10066 : /* Advance command counter in case same table is visited multiple times */
10067 7044 : CommandCounterIncrement();
10068 :
10069 : /*
10070 : * If the constraint got merged with an existing constraint, we're done.
10071 : * We mustn't recurse to child tables in this case, because they've
10072 : * already got the constraint, and visiting them again would lead to an
10073 : * incorrect value for coninhcount.
10074 : */
10075 7044 : if (newcons == NIL)
10076 130 : return address;
10077 :
10078 : /*
10079 : * If adding a NO INHERIT constraint, no need to find our children.
10080 : */
10081 6914 : if (constr->is_no_inherit)
10082 56 : return address;
10083 :
10084 : /*
10085 : * Propagate to children as appropriate. Unlike most other ALTER
10086 : * routines, we have to do this one level of recursion at a time; we can't
10087 : * use find_all_inheritors to do it in one pass.
10088 : */
10089 : children =
10090 6858 : find_inheritance_children(RelationGetRelid(rel), lockmode);
10091 :
10092 : /*
10093 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
10094 : * constraint creation only if there are no children currently. Error out
10095 : * otherwise.
10096 : */
10097 6858 : if (!recurse && children != NIL)
10098 4 : ereport(ERROR,
10099 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10100 : errmsg("constraint must be added to child tables too")));
10101 :
10102 : /*
10103 : * Recurse to create the constraint on each child.
10104 : */
10105 7517 : foreach(child, children)
10106 : {
10107 683 : Oid childrelid = lfirst_oid(child);
10108 : Relation childrel;
10109 : AlteredTableInfo *childtab;
10110 :
10111 : /* find_inheritance_children already got lock */
10112 683 : childrel = table_open(childrelid, NoLock);
10113 683 : CheckAlterTableIsSafe(childrel);
10114 :
10115 : /* Find or create work queue entry for this table */
10116 683 : childtab = ATGetQueueEntry(wqueue, childrel);
10117 :
10118 : /* Recurse to this child */
10119 683 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
10120 : constr, recurse, true, is_readd, lockmode);
10121 :
10122 663 : table_close(childrel, NoLock);
10123 : }
10124 :
10125 6834 : return address;
10126 : }
10127 :
10128 : /*
10129 : * Add a foreign-key constraint to a single table; return the new constraint's
10130 : * address.
10131 : *
10132 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
10133 : * lock on the rel, and have done appropriate validity checks for it.
10134 : * We do permissions checks here, however.
10135 : *
10136 : * When the referenced or referencing tables (or both) are partitioned,
10137 : * multiple pg_constraint rows are required -- one for each partitioned table
10138 : * and each partition on each side (fortunately, not one for every combination
10139 : * thereof). We also need action triggers on each leaf partition on the
10140 : * referenced side, and check triggers on each leaf partition on the
10141 : * referencing side.
10142 : */
10143 : static ObjectAddress
10144 1825 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
10145 : Constraint *fkconstraint,
10146 : bool recurse, bool recursing, LOCKMODE lockmode)
10147 : {
10148 : Relation pkrel;
10149 1825 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
10150 1825 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
10151 1825 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
10152 1825 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
10153 1825 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10154 1825 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10155 1825 : Oid opclasses[INDEX_MAX_KEYS] = {0};
10156 1825 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10157 1825 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10158 1825 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10159 1825 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10160 : bool with_period;
10161 : bool pk_has_without_overlaps;
10162 : int i;
10163 : int numfks,
10164 : numpks,
10165 : numfkdelsetcols;
10166 : Oid indexOid;
10167 : bool old_check_ok;
10168 : ObjectAddress address;
10169 1825 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10170 :
10171 : /*
10172 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10173 : * delete rows out from under us.
10174 : */
10175 1825 : if (OidIsValid(fkconstraint->old_pktable_oid))
10176 48 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10177 : else
10178 1777 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10179 :
10180 : /*
10181 : * Validity checks (permission checks wait till we have the column
10182 : * numbers)
10183 : */
10184 1821 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10185 4 : ereport(ERROR,
10186 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
10187 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10188 : RelationGetRelationName(rel),
10189 : RelationGetRelationName(pkrel)));
10190 :
10191 1817 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10192 236 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10193 0 : ereport(ERROR,
10194 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10195 : errmsg("referenced relation \"%s\" is not a table",
10196 : RelationGetRelationName(pkrel))));
10197 :
10198 1817 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
10199 1 : ereport(ERROR,
10200 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10201 : errmsg("permission denied: \"%s\" is a system catalog",
10202 : RelationGetRelationName(pkrel))));
10203 :
10204 : /*
10205 : * References from permanent or unlogged tables to temp tables, and from
10206 : * permanent tables to unlogged tables, are disallowed because the
10207 : * referenced data can vanish out from under us. References from temp
10208 : * tables to any other table type are also disallowed, because other
10209 : * backends might need to run the RI triggers on the perm table, but they
10210 : * can't reliably see tuples in the local buffers of other backends.
10211 : */
10212 1816 : switch (rel->rd_rel->relpersistence)
10213 : {
10214 1623 : case RELPERSISTENCE_PERMANENT:
10215 1623 : if (!RelationIsPermanent(pkrel))
10216 0 : ereport(ERROR,
10217 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10218 : errmsg("constraints on permanent tables may reference only permanent tables")));
10219 1623 : break;
10220 8 : case RELPERSISTENCE_UNLOGGED:
10221 8 : if (!RelationIsPermanent(pkrel)
10222 8 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10223 0 : ereport(ERROR,
10224 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10225 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10226 8 : break;
10227 185 : case RELPERSISTENCE_TEMP:
10228 185 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10229 0 : ereport(ERROR,
10230 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10231 : errmsg("constraints on temporary tables may reference only temporary tables")));
10232 185 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10233 0 : ereport(ERROR,
10234 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10235 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10236 185 : break;
10237 : }
10238 :
10239 : /*
10240 : * Look up the referencing attributes to make sure they exist, and record
10241 : * their attnums and type and collation OIDs.
10242 : */
10243 1816 : numfks = transformColumnNameList(RelationGetRelid(rel),
10244 : fkconstraint->fk_attrs,
10245 : fkattnum, fktypoid, fkcolloid);
10246 1796 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10247 1796 : if (with_period && !fkconstraint->fk_with_period)
10248 16 : ereport(ERROR,
10249 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10250 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10251 :
10252 1780 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10253 : fkconstraint->fk_del_set_cols,
10254 : fkdelsetcols, NULL, NULL);
10255 1776 : numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10256 : numfkdelsetcols,
10257 : fkdelsetcols,
10258 : fkconstraint->fk_del_set_cols);
10259 :
10260 : /*
10261 : * If the attribute list for the referenced table was omitted, lookup the
10262 : * definition of the primary key and use it. Otherwise, validate the
10263 : * supplied attribute list. In either case, discover the index OID and
10264 : * index opclasses, and the attnums and type and collation OIDs of the
10265 : * attributes.
10266 : */
10267 1772 : if (fkconstraint->pk_attrs == NIL)
10268 : {
10269 895 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10270 : &fkconstraint->pk_attrs,
10271 : pkattnum, pktypoid, pkcolloid,
10272 : opclasses, &pk_has_without_overlaps);
10273 :
10274 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10275 895 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10276 16 : ereport(ERROR,
10277 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10278 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10279 : }
10280 : else
10281 : {
10282 877 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10283 : fkconstraint->pk_attrs,
10284 : pkattnum, pktypoid, pkcolloid);
10285 :
10286 : /* Since we got pk_attrs, one should be a period. */
10287 857 : if (with_period && !fkconstraint->pk_with_period)
10288 16 : ereport(ERROR,
10289 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10290 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10291 :
10292 : /* Look for an index matching the column list */
10293 841 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10294 : with_period, opclasses, &pk_has_without_overlaps);
10295 : }
10296 :
10297 : /*
10298 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10299 : * must use PERIOD.
10300 : */
10301 1696 : if (pk_has_without_overlaps && !with_period)
10302 8 : ereport(ERROR,
10303 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10304 : errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10305 :
10306 : /*
10307 : * Now we can check permissions.
10308 : */
10309 1688 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10310 :
10311 : /*
10312 : * Check some things for generated columns.
10313 : */
10314 3945 : for (i = 0; i < numfks; i++)
10315 : {
10316 2277 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10317 :
10318 2277 : if (attgenerated)
10319 : {
10320 : /*
10321 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10322 : */
10323 32 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10324 32 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10325 32 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10326 8 : ereport(ERROR,
10327 : (errcode(ERRCODE_SYNTAX_ERROR),
10328 : errmsg("invalid %s action for foreign key constraint containing generated column",
10329 : "ON UPDATE")));
10330 24 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10331 16 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10332 8 : ereport(ERROR,
10333 : (errcode(ERRCODE_SYNTAX_ERROR),
10334 : errmsg("invalid %s action for foreign key constraint containing generated column",
10335 : "ON DELETE")));
10336 : }
10337 :
10338 : /*
10339 : * FKs on virtual columns are not supported. This would require
10340 : * various additional support in ri_triggers.c, including special
10341 : * handling in ri_NullCheck(), ri_KeysEqual(),
10342 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10343 : * as NULL there). Also not really practical as long as you can't
10344 : * index virtual columns.
10345 : */
10346 2261 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10347 4 : ereport(ERROR,
10348 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10349 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10350 : }
10351 :
10352 : /*
10353 : * Some actions are currently unsupported for foreign keys using PERIOD.
10354 : */
10355 1668 : if (fkconstraint->fk_with_period)
10356 : {
10357 178 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10358 170 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10359 158 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10360 146 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10361 44 : ereport(ERROR,
10362 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10363 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10364 : "ON UPDATE"));
10365 :
10366 134 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10367 130 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10368 130 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10369 130 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10370 4 : ereport(ERROR,
10371 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10372 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10373 : "ON DELETE"));
10374 : }
10375 :
10376 : /*
10377 : * Look up the equality operators to use in the constraint.
10378 : *
10379 : * Note that we have to be careful about the difference between the actual
10380 : * PK column type and the opclass' declared input type, which might be
10381 : * only binary-compatible with it. The declared opcintype is the right
10382 : * thing to probe pg_amop with.
10383 : */
10384 1620 : if (numfks != numpks)
10385 0 : ereport(ERROR,
10386 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10387 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10388 :
10389 : /*
10390 : * On the strength of a previous constraint, we might avoid scanning
10391 : * tables to validate this one. See below.
10392 : */
10393 1620 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10394 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10395 :
10396 3525 : for (i = 0; i < numpks; i++)
10397 : {
10398 2065 : Oid pktype = pktypoid[i];
10399 2065 : Oid fktype = fktypoid[i];
10400 : Oid fktyped;
10401 2065 : Oid pkcoll = pkcolloid[i];
10402 2065 : Oid fkcoll = fkcolloid[i];
10403 : HeapTuple cla_ht;
10404 : Form_pg_opclass cla_tup;
10405 : Oid amid;
10406 : Oid opfamily;
10407 : Oid opcintype;
10408 : bool for_overlaps;
10409 : CompareType cmptype;
10410 : Oid pfeqop;
10411 : Oid ppeqop;
10412 : Oid ffeqop;
10413 : int16 eqstrategy;
10414 : Oid pfeqop_right;
10415 :
10416 : /* We need several fields out of the pg_opclass entry */
10417 2065 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10418 2065 : if (!HeapTupleIsValid(cla_ht))
10419 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10420 2065 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10421 2065 : amid = cla_tup->opcmethod;
10422 2065 : opfamily = cla_tup->opcfamily;
10423 2065 : opcintype = cla_tup->opcintype;
10424 2065 : ReleaseSysCache(cla_ht);
10425 :
10426 : /*
10427 : * Get strategy number from index AM.
10428 : *
10429 : * For a normal foreign-key constraint, this should not fail, since we
10430 : * already checked that the index is unique and should therefore have
10431 : * appropriate equal operators. For a period foreign key, this could
10432 : * fail if we selected a non-matching exclusion constraint earlier.
10433 : * (XXX Maybe we should do these lookups earlier so we don't end up
10434 : * doing that.)
10435 : */
10436 2065 : for_overlaps = with_period && i == numpks - 1;
10437 2065 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10438 2065 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10439 2065 : if (eqstrategy == InvalidStrategy)
10440 0 : ereport(ERROR,
10441 : errcode(ERRCODE_UNDEFINED_OBJECT),
10442 : for_overlaps
10443 : ? errmsg("could not identify an overlaps operator for foreign key")
10444 : : errmsg("could not identify an equality operator for foreign key"),
10445 : errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10446 : cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10447 :
10448 : /*
10449 : * There had better be a primary equality operator for the index.
10450 : * We'll use it for PK = PK comparisons.
10451 : */
10452 2065 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10453 : eqstrategy);
10454 :
10455 2065 : if (!OidIsValid(ppeqop))
10456 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10457 : eqstrategy, opcintype, opcintype, opfamily);
10458 :
10459 : /*
10460 : * Are there equality operators that take exactly the FK type? Assume
10461 : * we should look through any domain here.
10462 : */
10463 2065 : fktyped = getBaseType(fktype);
10464 :
10465 2065 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10466 : eqstrategy);
10467 2065 : if (OidIsValid(pfeqop))
10468 : {
10469 1616 : pfeqop_right = fktyped;
10470 1616 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10471 : eqstrategy);
10472 : }
10473 : else
10474 : {
10475 : /* keep compiler quiet */
10476 449 : pfeqop_right = InvalidOid;
10477 449 : ffeqop = InvalidOid;
10478 : }
10479 :
10480 2065 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10481 : {
10482 : /*
10483 : * Otherwise, look for an implicit cast from the FK type to the
10484 : * opcintype, and if found, use the primary equality operator.
10485 : * This is a bit tricky because opcintype might be a polymorphic
10486 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10487 : * whether the two actual column types can be concurrently cast to
10488 : * that type. (Otherwise, we'd fail to reject combinations such
10489 : * as int[] and point[].)
10490 : */
10491 : Oid input_typeids[2];
10492 : Oid target_typeids[2];
10493 :
10494 449 : input_typeids[0] = pktype;
10495 449 : input_typeids[1] = fktype;
10496 449 : target_typeids[0] = opcintype;
10497 449 : target_typeids[1] = opcintype;
10498 449 : if (can_coerce_type(2, input_typeids, target_typeids,
10499 : COERCION_IMPLICIT))
10500 : {
10501 297 : pfeqop = ffeqop = ppeqop;
10502 297 : pfeqop_right = opcintype;
10503 : }
10504 : }
10505 :
10506 2065 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10507 152 : ereport(ERROR,
10508 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10509 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10510 : fkconstraint->conname),
10511 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10512 : "are of incompatible types: %s and %s.",
10513 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10514 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10515 : format_type_be(fktype),
10516 : format_type_be(pktype))));
10517 :
10518 : /*
10519 : * This shouldn't be possible, but better check to make sure we have a
10520 : * consistent state for the check below.
10521 : */
10522 1913 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10523 0 : elog(ERROR, "key columns are not both collatable");
10524 :
10525 1913 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10526 : {
10527 : bool pkcolldet;
10528 : bool fkcolldet;
10529 :
10530 69 : pkcolldet = get_collation_isdeterministic(pkcoll);
10531 69 : fkcolldet = get_collation_isdeterministic(fkcoll);
10532 :
10533 : /*
10534 : * SQL requires that both collations are the same. This is
10535 : * because we need a consistent notion of equality on both
10536 : * columns. We relax this by allowing different collations if
10537 : * they are both deterministic. (This is also for backward
10538 : * compatibility, because PostgreSQL has always allowed this.)
10539 : */
10540 69 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10541 8 : ereport(ERROR,
10542 : (errcode(ERRCODE_COLLATION_MISMATCH),
10543 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10544 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10545 : "have incompatible collations: \"%s\" and \"%s\". "
10546 : "If either collation is nondeterministic, then both collations have to be the same.",
10547 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10548 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10549 : get_collation_name(fkcoll),
10550 : get_collation_name(pkcoll))));
10551 : }
10552 :
10553 1905 : if (old_check_ok)
10554 : {
10555 : /*
10556 : * When a pfeqop changes, revalidate the constraint. We could
10557 : * permit intra-opfamily changes, but that adds subtle complexity
10558 : * without any concrete benefit for core types. We need not
10559 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10560 : */
10561 4 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10562 4 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10563 : old_pfeqop_item);
10564 : }
10565 1905 : if (old_check_ok)
10566 : {
10567 : Oid old_fktype;
10568 : Oid new_fktype;
10569 : CoercionPathType old_pathtype;
10570 : CoercionPathType new_pathtype;
10571 : Oid old_castfunc;
10572 : Oid new_castfunc;
10573 : Oid old_fkcoll;
10574 : Oid new_fkcoll;
10575 4 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10576 4 : fkattnum[i] - 1);
10577 :
10578 : /*
10579 : * Identify coercion pathways from each of the old and new FK-side
10580 : * column types to the right (foreign) operand type of the pfeqop.
10581 : * We may assume that pg_constraint.conkey is not changing.
10582 : */
10583 4 : old_fktype = attr->atttypid;
10584 4 : new_fktype = fktype;
10585 4 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10586 : &old_castfunc);
10587 4 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10588 : &new_castfunc);
10589 :
10590 4 : old_fkcoll = attr->attcollation;
10591 4 : new_fkcoll = fkcoll;
10592 :
10593 : /*
10594 : * Upon a change to the cast from the FK column to its pfeqop
10595 : * operand, revalidate the constraint. For this evaluation, a
10596 : * binary coercion cast is equivalent to no cast at all. While
10597 : * type implementors should design implicit casts with an eye
10598 : * toward consistency of operations like equality, we cannot
10599 : * assume here that they have done so.
10600 : *
10601 : * A function with a polymorphic argument could change behavior
10602 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10603 : * when the cast destination is polymorphic, we only avoid
10604 : * revalidation if the input type has not changed at all. Given
10605 : * just the core data types and operator classes, this requirement
10606 : * prevents no would-be optimizations.
10607 : *
10608 : * If the cast converts from a base type to a domain thereon, then
10609 : * that domain type must be the opcintype of the unique index.
10610 : * Necessarily, the primary key column must then be of the domain
10611 : * type. Since the constraint was previously valid, all values on
10612 : * the foreign side necessarily exist on the primary side and in
10613 : * turn conform to the domain. Consequently, we need not treat
10614 : * domains specially here.
10615 : *
10616 : * If the collation changes, revalidation is required, unless both
10617 : * collations are deterministic, because those share the same
10618 : * notion of equality (because texteq reduces to bitwise
10619 : * equality).
10620 : *
10621 : * We need not directly consider the PK type. It's necessarily
10622 : * binary coercible to the opcintype of the unique index column,
10623 : * and ri_triggers.c will only deal with PK datums in terms of
10624 : * that opcintype. Changing the opcintype also changes pfeqop.
10625 : */
10626 4 : old_check_ok = (new_pathtype == old_pathtype &&
10627 4 : new_castfunc == old_castfunc &&
10628 4 : (!IsPolymorphicType(pfeqop_right) ||
10629 8 : new_fktype == old_fktype) &&
10630 0 : (new_fkcoll == old_fkcoll ||
10631 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10632 : }
10633 :
10634 1905 : pfeqoperators[i] = pfeqop;
10635 1905 : ppeqoperators[i] = ppeqop;
10636 1905 : ffeqoperators[i] = ffeqop;
10637 : }
10638 :
10639 : /*
10640 : * For FKs with PERIOD we need additional operators to check whether the
10641 : * referencing row's range is contained by the aggregated ranges of the
10642 : * referenced row(s). For rangetypes and multirangetypes this is
10643 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10644 : * support for now. FKs will look these up at "runtime", but we should
10645 : * make sure the lookup works here, even if we don't use the values.
10646 : */
10647 1460 : if (with_period)
10648 : {
10649 : Oid periodoperoid;
10650 : Oid aggedperiodoperoid;
10651 : Oid intersectoperoid;
10652 :
10653 118 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10654 : &intersectoperoid);
10655 : }
10656 :
10657 : /* First, create the constraint catalog entry itself. */
10658 1460 : address = addFkConstraint(addFkBothSides,
10659 : fkconstraint->conname, fkconstraint, rel, pkrel,
10660 : indexOid,
10661 : InvalidOid, /* no parent constraint */
10662 : numfks,
10663 : pkattnum,
10664 : fkattnum,
10665 : pfeqoperators,
10666 : ppeqoperators,
10667 : ffeqoperators,
10668 : numfkdelsetcols,
10669 : fkdelsetcols,
10670 : false,
10671 : with_period);
10672 :
10673 : /* Next process the action triggers at the referenced side and recurse */
10674 1460 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10675 : indexOid,
10676 : address.objectId,
10677 : numfks,
10678 : pkattnum,
10679 : fkattnum,
10680 : pfeqoperators,
10681 : ppeqoperators,
10682 : ffeqoperators,
10683 : numfkdelsetcols,
10684 : fkdelsetcols,
10685 : old_check_ok,
10686 : InvalidOid, InvalidOid,
10687 : with_period);
10688 :
10689 : /* Lastly create the check triggers at the referencing side and recurse */
10690 1460 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10691 : indexOid,
10692 : address.objectId,
10693 : numfks,
10694 : pkattnum,
10695 : fkattnum,
10696 : pfeqoperators,
10697 : ppeqoperators,
10698 : ffeqoperators,
10699 : numfkdelsetcols,
10700 : fkdelsetcols,
10701 : old_check_ok,
10702 : lockmode,
10703 : InvalidOid, InvalidOid,
10704 : with_period);
10705 :
10706 : /*
10707 : * Done. Close pk table, but keep lock until we've committed.
10708 : */
10709 1460 : table_close(pkrel, NoLock);
10710 :
10711 1460 : return address;
10712 : }
10713 :
10714 : /*
10715 : * validateFkOnDeleteSetColumns
10716 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10717 : * column lists are valid.
10718 : *
10719 : * If there are duplicates in the fksetcolsattnums[] array, this silently
10720 : * removes the dups. The new count of numfksetcols is returned.
10721 : */
10722 : static int
10723 1776 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10724 : int numfksetcols, int16 *fksetcolsattnums,
10725 : List *fksetcols)
10726 : {
10727 1776 : int numcolsout = 0;
10728 :
10729 1796 : for (int i = 0; i < numfksetcols; i++)
10730 : {
10731 24 : int16 setcol_attnum = fksetcolsattnums[i];
10732 24 : bool seen = false;
10733 :
10734 : /* Make sure it's in fkattnums[] */
10735 44 : for (int j = 0; j < numfks; j++)
10736 : {
10737 40 : if (fkattnums[j] == setcol_attnum)
10738 : {
10739 20 : seen = true;
10740 20 : break;
10741 : }
10742 : }
10743 :
10744 24 : if (!seen)
10745 : {
10746 4 : char *col = strVal(list_nth(fksetcols, i));
10747 :
10748 4 : ereport(ERROR,
10749 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10750 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10751 : }
10752 :
10753 : /* Now check for dups */
10754 20 : seen = false;
10755 20 : for (int j = 0; j < numcolsout; j++)
10756 : {
10757 4 : if (fksetcolsattnums[j] == setcol_attnum)
10758 : {
10759 4 : seen = true;
10760 4 : break;
10761 : }
10762 : }
10763 20 : if (!seen)
10764 16 : fksetcolsattnums[numcolsout++] = setcol_attnum;
10765 : }
10766 1772 : return numcolsout;
10767 : }
10768 :
10769 : /*
10770 : * addFkConstraint
10771 : * Install pg_constraint entries to implement a foreign key constraint.
10772 : * Caller must separately invoke addFkRecurseReferenced and
10773 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10774 : * and (for partitioned tables) recurse to partitions.
10775 : *
10776 : * fkside: the side of the FK (or both) to create. Caller should
10777 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10778 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10779 : * addFkBothSides.
10780 : * constraintname: the base name for the constraint being added,
10781 : * copied to fkconstraint->conname if the latter is not set
10782 : * fkconstraint: the constraint being added
10783 : * rel: the root referencing relation
10784 : * pkrel: the referenced relation; might be a partition, if recursing
10785 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10786 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10787 : * top-level constraint
10788 : * numfks: the number of columns in the foreign key
10789 : * pkattnum: the attnum array of referenced attributes
10790 : * fkattnum: the attnum array of referencing attributes
10791 : * pf/pp/ffeqoperators: OID array of operators between columns
10792 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10793 : * (...) clause
10794 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10795 : * NULL/DEFAULT clause
10796 : * with_period: true if this is a temporal FK
10797 : */
10798 : static ObjectAddress
10799 2827 : addFkConstraint(addFkConstraintSides fkside,
10800 : char *constraintname, Constraint *fkconstraint,
10801 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10802 : int numfks, int16 *pkattnum,
10803 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10804 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10805 : bool is_internal, bool with_period)
10806 : {
10807 : ObjectAddress address;
10808 : Oid constrOid;
10809 : char *conname;
10810 : bool conislocal;
10811 : int16 coninhcount;
10812 : bool connoinherit;
10813 :
10814 : /*
10815 : * Verify relkind for each referenced partition. At the top level, this
10816 : * is redundant with a previous check, but we need it when recursing.
10817 : */
10818 2827 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10819 589 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10820 0 : ereport(ERROR,
10821 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10822 : errmsg("referenced relation \"%s\" is not a table",
10823 : RelationGetRelationName(pkrel))));
10824 :
10825 : /*
10826 : * Caller supplies us with a constraint name; however, it may be used in
10827 : * this partition, so come up with a different one in that case. Unless
10828 : * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10829 : * supplied name with an underscore and digit(s) appended.
10830 : */
10831 2827 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10832 : RelationGetRelid(rel),
10833 : constraintname))
10834 811 : conname = ChooseConstraintName(constraintname,
10835 : NULL,
10836 : "",
10837 811 : RelationGetNamespace(rel), NIL);
10838 : else
10839 2016 : conname = constraintname;
10840 :
10841 2827 : if (fkconstraint->conname == NULL)
10842 283 : fkconstraint->conname = pstrdup(conname);
10843 :
10844 2827 : if (OidIsValid(parentConstr))
10845 : {
10846 1367 : conislocal = false;
10847 1367 : coninhcount = 1;
10848 1367 : connoinherit = false;
10849 : }
10850 : else
10851 : {
10852 1460 : conislocal = true;
10853 1460 : coninhcount = 0;
10854 :
10855 : /*
10856 : * always inherit for partitioned tables, never for legacy inheritance
10857 : */
10858 1460 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10859 : }
10860 :
10861 : /*
10862 : * Record the FK constraint in pg_constraint.
10863 : */
10864 2827 : constrOid = CreateConstraintEntry(conname,
10865 2827 : RelationGetNamespace(rel),
10866 : CONSTRAINT_FOREIGN,
10867 2827 : fkconstraint->deferrable,
10868 2827 : fkconstraint->initdeferred,
10869 2827 : fkconstraint->is_enforced,
10870 2827 : fkconstraint->initially_valid,
10871 : parentConstr,
10872 : RelationGetRelid(rel),
10873 : fkattnum,
10874 : numfks,
10875 : numfks,
10876 : InvalidOid, /* not a domain constraint */
10877 : indexOid,
10878 : RelationGetRelid(pkrel),
10879 : pkattnum,
10880 : pfeqoperators,
10881 : ppeqoperators,
10882 : ffeqoperators,
10883 : numfks,
10884 2827 : fkconstraint->fk_upd_action,
10885 2827 : fkconstraint->fk_del_action,
10886 : fkdelsetcols,
10887 : numfkdelsetcols,
10888 2827 : fkconstraint->fk_matchtype,
10889 : NULL, /* no exclusion constraint */
10890 : NULL, /* no check constraint */
10891 : NULL,
10892 : conislocal, /* islocal */
10893 : coninhcount, /* inhcount */
10894 : connoinherit, /* conNoInherit */
10895 : with_period, /* conPeriod */
10896 : is_internal); /* is_internal */
10897 :
10898 2827 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10899 :
10900 : /*
10901 : * In partitioning cases, create the dependency entries for this
10902 : * constraint. (For non-partitioned cases, relevant entries were created
10903 : * by CreateConstraintEntry.)
10904 : *
10905 : * On the referenced side, we need the constraint to have an internal
10906 : * dependency on its parent constraint; this means that this constraint
10907 : * cannot be dropped on its own -- only through the parent constraint. It
10908 : * also means the containing partition cannot be dropped on its own, but
10909 : * it can be detached, at which point this dependency is removed (after
10910 : * verifying that no rows are referenced via this FK.)
10911 : *
10912 : * When processing the referencing side, we link the constraint via the
10913 : * special partitioning dependencies: the parent constraint is the primary
10914 : * dependent, and the partition on which the foreign key exists is the
10915 : * secondary dependency. That way, this constraint is dropped if either
10916 : * of these objects is.
10917 : *
10918 : * Note that this is only necessary for the subsidiary pg_constraint rows
10919 : * in partitions; the topmost row doesn't need any of this.
10920 : */
10921 2827 : if (OidIsValid(parentConstr))
10922 : {
10923 : ObjectAddress referenced;
10924 :
10925 1367 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10926 :
10927 : Assert(fkside != addFkBothSides);
10928 1367 : if (fkside == addFkReferencedSide)
10929 807 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10930 : else
10931 : {
10932 560 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10933 560 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10934 560 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10935 : }
10936 : }
10937 :
10938 : /* make new constraint visible, in case we add more */
10939 2827 : CommandCounterIncrement();
10940 :
10941 2827 : return address;
10942 : }
10943 :
10944 : /*
10945 : * addFkRecurseReferenced
10946 : * Recursive helper for the referenced side of foreign key creation,
10947 : * which creates the action triggers and recurses
10948 : *
10949 : * If the referenced relation is a plain relation, create the necessary action
10950 : * triggers that implement the constraint. If the referenced relation is a
10951 : * partitioned table, then we create a pg_constraint row referencing the parent
10952 : * of the referencing side for it and recurse on this routine for each
10953 : * partition.
10954 : *
10955 : * fkconstraint: the constraint being added
10956 : * rel: the root referencing relation
10957 : * pkrel: the referenced relation; might be a partition, if recursing
10958 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10959 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10960 : * top-level constraint
10961 : * numfks: the number of columns in the foreign key
10962 : * pkattnum: the attnum array of referenced attributes
10963 : * fkattnum: the attnum array of referencing attributes
10964 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10965 : * NULL/DEFAULT (...) clause
10966 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10967 : * NULL/DEFAULT clause
10968 : * pf/pp/ffeqoperators: OID array of operators between columns
10969 : * old_check_ok: true if this constraint replaces an existing one that
10970 : * was already validated (thus this one doesn't need validation)
10971 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10972 : * partition, the OIDs of the parent action triggers for DELETE and
10973 : * UPDATE respectively.
10974 : * with_period: true if this is a temporal FK
10975 : */
10976 : static void
10977 2339 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10978 : Relation pkrel, Oid indexOid, Oid parentConstr,
10979 : int numfks,
10980 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10981 : Oid *ppeqoperators, Oid *ffeqoperators,
10982 : int numfkdelsetcols, int16 *fkdelsetcols,
10983 : bool old_check_ok,
10984 : Oid parentDelTrigger, Oid parentUpdTrigger,
10985 : bool with_period)
10986 : {
10987 2339 : Oid deleteTriggerOid = InvalidOid,
10988 2339 : updateTriggerOid = InvalidOid;
10989 :
10990 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10991 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10992 :
10993 : /*
10994 : * Create action triggers to enforce the constraint, or skip them if the
10995 : * constraint is NOT ENFORCED.
10996 : */
10997 2339 : if (fkconstraint->is_enforced)
10998 2286 : createForeignKeyActionTriggers(RelationGetRelid(rel),
10999 : RelationGetRelid(pkrel),
11000 : fkconstraint,
11001 : parentConstr, indexOid,
11002 : parentDelTrigger, parentUpdTrigger,
11003 : &deleteTriggerOid, &updateTriggerOid);
11004 :
11005 : /*
11006 : * If the referenced table is partitioned, recurse on ourselves to handle
11007 : * each partition. We need one pg_constraint row created for each
11008 : * partition in addition to the pg_constraint row for the parent table.
11009 : */
11010 2339 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11011 : {
11012 373 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
11013 :
11014 1048 : for (int i = 0; i < pd->nparts; i++)
11015 : {
11016 : Relation partRel;
11017 : AttrMap *map;
11018 : AttrNumber *mapped_pkattnum;
11019 : Oid partIndexId;
11020 : ObjectAddress address;
11021 :
11022 : /* XXX would it be better to acquire these locks beforehand? */
11023 675 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
11024 :
11025 : /*
11026 : * Map the attribute numbers in the referenced side of the FK
11027 : * definition to match the partition's column layout.
11028 : */
11029 675 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
11030 : RelationGetDescr(pkrel),
11031 : false);
11032 675 : if (map)
11033 : {
11034 89 : mapped_pkattnum = palloc_array(AttrNumber, numfks);
11035 186 : for (int j = 0; j < numfks; j++)
11036 97 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
11037 : }
11038 : else
11039 586 : mapped_pkattnum = pkattnum;
11040 :
11041 : /* Determine the index to use at this level */
11042 675 : partIndexId = index_get_partition(partRel, indexOid);
11043 675 : if (!OidIsValid(partIndexId))
11044 0 : elog(ERROR, "index for %u not found in partition %s",
11045 : indexOid, RelationGetRelationName(partRel));
11046 :
11047 : /* Create entry at this level ... */
11048 675 : address = addFkConstraint(addFkReferencedSide,
11049 : fkconstraint->conname, fkconstraint, rel,
11050 : partRel, partIndexId, parentConstr,
11051 : numfks, mapped_pkattnum,
11052 : fkattnum, pfeqoperators, ppeqoperators,
11053 : ffeqoperators, numfkdelsetcols,
11054 : fkdelsetcols, true, with_period);
11055 : /* ... and recurse to our children */
11056 675 : addFkRecurseReferenced(fkconstraint, rel, partRel,
11057 : partIndexId, address.objectId, numfks,
11058 : mapped_pkattnum, fkattnum,
11059 : pfeqoperators, ppeqoperators, ffeqoperators,
11060 : numfkdelsetcols, fkdelsetcols,
11061 : old_check_ok,
11062 : deleteTriggerOid, updateTriggerOid,
11063 : with_period);
11064 :
11065 : /* Done -- clean up (but keep the lock) */
11066 675 : table_close(partRel, NoLock);
11067 675 : if (map)
11068 : {
11069 89 : pfree(mapped_pkattnum);
11070 89 : free_attrmap(map);
11071 : }
11072 : }
11073 : }
11074 2339 : }
11075 :
11076 : /*
11077 : * addFkRecurseReferencing
11078 : * Recursive helper for the referencing side of foreign key creation,
11079 : * which creates the check triggers and recurses
11080 : *
11081 : * If the referencing relation is a plain relation, create the necessary check
11082 : * triggers that implement the constraint, and set up for Phase 3 constraint
11083 : * verification. If the referencing relation is a partitioned table, then
11084 : * we create a pg_constraint row for it and recurse on this routine for each
11085 : * partition.
11086 : *
11087 : * We assume that the referenced relation is locked against concurrent
11088 : * deletions. If it's a partitioned relation, every partition must be so
11089 : * locked.
11090 : *
11091 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
11092 : * of an ALTER TABLE sequence.
11093 : * fkconstraint: the constraint being added
11094 : * rel: the referencing relation; might be a partition, if recursing
11095 : * pkrel: the root referenced relation
11096 : * indexOid: the OID of the index (on pkrel) implementing this constraint
11097 : * parentConstr: the OID of the parent constraint (there is always one)
11098 : * numfks: the number of columns in the foreign key
11099 : * pkattnum: the attnum array of referenced attributes
11100 : * fkattnum: the attnum array of referencing attributes
11101 : * pf/pp/ffeqoperators: OID array of operators between columns
11102 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
11103 : * (...) clause
11104 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
11105 : * NULL/DEFAULT clause
11106 : * old_check_ok: true if this constraint replaces an existing one that
11107 : * was already validated (thus this one doesn't need validation)
11108 : * lockmode: the lockmode to acquire on partitions when recursing
11109 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
11110 : * a partition, the OIDs of the parent check triggers for INSERT and
11111 : * UPDATE respectively.
11112 : * with_period: true if this is a temporal FK
11113 : */
11114 : static void
11115 2020 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
11116 : Relation pkrel, Oid indexOid, Oid parentConstr,
11117 : int numfks, int16 *pkattnum, int16 *fkattnum,
11118 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11119 : int numfkdelsetcols, int16 *fkdelsetcols,
11120 : bool old_check_ok, LOCKMODE lockmode,
11121 : Oid parentInsTrigger, Oid parentUpdTrigger,
11122 : bool with_period)
11123 : {
11124 2020 : Oid insertTriggerOid = InvalidOid,
11125 2020 : updateTriggerOid = InvalidOid;
11126 :
11127 : Assert(OidIsValid(parentConstr));
11128 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
11129 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
11130 :
11131 2020 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11132 0 : ereport(ERROR,
11133 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11134 : errmsg("foreign key constraints are not supported on foreign tables")));
11135 :
11136 : /*
11137 : * Add check triggers if the constraint is ENFORCED, and if needed,
11138 : * schedule them to be checked in Phase 3.
11139 : *
11140 : * If the relation is partitioned, drill down to do it to its partitions.
11141 : */
11142 2020 : if (fkconstraint->is_enforced)
11143 1983 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
11144 : RelationGetRelid(pkrel),
11145 : fkconstraint,
11146 : parentConstr,
11147 : indexOid,
11148 : parentInsTrigger, parentUpdTrigger,
11149 : &insertTriggerOid, &updateTriggerOid);
11150 :
11151 2020 : if (rel->rd_rel->relkind == RELKIND_RELATION)
11152 : {
11153 : /*
11154 : * Tell Phase 3 to check that the constraint is satisfied by existing
11155 : * rows. We can skip this during table creation, when constraint is
11156 : * specified as NOT ENFORCED, or when requested explicitly by
11157 : * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11158 : * recreating a constraint following a SET DATA TYPE operation that
11159 : * did not impugn its validity.
11160 : */
11161 1697 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11162 599 : fkconstraint->is_enforced)
11163 : {
11164 : NewConstraint *newcon;
11165 : AlteredTableInfo *tab;
11166 :
11167 599 : tab = ATGetQueueEntry(wqueue, rel);
11168 :
11169 599 : newcon = palloc0_object(NewConstraint);
11170 599 : newcon->name = get_constraint_name(parentConstr);
11171 599 : newcon->contype = CONSTR_FOREIGN;
11172 599 : newcon->refrelid = RelationGetRelid(pkrel);
11173 599 : newcon->refindid = indexOid;
11174 599 : newcon->conid = parentConstr;
11175 599 : newcon->conwithperiod = fkconstraint->fk_with_period;
11176 599 : newcon->qual = (Node *) fkconstraint;
11177 :
11178 599 : tab->constraints = lappend(tab->constraints, newcon);
11179 : }
11180 : }
11181 323 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11182 : {
11183 323 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
11184 : Relation trigrel;
11185 :
11186 : /*
11187 : * Triggers of the foreign keys will be manipulated a bunch of times
11188 : * in the loop below. To avoid repeatedly opening/closing the trigger
11189 : * catalog relation, we open it here and pass it to the subroutines
11190 : * called below.
11191 : */
11192 323 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11193 :
11194 : /*
11195 : * Recurse to take appropriate action on each partition; either we
11196 : * find an existing constraint to reparent to ours, or we create a new
11197 : * one.
11198 : */
11199 608 : for (int i = 0; i < pd->nparts; i++)
11200 : {
11201 289 : Relation partition = table_open(pd->oids[i], lockmode);
11202 : List *partFKs;
11203 : AttrMap *attmap;
11204 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11205 : bool attached;
11206 : ObjectAddress address;
11207 :
11208 289 : CheckAlterTableIsSafe(partition);
11209 :
11210 285 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
11211 : RelationGetDescr(rel),
11212 : false);
11213 714 : for (int j = 0; j < numfks; j++)
11214 429 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11215 :
11216 : /* Check whether an existing constraint can be repurposed */
11217 285 : partFKs = copyObject(RelationGetFKeyList(partition));
11218 285 : attached = false;
11219 581 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11220 : {
11221 19 : if (tryAttachPartitionForeignKey(wqueue,
11222 : fk,
11223 : partition,
11224 : parentConstr,
11225 : numfks,
11226 : mapped_fkattnum,
11227 : pkattnum,
11228 : pfeqoperators,
11229 : insertTriggerOid,
11230 : updateTriggerOid,
11231 : trigrel))
11232 : {
11233 8 : attached = true;
11234 8 : break;
11235 : }
11236 : }
11237 285 : if (attached)
11238 : {
11239 8 : table_close(partition, NoLock);
11240 8 : continue;
11241 : }
11242 :
11243 : /*
11244 : * No luck finding a good constraint to reuse; create our own.
11245 : */
11246 277 : address = addFkConstraint(addFkReferencingSide,
11247 : fkconstraint->conname, fkconstraint,
11248 : partition, pkrel, indexOid, parentConstr,
11249 : numfks, pkattnum,
11250 : mapped_fkattnum, pfeqoperators,
11251 : ppeqoperators, ffeqoperators,
11252 : numfkdelsetcols, fkdelsetcols, true,
11253 : with_period);
11254 :
11255 : /* call ourselves to finalize the creation and we're done */
11256 277 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11257 : indexOid,
11258 : address.objectId,
11259 : numfks,
11260 : pkattnum,
11261 : mapped_fkattnum,
11262 : pfeqoperators,
11263 : ppeqoperators,
11264 : ffeqoperators,
11265 : numfkdelsetcols,
11266 : fkdelsetcols,
11267 : old_check_ok,
11268 : lockmode,
11269 : insertTriggerOid,
11270 : updateTriggerOid,
11271 : with_period);
11272 :
11273 277 : table_close(partition, NoLock);
11274 : }
11275 :
11276 319 : table_close(trigrel, RowExclusiveLock);
11277 : }
11278 2016 : }
11279 :
11280 : /*
11281 : * CloneForeignKeyConstraints
11282 : * Clone foreign keys from a partitioned table to a newly acquired
11283 : * partition.
11284 : *
11285 : * partitionRel is a partition of parentRel, so we can be certain that it has
11286 : * the same columns with the same datatypes. The columns may be in different
11287 : * order, though.
11288 : *
11289 : * wqueue must be passed to set up phase 3 constraint checking, unless the
11290 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
11291 : * PARTITION OF).
11292 : */
11293 : static void
11294 7931 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
11295 : Relation partitionRel)
11296 : {
11297 : /* This only works for declarative partitioning */
11298 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11299 :
11300 : /*
11301 : * First, clone constraints where the parent is on the referencing side.
11302 : */
11303 7931 : CloneFkReferencing(wqueue, parentRel, partitionRel);
11304 :
11305 : /*
11306 : * Clone constraints for which the parent is on the referenced side.
11307 : */
11308 7919 : CloneFkReferenced(parentRel, partitionRel);
11309 7919 : }
11310 :
11311 : /*
11312 : * CloneFkReferenced
11313 : * Subroutine for CloneForeignKeyConstraints
11314 : *
11315 : * Find all the FKs that have the parent relation on the referenced side;
11316 : * clone those constraints to the given partition. This is to be called
11317 : * when the partition is being created or attached.
11318 : *
11319 : * This recurses to partitions, if the relation being attached is partitioned.
11320 : * Recursion is done by calling addFkRecurseReferenced.
11321 : */
11322 : static void
11323 7919 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11324 : {
11325 : Relation pg_constraint;
11326 : AttrMap *attmap;
11327 : ListCell *cell;
11328 : SysScanDesc scan;
11329 : ScanKeyData key[2];
11330 : HeapTuple tuple;
11331 7919 : List *clone = NIL;
11332 : Relation trigrel;
11333 :
11334 : /*
11335 : * Search for any constraints where this partition's parent is in the
11336 : * referenced side. However, we must not clone any constraint whose
11337 : * parent constraint is also going to be cloned, to avoid duplicates. So
11338 : * do it in two steps: first construct the list of constraints to clone,
11339 : * then go over that list cloning those whose parents are not in the list.
11340 : * (We must not rely on the parent being seen first, since the catalog
11341 : * scan could return children first.)
11342 : */
11343 7919 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11344 7919 : ScanKeyInit(&key[0],
11345 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11346 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11347 7919 : ScanKeyInit(&key[1],
11348 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11349 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11350 : /* This is a seqscan, as we don't have a usable index ... */
11351 7919 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11352 : NULL, 2, key);
11353 8199 : while ((tuple = systable_getnext(scan)) != NULL)
11354 : {
11355 280 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11356 :
11357 280 : clone = lappend_oid(clone, constrForm->oid);
11358 : }
11359 7919 : systable_endscan(scan);
11360 7919 : table_close(pg_constraint, RowShareLock);
11361 :
11362 : /*
11363 : * Triggers of the foreign keys will be manipulated a bunch of times in
11364 : * the loop below. To avoid repeatedly opening/closing the trigger
11365 : * catalog relation, we open it here and pass it to the subroutines called
11366 : * below.
11367 : */
11368 7919 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11369 :
11370 7919 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11371 : RelationGetDescr(parentRel),
11372 : false);
11373 8199 : foreach(cell, clone)
11374 : {
11375 280 : Oid constrOid = lfirst_oid(cell);
11376 : Form_pg_constraint constrForm;
11377 : Relation fkRel;
11378 : Oid indexOid;
11379 : Oid partIndexId;
11380 : int numfks;
11381 : AttrNumber conkey[INDEX_MAX_KEYS];
11382 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11383 : AttrNumber confkey[INDEX_MAX_KEYS];
11384 : Oid conpfeqop[INDEX_MAX_KEYS];
11385 : Oid conppeqop[INDEX_MAX_KEYS];
11386 : Oid conffeqop[INDEX_MAX_KEYS];
11387 : int numfkdelsetcols;
11388 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11389 : Constraint *fkconstraint;
11390 : ObjectAddress address;
11391 280 : Oid deleteTriggerOid = InvalidOid,
11392 280 : updateTriggerOid = InvalidOid;
11393 :
11394 280 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11395 280 : if (!HeapTupleIsValid(tuple))
11396 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11397 280 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11398 :
11399 : /*
11400 : * As explained above: don't try to clone a constraint for which we're
11401 : * going to clone the parent.
11402 : */
11403 280 : if (list_member_oid(clone, constrForm->conparentid))
11404 : {
11405 148 : ReleaseSysCache(tuple);
11406 148 : continue;
11407 : }
11408 :
11409 : /* We need the same lock level that CreateTrigger will acquire */
11410 132 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11411 :
11412 132 : indexOid = constrForm->conindid;
11413 132 : DeconstructFkConstraintRow(tuple,
11414 : &numfks,
11415 : conkey,
11416 : confkey,
11417 : conpfeqop,
11418 : conppeqop,
11419 : conffeqop,
11420 : &numfkdelsetcols,
11421 : confdelsetcols);
11422 :
11423 292 : for (int i = 0; i < numfks; i++)
11424 160 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11425 :
11426 132 : fkconstraint = makeNode(Constraint);
11427 132 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11428 132 : fkconstraint->conname = NameStr(constrForm->conname);
11429 132 : fkconstraint->deferrable = constrForm->condeferrable;
11430 132 : fkconstraint->initdeferred = constrForm->condeferred;
11431 132 : fkconstraint->location = -1;
11432 132 : fkconstraint->pktable = NULL;
11433 : /* ->fk_attrs determined below */
11434 132 : fkconstraint->pk_attrs = NIL;
11435 132 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11436 132 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11437 132 : fkconstraint->fk_del_action = constrForm->confdeltype;
11438 132 : fkconstraint->fk_del_set_cols = NIL;
11439 132 : fkconstraint->old_conpfeqop = NIL;
11440 132 : fkconstraint->old_pktable_oid = InvalidOid;
11441 132 : fkconstraint->is_enforced = constrForm->conenforced;
11442 132 : fkconstraint->skip_validation = false;
11443 132 : fkconstraint->initially_valid = constrForm->convalidated;
11444 :
11445 : /* set up colnames that are used to generate the constraint name */
11446 292 : for (int i = 0; i < numfks; i++)
11447 : {
11448 : Form_pg_attribute att;
11449 :
11450 160 : att = TupleDescAttr(RelationGetDescr(fkRel),
11451 160 : conkey[i] - 1);
11452 160 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11453 160 : makeString(NameStr(att->attname)));
11454 : }
11455 :
11456 : /*
11457 : * Add the new foreign key constraint pointing to the new partition.
11458 : * Because this new partition appears in the referenced side of the
11459 : * constraint, we don't need to set up for Phase 3 check.
11460 : */
11461 132 : partIndexId = index_get_partition(partitionRel, indexOid);
11462 132 : if (!OidIsValid(partIndexId))
11463 0 : elog(ERROR, "index for %u not found in partition %s",
11464 : indexOid, RelationGetRelationName(partitionRel));
11465 :
11466 : /*
11467 : * Get the "action" triggers belonging to the constraint to pass as
11468 : * parent OIDs for similar triggers that will be created on the
11469 : * partition in addFkRecurseReferenced().
11470 : */
11471 132 : if (constrForm->conenforced)
11472 128 : GetForeignKeyActionTriggers(trigrel, constrOid,
11473 : constrForm->confrelid, constrForm->conrelid,
11474 : &deleteTriggerOid, &updateTriggerOid);
11475 :
11476 : /* Add this constraint ... */
11477 132 : address = addFkConstraint(addFkReferencedSide,
11478 : fkconstraint->conname, fkconstraint, fkRel,
11479 : partitionRel, partIndexId, constrOid,
11480 : numfks, mapped_confkey,
11481 : conkey, conpfeqop, conppeqop, conffeqop,
11482 : numfkdelsetcols, confdelsetcols, false,
11483 132 : constrForm->conperiod);
11484 : /* ... and recurse */
11485 132 : addFkRecurseReferenced(fkconstraint,
11486 : fkRel,
11487 : partitionRel,
11488 : partIndexId,
11489 : address.objectId,
11490 : numfks,
11491 : mapped_confkey,
11492 : conkey,
11493 : conpfeqop,
11494 : conppeqop,
11495 : conffeqop,
11496 : numfkdelsetcols,
11497 : confdelsetcols,
11498 : true,
11499 : deleteTriggerOid,
11500 : updateTriggerOid,
11501 132 : constrForm->conperiod);
11502 :
11503 132 : table_close(fkRel, NoLock);
11504 132 : ReleaseSysCache(tuple);
11505 : }
11506 :
11507 7919 : table_close(trigrel, RowExclusiveLock);
11508 7919 : }
11509 :
11510 : /*
11511 : * CloneFkReferencing
11512 : * Subroutine for CloneForeignKeyConstraints
11513 : *
11514 : * For each FK constraint of the parent relation in the given list, find an
11515 : * equivalent constraint in its partition relation that can be reparented;
11516 : * if one cannot be found, create a new constraint in the partition as its
11517 : * child.
11518 : *
11519 : * If wqueue is given, it is used to set up phase-3 verification for each
11520 : * cloned constraint; omit it if such verification is not needed
11521 : * (example: the partition is being created anew).
11522 : */
11523 : static void
11524 7931 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11525 : {
11526 : AttrMap *attmap;
11527 : List *partFKs;
11528 7931 : List *clone = NIL;
11529 : ListCell *cell;
11530 : Relation trigrel;
11531 :
11532 : /* obtain a list of constraints that we need to clone */
11533 8790 : foreach(cell, RelationGetFKeyList(parentRel))
11534 : {
11535 863 : ForeignKeyCacheInfo *fk = lfirst(cell);
11536 :
11537 : /*
11538 : * Refuse to attach a table as partition that this partitioned table
11539 : * already has a foreign key to. This isn't useful schema, which is
11540 : * proven by the fact that there have been no user complaints that
11541 : * it's already impossible to achieve this in the opposite direction,
11542 : * i.e., creating a foreign key that references a partition. This
11543 : * restriction allows us to dodge some complexities around
11544 : * pg_constraint and pg_trigger row creations that would be needed
11545 : * during ATTACH/DETACH for this kind of relationship.
11546 : */
11547 863 : if (fk->confrelid == RelationGetRelid(partRel))
11548 4 : ereport(ERROR,
11549 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11550 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11551 : RelationGetRelationName(partRel),
11552 : get_constraint_name(fk->conoid))));
11553 :
11554 859 : clone = lappend_oid(clone, fk->conoid);
11555 : }
11556 :
11557 : /*
11558 : * Silently do nothing if there's nothing to do. In particular, this
11559 : * avoids throwing a spurious error for foreign tables.
11560 : */
11561 7927 : if (clone == NIL)
11562 7572 : return;
11563 :
11564 355 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11565 0 : ereport(ERROR,
11566 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11567 : errmsg("foreign key constraints are not supported on foreign tables")));
11568 :
11569 : /*
11570 : * Triggers of the foreign keys will be manipulated a bunch of times in
11571 : * the loop below. To avoid repeatedly opening/closing the trigger
11572 : * catalog relation, we open it here and pass it to the subroutines called
11573 : * below.
11574 : */
11575 355 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11576 :
11577 : /*
11578 : * The constraint key may differ, if the columns in the partition are
11579 : * different. This map is used to convert them.
11580 : */
11581 355 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11582 : RelationGetDescr(parentRel),
11583 : false);
11584 :
11585 355 : partFKs = copyObject(RelationGetFKeyList(partRel));
11586 :
11587 1206 : foreach(cell, clone)
11588 : {
11589 859 : Oid parentConstrOid = lfirst_oid(cell);
11590 : Form_pg_constraint constrForm;
11591 : Relation pkrel;
11592 : HeapTuple tuple;
11593 : int numfks;
11594 : AttrNumber conkey[INDEX_MAX_KEYS];
11595 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11596 : AttrNumber confkey[INDEX_MAX_KEYS];
11597 : Oid conpfeqop[INDEX_MAX_KEYS];
11598 : Oid conppeqop[INDEX_MAX_KEYS];
11599 : Oid conffeqop[INDEX_MAX_KEYS];
11600 : int numfkdelsetcols;
11601 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11602 : Constraint *fkconstraint;
11603 : bool attached;
11604 : Oid indexOid;
11605 : ObjectAddress address;
11606 : ListCell *lc;
11607 859 : Oid insertTriggerOid = InvalidOid,
11608 859 : updateTriggerOid = InvalidOid;
11609 : bool with_period;
11610 :
11611 859 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11612 859 : if (!HeapTupleIsValid(tuple))
11613 0 : elog(ERROR, "cache lookup failed for constraint %u",
11614 : parentConstrOid);
11615 859 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11616 :
11617 : /* Don't clone constraints whose parents are being cloned */
11618 859 : if (list_member_oid(clone, constrForm->conparentid))
11619 : {
11620 472 : ReleaseSysCache(tuple);
11621 572 : continue;
11622 : }
11623 :
11624 : /*
11625 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11626 : * relation, that means to lock all partitions.
11627 : */
11628 387 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11629 387 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11630 164 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11631 : ShareRowExclusiveLock, NULL);
11632 :
11633 387 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11634 : conpfeqop, conppeqop, conffeqop,
11635 : &numfkdelsetcols, confdelsetcols);
11636 930 : for (int i = 0; i < numfks; i++)
11637 543 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11638 :
11639 : /*
11640 : * Get the "check" triggers belonging to the constraint, if it is
11641 : * ENFORCED, to pass as parent OIDs for similar triggers that will be
11642 : * created on the partition in addFkRecurseReferencing(). They are
11643 : * also passed to tryAttachPartitionForeignKey() below to simply
11644 : * assign as parents to the partition's existing "check" triggers,
11645 : * that is, if the corresponding constraints is deemed attachable to
11646 : * the parent constraint.
11647 : */
11648 387 : if (constrForm->conenforced)
11649 379 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11650 : constrForm->confrelid, constrForm->conrelid,
11651 : &insertTriggerOid, &updateTriggerOid);
11652 :
11653 : /*
11654 : * Before creating a new constraint, see whether any existing FKs are
11655 : * fit for the purpose. If one is, attach the parent constraint to
11656 : * it, and don't clone anything. This way we avoid the expensive
11657 : * verification step and don't end up with a duplicate FK, and we
11658 : * don't need to recurse to partitions for this constraint.
11659 : */
11660 387 : attached = false;
11661 447 : foreach(lc, partFKs)
11662 : {
11663 164 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11664 :
11665 164 : if (tryAttachPartitionForeignKey(wqueue,
11666 : fk,
11667 : partRel,
11668 : parentConstrOid,
11669 : numfks,
11670 : mapped_conkey,
11671 : confkey,
11672 : conpfeqop,
11673 : insertTriggerOid,
11674 : updateTriggerOid,
11675 : trigrel))
11676 : {
11677 100 : attached = true;
11678 100 : table_close(pkrel, NoLock);
11679 100 : break;
11680 : }
11681 : }
11682 383 : if (attached)
11683 : {
11684 100 : ReleaseSysCache(tuple);
11685 100 : continue;
11686 : }
11687 :
11688 : /* No dice. Set up to create our own constraint */
11689 283 : fkconstraint = makeNode(Constraint);
11690 283 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11691 : /* ->conname determined below */
11692 283 : fkconstraint->deferrable = constrForm->condeferrable;
11693 283 : fkconstraint->initdeferred = constrForm->condeferred;
11694 283 : fkconstraint->location = -1;
11695 283 : fkconstraint->pktable = NULL;
11696 : /* ->fk_attrs determined below */
11697 283 : fkconstraint->pk_attrs = NIL;
11698 283 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11699 283 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11700 283 : fkconstraint->fk_del_action = constrForm->confdeltype;
11701 283 : fkconstraint->fk_del_set_cols = NIL;
11702 283 : fkconstraint->old_conpfeqop = NIL;
11703 283 : fkconstraint->old_pktable_oid = InvalidOid;
11704 283 : fkconstraint->is_enforced = constrForm->conenforced;
11705 283 : fkconstraint->skip_validation = false;
11706 283 : fkconstraint->initially_valid = constrForm->convalidated;
11707 646 : for (int i = 0; i < numfks; i++)
11708 : {
11709 : Form_pg_attribute att;
11710 :
11711 363 : att = TupleDescAttr(RelationGetDescr(partRel),
11712 363 : mapped_conkey[i] - 1);
11713 363 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11714 363 : makeString(NameStr(att->attname)));
11715 : }
11716 :
11717 283 : indexOid = constrForm->conindid;
11718 283 : with_period = constrForm->conperiod;
11719 :
11720 : /* Create the pg_constraint entry at this level */
11721 283 : address = addFkConstraint(addFkReferencingSide,
11722 283 : NameStr(constrForm->conname), fkconstraint,
11723 : partRel, pkrel, indexOid, parentConstrOid,
11724 : numfks, confkey,
11725 : mapped_conkey, conpfeqop,
11726 : conppeqop, conffeqop,
11727 : numfkdelsetcols, confdelsetcols,
11728 : false, with_period);
11729 :
11730 : /* Done with the cloned constraint's tuple */
11731 283 : ReleaseSysCache(tuple);
11732 :
11733 : /* Create the check triggers, and recurse to partitions, if any */
11734 283 : addFkRecurseReferencing(wqueue,
11735 : fkconstraint,
11736 : partRel,
11737 : pkrel,
11738 : indexOid,
11739 : address.objectId,
11740 : numfks,
11741 : confkey,
11742 : mapped_conkey,
11743 : conpfeqop,
11744 : conppeqop,
11745 : conffeqop,
11746 : numfkdelsetcols,
11747 : confdelsetcols,
11748 : false, /* no old check exists */
11749 : AccessExclusiveLock,
11750 : insertTriggerOid,
11751 : updateTriggerOid,
11752 : with_period);
11753 279 : table_close(pkrel, NoLock);
11754 : }
11755 :
11756 347 : table_close(trigrel, RowExclusiveLock);
11757 : }
11758 :
11759 : /*
11760 : * When the parent of a partition receives [the referencing side of] a foreign
11761 : * key, we must propagate that foreign key to the partition. However, the
11762 : * partition might already have an equivalent foreign key; this routine
11763 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11764 : * by the other parameters. If they are equivalent, create the link between
11765 : * the two constraints and return true.
11766 : *
11767 : * If the given FK does not match the one defined by rest of the params,
11768 : * return false.
11769 : */
11770 : static bool
11771 183 : tryAttachPartitionForeignKey(List **wqueue,
11772 : ForeignKeyCacheInfo *fk,
11773 : Relation partition,
11774 : Oid parentConstrOid,
11775 : int numfks,
11776 : AttrNumber *mapped_conkey,
11777 : AttrNumber *confkey,
11778 : Oid *conpfeqop,
11779 : Oid parentInsTrigger,
11780 : Oid parentUpdTrigger,
11781 : Relation trigrel)
11782 : {
11783 : HeapTuple parentConstrTup;
11784 : Form_pg_constraint parentConstr;
11785 : HeapTuple partcontup;
11786 : Form_pg_constraint partConstr;
11787 :
11788 183 : parentConstrTup = SearchSysCache1(CONSTROID,
11789 : ObjectIdGetDatum(parentConstrOid));
11790 183 : if (!HeapTupleIsValid(parentConstrTup))
11791 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11792 183 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11793 :
11794 : /*
11795 : * Do some quick & easy initial checks. If any of these fail, we cannot
11796 : * use this constraint.
11797 : */
11798 183 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11799 : {
11800 0 : ReleaseSysCache(parentConstrTup);
11801 0 : return false;
11802 : }
11803 505 : for (int i = 0; i < numfks; i++)
11804 : {
11805 324 : if (fk->conkey[i] != mapped_conkey[i] ||
11806 322 : fk->confkey[i] != confkey[i] ||
11807 322 : fk->conpfeqop[i] != conpfeqop[i])
11808 : {
11809 2 : ReleaseSysCache(parentConstrTup);
11810 2 : return false;
11811 : }
11812 : }
11813 :
11814 : /* Looks good so far; perform more extensive checks. */
11815 181 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11816 181 : if (!HeapTupleIsValid(partcontup))
11817 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11818 181 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11819 :
11820 : /*
11821 : * An error should be raised if the constraint enforceability is
11822 : * different. Returning false without raising an error, as we do for other
11823 : * attributes, could lead to a duplicate constraint with the same
11824 : * enforceability as the parent. While this may be acceptable, it may not
11825 : * be ideal. Therefore, it's better to raise an error and allow the user
11826 : * to correct the enforceability before proceeding.
11827 : */
11828 181 : if (partConstr->conenforced != parentConstr->conenforced)
11829 4 : ereport(ERROR,
11830 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11831 : errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11832 : NameStr(parentConstr->conname),
11833 : NameStr(partConstr->conname),
11834 : RelationGetRelationName(partition))));
11835 :
11836 177 : if (OidIsValid(partConstr->conparentid) ||
11837 158 : partConstr->condeferrable != parentConstr->condeferrable ||
11838 140 : partConstr->condeferred != parentConstr->condeferred ||
11839 140 : partConstr->confupdtype != parentConstr->confupdtype ||
11840 117 : partConstr->confdeltype != parentConstr->confdeltype ||
11841 117 : partConstr->confmatchtype != parentConstr->confmatchtype)
11842 : {
11843 69 : ReleaseSysCache(parentConstrTup);
11844 69 : ReleaseSysCache(partcontup);
11845 69 : return false;
11846 : }
11847 :
11848 108 : ReleaseSysCache(parentConstrTup);
11849 108 : ReleaseSysCache(partcontup);
11850 :
11851 : /* Looks good! Attach this constraint. */
11852 108 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11853 : parentConstrOid, parentInsTrigger,
11854 : parentUpdTrigger, trigrel);
11855 :
11856 108 : return true;
11857 : }
11858 :
11859 : /*
11860 : * AttachPartitionForeignKey
11861 : *
11862 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11863 : * attaching the constraint, removing redundant triggers and entries from
11864 : * pg_constraint, and setting the constraint's parent.
11865 : */
11866 : static void
11867 108 : AttachPartitionForeignKey(List **wqueue,
11868 : Relation partition,
11869 : Oid partConstrOid,
11870 : Oid parentConstrOid,
11871 : Oid parentInsTrigger,
11872 : Oid parentUpdTrigger,
11873 : Relation trigrel)
11874 : {
11875 : HeapTuple parentConstrTup;
11876 : Form_pg_constraint parentConstr;
11877 : HeapTuple partcontup;
11878 : Form_pg_constraint partConstr;
11879 : bool queueValidation;
11880 : Oid partConstrFrelid;
11881 : Oid partConstrRelid;
11882 : bool parentConstrIsEnforced;
11883 :
11884 : /* Fetch the parent constraint tuple */
11885 108 : parentConstrTup = SearchSysCache1(CONSTROID,
11886 : ObjectIdGetDatum(parentConstrOid));
11887 108 : if (!HeapTupleIsValid(parentConstrTup))
11888 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11889 108 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11890 108 : parentConstrIsEnforced = parentConstr->conenforced;
11891 :
11892 : /* Fetch the child constraint tuple */
11893 108 : partcontup = SearchSysCache1(CONSTROID,
11894 : ObjectIdGetDatum(partConstrOid));
11895 108 : if (!HeapTupleIsValid(partcontup))
11896 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11897 108 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11898 108 : partConstrFrelid = partConstr->confrelid;
11899 108 : partConstrRelid = partConstr->conrelid;
11900 :
11901 : /*
11902 : * If the referenced table is partitioned, then the partition we're
11903 : * attaching now has extra pg_constraint rows and action triggers that are
11904 : * no longer needed. Remove those.
11905 : */
11906 108 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11907 : {
11908 24 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11909 :
11910 24 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11911 : partConstrRelid);
11912 :
11913 24 : table_close(pg_constraint, RowShareLock);
11914 : }
11915 :
11916 : /*
11917 : * Will we need to validate this constraint? A valid parent constraint
11918 : * implies that all child constraints have been validated, so if this one
11919 : * isn't, we must trigger phase 3 validation.
11920 : */
11921 108 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11922 :
11923 108 : ReleaseSysCache(partcontup);
11924 108 : ReleaseSysCache(parentConstrTup);
11925 :
11926 : /*
11927 : * The action triggers in the new partition become redundant -- the parent
11928 : * table already has equivalent ones, and those will be able to reach the
11929 : * partition. Remove the ones in the partition. We identify them because
11930 : * they have our constraint OID, as well as being on the referenced rel.
11931 : */
11932 108 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11933 : partConstrRelid);
11934 :
11935 108 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11936 : RelationGetRelid(partition));
11937 :
11938 : /*
11939 : * Like the constraint, attach partition's "check" triggers to the
11940 : * corresponding parent triggers if the constraint is ENFORCED. NOT
11941 : * ENFORCED constraints do not have these triggers.
11942 : */
11943 108 : if (parentConstrIsEnforced)
11944 : {
11945 : Oid insertTriggerOid,
11946 : updateTriggerOid;
11947 :
11948 100 : GetForeignKeyCheckTriggers(trigrel,
11949 : partConstrOid, partConstrFrelid, partConstrRelid,
11950 : &insertTriggerOid, &updateTriggerOid);
11951 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11952 100 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11953 : RelationGetRelid(partition));
11954 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11955 100 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11956 : RelationGetRelid(partition));
11957 : }
11958 :
11959 : /*
11960 : * We updated this pg_constraint row above to set its parent; validating
11961 : * it will cause its convalidated flag to change, so we need CCI here. In
11962 : * addition, we need it unconditionally for the rare case where the parent
11963 : * table has *two* identical constraints; when reaching this function for
11964 : * the second one, we must have made our changes visible, otherwise we
11965 : * would try to attach both to this one.
11966 : */
11967 108 : CommandCounterIncrement();
11968 :
11969 : /* If validation is needed, put it in the queue now. */
11970 108 : if (queueValidation)
11971 : {
11972 : Relation conrel;
11973 : Oid confrelid;
11974 :
11975 12 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11976 :
11977 12 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11978 12 : if (!HeapTupleIsValid(partcontup))
11979 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11980 :
11981 12 : confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11982 :
11983 : /* Use the same lock as for AT_ValidateConstraint */
11984 12 : QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11985 : partcontup, ShareUpdateExclusiveLock);
11986 12 : ReleaseSysCache(partcontup);
11987 12 : table_close(conrel, RowExclusiveLock);
11988 : }
11989 108 : }
11990 :
11991 : /*
11992 : * RemoveInheritedConstraint
11993 : *
11994 : * Removes the constraint and its associated trigger from the specified
11995 : * relation, which inherited the given constraint.
11996 : */
11997 : static void
11998 24 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11999 : Oid conrelid)
12000 : {
12001 : ObjectAddresses *objs;
12002 : HeapTuple consttup;
12003 : ScanKeyData key;
12004 : SysScanDesc scan;
12005 : HeapTuple trigtup;
12006 :
12007 24 : ScanKeyInit(&key,
12008 : Anum_pg_constraint_conrelid,
12009 : BTEqualStrategyNumber, F_OIDEQ,
12010 : ObjectIdGetDatum(conrelid));
12011 :
12012 24 : scan = systable_beginscan(conrel,
12013 : ConstraintRelidTypidNameIndexId,
12014 : true, NULL, 1, &key);
12015 24 : objs = new_object_addresses();
12016 216 : while ((consttup = systable_getnext(scan)) != NULL)
12017 : {
12018 192 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
12019 :
12020 192 : if (conform->conparentid != conoid)
12021 140 : continue;
12022 : else
12023 : {
12024 : ObjectAddress addr;
12025 : SysScanDesc scan2;
12026 : ScanKeyData key2;
12027 : int n PG_USED_FOR_ASSERTS_ONLY;
12028 :
12029 52 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
12030 52 : add_exact_object_address(&addr, objs);
12031 :
12032 : /*
12033 : * First we must delete the dependency record that binds the
12034 : * constraint records together.
12035 : */
12036 52 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
12037 : conform->oid,
12038 : DEPENDENCY_INTERNAL,
12039 : ConstraintRelationId,
12040 : conoid);
12041 : Assert(n == 1); /* actually only one is expected */
12042 :
12043 : /*
12044 : * Now search for the triggers for this constraint and set them up
12045 : * for deletion too
12046 : */
12047 52 : ScanKeyInit(&key2,
12048 : Anum_pg_trigger_tgconstraint,
12049 : BTEqualStrategyNumber, F_OIDEQ,
12050 : ObjectIdGetDatum(conform->oid));
12051 52 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
12052 : true, NULL, 1, &key2);
12053 156 : while ((trigtup = systable_getnext(scan2)) != NULL)
12054 : {
12055 104 : ObjectAddressSet(addr, TriggerRelationId,
12056 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
12057 104 : add_exact_object_address(&addr, objs);
12058 : }
12059 52 : systable_endscan(scan2);
12060 : }
12061 : }
12062 : /* make the dependency deletions visible */
12063 24 : CommandCounterIncrement();
12064 24 : performMultipleDeletions(objs, DROP_RESTRICT,
12065 : PERFORM_DELETION_INTERNAL);
12066 24 : systable_endscan(scan);
12067 24 : }
12068 :
12069 : /*
12070 : * DropForeignKeyConstraintTriggers
12071 : *
12072 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
12073 : * action triggers for the foreign key constraint.
12074 : *
12075 : * If valid confrelid and conrelid values are not provided, the respective
12076 : * trigger check will be skipped, and the trigger will be considered for
12077 : * removal.
12078 : */
12079 : static void
12080 160 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
12081 : Oid conrelid)
12082 : {
12083 : ScanKeyData key;
12084 : SysScanDesc scan;
12085 : HeapTuple trigtup;
12086 :
12087 160 : ScanKeyInit(&key,
12088 : Anum_pg_trigger_tgconstraint,
12089 : BTEqualStrategyNumber, F_OIDEQ,
12090 : ObjectIdGetDatum(conoid));
12091 160 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12092 : NULL, 1, &key);
12093 696 : while ((trigtup = systable_getnext(scan)) != NULL)
12094 : {
12095 536 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12096 : ObjectAddress trigger;
12097 :
12098 : /* Invalid if trigger is not for a referential integrity constraint */
12099 536 : if (!OidIsValid(trgform->tgconstrrelid))
12100 200 : continue;
12101 536 : if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12102 200 : continue;
12103 336 : if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12104 0 : continue;
12105 :
12106 : /* We should be dropping trigger related to foreign key constraint */
12107 : Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12108 : trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12109 : trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12110 : trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12111 : trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12112 : trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12113 : trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12114 : trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12115 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12116 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12117 : trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12118 : trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12119 :
12120 : /*
12121 : * The constraint is originally set up to contain this trigger as an
12122 : * implementation object, so there's a dependency record that links
12123 : * the two; however, since the trigger is no longer needed, we remove
12124 : * the dependency link in order to be able to drop the trigger while
12125 : * keeping the constraint intact.
12126 : */
12127 336 : deleteDependencyRecordsFor(TriggerRelationId,
12128 : trgform->oid,
12129 : false);
12130 : /* make dependency deletion visible to performDeletion */
12131 336 : CommandCounterIncrement();
12132 336 : ObjectAddressSet(trigger, TriggerRelationId,
12133 : trgform->oid);
12134 336 : performDeletion(&trigger, DROP_RESTRICT, 0);
12135 : /* make trigger drop visible, in case the loop iterates */
12136 336 : CommandCounterIncrement();
12137 : }
12138 :
12139 160 : systable_endscan(scan);
12140 160 : }
12141 :
12142 : /*
12143 : * GetForeignKeyActionTriggers
12144 : * Returns delete and update "action" triggers of the given relation
12145 : * belonging to the given constraint
12146 : */
12147 : static void
12148 128 : GetForeignKeyActionTriggers(Relation trigrel,
12149 : Oid conoid, Oid confrelid, Oid conrelid,
12150 : Oid *deleteTriggerOid,
12151 : Oid *updateTriggerOid)
12152 : {
12153 : ScanKeyData key;
12154 : SysScanDesc scan;
12155 : HeapTuple trigtup;
12156 :
12157 128 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12158 128 : ScanKeyInit(&key,
12159 : Anum_pg_trigger_tgconstraint,
12160 : BTEqualStrategyNumber, F_OIDEQ,
12161 : ObjectIdGetDatum(conoid));
12162 :
12163 128 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12164 : NULL, 1, &key);
12165 267 : while ((trigtup = systable_getnext(scan)) != NULL)
12166 : {
12167 267 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12168 :
12169 267 : if (trgform->tgconstrrelid != conrelid)
12170 11 : continue;
12171 256 : if (trgform->tgrelid != confrelid)
12172 0 : continue;
12173 : /* Only ever look at "action" triggers on the PK side. */
12174 256 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12175 0 : continue;
12176 256 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
12177 : {
12178 : Assert(*deleteTriggerOid == InvalidOid);
12179 128 : *deleteTriggerOid = trgform->oid;
12180 : }
12181 128 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12182 : {
12183 : Assert(*updateTriggerOid == InvalidOid);
12184 128 : *updateTriggerOid = trgform->oid;
12185 : }
12186 : #ifndef USE_ASSERT_CHECKING
12187 : /* In an assert-enabled build, continue looking to find duplicates */
12188 256 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12189 128 : break;
12190 : #endif
12191 : }
12192 :
12193 128 : if (!OidIsValid(*deleteTriggerOid))
12194 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12195 : conoid);
12196 128 : if (!OidIsValid(*updateTriggerOid))
12197 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12198 : conoid);
12199 :
12200 128 : systable_endscan(scan);
12201 128 : }
12202 :
12203 : /*
12204 : * GetForeignKeyCheckTriggers
12205 : * Returns insert and update "check" triggers of the given relation
12206 : * belonging to the given constraint
12207 : */
12208 : static void
12209 551 : GetForeignKeyCheckTriggers(Relation trigrel,
12210 : Oid conoid, Oid confrelid, Oid conrelid,
12211 : Oid *insertTriggerOid,
12212 : Oid *updateTriggerOid)
12213 : {
12214 : ScanKeyData key;
12215 : SysScanDesc scan;
12216 : HeapTuple trigtup;
12217 :
12218 551 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
12219 551 : ScanKeyInit(&key,
12220 : Anum_pg_trigger_tgconstraint,
12221 : BTEqualStrategyNumber, F_OIDEQ,
12222 : ObjectIdGetDatum(conoid));
12223 :
12224 551 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12225 : NULL, 1, &key);
12226 1772 : while ((trigtup = systable_getnext(scan)) != NULL)
12227 : {
12228 1772 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12229 :
12230 1772 : if (trgform->tgconstrrelid != confrelid)
12231 598 : continue;
12232 1174 : if (trgform->tgrelid != conrelid)
12233 0 : continue;
12234 : /* Only ever look at "check" triggers on the FK side. */
12235 1174 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12236 72 : continue;
12237 1102 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
12238 : {
12239 : Assert(*insertTriggerOid == InvalidOid);
12240 551 : *insertTriggerOid = trgform->oid;
12241 : }
12242 551 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12243 : {
12244 : Assert(*updateTriggerOid == InvalidOid);
12245 551 : *updateTriggerOid = trgform->oid;
12246 : }
12247 : #ifndef USE_ASSERT_CHECKING
12248 : /* In an assert-enabled build, continue looking to find duplicates. */
12249 1102 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12250 551 : break;
12251 : #endif
12252 : }
12253 :
12254 551 : if (!OidIsValid(*insertTriggerOid))
12255 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12256 : conoid);
12257 551 : if (!OidIsValid(*updateTriggerOid))
12258 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12259 : conoid);
12260 :
12261 551 : systable_endscan(scan);
12262 551 : }
12263 :
12264 : /*
12265 : * ALTER TABLE ALTER CONSTRAINT
12266 : *
12267 : * Update the attributes of a constraint.
12268 : *
12269 : * Currently works for Foreign Key, Check, and not null constraints.
12270 : *
12271 : * If the constraint is modified, returns its address; otherwise, return
12272 : * InvalidObjectAddress.
12273 : */
12274 : static ObjectAddress
12275 292 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
12276 : bool recurse, LOCKMODE lockmode)
12277 : {
12278 : Relation conrel;
12279 : Relation tgrel;
12280 : SysScanDesc scan;
12281 : ScanKeyData skey[3];
12282 : HeapTuple contuple;
12283 : Form_pg_constraint currcon;
12284 : ObjectAddress address;
12285 :
12286 : /*
12287 : * Disallow altering ONLY a partitioned table, as it would make no sense.
12288 : * This is okay for legacy inheritance.
12289 : */
12290 292 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12291 0 : ereport(ERROR,
12292 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12293 : errmsg("constraint must be altered in child tables too"),
12294 : errhint("Do not specify the ONLY keyword."));
12295 :
12296 :
12297 292 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12298 292 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12299 :
12300 : /*
12301 : * Find and check the target constraint
12302 : */
12303 292 : ScanKeyInit(&skey[0],
12304 : Anum_pg_constraint_conrelid,
12305 : BTEqualStrategyNumber, F_OIDEQ,
12306 : ObjectIdGetDatum(RelationGetRelid(rel)));
12307 292 : ScanKeyInit(&skey[1],
12308 : Anum_pg_constraint_contypid,
12309 : BTEqualStrategyNumber, F_OIDEQ,
12310 : ObjectIdGetDatum(InvalidOid));
12311 292 : ScanKeyInit(&skey[2],
12312 : Anum_pg_constraint_conname,
12313 : BTEqualStrategyNumber, F_NAMEEQ,
12314 292 : CStringGetDatum(cmdcon->conname));
12315 292 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12316 : true, NULL, 3, skey);
12317 :
12318 : /* There can be at most one matching row */
12319 292 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12320 4 : ereport(ERROR,
12321 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12322 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12323 : cmdcon->conname, RelationGetRelationName(rel))));
12324 :
12325 288 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12326 288 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12327 0 : ereport(ERROR,
12328 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12329 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12330 : cmdcon->conname, RelationGetRelationName(rel))));
12331 288 : if (cmdcon->alterEnforceability &&
12332 156 : (currcon->contype != CONSTRAINT_FOREIGN && currcon->contype != CONSTRAINT_CHECK))
12333 8 : ereport(ERROR,
12334 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12335 : errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12336 : cmdcon->conname, RelationGetRelationName(rel)),
12337 : errhint("Only foreign key and check constraints can change enforceability.")));
12338 280 : if (cmdcon->alterInheritability &&
12339 60 : currcon->contype != CONSTRAINT_NOTNULL)
12340 16 : ereport(ERROR,
12341 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12342 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12343 : cmdcon->conname, RelationGetRelationName(rel)));
12344 :
12345 : /* Refuse to modify inheritability of inherited constraints */
12346 264 : if (cmdcon->alterInheritability &&
12347 44 : cmdcon->noinherit && currcon->coninhcount > 0)
12348 4 : ereport(ERROR,
12349 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12350 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12351 : NameStr(currcon->conname),
12352 : RelationGetRelationName(rel)));
12353 :
12354 : /*
12355 : * If it's not the topmost constraint, raise an error.
12356 : *
12357 : * Altering a non-topmost constraint leaves some triggers untouched, since
12358 : * they are not directly connected to this constraint; also, pg_dump would
12359 : * ignore the deferrability status of the individual constraint, since it
12360 : * only dumps topmost constraints. Avoid these problems by refusing this
12361 : * operation and telling the user to alter the parent constraint instead.
12362 : */
12363 260 : if (OidIsValid(currcon->conparentid))
12364 : {
12365 : HeapTuple tp;
12366 8 : Oid parent = currcon->conparentid;
12367 8 : char *ancestorname = NULL;
12368 8 : char *ancestortable = NULL;
12369 :
12370 : /* Loop to find the topmost constraint */
12371 16 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12372 : {
12373 16 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12374 :
12375 : /* If no parent, this is the constraint we want */
12376 16 : if (!OidIsValid(contup->conparentid))
12377 : {
12378 8 : ancestorname = pstrdup(NameStr(contup->conname));
12379 8 : ancestortable = get_rel_name(contup->conrelid);
12380 8 : ReleaseSysCache(tp);
12381 8 : break;
12382 : }
12383 :
12384 8 : parent = contup->conparentid;
12385 8 : ReleaseSysCache(tp);
12386 : }
12387 :
12388 8 : ereport(ERROR,
12389 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12390 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12391 : cmdcon->conname, RelationGetRelationName(rel)),
12392 : ancestorname && ancestortable ?
12393 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12394 : cmdcon->conname, ancestorname, ancestortable) : 0,
12395 : errhint("You may alter the constraint it derives from instead.")));
12396 : }
12397 :
12398 252 : address = InvalidObjectAddress;
12399 :
12400 : /*
12401 : * Do the actual catalog work, and recurse if necessary.
12402 : */
12403 252 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12404 : contuple, recurse, lockmode))
12405 232 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12406 :
12407 240 : systable_endscan(scan);
12408 :
12409 240 : table_close(tgrel, RowExclusiveLock);
12410 240 : table_close(conrel, RowExclusiveLock);
12411 :
12412 240 : return address;
12413 : }
12414 :
12415 : /*
12416 : * A subroutine of ATExecAlterConstraint that calls the respective routines for
12417 : * altering constraint's enforceability, deferrability or inheritability.
12418 : */
12419 : static bool
12420 252 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12421 : Relation conrel, Relation tgrel, Relation rel,
12422 : HeapTuple contuple, bool recurse,
12423 : LOCKMODE lockmode)
12424 : {
12425 : Form_pg_constraint currcon;
12426 252 : bool changed = false;
12427 252 : List *otherrelids = NIL;
12428 :
12429 252 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12430 :
12431 : /*
12432 : * Do the catalog work for the enforceability or deferrability change,
12433 : * recurse if necessary.
12434 : *
12435 : * Note that even if deferrability is requested to be altered along with
12436 : * enforceability, we don't need to explicitly update multiple entries in
12437 : * pg_trigger related to deferrability.
12438 : *
12439 : * Modifying foreign key enforceability involves either creating or
12440 : * dropping the trigger, during which the deferrability setting will be
12441 : * adjusted automatically.
12442 : */
12443 252 : if (cmdcon->alterEnforceability)
12444 : {
12445 148 : if (currcon->contype == CONSTRAINT_FOREIGN)
12446 60 : changed = ATExecAlterFKConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12447 : currcon->conrelid,
12448 : currcon->confrelid,
12449 : contuple, lockmode,
12450 : InvalidOid, InvalidOid,
12451 : InvalidOid, InvalidOid);
12452 88 : else if (currcon->contype == CONSTRAINT_CHECK)
12453 88 : changed = ATExecAlterCheckConstrEnforceability(wqueue, cmdcon, conrel,
12454 : contuple, recurse, false,
12455 : lockmode);
12456 : }
12457 168 : else if (cmdcon->alterDeferrability &&
12458 64 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12459 : contuple, recurse, &otherrelids,
12460 : lockmode))
12461 : {
12462 : /*
12463 : * AlterConstrUpdateConstraintEntry already invalidated relcache for
12464 : * the relations having the constraint itself; here we also invalidate
12465 : * for relations that have any triggers that are part of the
12466 : * constraint.
12467 : */
12468 204 : foreach_oid(relid, otherrelids)
12469 76 : CacheInvalidateRelcacheByRelid(relid);
12470 :
12471 64 : changed = true;
12472 : }
12473 :
12474 : /*
12475 : * Do the catalog work for the inheritability change.
12476 : */
12477 280 : if (cmdcon->alterInheritability &&
12478 40 : ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12479 : lockmode))
12480 36 : changed = true;
12481 :
12482 240 : return changed;
12483 : }
12484 :
12485 : /*
12486 : * Returns true if the foreign key constraint's enforceability is altered.
12487 : *
12488 : * Depending on whether the constraint is being set to ENFORCED or NOT
12489 : * ENFORCED, it creates or drops the trigger accordingly.
12490 : *
12491 : * Note that we must recurse even when trying to change a constraint to not
12492 : * enforced if it is already not enforced, in case descendant constraints
12493 : * might be enforced and need to be changed to not enforced. Conversely, we
12494 : * should do nothing if a constraint is being set to enforced and is already
12495 : * enforced, as descendant constraints cannot be different in that case.
12496 : */
12497 : static bool
12498 128 : ATExecAlterFKConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
12499 : Relation conrel, Relation tgrel,
12500 : Oid fkrelid, Oid pkrelid,
12501 : HeapTuple contuple, LOCKMODE lockmode,
12502 : Oid ReferencedParentDelTrigger,
12503 : Oid ReferencedParentUpdTrigger,
12504 : Oid ReferencingParentInsTrigger,
12505 : Oid ReferencingParentUpdTrigger)
12506 : {
12507 : Form_pg_constraint currcon;
12508 : Oid conoid;
12509 : Relation rel;
12510 128 : bool changed = false;
12511 :
12512 : /* Since this function recurses, it could be driven to stack overflow */
12513 128 : check_stack_depth();
12514 :
12515 : Assert(cmdcon->alterEnforceability);
12516 :
12517 128 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12518 128 : conoid = currcon->oid;
12519 :
12520 : /* Should be foreign key constraint */
12521 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12522 :
12523 128 : rel = table_open(currcon->conrelid, lockmode);
12524 :
12525 128 : if (currcon->conenforced != cmdcon->is_enforced)
12526 : {
12527 124 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12528 124 : changed = true;
12529 : }
12530 :
12531 : /* Drop triggers */
12532 128 : if (!cmdcon->is_enforced)
12533 : {
12534 : /*
12535 : * When setting a constraint to NOT ENFORCED, the constraint triggers
12536 : * need to be dropped. Therefore, we must process the child relations
12537 : * first, followed by the parent, to account for dependencies.
12538 : */
12539 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12540 40 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12541 12 : AlterFKConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12542 : fkrelid, pkrelid, contuple,
12543 : lockmode, InvalidOid, InvalidOid,
12544 : InvalidOid, InvalidOid);
12545 :
12546 : /* Drop all the triggers */
12547 52 : DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
12548 : }
12549 76 : else if (changed) /* Create triggers */
12550 : {
12551 76 : Oid ReferencedDelTriggerOid = InvalidOid,
12552 76 : ReferencedUpdTriggerOid = InvalidOid,
12553 76 : ReferencingInsTriggerOid = InvalidOid,
12554 76 : ReferencingUpdTriggerOid = InvalidOid;
12555 :
12556 : /* Prepare the minimal information required for trigger creation. */
12557 76 : Constraint *fkconstraint = makeNode(Constraint);
12558 :
12559 76 : fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12560 76 : fkconstraint->fk_matchtype = currcon->confmatchtype;
12561 76 : fkconstraint->fk_upd_action = currcon->confupdtype;
12562 76 : fkconstraint->fk_del_action = currcon->confdeltype;
12563 76 : fkconstraint->deferrable = currcon->condeferrable;
12564 76 : fkconstraint->initdeferred = currcon->condeferred;
12565 :
12566 : /* Create referenced triggers */
12567 76 : if (currcon->conrelid == fkrelid)
12568 48 : createForeignKeyActionTriggers(currcon->conrelid,
12569 : currcon->confrelid,
12570 : fkconstraint,
12571 : conoid,
12572 : currcon->conindid,
12573 : ReferencedParentDelTrigger,
12574 : ReferencedParentUpdTrigger,
12575 : &ReferencedDelTriggerOid,
12576 : &ReferencedUpdTriggerOid);
12577 :
12578 : /* Create referencing triggers */
12579 76 : if (currcon->confrelid == pkrelid)
12580 64 : createForeignKeyCheckTriggers(currcon->conrelid,
12581 : pkrelid,
12582 : fkconstraint,
12583 : conoid,
12584 : currcon->conindid,
12585 : ReferencingParentInsTrigger,
12586 : ReferencingParentUpdTrigger,
12587 : &ReferencingInsTriggerOid,
12588 : &ReferencingUpdTriggerOid);
12589 :
12590 : /*
12591 : * Tell Phase 3 to check that the constraint is satisfied by existing
12592 : * rows. Only applies to leaf partitions, and (for constraints that
12593 : * reference a partitioned table) only if this is not one of the
12594 : * pg_constraint rows that exist solely to support action triggers.
12595 : */
12596 76 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
12597 64 : currcon->confrelid == pkrelid)
12598 : {
12599 : AlteredTableInfo *tab;
12600 : NewConstraint *newcon;
12601 :
12602 52 : newcon = palloc0_object(NewConstraint);
12603 52 : newcon->name = fkconstraint->conname;
12604 52 : newcon->contype = CONSTR_FOREIGN;
12605 52 : newcon->refrelid = currcon->confrelid;
12606 52 : newcon->refindid = currcon->conindid;
12607 52 : newcon->conid = currcon->oid;
12608 52 : newcon->qual = (Node *) fkconstraint;
12609 :
12610 : /* Find or create work queue entry for this table */
12611 52 : tab = ATGetQueueEntry(wqueue, rel);
12612 52 : tab->constraints = lappend(tab->constraints, newcon);
12613 : }
12614 :
12615 : /*
12616 : * If the table at either end of the constraint is partitioned, we
12617 : * need to recurse and create triggers for each constraint that is a
12618 : * child of this one.
12619 : */
12620 140 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12621 64 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12622 20 : AlterFKConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12623 : fkrelid, pkrelid, contuple,
12624 : lockmode,
12625 : ReferencedDelTriggerOid,
12626 : ReferencedUpdTriggerOid,
12627 : ReferencingInsTriggerOid,
12628 : ReferencingUpdTriggerOid);
12629 : }
12630 :
12631 128 : table_close(rel, NoLock);
12632 :
12633 128 : return changed;
12634 : }
12635 :
12636 : /*
12637 : * Returns true if the CHECK constraint's enforceability is altered.
12638 : */
12639 : static bool
12640 236 : ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
12641 : Relation conrel, HeapTuple contuple,
12642 : bool recurse, bool recursing, LOCKMODE lockmode)
12643 : {
12644 : Form_pg_constraint currcon;
12645 : Relation rel;
12646 236 : bool changed = false;
12647 236 : List *children = NIL;
12648 :
12649 : /* Since this function recurses, it could be driven to stack overflow */
12650 236 : check_stack_depth();
12651 :
12652 : Assert(cmdcon->alterEnforceability);
12653 :
12654 236 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12655 :
12656 : Assert(currcon->contype == CONSTRAINT_CHECK);
12657 :
12658 : /*
12659 : * Parent relation already locked by caller, children will be locked by
12660 : * find_all_inheritors. So NoLock is fine here.
12661 : */
12662 236 : rel = table_open(currcon->conrelid, NoLock);
12663 :
12664 236 : if (currcon->conenforced != cmdcon->is_enforced)
12665 : {
12666 196 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12667 196 : changed = true;
12668 : }
12669 :
12670 : /*
12671 : * Note that we must recurse even when trying to change a check constraint
12672 : * to not enforced if it is already not enforced, in case descendant
12673 : * constraints might be enforced and need to be changed to not enforced.
12674 : * Conversely, we should do nothing if a constraint is being set to
12675 : * enforced and is already enforced, as descendant constraints cannot be
12676 : * different in that case.
12677 : */
12678 236 : if (!cmdcon->is_enforced || changed)
12679 : {
12680 : /*
12681 : * If we're recursing, the parent has already done this, so skip it.
12682 : * Also, if the constraint is a NO INHERIT constraint, we shouldn't
12683 : * try to look for it in the children.
12684 : */
12685 220 : if (!recursing && !currcon->connoinherit)
12686 84 : children = find_all_inheritors(RelationGetRelid(rel),
12687 : lockmode, NULL);
12688 :
12689 664 : foreach_oid(childoid, children)
12690 : {
12691 240 : if (childoid == RelationGetRelid(rel))
12692 84 : continue;
12693 :
12694 : /*
12695 : * If we are told not to recurse, there had better not be any
12696 : * child tables, because we can't change constraint enforceability
12697 : * on the parent unless we have changed enforceability for all
12698 : * child.
12699 : */
12700 156 : if (!recurse)
12701 8 : ereport(ERROR,
12702 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12703 : errmsg("constraint must be altered on child tables too"),
12704 : errhint("Do not specify the ONLY keyword."));
12705 :
12706 148 : AlterCheckConstrEnforceabilityRecurse(wqueue, cmdcon, conrel,
12707 : childoid, false, true,
12708 : lockmode);
12709 : }
12710 : }
12711 :
12712 : /*
12713 : * Tell Phase 3 to check that the constraint is satisfied by existing
12714 : * rows. We only need do this when altering the constraint from NOT
12715 : * ENFORCED to ENFORCED.
12716 : */
12717 228 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
12718 180 : !currcon->conenforced &&
12719 128 : cmdcon->is_enforced)
12720 : {
12721 : AlteredTableInfo *tab;
12722 : NewConstraint *newcon;
12723 : Datum val;
12724 : char *conbin;
12725 :
12726 116 : newcon = palloc0_object(NewConstraint);
12727 116 : newcon->name = pstrdup(NameStr(currcon->conname));
12728 116 : newcon->contype = CONSTR_CHECK;
12729 :
12730 116 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
12731 : Anum_pg_constraint_conbin);
12732 116 : conbin = TextDatumGetCString(val);
12733 116 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
12734 :
12735 : /* Find or create work queue entry for this table */
12736 116 : tab = ATGetQueueEntry(wqueue, rel);
12737 116 : tab->constraints = lappend(tab->constraints, newcon);
12738 : }
12739 :
12740 228 : table_close(rel, NoLock);
12741 :
12742 228 : return changed;
12743 : }
12744 :
12745 : /*
12746 : * Invokes ATExecAlterCheckConstrEnforceability for each CHECK constraint that
12747 : * is a child of the specified constraint.
12748 : *
12749 : * We rely on the parent and child tables having identical CHECK constraint
12750 : * names to retrieve the child's pg_constraint tuple.
12751 : *
12752 : * The arguments to this function have the same meaning as the arguments to
12753 : * ATExecAlterCheckConstrEnforceability.
12754 : */
12755 : static void
12756 148 : AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12757 : Relation conrel, Oid conrelid,
12758 : bool recurse, bool recursing,
12759 : LOCKMODE lockmode)
12760 : {
12761 : SysScanDesc pscan;
12762 : HeapTuple childtup;
12763 : ScanKeyData skey[3];
12764 :
12765 148 : ScanKeyInit(&skey[0],
12766 : Anum_pg_constraint_conrelid,
12767 : BTEqualStrategyNumber, F_OIDEQ,
12768 : ObjectIdGetDatum(conrelid));
12769 148 : ScanKeyInit(&skey[1],
12770 : Anum_pg_constraint_contypid,
12771 : BTEqualStrategyNumber, F_OIDEQ,
12772 : ObjectIdGetDatum(InvalidOid));
12773 148 : ScanKeyInit(&skey[2],
12774 : Anum_pg_constraint_conname,
12775 : BTEqualStrategyNumber, F_NAMEEQ,
12776 148 : CStringGetDatum(cmdcon->conname));
12777 :
12778 148 : pscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
12779 : NULL, 3, skey);
12780 :
12781 148 : if (!HeapTupleIsValid(childtup = systable_getnext(pscan)))
12782 0 : ereport(ERROR,
12783 : errcode(ERRCODE_UNDEFINED_OBJECT),
12784 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12785 : cmdcon->conname, get_rel_name(conrelid)));
12786 :
12787 148 : ATExecAlterCheckConstrEnforceability(wqueue, cmdcon, conrel, childtup,
12788 : recurse, recursing, lockmode);
12789 :
12790 148 : systable_endscan(pscan);
12791 148 : }
12792 :
12793 : /*
12794 : * Returns true if the constraint's deferrability is altered.
12795 : *
12796 : * *otherrelids is appended OIDs of relations containing affected triggers.
12797 : *
12798 : * Note that we must recurse even when the values are correct, in case
12799 : * indirect descendants have had their constraints altered locally.
12800 : * (This could be avoided if we forbade altering constraints in partitions
12801 : * but existing releases don't do that.)
12802 : */
12803 : static bool
12804 108 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
12805 : Relation conrel, Relation tgrel, Relation rel,
12806 : HeapTuple contuple, bool recurse,
12807 : List **otherrelids, LOCKMODE lockmode)
12808 : {
12809 : Form_pg_constraint currcon;
12810 : Oid refrelid;
12811 108 : bool changed = false;
12812 :
12813 : /* since this function recurses, it could be driven to stack overflow */
12814 108 : check_stack_depth();
12815 :
12816 : Assert(cmdcon->alterDeferrability);
12817 :
12818 108 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12819 108 : refrelid = currcon->confrelid;
12820 :
12821 : /* Should be foreign key constraint */
12822 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12823 :
12824 : /*
12825 : * If called to modify a constraint that's already in the desired state,
12826 : * silently do nothing.
12827 : */
12828 108 : if (currcon->condeferrable != cmdcon->deferrable ||
12829 4 : currcon->condeferred != cmdcon->initdeferred)
12830 : {
12831 108 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12832 108 : changed = true;
12833 :
12834 : /*
12835 : * Now we need to update the multiple entries in pg_trigger that
12836 : * implement the constraint.
12837 : */
12838 108 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12839 108 : cmdcon->deferrable,
12840 108 : cmdcon->initdeferred, otherrelids);
12841 : }
12842 :
12843 : /*
12844 : * If the table at either end of the constraint is partitioned, we need to
12845 : * handle every constraint that is a child of this one.
12846 : */
12847 108 : if (recurse && changed &&
12848 200 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12849 92 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12850 28 : AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12851 : contuple, recurse, otherrelids,
12852 : lockmode);
12853 :
12854 108 : return changed;
12855 : }
12856 :
12857 : /*
12858 : * Returns true if the constraint's inheritability is altered.
12859 : */
12860 : static bool
12861 40 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
12862 : Relation conrel, Relation rel,
12863 : HeapTuple contuple, LOCKMODE lockmode)
12864 : {
12865 : Form_pg_constraint currcon;
12866 : AttrNumber colNum;
12867 : char *colName;
12868 : List *children;
12869 :
12870 : Assert(cmdcon->alterInheritability);
12871 :
12872 40 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12873 :
12874 : /* The current implementation only works for NOT NULL constraints */
12875 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12876 :
12877 : /*
12878 : * If called to modify a constraint that's already in the desired state,
12879 : * silently do nothing.
12880 : */
12881 40 : if (cmdcon->noinherit == currcon->connoinherit)
12882 0 : return false;
12883 :
12884 40 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12885 40 : CommandCounterIncrement();
12886 :
12887 : /* Fetch the column number and name */
12888 40 : colNum = extractNotNullColumn(contuple);
12889 40 : colName = get_attname(currcon->conrelid, colNum, false);
12890 :
12891 : /*
12892 : * Propagate the change to children. For this subcommand type we don't
12893 : * recursively affect children, just the immediate level.
12894 : */
12895 40 : children = find_inheritance_children(RelationGetRelid(rel),
12896 : lockmode);
12897 128 : foreach_oid(childoid, children)
12898 : {
12899 : ObjectAddress addr;
12900 :
12901 56 : if (cmdcon->noinherit)
12902 : {
12903 : HeapTuple childtup;
12904 : Form_pg_constraint childcon;
12905 :
12906 20 : childtup = findNotNullConstraint(childoid, colName);
12907 20 : if (!childtup)
12908 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12909 : colName, childoid);
12910 20 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12911 : Assert(childcon->coninhcount > 0);
12912 20 : childcon->coninhcount--;
12913 20 : childcon->conislocal = true;
12914 20 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12915 20 : heap_freetuple(childtup);
12916 : }
12917 : else
12918 : {
12919 36 : Relation childrel = table_open(childoid, NoLock);
12920 :
12921 36 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12922 : colName, true, true, lockmode);
12923 32 : if (OidIsValid(addr.objectId))
12924 32 : CommandCounterIncrement();
12925 32 : table_close(childrel, NoLock);
12926 : }
12927 : }
12928 :
12929 36 : return true;
12930 : }
12931 :
12932 : /*
12933 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12934 : * trigger's deferrability.
12935 : *
12936 : * The arguments to this function have the same meaning as the arguments to
12937 : * ATExecAlterConstrDeferrability.
12938 : */
12939 : static void
12940 108 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12941 : bool deferrable, bool initdeferred,
12942 : List **otherrelids)
12943 : {
12944 : HeapTuple tgtuple;
12945 : ScanKeyData tgkey;
12946 : SysScanDesc tgscan;
12947 :
12948 108 : ScanKeyInit(&tgkey,
12949 : Anum_pg_trigger_tgconstraint,
12950 : BTEqualStrategyNumber, F_OIDEQ,
12951 : ObjectIdGetDatum(conoid));
12952 108 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12953 : NULL, 1, &tgkey);
12954 420 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12955 : {
12956 312 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12957 : Form_pg_trigger copy_tg;
12958 : HeapTuple tgCopyTuple;
12959 :
12960 : /*
12961 : * Remember OIDs of other relation(s) involved in FK constraint.
12962 : * (Note: it's likely that we could skip forcing a relcache inval for
12963 : * other rels that don't have a trigger whose properties change, but
12964 : * let's be conservative.)
12965 : */
12966 312 : if (tgform->tgrelid != RelationGetRelid(rel))
12967 152 : *otherrelids = list_append_unique_oid(*otherrelids,
12968 : tgform->tgrelid);
12969 :
12970 : /*
12971 : * Update enable status and deferrability of RI_FKey_noaction_del,
12972 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12973 : * triggers, but not others; see createForeignKeyActionTriggers and
12974 : * CreateFKCheckTrigger.
12975 : */
12976 312 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12977 248 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12978 172 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12979 92 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12980 12 : continue;
12981 :
12982 300 : tgCopyTuple = heap_copytuple(tgtuple);
12983 300 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12984 :
12985 300 : copy_tg->tgdeferrable = deferrable;
12986 300 : copy_tg->tginitdeferred = initdeferred;
12987 300 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12988 :
12989 300 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12990 :
12991 300 : heap_freetuple(tgCopyTuple);
12992 : }
12993 :
12994 108 : systable_endscan(tgscan);
12995 108 : }
12996 :
12997 : /*
12998 : * Invokes ATExecAlterFKConstrEnforceability for each foreign key constraint
12999 : * that is a child of the specified constraint.
13000 : *
13001 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
13002 : * list of child relations and recursing; instead it uses the conparentid
13003 : * relationships. This may need to be reconsidered.
13004 : *
13005 : * The arguments to this function have the same meaning as the arguments to
13006 : * ATExecAlterFKConstrEnforceability.
13007 : */
13008 : static void
13009 32 : AlterFKConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
13010 : Relation conrel, Relation tgrel,
13011 : Oid fkrelid, Oid pkrelid,
13012 : HeapTuple contuple, LOCKMODE lockmode,
13013 : Oid ReferencedParentDelTrigger,
13014 : Oid ReferencedParentUpdTrigger,
13015 : Oid ReferencingParentInsTrigger,
13016 : Oid ReferencingParentUpdTrigger)
13017 : {
13018 : Form_pg_constraint currcon;
13019 : Oid conoid;
13020 : ScanKeyData pkey;
13021 : SysScanDesc pscan;
13022 : HeapTuple childtup;
13023 :
13024 32 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
13025 32 : conoid = currcon->oid;
13026 :
13027 32 : ScanKeyInit(&pkey,
13028 : Anum_pg_constraint_conparentid,
13029 : BTEqualStrategyNumber, F_OIDEQ,
13030 : ObjectIdGetDatum(conoid));
13031 :
13032 32 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13033 : true, NULL, 1, &pkey);
13034 :
13035 100 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13036 68 : ATExecAlterFKConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
13037 : pkrelid, childtup, lockmode,
13038 : ReferencedParentDelTrigger,
13039 : ReferencedParentUpdTrigger,
13040 : ReferencingParentInsTrigger,
13041 : ReferencingParentUpdTrigger);
13042 :
13043 32 : systable_endscan(pscan);
13044 32 : }
13045 :
13046 : /*
13047 : * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
13048 : * the specified constraint.
13049 : *
13050 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
13051 : * list of child relations and recursing; instead it uses the conparentid
13052 : * relationships. This may need to be reconsidered.
13053 : *
13054 : * The arguments to this function have the same meaning as the arguments to
13055 : * ATExecAlterConstrDeferrability.
13056 : */
13057 : static void
13058 28 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
13059 : Relation conrel, Relation tgrel, Relation rel,
13060 : HeapTuple contuple, bool recurse,
13061 : List **otherrelids, LOCKMODE lockmode)
13062 : {
13063 : Form_pg_constraint currcon;
13064 : Oid conoid;
13065 : ScanKeyData pkey;
13066 : SysScanDesc pscan;
13067 : HeapTuple childtup;
13068 :
13069 28 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
13070 28 : conoid = currcon->oid;
13071 :
13072 28 : ScanKeyInit(&pkey,
13073 : Anum_pg_constraint_conparentid,
13074 : BTEqualStrategyNumber, F_OIDEQ,
13075 : ObjectIdGetDatum(conoid));
13076 :
13077 28 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13078 : true, NULL, 1, &pkey);
13079 :
13080 72 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13081 : {
13082 44 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13083 : Relation childrel;
13084 :
13085 44 : childrel = table_open(childcon->conrelid, lockmode);
13086 :
13087 44 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
13088 : childtup, recurse, otherrelids, lockmode);
13089 44 : table_close(childrel, NoLock);
13090 : }
13091 :
13092 28 : systable_endscan(pscan);
13093 28 : }
13094 :
13095 : /*
13096 : * Update the constraint entry for the given ATAlterConstraint command, and
13097 : * invoke the appropriate hooks.
13098 : */
13099 : static void
13100 468 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
13101 : HeapTuple contuple)
13102 : {
13103 : HeapTuple copyTuple;
13104 : Form_pg_constraint copy_con;
13105 :
13106 : Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
13107 : cmdcon->alterInheritability);
13108 :
13109 468 : copyTuple = heap_copytuple(contuple);
13110 468 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13111 :
13112 468 : if (cmdcon->alterEnforceability)
13113 : {
13114 320 : copy_con->conenforced = cmdcon->is_enforced;
13115 :
13116 : /*
13117 : * NB: The convalidated status is irrelevant when the constraint is
13118 : * set to NOT ENFORCED, but for consistency, it should still be set
13119 : * appropriately. Similarly, if the constraint is later changed to
13120 : * ENFORCED, validation will be performed during phase 3, so it makes
13121 : * sense to mark it as valid in that case.
13122 : */
13123 320 : copy_con->convalidated = cmdcon->is_enforced;
13124 : }
13125 468 : if (cmdcon->alterDeferrability)
13126 : {
13127 112 : copy_con->condeferrable = cmdcon->deferrable;
13128 112 : copy_con->condeferred = cmdcon->initdeferred;
13129 : }
13130 468 : if (cmdcon->alterInheritability)
13131 40 : copy_con->connoinherit = cmdcon->noinherit;
13132 :
13133 468 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13134 468 : InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
13135 :
13136 : /* Make new constraint flags visible to others */
13137 468 : CacheInvalidateRelcacheByRelid(copy_con->conrelid);
13138 :
13139 468 : heap_freetuple(copyTuple);
13140 468 : }
13141 :
13142 : /*
13143 : * ALTER TABLE VALIDATE CONSTRAINT
13144 : *
13145 : * XXX The reason we handle recursion here rather than at Phase 1 is because
13146 : * there's no good way to skip recursing when handling foreign keys: there is
13147 : * no need to lock children in that case, yet we wouldn't be able to avoid
13148 : * doing so at that level.
13149 : *
13150 : * Return value is the address of the validated constraint. If the constraint
13151 : * was already validated, InvalidObjectAddress is returned.
13152 : */
13153 : static ObjectAddress
13154 347 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
13155 : bool recurse, bool recursing, LOCKMODE lockmode)
13156 : {
13157 : Relation conrel;
13158 : SysScanDesc scan;
13159 : ScanKeyData skey[3];
13160 : HeapTuple tuple;
13161 : Form_pg_constraint con;
13162 : ObjectAddress address;
13163 :
13164 347 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13165 :
13166 : /*
13167 : * Find and check the target constraint
13168 : */
13169 347 : ScanKeyInit(&skey[0],
13170 : Anum_pg_constraint_conrelid,
13171 : BTEqualStrategyNumber, F_OIDEQ,
13172 : ObjectIdGetDatum(RelationGetRelid(rel)));
13173 347 : ScanKeyInit(&skey[1],
13174 : Anum_pg_constraint_contypid,
13175 : BTEqualStrategyNumber, F_OIDEQ,
13176 : ObjectIdGetDatum(InvalidOid));
13177 347 : ScanKeyInit(&skey[2],
13178 : Anum_pg_constraint_conname,
13179 : BTEqualStrategyNumber, F_NAMEEQ,
13180 : CStringGetDatum(constrName));
13181 347 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13182 : true, NULL, 3, skey);
13183 :
13184 : /* There can be at most one matching row */
13185 347 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
13186 0 : ereport(ERROR,
13187 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13188 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13189 : constrName, RelationGetRelationName(rel))));
13190 :
13191 347 : con = (Form_pg_constraint) GETSTRUCT(tuple);
13192 347 : if (con->contype != CONSTRAINT_FOREIGN &&
13193 170 : con->contype != CONSTRAINT_CHECK &&
13194 74 : con->contype != CONSTRAINT_NOTNULL)
13195 0 : ereport(ERROR,
13196 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
13197 : errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
13198 : constrName, RelationGetRelationName(rel)),
13199 : errdetail("This operation is not supported for this type of constraint."));
13200 :
13201 347 : if (!con->conenforced)
13202 4 : ereport(ERROR,
13203 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13204 : errmsg("cannot validate NOT ENFORCED constraint")));
13205 :
13206 343 : if (!con->convalidated)
13207 : {
13208 331 : if (con->contype == CONSTRAINT_FOREIGN)
13209 : {
13210 173 : QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
13211 : tuple, lockmode);
13212 : }
13213 158 : else if (con->contype == CONSTRAINT_CHECK)
13214 : {
13215 84 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
13216 : tuple, recurse, recursing, lockmode);
13217 : }
13218 74 : else if (con->contype == CONSTRAINT_NOTNULL)
13219 : {
13220 74 : QueueNNConstraintValidation(wqueue, conrel, rel,
13221 : tuple, recurse, recursing, lockmode);
13222 : }
13223 :
13224 331 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
13225 : }
13226 : else
13227 12 : address = InvalidObjectAddress; /* already validated */
13228 :
13229 343 : systable_endscan(scan);
13230 :
13231 343 : table_close(conrel, RowExclusiveLock);
13232 :
13233 343 : return address;
13234 : }
13235 :
13236 : /*
13237 : * QueueFKConstraintValidation
13238 : *
13239 : * Add an entry to the wqueue to validate the given foreign key constraint in
13240 : * Phase 3 and update the convalidated field in the pg_constraint catalog
13241 : * for the specified relation and all its children.
13242 : */
13243 : static void
13244 225 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
13245 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
13246 : {
13247 : Form_pg_constraint con;
13248 : AlteredTableInfo *tab;
13249 : HeapTuple copyTuple;
13250 : Form_pg_constraint copy_con;
13251 :
13252 225 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13253 : Assert(con->contype == CONSTRAINT_FOREIGN);
13254 : Assert(!con->convalidated);
13255 :
13256 : /*
13257 : * Add the validation to phase 3's queue; not needed for partitioned
13258 : * tables themselves, only for their partitions.
13259 : *
13260 : * When the referenced table (pkrelid) is partitioned, the referencing
13261 : * table (fkrel) has one pg_constraint row pointing to each partition
13262 : * thereof. These rows are there only to support action triggers and no
13263 : * table scan is needed, therefore skip this for them as well.
13264 : */
13265 225 : if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13266 193 : con->confrelid == pkrelid)
13267 : {
13268 : NewConstraint *newcon;
13269 : Constraint *fkconstraint;
13270 :
13271 : /* Queue validation for phase 3 */
13272 181 : fkconstraint = makeNode(Constraint);
13273 : /* for now this is all we need */
13274 181 : fkconstraint->conname = pstrdup(NameStr(con->conname));
13275 :
13276 181 : newcon = palloc0_object(NewConstraint);
13277 181 : newcon->name = fkconstraint->conname;
13278 181 : newcon->contype = CONSTR_FOREIGN;
13279 181 : newcon->refrelid = con->confrelid;
13280 181 : newcon->refindid = con->conindid;
13281 181 : newcon->conid = con->oid;
13282 181 : newcon->qual = (Node *) fkconstraint;
13283 :
13284 : /* Find or create work queue entry for this table */
13285 181 : tab = ATGetQueueEntry(wqueue, fkrel);
13286 181 : tab->constraints = lappend(tab->constraints, newcon);
13287 : }
13288 :
13289 : /*
13290 : * If the table at either end of the constraint is partitioned, we need to
13291 : * recurse and handle every unvalidated constraint that is a child of this
13292 : * constraint.
13293 : */
13294 418 : if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13295 193 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13296 : {
13297 : ScanKeyData pkey;
13298 : SysScanDesc pscan;
13299 : HeapTuple childtup;
13300 :
13301 52 : ScanKeyInit(&pkey,
13302 : Anum_pg_constraint_conparentid,
13303 : BTEqualStrategyNumber, F_OIDEQ,
13304 : ObjectIdGetDatum(con->oid));
13305 :
13306 52 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13307 : true, NULL, 1, &pkey);
13308 :
13309 104 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13310 : {
13311 : Form_pg_constraint childcon;
13312 : Relation childrel;
13313 :
13314 52 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13315 :
13316 : /*
13317 : * If the child constraint has already been validated, no further
13318 : * action is required for it or its descendants, as they are all
13319 : * valid.
13320 : */
13321 52 : if (childcon->convalidated)
13322 12 : continue;
13323 :
13324 40 : childrel = table_open(childcon->conrelid, lockmode);
13325 :
13326 : /*
13327 : * NB: Note that pkrelid should be passed as-is during recursion,
13328 : * as it is required to identify the root referenced table.
13329 : */
13330 40 : QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13331 : childtup, lockmode);
13332 40 : table_close(childrel, NoLock);
13333 : }
13334 :
13335 52 : systable_endscan(pscan);
13336 : }
13337 :
13338 : /*
13339 : * Now mark the pg_constraint row as validated (even if we didn't check,
13340 : * notably the ones for partitions on the referenced side).
13341 : *
13342 : * We rely on transaction abort to roll back this change if phase 3
13343 : * ultimately finds violating rows. This is a bit ugly.
13344 : */
13345 225 : copyTuple = heap_copytuple(contuple);
13346 225 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13347 225 : copy_con->convalidated = true;
13348 225 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13349 :
13350 225 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13351 :
13352 225 : heap_freetuple(copyTuple);
13353 225 : }
13354 :
13355 : /*
13356 : * QueueCheckConstraintValidation
13357 : *
13358 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
13359 : * and update the convalidated field in the pg_constraint catalog for the
13360 : * specified relation and all its inheriting children.
13361 : */
13362 : static void
13363 84 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13364 : char *constrName, HeapTuple contuple,
13365 : bool recurse, bool recursing, LOCKMODE lockmode)
13366 : {
13367 : Form_pg_constraint con;
13368 : AlteredTableInfo *tab;
13369 : HeapTuple copyTuple;
13370 : Form_pg_constraint copy_con;
13371 :
13372 84 : List *children = NIL;
13373 : ListCell *child;
13374 : NewConstraint *newcon;
13375 : Datum val;
13376 : char *conbin;
13377 :
13378 84 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13379 : Assert(con->contype == CONSTRAINT_CHECK);
13380 :
13381 : /*
13382 : * If we're recursing, the parent has already done this, so skip it. Also,
13383 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13384 : * for it in the children.
13385 : */
13386 84 : if (!recursing && !con->connoinherit)
13387 48 : children = find_all_inheritors(RelationGetRelid(rel),
13388 : lockmode, NULL);
13389 :
13390 : /*
13391 : * For CHECK constraints, we must ensure that we only mark the constraint
13392 : * as validated on the parent if it's already validated on the children.
13393 : *
13394 : * We recurse before validating on the parent, to reduce risk of
13395 : * deadlocks.
13396 : */
13397 164 : foreach(child, children)
13398 : {
13399 80 : Oid childoid = lfirst_oid(child);
13400 : Relation childrel;
13401 :
13402 80 : if (childoid == RelationGetRelid(rel))
13403 48 : continue;
13404 :
13405 : /*
13406 : * If we are told not to recurse, there had better not be any child
13407 : * tables, because we can't mark the constraint on the parent valid
13408 : * unless it is valid for all child tables.
13409 : */
13410 32 : if (!recurse)
13411 0 : ereport(ERROR,
13412 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13413 : errmsg("constraint must be validated on child tables too")));
13414 :
13415 : /* find_all_inheritors already got lock */
13416 32 : childrel = table_open(childoid, NoLock);
13417 :
13418 32 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
13419 : true, lockmode);
13420 32 : table_close(childrel, NoLock);
13421 : }
13422 :
13423 : /* Queue validation for phase 3 */
13424 84 : newcon = palloc0_object(NewConstraint);
13425 84 : newcon->name = constrName;
13426 84 : newcon->contype = CONSTR_CHECK;
13427 84 : newcon->refrelid = InvalidOid;
13428 84 : newcon->refindid = InvalidOid;
13429 84 : newcon->conid = con->oid;
13430 :
13431 84 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13432 : Anum_pg_constraint_conbin);
13433 84 : conbin = TextDatumGetCString(val);
13434 84 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13435 :
13436 : /* Find or create work queue entry for this table */
13437 84 : tab = ATGetQueueEntry(wqueue, rel);
13438 84 : tab->constraints = lappend(tab->constraints, newcon);
13439 :
13440 : /*
13441 : * Invalidate relcache so that others see the new validated constraint.
13442 : */
13443 84 : CacheInvalidateRelcache(rel);
13444 :
13445 : /*
13446 : * Now update the catalog, while we have the door open.
13447 : */
13448 84 : copyTuple = heap_copytuple(contuple);
13449 84 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13450 84 : copy_con->convalidated = true;
13451 84 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13452 :
13453 84 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13454 :
13455 84 : heap_freetuple(copyTuple);
13456 84 : }
13457 :
13458 : /*
13459 : * QueueNNConstraintValidation
13460 : *
13461 : * Add an entry to the wqueue to validate the given not-null constraint in
13462 : * Phase 3 and update the convalidated field in the pg_constraint catalog for
13463 : * the specified relation and all its inheriting children.
13464 : */
13465 : static void
13466 74 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13467 : HeapTuple contuple, bool recurse, bool recursing,
13468 : LOCKMODE lockmode)
13469 : {
13470 : Form_pg_constraint con;
13471 : AlteredTableInfo *tab;
13472 : HeapTuple copyTuple;
13473 : Form_pg_constraint copy_con;
13474 74 : List *children = NIL;
13475 : AttrNumber attnum;
13476 : char *colname;
13477 :
13478 74 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13479 : Assert(con->contype == CONSTRAINT_NOTNULL);
13480 :
13481 74 : attnum = extractNotNullColumn(contuple);
13482 :
13483 : /*
13484 : * If we're recursing, we've already done this for parent, so skip it.
13485 : * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13486 : * look for it in the children.
13487 : *
13488 : * We recurse before validating on the parent, to reduce risk of
13489 : * deadlocks.
13490 : */
13491 74 : if (!recursing && !con->connoinherit)
13492 50 : children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13493 :
13494 74 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13495 250 : foreach_oid(childoid, children)
13496 : {
13497 : Relation childrel;
13498 : HeapTuple contup;
13499 : Form_pg_constraint childcon;
13500 : char *conname;
13501 :
13502 102 : if (childoid == RelationGetRelid(rel))
13503 50 : continue;
13504 :
13505 : /*
13506 : * If we are told not to recurse, there had better not be any child
13507 : * tables, because we can't mark the constraint on the parent valid
13508 : * unless it is valid for all child tables.
13509 : */
13510 52 : if (!recurse)
13511 0 : ereport(ERROR,
13512 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13513 : errmsg("constraint must be validated on child tables too"));
13514 :
13515 : /*
13516 : * The column on child might have a different attnum, so search by
13517 : * column name.
13518 : */
13519 52 : contup = findNotNullConstraint(childoid, colname);
13520 52 : if (!contup)
13521 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13522 : colname, get_rel_name(childoid));
13523 52 : childcon = (Form_pg_constraint) GETSTRUCT(contup);
13524 52 : if (childcon->convalidated)
13525 28 : continue;
13526 :
13527 : /* find_all_inheritors already got lock */
13528 24 : childrel = table_open(childoid, NoLock);
13529 24 : conname = pstrdup(NameStr(childcon->conname));
13530 :
13531 : /* XXX improve ATExecValidateConstraint API to avoid double search */
13532 24 : ATExecValidateConstraint(wqueue, childrel, conname,
13533 : false, true, lockmode);
13534 24 : table_close(childrel, NoLock);
13535 : }
13536 :
13537 : /* Set attnotnull appropriately without queueing another validation */
13538 74 : set_attnotnull(NULL, rel, attnum, true, false);
13539 :
13540 74 : tab = ATGetQueueEntry(wqueue, rel);
13541 74 : tab->verify_new_notnull = true;
13542 :
13543 : /*
13544 : * Invalidate relcache so that others see the new validated constraint.
13545 : */
13546 74 : CacheInvalidateRelcache(rel);
13547 :
13548 : /*
13549 : * Now update the catalogs, while we have the door open.
13550 : */
13551 74 : copyTuple = heap_copytuple(contuple);
13552 74 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13553 74 : copy_con->convalidated = true;
13554 74 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13555 :
13556 74 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13557 :
13558 74 : heap_freetuple(copyTuple);
13559 74 : }
13560 :
13561 : /*
13562 : * transformColumnNameList - transform list of column names
13563 : *
13564 : * Lookup each name and return its attnum and, optionally, type and collation
13565 : * OIDs
13566 : *
13567 : * Note: the name of this function suggests that it's general-purpose,
13568 : * but actually it's only used to look up names appearing in foreign-key
13569 : * clauses. The error messages would need work to use it in other cases,
13570 : * and perhaps the validity checks as well.
13571 : */
13572 : static int
13573 4473 : transformColumnNameList(Oid relId, List *colList,
13574 : int16 *attnums, Oid *atttypids, Oid *attcollids)
13575 : {
13576 : ListCell *l;
13577 : int attnum;
13578 :
13579 4473 : attnum = 0;
13580 8124 : foreach(l, colList)
13581 : {
13582 3695 : char *attname = strVal(lfirst(l));
13583 : HeapTuple atttuple;
13584 : Form_pg_attribute attform;
13585 :
13586 3695 : atttuple = SearchSysCacheAttName(relId, attname);
13587 3695 : if (!HeapTupleIsValid(atttuple))
13588 36 : ereport(ERROR,
13589 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13590 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13591 : attname)));
13592 3659 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13593 3659 : if (attform->attnum < 0)
13594 8 : ereport(ERROR,
13595 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13596 : errmsg("system columns cannot be used in foreign keys")));
13597 3651 : if (attnum >= INDEX_MAX_KEYS)
13598 0 : ereport(ERROR,
13599 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
13600 : errmsg("cannot have more than %d keys in a foreign key",
13601 : INDEX_MAX_KEYS)));
13602 3651 : attnums[attnum] = attform->attnum;
13603 3651 : if (atttypids != NULL)
13604 3627 : atttypids[attnum] = attform->atttypid;
13605 3651 : if (attcollids != NULL)
13606 3627 : attcollids[attnum] = attform->attcollation;
13607 3651 : ReleaseSysCache(atttuple);
13608 3651 : attnum++;
13609 : }
13610 :
13611 4429 : return attnum;
13612 : }
13613 :
13614 : /*
13615 : * transformFkeyGetPrimaryKey -
13616 : *
13617 : * Look up the names, attnums, types, and collations of the primary key attributes
13618 : * for the pkrel. Also return the index OID and index opclasses of the
13619 : * index supporting the primary key. Also return whether the index has
13620 : * WITHOUT OVERLAPS.
13621 : *
13622 : * All parameters except pkrel are output parameters. Also, the function
13623 : * return value is the number of attributes in the primary key.
13624 : *
13625 : * Used when the column list in the REFERENCES specification is omitted.
13626 : */
13627 : static int
13628 895 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
13629 : List **attnamelist,
13630 : int16 *attnums, Oid *atttypids, Oid *attcollids,
13631 : Oid *opclasses, bool *pk_has_without_overlaps)
13632 : {
13633 : List *indexoidlist;
13634 : ListCell *indexoidscan;
13635 895 : HeapTuple indexTuple = NULL;
13636 895 : Form_pg_index indexStruct = NULL;
13637 : Datum indclassDatum;
13638 : oidvector *indclass;
13639 : int i;
13640 :
13641 : /*
13642 : * Get the list of index OIDs for the table from the relcache, and look up
13643 : * each one in the pg_index syscache until we find one marked primary key
13644 : * (hopefully there isn't more than one such). Insist it's valid, too.
13645 : */
13646 895 : *indexOid = InvalidOid;
13647 :
13648 895 : indexoidlist = RelationGetIndexList(pkrel);
13649 :
13650 899 : foreach(indexoidscan, indexoidlist)
13651 : {
13652 899 : Oid indexoid = lfirst_oid(indexoidscan);
13653 :
13654 899 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13655 899 : if (!HeapTupleIsValid(indexTuple))
13656 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13657 899 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13658 899 : if (indexStruct->indisprimary && indexStruct->indisvalid)
13659 : {
13660 : /*
13661 : * Refuse to use a deferrable primary key. This is per SQL spec,
13662 : * and there would be a lot of interesting semantic problems if we
13663 : * tried to allow it.
13664 : */
13665 895 : if (!indexStruct->indimmediate)
13666 0 : ereport(ERROR,
13667 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13668 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13669 : RelationGetRelationName(pkrel))));
13670 :
13671 895 : *indexOid = indexoid;
13672 895 : break;
13673 : }
13674 4 : ReleaseSysCache(indexTuple);
13675 : }
13676 :
13677 895 : list_free(indexoidlist);
13678 :
13679 : /*
13680 : * Check that we found it
13681 : */
13682 895 : if (!OidIsValid(*indexOid))
13683 0 : ereport(ERROR,
13684 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13685 : errmsg("there is no primary key for referenced table \"%s\"",
13686 : RelationGetRelationName(pkrel))));
13687 :
13688 : /* Must get indclass the hard way */
13689 895 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13690 : Anum_pg_index_indclass);
13691 895 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13692 :
13693 : /*
13694 : * Now build the list of PK attributes from the indkey definition (we
13695 : * assume a primary key cannot have expressional elements)
13696 : */
13697 895 : *attnamelist = NIL;
13698 2106 : for (i = 0; i < indexStruct->indnkeyatts; i++)
13699 : {
13700 1211 : int pkattno = indexStruct->indkey.values[i];
13701 :
13702 1211 : attnums[i] = pkattno;
13703 1211 : atttypids[i] = attnumTypeId(pkrel, pkattno);
13704 1211 : attcollids[i] = attnumCollationId(pkrel, pkattno);
13705 1211 : opclasses[i] = indclass->values[i];
13706 1211 : *attnamelist = lappend(*attnamelist,
13707 1211 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13708 : }
13709 :
13710 895 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13711 :
13712 895 : ReleaseSysCache(indexTuple);
13713 :
13714 895 : return i;
13715 : }
13716 :
13717 : /*
13718 : * transformFkeyCheckAttrs -
13719 : *
13720 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13721 : * reference as part of a foreign key constraint.
13722 : *
13723 : * Returns the OID of the unique index supporting the constraint and
13724 : * populates the caller-provided 'opclasses' array with the opclasses
13725 : * associated with the index columns. Also sets whether the index
13726 : * uses WITHOUT OVERLAPS.
13727 : *
13728 : * Raises an ERROR on validation failure.
13729 : */
13730 : static Oid
13731 841 : transformFkeyCheckAttrs(Relation pkrel,
13732 : int numattrs, int16 *attnums,
13733 : bool with_period, Oid *opclasses,
13734 : bool *pk_has_without_overlaps)
13735 : {
13736 841 : Oid indexoid = InvalidOid;
13737 841 : bool found = false;
13738 841 : bool found_deferrable = false;
13739 : List *indexoidlist;
13740 : ListCell *indexoidscan;
13741 : int i,
13742 : j;
13743 :
13744 : /*
13745 : * Reject duplicate appearances of columns in the referenced-columns list.
13746 : * Such a case is forbidden by the SQL standard, and even if we thought it
13747 : * useful to allow it, there would be ambiguity about how to match the
13748 : * list to unique indexes (in particular, it'd be unclear which index
13749 : * opclass goes with which FK column).
13750 : */
13751 1963 : for (i = 0; i < numattrs; i++)
13752 : {
13753 1481 : for (j = i + 1; j < numattrs; j++)
13754 : {
13755 359 : if (attnums[i] == attnums[j])
13756 16 : ereport(ERROR,
13757 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13758 : errmsg("foreign key referenced-columns list must not contain duplicates")));
13759 : }
13760 : }
13761 :
13762 : /*
13763 : * Get the list of index OIDs for the table from the relcache, and look up
13764 : * each one in the pg_index syscache, and match unique indexes to the list
13765 : * of attnums we are given.
13766 : */
13767 825 : indexoidlist = RelationGetIndexList(pkrel);
13768 :
13769 944 : foreach(indexoidscan, indexoidlist)
13770 : {
13771 : HeapTuple indexTuple;
13772 : Form_pg_index indexStruct;
13773 :
13774 936 : indexoid = lfirst_oid(indexoidscan);
13775 936 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13776 936 : if (!HeapTupleIsValid(indexTuple))
13777 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13778 936 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13779 :
13780 : /*
13781 : * Must have the right number of columns; must be unique (or if
13782 : * temporal then exclusion instead) and not a partial index; forget it
13783 : * if there are any expressions, too. Invalid indexes are out as well.
13784 : */
13785 936 : if (indexStruct->indnkeyatts == numattrs &&
13786 855 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13787 1710 : indexStruct->indisvalid &&
13788 1710 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13789 855 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13790 : {
13791 : Datum indclassDatum;
13792 : oidvector *indclass;
13793 :
13794 : /* Must get indclass the hard way */
13795 855 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13796 : Anum_pg_index_indclass);
13797 855 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13798 :
13799 : /*
13800 : * The given attnum list may match the index columns in any order.
13801 : * Check for a match, and extract the appropriate opclasses while
13802 : * we're at it.
13803 : *
13804 : * We know that attnums[] is duplicate-free per the test at the
13805 : * start of this function, and we checked above that the number of
13806 : * index columns agrees, so if we find a match for each attnums[]
13807 : * entry then we must have a one-to-one match in some order.
13808 : */
13809 1969 : for (i = 0; i < numattrs; i++)
13810 : {
13811 1152 : found = false;
13812 1533 : for (j = 0; j < numattrs; j++)
13813 : {
13814 1495 : if (attnums[i] == indexStruct->indkey.values[j])
13815 : {
13816 1114 : opclasses[i] = indclass->values[j];
13817 1114 : found = true;
13818 1114 : break;
13819 : }
13820 : }
13821 1152 : if (!found)
13822 38 : break;
13823 : }
13824 : /* The last attribute in the index must be the PERIOD FK part */
13825 855 : if (found && with_period)
13826 : {
13827 80 : int16 periodattnum = attnums[numattrs - 1];
13828 :
13829 80 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13830 : }
13831 :
13832 : /*
13833 : * Refuse to use a deferrable unique/primary key. This is per SQL
13834 : * spec, and there would be a lot of interesting semantic problems
13835 : * if we tried to allow it.
13836 : */
13837 855 : if (found && !indexStruct->indimmediate)
13838 : {
13839 : /*
13840 : * Remember that we found an otherwise matching index, so that
13841 : * we can generate a more appropriate error message.
13842 : */
13843 0 : found_deferrable = true;
13844 0 : found = false;
13845 : }
13846 :
13847 : /* We need to know whether the index has WITHOUT OVERLAPS */
13848 855 : if (found)
13849 817 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13850 : }
13851 936 : ReleaseSysCache(indexTuple);
13852 936 : if (found)
13853 817 : break;
13854 : }
13855 :
13856 825 : if (!found)
13857 : {
13858 8 : if (found_deferrable)
13859 0 : ereport(ERROR,
13860 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13861 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13862 : RelationGetRelationName(pkrel))));
13863 : else
13864 8 : ereport(ERROR,
13865 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13866 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13867 : RelationGetRelationName(pkrel))));
13868 : }
13869 :
13870 817 : list_free(indexoidlist);
13871 :
13872 817 : return indexoid;
13873 : }
13874 :
13875 : /*
13876 : * findFkeyCast -
13877 : *
13878 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13879 : * Caller has equal regard for binary coercibility and for an exact match.
13880 : */
13881 : static CoercionPathType
13882 8 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13883 : {
13884 : CoercionPathType ret;
13885 :
13886 8 : if (targetTypeId == sourceTypeId)
13887 : {
13888 8 : ret = COERCION_PATH_RELABELTYPE;
13889 8 : *funcid = InvalidOid;
13890 : }
13891 : else
13892 : {
13893 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13894 : COERCION_IMPLICIT, funcid);
13895 0 : if (ret == COERCION_PATH_NONE)
13896 : /* A previously-relied-upon cast is now gone. */
13897 0 : elog(ERROR, "could not find cast from %u to %u",
13898 : sourceTypeId, targetTypeId);
13899 : }
13900 :
13901 8 : return ret;
13902 : }
13903 :
13904 : /*
13905 : * Permissions checks on the referenced table for ADD FOREIGN KEY
13906 : *
13907 : * Note: we have already checked that the user owns the referencing table,
13908 : * else we'd have failed much earlier; no additional checks are needed for it.
13909 : */
13910 : static void
13911 1688 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13912 : {
13913 1688 : Oid roleid = GetUserId();
13914 : AclResult aclresult;
13915 : int i;
13916 :
13917 : /* Okay if we have relation-level REFERENCES permission */
13918 1688 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13919 : ACL_REFERENCES);
13920 1688 : if (aclresult == ACLCHECK_OK)
13921 1688 : return;
13922 : /* Else we must have REFERENCES on each column */
13923 0 : for (i = 0; i < natts; i++)
13924 : {
13925 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13926 : roleid, ACL_REFERENCES);
13927 0 : if (aclresult != ACLCHECK_OK)
13928 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13929 0 : RelationGetRelationName(rel));
13930 : }
13931 : }
13932 :
13933 : /*
13934 : * Scan the existing rows in a table to verify they meet a proposed FK
13935 : * constraint.
13936 : *
13937 : * Caller must have opened and locked both relations appropriately.
13938 : */
13939 : static void
13940 828 : validateForeignKeyConstraint(char *conname,
13941 : Relation rel,
13942 : Relation pkrel,
13943 : Oid pkindOid,
13944 : Oid constraintOid,
13945 : bool hasperiod)
13946 : {
13947 : TupleTableSlot *slot;
13948 : TableScanDesc scan;
13949 828 : Trigger trig = {0};
13950 : Snapshot snapshot;
13951 : MemoryContext oldcxt;
13952 : MemoryContext perTupCxt;
13953 :
13954 828 : ereport(DEBUG1,
13955 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13956 :
13957 : /*
13958 : * Build a trigger call structure; we'll need it either way.
13959 : */
13960 828 : trig.tgoid = InvalidOid;
13961 828 : trig.tgname = conname;
13962 828 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13963 828 : trig.tgisinternal = true;
13964 828 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13965 828 : trig.tgconstrindid = pkindOid;
13966 828 : trig.tgconstraint = constraintOid;
13967 828 : trig.tgdeferrable = false;
13968 828 : trig.tginitdeferred = false;
13969 : /* we needn't fill in remaining fields */
13970 :
13971 : /*
13972 : * See if we can do it with a single LEFT JOIN query. A false result
13973 : * indicates we must proceed with the fire-the-trigger method. We can't do
13974 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13975 : * left joins.
13976 : */
13977 828 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13978 698 : return;
13979 :
13980 : /*
13981 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13982 : * if that tuple had just been inserted. If any of those fail, it should
13983 : * ereport(ERROR) and that's that.
13984 : */
13985 71 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13986 71 : slot = table_slot_create(rel, NULL);
13987 71 : scan = table_beginscan(rel, snapshot, 0, NULL,
13988 : SO_NONE);
13989 71 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13990 : "validateForeignKeyConstraint",
13991 : ALLOCSET_SMALL_SIZES);
13992 71 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13993 :
13994 127 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13995 : {
13996 68 : LOCAL_FCINFO(fcinfo, 0);
13997 68 : TriggerData trigdata = {0};
13998 :
13999 68 : CHECK_FOR_INTERRUPTS();
14000 :
14001 : /*
14002 : * Make a call to the trigger function
14003 : *
14004 : * No parameters are passed, but we do set a context
14005 : */
14006 340 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
14007 :
14008 : /*
14009 : * We assume RI_FKey_check_ins won't look at flinfo...
14010 : */
14011 68 : trigdata.type = T_TriggerData;
14012 68 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
14013 68 : trigdata.tg_relation = rel;
14014 68 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
14015 68 : trigdata.tg_trigslot = slot;
14016 68 : trigdata.tg_trigger = &trig;
14017 :
14018 68 : fcinfo->context = (Node *) &trigdata;
14019 :
14020 68 : RI_FKey_check_ins(fcinfo);
14021 :
14022 56 : MemoryContextReset(perTupCxt);
14023 : }
14024 :
14025 59 : MemoryContextSwitchTo(oldcxt);
14026 59 : MemoryContextDelete(perTupCxt);
14027 59 : table_endscan(scan);
14028 59 : UnregisterSnapshot(snapshot);
14029 59 : ExecDropSingleTupleTableSlot(slot);
14030 : }
14031 :
14032 : /*
14033 : * CreateFKCheckTrigger
14034 : * Creates the insert (on_insert=true) or update "check" trigger that
14035 : * implements a given foreign key
14036 : *
14037 : * Returns the OID of the so created trigger.
14038 : */
14039 : static Oid
14040 4094 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
14041 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
14042 : bool on_insert)
14043 : {
14044 : ObjectAddress trigAddress;
14045 : CreateTrigStmt *fk_trigger;
14046 :
14047 : /*
14048 : * Note: for a self-referential FK (referencing and referenced tables are
14049 : * the same), it is important that the ON UPDATE action fires before the
14050 : * CHECK action, since both triggers will fire on the same row during an
14051 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
14052 : * state of the row. Triggers fire in name order, so we ensure this by
14053 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
14054 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
14055 : */
14056 4094 : fk_trigger = makeNode(CreateTrigStmt);
14057 4094 : fk_trigger->replace = false;
14058 4094 : fk_trigger->isconstraint = true;
14059 4094 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
14060 4094 : fk_trigger->relation = NULL;
14061 :
14062 : /* Either ON INSERT or ON UPDATE */
14063 4094 : if (on_insert)
14064 : {
14065 2047 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
14066 2047 : fk_trigger->events = TRIGGER_TYPE_INSERT;
14067 : }
14068 : else
14069 : {
14070 2047 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
14071 2047 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
14072 : }
14073 :
14074 4094 : fk_trigger->args = NIL;
14075 4094 : fk_trigger->row = true;
14076 4094 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
14077 4094 : fk_trigger->columns = NIL;
14078 4094 : fk_trigger->whenClause = NULL;
14079 4094 : fk_trigger->transitionRels = NIL;
14080 4094 : fk_trigger->deferrable = fkconstraint->deferrable;
14081 4094 : fk_trigger->initdeferred = fkconstraint->initdeferred;
14082 4094 : fk_trigger->constrrel = NULL;
14083 :
14084 4094 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
14085 : constraintOid, indexOid, InvalidOid,
14086 : parentTrigOid, NULL, true, false);
14087 :
14088 : /* Make changes-so-far visible */
14089 4094 : CommandCounterIncrement();
14090 :
14091 4094 : return trigAddress.objectId;
14092 : }
14093 :
14094 : /*
14095 : * createForeignKeyActionTriggers
14096 : * Create the referenced-side "action" triggers that implement a foreign
14097 : * key.
14098 : *
14099 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
14100 : * *updateTrigOid.
14101 : */
14102 : static void
14103 2334 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
14104 : Oid constraintOid, Oid indexOid,
14105 : Oid parentDelTrigger, Oid parentUpdTrigger,
14106 : Oid *deleteTrigOid, Oid *updateTrigOid)
14107 : {
14108 : CreateTrigStmt *fk_trigger;
14109 : ObjectAddress trigAddress;
14110 :
14111 : /*
14112 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14113 : * DELETE action on the referenced table.
14114 : */
14115 2334 : fk_trigger = makeNode(CreateTrigStmt);
14116 2334 : fk_trigger->replace = false;
14117 2334 : fk_trigger->isconstraint = true;
14118 2334 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
14119 2334 : fk_trigger->relation = NULL;
14120 2334 : fk_trigger->args = NIL;
14121 2334 : fk_trigger->row = true;
14122 2334 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
14123 2334 : fk_trigger->events = TRIGGER_TYPE_DELETE;
14124 2334 : fk_trigger->columns = NIL;
14125 2334 : fk_trigger->whenClause = NULL;
14126 2334 : fk_trigger->transitionRels = NIL;
14127 2334 : fk_trigger->constrrel = NULL;
14128 :
14129 2334 : switch (fkconstraint->fk_del_action)
14130 : {
14131 1905 : case FKCONSTR_ACTION_NOACTION:
14132 1905 : fk_trigger->deferrable = fkconstraint->deferrable;
14133 1905 : fk_trigger->initdeferred = fkconstraint->initdeferred;
14134 1905 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
14135 1905 : break;
14136 20 : case FKCONSTR_ACTION_RESTRICT:
14137 20 : fk_trigger->deferrable = false;
14138 20 : fk_trigger->initdeferred = false;
14139 20 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
14140 20 : break;
14141 306 : case FKCONSTR_ACTION_CASCADE:
14142 306 : fk_trigger->deferrable = false;
14143 306 : fk_trigger->initdeferred = false;
14144 306 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
14145 306 : break;
14146 63 : case FKCONSTR_ACTION_SETNULL:
14147 63 : fk_trigger->deferrable = false;
14148 63 : fk_trigger->initdeferred = false;
14149 63 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
14150 63 : break;
14151 40 : case FKCONSTR_ACTION_SETDEFAULT:
14152 40 : fk_trigger->deferrable = false;
14153 40 : fk_trigger->initdeferred = false;
14154 40 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
14155 40 : break;
14156 0 : default:
14157 0 : elog(ERROR, "unrecognized FK action type: %d",
14158 : (int) fkconstraint->fk_del_action);
14159 : break;
14160 : }
14161 :
14162 2334 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
14163 : constraintOid, indexOid, InvalidOid,
14164 : parentDelTrigger, NULL, true, false);
14165 2334 : if (deleteTrigOid)
14166 2334 : *deleteTrigOid = trigAddress.objectId;
14167 :
14168 : /* Make changes-so-far visible */
14169 2334 : CommandCounterIncrement();
14170 :
14171 : /*
14172 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14173 : * UPDATE action on the referenced table.
14174 : */
14175 2334 : fk_trigger = makeNode(CreateTrigStmt);
14176 2334 : fk_trigger->replace = false;
14177 2334 : fk_trigger->isconstraint = true;
14178 2334 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
14179 2334 : fk_trigger->relation = NULL;
14180 2334 : fk_trigger->args = NIL;
14181 2334 : fk_trigger->row = true;
14182 2334 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
14183 2334 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
14184 2334 : fk_trigger->columns = NIL;
14185 2334 : fk_trigger->whenClause = NULL;
14186 2334 : fk_trigger->transitionRels = NIL;
14187 2334 : fk_trigger->constrrel = NULL;
14188 :
14189 2334 : switch (fkconstraint->fk_upd_action)
14190 : {
14191 2030 : case FKCONSTR_ACTION_NOACTION:
14192 2030 : fk_trigger->deferrable = fkconstraint->deferrable;
14193 2030 : fk_trigger->initdeferred = fkconstraint->initdeferred;
14194 2030 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
14195 2030 : break;
14196 24 : case FKCONSTR_ACTION_RESTRICT:
14197 24 : fk_trigger->deferrable = false;
14198 24 : fk_trigger->initdeferred = false;
14199 24 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
14200 24 : break;
14201 211 : case FKCONSTR_ACTION_CASCADE:
14202 211 : fk_trigger->deferrable = false;
14203 211 : fk_trigger->initdeferred = false;
14204 211 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
14205 211 : break;
14206 41 : case FKCONSTR_ACTION_SETNULL:
14207 41 : fk_trigger->deferrable = false;
14208 41 : fk_trigger->initdeferred = false;
14209 41 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
14210 41 : break;
14211 28 : case FKCONSTR_ACTION_SETDEFAULT:
14212 28 : fk_trigger->deferrable = false;
14213 28 : fk_trigger->initdeferred = false;
14214 28 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
14215 28 : break;
14216 0 : default:
14217 0 : elog(ERROR, "unrecognized FK action type: %d",
14218 : (int) fkconstraint->fk_upd_action);
14219 : break;
14220 : }
14221 :
14222 2334 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
14223 : constraintOid, indexOid, InvalidOid,
14224 : parentUpdTrigger, NULL, true, false);
14225 2334 : if (updateTrigOid)
14226 2334 : *updateTrigOid = trigAddress.objectId;
14227 2334 : }
14228 :
14229 : /*
14230 : * createForeignKeyCheckTriggers
14231 : * Create the referencing-side "check" triggers that implement a foreign
14232 : * key.
14233 : *
14234 : * Returns the OIDs of the so created triggers in *insertTrigOid and
14235 : * *updateTrigOid.
14236 : */
14237 : static void
14238 2047 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
14239 : Constraint *fkconstraint, Oid constraintOid,
14240 : Oid indexOid,
14241 : Oid parentInsTrigger, Oid parentUpdTrigger,
14242 : Oid *insertTrigOid, Oid *updateTrigOid)
14243 : {
14244 2047 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14245 : constraintOid, indexOid,
14246 : parentInsTrigger, true);
14247 2047 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14248 : constraintOid, indexOid,
14249 : parentUpdTrigger, false);
14250 2047 : }
14251 :
14252 : /*
14253 : * ALTER TABLE DROP CONSTRAINT
14254 : *
14255 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
14256 : */
14257 : static void
14258 573 : ATExecDropConstraint(Relation rel, const char *constrName,
14259 : DropBehavior behavior, bool recurse,
14260 : bool missing_ok, LOCKMODE lockmode)
14261 : {
14262 : Relation conrel;
14263 : SysScanDesc scan;
14264 : ScanKeyData skey[3];
14265 : HeapTuple tuple;
14266 573 : bool found = false;
14267 :
14268 573 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14269 :
14270 : /*
14271 : * Find and drop the target constraint
14272 : */
14273 573 : ScanKeyInit(&skey[0],
14274 : Anum_pg_constraint_conrelid,
14275 : BTEqualStrategyNumber, F_OIDEQ,
14276 : ObjectIdGetDatum(RelationGetRelid(rel)));
14277 573 : ScanKeyInit(&skey[1],
14278 : Anum_pg_constraint_contypid,
14279 : BTEqualStrategyNumber, F_OIDEQ,
14280 : ObjectIdGetDatum(InvalidOid));
14281 573 : ScanKeyInit(&skey[2],
14282 : Anum_pg_constraint_conname,
14283 : BTEqualStrategyNumber, F_NAMEEQ,
14284 : CStringGetDatum(constrName));
14285 573 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14286 : true, NULL, 3, skey);
14287 :
14288 : /* There can be at most one matching row */
14289 573 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14290 : {
14291 549 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
14292 : missing_ok, lockmode);
14293 425 : found = true;
14294 : }
14295 :
14296 449 : systable_endscan(scan);
14297 :
14298 449 : if (!found)
14299 : {
14300 24 : if (!missing_ok)
14301 16 : ereport(ERROR,
14302 : errcode(ERRCODE_UNDEFINED_OBJECT),
14303 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14304 : constrName, RelationGetRelationName(rel)));
14305 : else
14306 8 : ereport(NOTICE,
14307 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14308 : constrName, RelationGetRelationName(rel)));
14309 : }
14310 :
14311 433 : table_close(conrel, RowExclusiveLock);
14312 433 : }
14313 :
14314 : /*
14315 : * Remove a constraint, using its pg_constraint tuple
14316 : *
14317 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14318 : * DROP NOT NULL.
14319 : *
14320 : * Returns the address of the constraint being removed.
14321 : */
14322 : static ObjectAddress
14323 853 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
14324 : bool recurse, bool recursing, bool missing_ok,
14325 : LOCKMODE lockmode)
14326 : {
14327 : Relation conrel;
14328 : Form_pg_constraint con;
14329 : ObjectAddress conobj;
14330 : List *children;
14331 853 : bool is_no_inherit_constraint = false;
14332 : char *constrName;
14333 853 : char *colname = NULL;
14334 :
14335 : /* Guard against stack overflow due to overly deep inheritance tree. */
14336 853 : check_stack_depth();
14337 :
14338 : /* At top level, permission check was done in ATPrepCmd, else do it */
14339 853 : if (recursing)
14340 159 : ATSimplePermissions(AT_DropConstraint, rel,
14341 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
14342 :
14343 849 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14344 :
14345 849 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14346 849 : constrName = NameStr(con->conname);
14347 :
14348 : /* Don't allow drop of inherited constraints */
14349 849 : if (con->coninhcount > 0 && !recursing)
14350 104 : ereport(ERROR,
14351 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14352 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14353 : constrName, RelationGetRelationName(rel))));
14354 :
14355 : /*
14356 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14357 : *
14358 : * While doing that, we're in a good position to disallow dropping a not-
14359 : * null constraint underneath a primary key, a replica identity index, or
14360 : * a generated identity column.
14361 : */
14362 745 : if (con->contype == CONSTRAINT_NOTNULL)
14363 : {
14364 207 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14365 207 : AttrNumber attnum = extractNotNullColumn(constraintTup);
14366 : Bitmapset *pkattrs;
14367 : Bitmapset *irattrs;
14368 : HeapTuple atttup;
14369 : Form_pg_attribute attForm;
14370 :
14371 : /* save column name for recursion step */
14372 207 : colname = get_attname(RelationGetRelid(rel), attnum, false);
14373 :
14374 : /*
14375 : * Disallow if it's in the primary key. For partitioned tables we
14376 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14377 : * return NULL if the primary key is invalid; but we still need to
14378 : * protect not-null constraints under such a constraint, so check the
14379 : * slow way.
14380 : */
14381 207 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
14382 :
14383 207 : if (pkattrs == NULL &&
14384 187 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14385 : {
14386 12 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14387 :
14388 12 : if (OidIsValid(pkindex))
14389 : {
14390 0 : Relation pk = relation_open(pkindex, AccessShareLock);
14391 :
14392 0 : pkattrs = NULL;
14393 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14394 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14395 :
14396 0 : relation_close(pk, AccessShareLock);
14397 : }
14398 : }
14399 :
14400 227 : if (pkattrs &&
14401 20 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
14402 16 : ereport(ERROR,
14403 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14404 : errmsg("column \"%s\" is in a primary key",
14405 : get_attname(RelationGetRelid(rel), attnum, false)));
14406 :
14407 : /* Disallow if it's in the replica identity */
14408 191 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
14409 191 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
14410 8 : ereport(ERROR,
14411 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14412 : errmsg("column \"%s\" is in index used as replica identity",
14413 : get_attname(RelationGetRelid(rel), attnum, false)));
14414 :
14415 : /* Disallow if it's a GENERATED AS IDENTITY column */
14416 183 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
14417 183 : if (!HeapTupleIsValid(atttup))
14418 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14419 : attnum, RelationGetRelid(rel));
14420 183 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14421 183 : if (attForm->attidentity != '\0')
14422 0 : ereport(ERROR,
14423 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14424 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
14425 : get_attname(RelationGetRelid(rel), attnum,
14426 : false),
14427 : RelationGetRelationName(rel)));
14428 :
14429 : /* All good -- reset attnotnull if needed */
14430 183 : if (attForm->attnotnull)
14431 : {
14432 183 : attForm->attnotnull = false;
14433 183 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14434 : }
14435 :
14436 183 : table_close(attrel, RowExclusiveLock);
14437 : }
14438 :
14439 721 : is_no_inherit_constraint = con->connoinherit;
14440 :
14441 : /*
14442 : * If it's a foreign-key constraint, we'd better lock the referenced table
14443 : * and check that that's not in use, just as we've already done for the
14444 : * constrained table (else we might, eg, be dropping a trigger that has
14445 : * unfired events). But we can/must skip that in the self-referential
14446 : * case.
14447 : */
14448 721 : if (con->contype == CONSTRAINT_FOREIGN &&
14449 112 : con->confrelid != RelationGetRelid(rel))
14450 : {
14451 : Relation frel;
14452 :
14453 : /* Must match lock taken by RemoveTriggerById: */
14454 112 : frel = table_open(con->confrelid, AccessExclusiveLock);
14455 112 : CheckAlterTableIsSafe(frel);
14456 108 : table_close(frel, NoLock);
14457 : }
14458 :
14459 : /*
14460 : * Perform the actual constraint deletion
14461 : */
14462 717 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14463 717 : performDeletion(&conobj, behavior, 0);
14464 :
14465 : /*
14466 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14467 : * are dropped via the dependency mechanism, so we're done here.
14468 : */
14469 693 : if (con->contype != CONSTRAINT_CHECK &&
14470 416 : con->contype != CONSTRAINT_NOTNULL &&
14471 233 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14472 : {
14473 52 : table_close(conrel, RowExclusiveLock);
14474 52 : return conobj;
14475 : }
14476 :
14477 : /*
14478 : * Propagate to children as appropriate. Unlike most other ALTER
14479 : * routines, we have to do this one level of recursion at a time; we can't
14480 : * use find_all_inheritors to do it in one pass.
14481 : */
14482 641 : if (!is_no_inherit_constraint)
14483 452 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14484 : else
14485 189 : children = NIL;
14486 :
14487 1557 : foreach_oid(childrelid, children)
14488 : {
14489 : Relation childrel;
14490 : HeapTuple tuple;
14491 : Form_pg_constraint childcon;
14492 :
14493 : /* find_inheritance_children already got lock */
14494 283 : childrel = table_open(childrelid, NoLock);
14495 283 : CheckAlterTableIsSafe(childrel);
14496 :
14497 : /*
14498 : * We search for not-null constraints by column name, and others by
14499 : * constraint name.
14500 : */
14501 283 : if (con->contype == CONSTRAINT_NOTNULL)
14502 : {
14503 98 : tuple = findNotNullConstraint(childrelid, colname);
14504 98 : if (!HeapTupleIsValid(tuple))
14505 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14506 : colname, RelationGetRelid(childrel));
14507 : }
14508 : else
14509 : {
14510 : SysScanDesc scan;
14511 : ScanKeyData skey[3];
14512 :
14513 185 : ScanKeyInit(&skey[0],
14514 : Anum_pg_constraint_conrelid,
14515 : BTEqualStrategyNumber, F_OIDEQ,
14516 : ObjectIdGetDatum(childrelid));
14517 185 : ScanKeyInit(&skey[1],
14518 : Anum_pg_constraint_contypid,
14519 : BTEqualStrategyNumber, F_OIDEQ,
14520 : ObjectIdGetDatum(InvalidOid));
14521 185 : ScanKeyInit(&skey[2],
14522 : Anum_pg_constraint_conname,
14523 : BTEqualStrategyNumber, F_NAMEEQ,
14524 : CStringGetDatum(constrName));
14525 185 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14526 : true, NULL, 3, skey);
14527 : /* There can only be one, so no need to loop */
14528 185 : tuple = systable_getnext(scan);
14529 185 : if (!HeapTupleIsValid(tuple))
14530 0 : ereport(ERROR,
14531 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14532 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14533 : constrName,
14534 : RelationGetRelationName(childrel))));
14535 185 : tuple = heap_copytuple(tuple);
14536 185 : systable_endscan(scan);
14537 : }
14538 :
14539 283 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14540 :
14541 : /* Right now only CHECK and not-null constraints can be inherited */
14542 283 : if (childcon->contype != CONSTRAINT_CHECK &&
14543 98 : childcon->contype != CONSTRAINT_NOTNULL)
14544 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14545 :
14546 283 : if (childcon->coninhcount <= 0) /* shouldn't happen */
14547 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14548 : childrelid, NameStr(childcon->conname));
14549 :
14550 283 : if (recurse)
14551 : {
14552 : /*
14553 : * If the child constraint has other definition sources, just
14554 : * decrement its inheritance count; if not, recurse to delete it.
14555 : */
14556 215 : if (childcon->coninhcount == 1 && !childcon->conislocal)
14557 : {
14558 : /* Time to delete this child constraint, too */
14559 159 : dropconstraint_internal(childrel, tuple, behavior,
14560 : recurse, true, missing_ok,
14561 : lockmode);
14562 : }
14563 : else
14564 : {
14565 : /* Child constraint must survive my deletion */
14566 56 : childcon->coninhcount--;
14567 56 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14568 :
14569 : /* Make update visible */
14570 56 : CommandCounterIncrement();
14571 : }
14572 : }
14573 : else
14574 : {
14575 : /*
14576 : * If we were told to drop ONLY in this table (no recursion) and
14577 : * there are no further parents for this constraint, we need to
14578 : * mark the inheritors' constraints as locally defined rather than
14579 : * inherited.
14580 : */
14581 68 : childcon->coninhcount--;
14582 68 : if (childcon->coninhcount == 0)
14583 68 : childcon->conislocal = true;
14584 :
14585 68 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14586 :
14587 : /* Make update visible */
14588 68 : CommandCounterIncrement();
14589 : }
14590 :
14591 279 : heap_freetuple(tuple);
14592 :
14593 279 : table_close(childrel, NoLock);
14594 : }
14595 :
14596 637 : table_close(conrel, RowExclusiveLock);
14597 :
14598 637 : return conobj;
14599 : }
14600 :
14601 : /*
14602 : * ALTER COLUMN TYPE
14603 : *
14604 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14605 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14606 : * transformed (and must be, because we rely on some transformed fields).
14607 : *
14608 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14609 : * table will be done "in parallel" during phase 3, so all the USING
14610 : * expressions should be parsed assuming the original column types. Also,
14611 : * this allows a USING expression to refer to a field that will be dropped.
14612 : *
14613 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14614 : * the first two execution steps in phase 2; they must not see the effects
14615 : * of any other subcommand types, since the USING expressions are parsed
14616 : * against the unmodified table's state.
14617 : */
14618 : static void
14619 947 : ATPrepAlterColumnType(List **wqueue,
14620 : AlteredTableInfo *tab, Relation rel,
14621 : bool recurse, bool recursing,
14622 : AlterTableCmd *cmd, LOCKMODE lockmode,
14623 : AlterTableUtilityContext *context)
14624 : {
14625 947 : char *colName = cmd->name;
14626 947 : ColumnDef *def = (ColumnDef *) cmd->def;
14627 947 : TypeName *typeName = def->typeName;
14628 947 : Node *transform = def->cooked_default;
14629 : HeapTuple tuple;
14630 : Form_pg_attribute attTup;
14631 : AttrNumber attnum;
14632 : Oid targettype;
14633 : int32 targettypmod;
14634 : Oid targetcollid;
14635 : NewColumnValue *newval;
14636 947 : ParseState *pstate = make_parsestate(NULL);
14637 : AclResult aclresult;
14638 : bool is_expr;
14639 :
14640 947 : pstate->p_sourcetext = context->queryString;
14641 :
14642 947 : if (rel->rd_rel->reloftype && !recursing)
14643 4 : ereport(ERROR,
14644 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14645 : errmsg("cannot alter column type of typed table"),
14646 : parser_errposition(pstate, def->location)));
14647 :
14648 : /* lookup the attribute so we can check inheritance status */
14649 943 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14650 943 : if (!HeapTupleIsValid(tuple))
14651 0 : ereport(ERROR,
14652 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14653 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14654 : colName, RelationGetRelationName(rel)),
14655 : parser_errposition(pstate, def->location)));
14656 943 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14657 943 : attnum = attTup->attnum;
14658 :
14659 : /* Can't alter a system attribute */
14660 943 : if (attnum <= 0)
14661 4 : ereport(ERROR,
14662 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14663 : errmsg("cannot alter system column \"%s\"", colName),
14664 : parser_errposition(pstate, def->location)));
14665 :
14666 : /*
14667 : * Cannot specify USING when altering type of a generated column, because
14668 : * that would violate the generation expression.
14669 : */
14670 939 : if (attTup->attgenerated && def->cooked_default)
14671 8 : ereport(ERROR,
14672 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14673 : errmsg("cannot specify USING when altering type of generated column"),
14674 : errdetail("Column \"%s\" is a generated column.", colName),
14675 : parser_errposition(pstate, def->location)));
14676 :
14677 : /*
14678 : * Don't alter inherited columns. At outer level, there had better not be
14679 : * any inherited definition; when recursing, we assume this was checked at
14680 : * the parent level (see below).
14681 : */
14682 931 : if (attTup->attinhcount > 0 && !recursing)
14683 4 : ereport(ERROR,
14684 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14685 : errmsg("cannot alter inherited column \"%s\"", colName),
14686 : parser_errposition(pstate, def->location)));
14687 :
14688 : /* Don't alter columns used in the partition key */
14689 927 : if (has_partition_attrs(rel,
14690 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
14691 : &is_expr))
14692 12 : ereport(ERROR,
14693 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14694 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14695 : colName, RelationGetRelationName(rel)),
14696 : parser_errposition(pstate, def->location)));
14697 :
14698 : /* Look up the target type */
14699 915 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14700 :
14701 911 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14702 911 : if (aclresult != ACLCHECK_OK)
14703 8 : aclcheck_error_type(aclresult, targettype);
14704 :
14705 : /* And the collation */
14706 903 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
14707 :
14708 : /* make sure datatype is legal for a column */
14709 1798 : CheckAttributeType(colName, targettype, targetcollid,
14710 899 : list_make1_oid(rel->rd_rel->reltype),
14711 899 : (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14712 :
14713 891 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14714 : {
14715 : /* do nothing */
14716 : }
14717 867 : else if (tab->relkind == RELKIND_RELATION ||
14718 133 : tab->relkind == RELKIND_PARTITIONED_TABLE)
14719 : {
14720 : /*
14721 : * Set up an expression to transform the old data value to the new
14722 : * type. If a USING option was given, use the expression as
14723 : * transformed by transformAlterTableStmt, else just take the old
14724 : * value and try to coerce it. We do this first so that type
14725 : * incompatibility can be detected before we waste effort, and because
14726 : * we need the expression to be parsed against the original table row
14727 : * type.
14728 : */
14729 778 : if (!transform)
14730 : {
14731 627 : transform = (Node *) makeVar(1, attnum,
14732 : attTup->atttypid, attTup->atttypmod,
14733 : attTup->attcollation,
14734 : 0);
14735 : }
14736 :
14737 778 : transform = coerce_to_target_type(pstate,
14738 : transform, exprType(transform),
14739 : targettype, targettypmod,
14740 : COERCION_ASSIGNMENT,
14741 : COERCE_IMPLICIT_CAST,
14742 : -1);
14743 778 : if (transform == NULL)
14744 : {
14745 : /* error text depends on whether USING was specified or not */
14746 15 : if (def->cooked_default != NULL)
14747 4 : ereport(ERROR,
14748 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14749 : errmsg("result of USING clause for column \"%s\""
14750 : " cannot be cast automatically to type %s",
14751 : colName, format_type_be(targettype)),
14752 : errhint("You might need to add an explicit cast.")));
14753 : else
14754 11 : ereport(ERROR,
14755 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14756 : errmsg("column \"%s\" cannot be cast automatically to type %s",
14757 : colName, format_type_be(targettype)),
14758 : !attTup->attgenerated ?
14759 : /* translator: USING is SQL, don't translate it */
14760 : errhint("You might need to specify \"USING %s::%s\".",
14761 : quote_identifier(colName),
14762 : format_type_with_typemod(targettype,
14763 : targettypmod)) : 0));
14764 : }
14765 :
14766 : /* Fix collations after all else */
14767 763 : assign_expr_collations(pstate, transform);
14768 :
14769 : /* Expand virtual generated columns in the expr. */
14770 763 : transform = expand_generated_columns_in_expr(transform, rel, 1);
14771 :
14772 : /* Plan the expr now so we can accurately assess the need to rewrite. */
14773 763 : transform = (Node *) expression_planner((Expr *) transform);
14774 :
14775 : /*
14776 : * Add a work queue item to make ATRewriteTable update the column
14777 : * contents.
14778 : */
14779 763 : newval = palloc0_object(NewColumnValue);
14780 763 : newval->attnum = attnum;
14781 763 : newval->expr = (Expr *) transform;
14782 763 : newval->is_generated = false;
14783 :
14784 763 : tab->newvals = lappend(tab->newvals, newval);
14785 1394 : if (ATColumnChangeRequiresRewrite(transform, attnum))
14786 631 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
14787 : }
14788 89 : else if (transform)
14789 8 : ereport(ERROR,
14790 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14791 : errmsg("\"%s\" is not a table",
14792 : RelationGetRelationName(rel))));
14793 :
14794 868 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14795 : {
14796 : /*
14797 : * For relations or columns without storage, do this check now.
14798 : * Regular tables will check it later when the table is being
14799 : * rewritten.
14800 : */
14801 149 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14802 : }
14803 :
14804 836 : ReleaseSysCache(tuple);
14805 :
14806 : /*
14807 : * Recurse manually by queueing a new command for each child, if
14808 : * necessary. We cannot apply ATSimpleRecursion here because we need to
14809 : * remap attribute numbers in the USING expression, if any.
14810 : *
14811 : * If we are told not to recurse, there had better not be any child
14812 : * tables; else the alter would put them out of step.
14813 : */
14814 836 : if (recurse)
14815 : {
14816 665 : Oid relid = RelationGetRelid(rel);
14817 : List *child_oids,
14818 : *child_numparents;
14819 : ListCell *lo,
14820 : *li;
14821 :
14822 665 : child_oids = find_all_inheritors(relid, lockmode,
14823 : &child_numparents);
14824 :
14825 : /*
14826 : * find_all_inheritors does the recursive search of the inheritance
14827 : * hierarchy, so all we have to do is process all of the relids in the
14828 : * list that it returns.
14829 : */
14830 1468 : forboth(lo, child_oids, li, child_numparents)
14831 : {
14832 819 : Oid childrelid = lfirst_oid(lo);
14833 819 : int numparents = lfirst_int(li);
14834 : Relation childrel;
14835 : HeapTuple childtuple;
14836 : Form_pg_attribute childattTup;
14837 :
14838 819 : if (childrelid == relid)
14839 665 : continue;
14840 :
14841 : /* find_all_inheritors already got lock */
14842 154 : childrel = relation_open(childrelid, NoLock);
14843 154 : CheckAlterTableIsSafe(childrel);
14844 :
14845 : /*
14846 : * Verify that the child doesn't have any inherited definitions of
14847 : * this column that came from outside this inheritance hierarchy.
14848 : * (renameatt makes a similar test, though in a different way
14849 : * because of its different recursion mechanism.)
14850 : */
14851 154 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14852 : colName);
14853 154 : if (!HeapTupleIsValid(childtuple))
14854 0 : ereport(ERROR,
14855 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14856 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14857 : colName, RelationGetRelationName(childrel))));
14858 154 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14859 :
14860 154 : if (childattTup->attinhcount > numparents)
14861 4 : ereport(ERROR,
14862 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14863 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14864 : colName, RelationGetRelationName(childrel))));
14865 :
14866 150 : ReleaseSysCache(childtuple);
14867 :
14868 : /*
14869 : * Remap the attribute numbers. If no USING expression was
14870 : * specified, there is no need for this step.
14871 : */
14872 150 : if (def->cooked_default)
14873 : {
14874 : AttrMap *attmap;
14875 : bool found_whole_row;
14876 :
14877 : /* create a copy to scribble on */
14878 52 : cmd = copyObject(cmd);
14879 :
14880 52 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14881 : RelationGetDescr(rel),
14882 : false);
14883 104 : ((ColumnDef *) cmd->def)->cooked_default =
14884 52 : map_variable_attnos(def->cooked_default,
14885 : 1, 0,
14886 : attmap,
14887 : InvalidOid, &found_whole_row);
14888 52 : if (found_whole_row)
14889 4 : ereport(ERROR,
14890 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14891 : errmsg("cannot convert whole-row table reference"),
14892 : errdetail("USING expression contains a whole-row table reference.")));
14893 48 : pfree(attmap);
14894 : }
14895 146 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14896 138 : relation_close(childrel, NoLock);
14897 : }
14898 : }
14899 204 : else if (!recursing &&
14900 33 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
14901 0 : ereport(ERROR,
14902 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14903 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
14904 : colName)));
14905 :
14906 820 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14907 33 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14908 816 : }
14909 :
14910 : /*
14911 : * When the data type of a column is changed, a rewrite might not be required
14912 : * if the new type is sufficiently identical to the old one, and the USING
14913 : * clause isn't trying to insert some other value. It's safe to skip the
14914 : * rewrite in these cases:
14915 : *
14916 : * - the old type is binary coercible to the new type
14917 : * - the new type is an unconstrained domain over the old type
14918 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14919 : *
14920 : * In the case of a constrained domain, we could get by with scanning the
14921 : * table and checking the constraint rather than actually rewriting it, but we
14922 : * don't currently try to do that.
14923 : */
14924 : static bool
14925 763 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
14926 : {
14927 : Assert(expr != NULL);
14928 :
14929 : for (;;)
14930 : {
14931 : /* only one varno, so no need to check that */
14932 839 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14933 132 : return false;
14934 707 : else if (IsA(expr, RelabelType))
14935 68 : expr = (Node *) ((RelabelType *) expr)->arg;
14936 639 : else if (IsA(expr, CoerceToDomain))
14937 : {
14938 4 : CoerceToDomain *d = (CoerceToDomain *) expr;
14939 :
14940 4 : if (DomainHasConstraints(d->resulttype, NULL))
14941 4 : return true;
14942 0 : expr = (Node *) d->arg;
14943 : }
14944 635 : else if (IsA(expr, FuncExpr))
14945 : {
14946 502 : FuncExpr *f = (FuncExpr *) expr;
14947 :
14948 502 : switch (f->funcid)
14949 : {
14950 12 : case F_TIMESTAMPTZ_TIMESTAMP:
14951 : case F_TIMESTAMP_TIMESTAMPTZ:
14952 12 : if (TimestampTimestampTzRequiresRewrite())
14953 4 : return true;
14954 : else
14955 8 : expr = linitial(f->args);
14956 8 : break;
14957 490 : default:
14958 490 : return true;
14959 : }
14960 : }
14961 : else
14962 133 : return true;
14963 : }
14964 : }
14965 :
14966 : /*
14967 : * ALTER COLUMN .. SET DATA TYPE
14968 : *
14969 : * Return the address of the modified column.
14970 : */
14971 : static ObjectAddress
14972 792 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14973 : AlterTableCmd *cmd, LOCKMODE lockmode)
14974 : {
14975 792 : char *colName = cmd->name;
14976 792 : ColumnDef *def = (ColumnDef *) cmd->def;
14977 792 : TypeName *typeName = def->typeName;
14978 : HeapTuple heapTup;
14979 : Form_pg_attribute attTup,
14980 : attOldTup;
14981 : AttrNumber attnum;
14982 : HeapTuple typeTuple;
14983 : Form_pg_type tform;
14984 : Oid targettype;
14985 : int32 targettypmod;
14986 : Oid targetcollid;
14987 : Node *defaultexpr;
14988 : Relation attrelation;
14989 : Relation depRel;
14990 : ScanKeyData key[3];
14991 : SysScanDesc scan;
14992 : HeapTuple depTup;
14993 : ObjectAddress address;
14994 :
14995 : /*
14996 : * Clear all the missing values if we're rewriting the table, since this
14997 : * renders them pointless.
14998 : */
14999 792 : if (tab->rewrite)
15000 : {
15001 : Relation newrel;
15002 :
15003 591 : newrel = table_open(RelationGetRelid(rel), NoLock);
15004 591 : RelationClearMissing(newrel);
15005 591 : relation_close(newrel, NoLock);
15006 : /* make sure we don't conflict with later attribute modifications */
15007 591 : CommandCounterIncrement();
15008 : }
15009 :
15010 792 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
15011 :
15012 : /* Look up the target column */
15013 792 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
15014 792 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
15015 0 : ereport(ERROR,
15016 : (errcode(ERRCODE_UNDEFINED_COLUMN),
15017 : errmsg("column \"%s\" of relation \"%s\" does not exist",
15018 : colName, RelationGetRelationName(rel))));
15019 792 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
15020 792 : attnum = attTup->attnum;
15021 792 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
15022 :
15023 : /* Check for multiple ALTER TYPE on same column --- can't cope */
15024 792 : if (attTup->atttypid != attOldTup->atttypid ||
15025 792 : attTup->atttypmod != attOldTup->atttypmod)
15026 0 : ereport(ERROR,
15027 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15028 : errmsg("cannot alter type of column \"%s\" twice",
15029 : colName)));
15030 :
15031 : /* Look up the target type (should not fail, since prep found it) */
15032 792 : typeTuple = typenameType(NULL, typeName, &targettypmod);
15033 792 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
15034 792 : targettype = tform->oid;
15035 : /* And the collation */
15036 792 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
15037 :
15038 : /*
15039 : * If there is a default expression for the column, get it and ensure we
15040 : * can coerce it to the new datatype. (We must do this before changing
15041 : * the column type, because build_column_default itself will try to
15042 : * coerce, and will not issue the error message we want if it fails.)
15043 : *
15044 : * We remove any implicit coercion steps at the top level of the old
15045 : * default expression; this has been agreed to satisfy the principle of
15046 : * least surprise. (The conversion to the new column type should act like
15047 : * it started from what the user sees as the stored expression, and the
15048 : * implicit coercions aren't going to be shown.)
15049 : */
15050 792 : if (attTup->atthasdef)
15051 : {
15052 64 : defaultexpr = build_column_default(rel, attnum);
15053 : Assert(defaultexpr);
15054 64 : defaultexpr = strip_implicit_coercions(defaultexpr);
15055 64 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
15056 : defaultexpr, exprType(defaultexpr),
15057 : targettype, targettypmod,
15058 : COERCION_ASSIGNMENT,
15059 : COERCE_IMPLICIT_CAST,
15060 : -1);
15061 64 : if (defaultexpr == NULL)
15062 : {
15063 4 : if (attTup->attgenerated)
15064 0 : ereport(ERROR,
15065 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15066 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
15067 : colName, format_type_be(targettype))));
15068 : else
15069 4 : ereport(ERROR,
15070 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15071 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
15072 : colName, format_type_be(targettype))));
15073 : }
15074 : }
15075 : else
15076 728 : defaultexpr = NULL;
15077 :
15078 : /*
15079 : * Find everything that depends on the column (constraints, indexes, etc),
15080 : * and record enough information to let us recreate the objects.
15081 : *
15082 : * The actual recreation does not happen here, but only after we have
15083 : * performed all the individual ALTER TYPE operations. We have to save
15084 : * the info before executing ALTER TYPE, though, else the deparser will
15085 : * get confused.
15086 : */
15087 788 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
15088 :
15089 : /*
15090 : * Now scan for dependencies of this column on other things. The only
15091 : * things we should find are the dependency on the column datatype and
15092 : * possibly a collation dependency. Those can be removed.
15093 : */
15094 764 : depRel = table_open(DependRelationId, RowExclusiveLock);
15095 :
15096 764 : ScanKeyInit(&key[0],
15097 : Anum_pg_depend_classid,
15098 : BTEqualStrategyNumber, F_OIDEQ,
15099 : ObjectIdGetDatum(RelationRelationId));
15100 764 : ScanKeyInit(&key[1],
15101 : Anum_pg_depend_objid,
15102 : BTEqualStrategyNumber, F_OIDEQ,
15103 : ObjectIdGetDatum(RelationGetRelid(rel)));
15104 764 : ScanKeyInit(&key[2],
15105 : Anum_pg_depend_objsubid,
15106 : BTEqualStrategyNumber, F_INT4EQ,
15107 : Int32GetDatum((int32) attnum));
15108 :
15109 764 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
15110 : NULL, 3, key);
15111 :
15112 766 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15113 : {
15114 2 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15115 : ObjectAddress foundObject;
15116 :
15117 2 : foundObject.classId = foundDep->refclassid;
15118 2 : foundObject.objectId = foundDep->refobjid;
15119 2 : foundObject.objectSubId = foundDep->refobjsubid;
15120 :
15121 2 : if (foundDep->deptype != DEPENDENCY_NORMAL)
15122 0 : elog(ERROR, "found unexpected dependency type '%c'",
15123 : foundDep->deptype);
15124 2 : if (!(foundDep->refclassid == TypeRelationId &&
15125 2 : foundDep->refobjid == attTup->atttypid) &&
15126 0 : !(foundDep->refclassid == CollationRelationId &&
15127 0 : foundDep->refobjid == attTup->attcollation))
15128 0 : elog(ERROR, "found unexpected dependency for column: %s",
15129 : getObjectDescription(&foundObject, false));
15130 :
15131 2 : CatalogTupleDelete(depRel, &depTup->t_self);
15132 : }
15133 :
15134 764 : systable_endscan(scan);
15135 :
15136 764 : table_close(depRel, RowExclusiveLock);
15137 :
15138 : /*
15139 : * Here we go --- change the recorded column type and collation. (Note
15140 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
15141 : * fix up the missing value if any.
15142 : */
15143 764 : if (attTup->atthasmissing)
15144 : {
15145 : Datum missingval;
15146 : bool missingNull;
15147 :
15148 : /* if rewrite is true the missing value should already be cleared */
15149 : Assert(tab->rewrite == 0);
15150 :
15151 : /* Get the missing value datum */
15152 4 : missingval = heap_getattr(heapTup,
15153 : Anum_pg_attribute_attmissingval,
15154 : attrelation->rd_att,
15155 : &missingNull);
15156 :
15157 : /* if it's a null array there is nothing to do */
15158 :
15159 4 : if (!missingNull)
15160 : {
15161 : /*
15162 : * Get the datum out of the array and repack it in a new array
15163 : * built with the new type data. We assume that since the table
15164 : * doesn't need rewriting, the actual Datum doesn't need to be
15165 : * changed, only the array metadata.
15166 : */
15167 :
15168 4 : int one = 1;
15169 : bool isNull;
15170 4 : Datum valuesAtt[Natts_pg_attribute] = {0};
15171 4 : bool nullsAtt[Natts_pg_attribute] = {0};
15172 4 : bool replacesAtt[Natts_pg_attribute] = {0};
15173 : HeapTuple newTup;
15174 :
15175 8 : missingval = array_get_element(missingval,
15176 : 1,
15177 : &one,
15178 : 0,
15179 4 : attTup->attlen,
15180 4 : attTup->attbyval,
15181 4 : attTup->attalign,
15182 : &isNull);
15183 4 : missingval = PointerGetDatum(construct_array(&missingval,
15184 : 1,
15185 : targettype,
15186 4 : tform->typlen,
15187 4 : tform->typbyval,
15188 4 : tform->typalign));
15189 :
15190 4 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
15191 4 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
15192 4 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
15193 :
15194 4 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
15195 : valuesAtt, nullsAtt, replacesAtt);
15196 4 : heap_freetuple(heapTup);
15197 4 : heapTup = newTup;
15198 4 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
15199 : }
15200 : }
15201 :
15202 764 : attTup->atttypid = targettype;
15203 764 : attTup->atttypmod = targettypmod;
15204 764 : attTup->attcollation = targetcollid;
15205 764 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
15206 0 : ereport(ERROR,
15207 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15208 : errmsg("too many array dimensions"));
15209 764 : attTup->attndims = list_length(typeName->arrayBounds);
15210 764 : attTup->attlen = tform->typlen;
15211 764 : attTup->attbyval = tform->typbyval;
15212 764 : attTup->attalign = tform->typalign;
15213 764 : attTup->attstorage = tform->typstorage;
15214 764 : attTup->attcompression = InvalidCompressionMethod;
15215 :
15216 764 : ReleaseSysCache(typeTuple);
15217 :
15218 764 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
15219 :
15220 764 : table_close(attrelation, RowExclusiveLock);
15221 :
15222 : /* Install dependencies on new datatype and collation */
15223 764 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
15224 764 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
15225 :
15226 : /*
15227 : * Drop any pg_statistic entry for the column, since it's now wrong type
15228 : */
15229 764 : RemoveStatistics(RelationGetRelid(rel), attnum);
15230 :
15231 764 : InvokeObjectPostAlterHook(RelationRelationId,
15232 : RelationGetRelid(rel), attnum);
15233 :
15234 : /*
15235 : * Update the default, if present, by brute force --- remove and re-add
15236 : * the default. Probably unsafe to take shortcuts, since the new version
15237 : * may well have additional dependencies. (It's okay to do this now,
15238 : * rather than after other ALTER TYPE commands, since the default won't
15239 : * depend on other column types.)
15240 : */
15241 764 : if (defaultexpr)
15242 : {
15243 : /*
15244 : * If it's a GENERATED default, drop its dependency records, in
15245 : * particular its INTERNAL dependency on the column, which would
15246 : * otherwise cause dependency.c to refuse to perform the deletion.
15247 : */
15248 60 : if (attTup->attgenerated)
15249 : {
15250 28 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
15251 :
15252 28 : if (!OidIsValid(attrdefoid))
15253 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15254 : RelationGetRelid(rel), attnum);
15255 28 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
15256 : }
15257 :
15258 : /*
15259 : * Make updates-so-far visible, particularly the new pg_attribute row
15260 : * which will be updated again.
15261 : */
15262 60 : CommandCounterIncrement();
15263 :
15264 : /*
15265 : * We use RESTRICT here for safety, but at present we do not expect
15266 : * anything to depend on the default.
15267 : */
15268 60 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
15269 : true);
15270 :
15271 60 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15272 : }
15273 :
15274 764 : ObjectAddressSubSet(address, RelationRelationId,
15275 : RelationGetRelid(rel), attnum);
15276 :
15277 : /* Cleanup */
15278 764 : heap_freetuple(heapTup);
15279 :
15280 764 : return address;
15281 : }
15282 :
15283 : /*
15284 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15285 : * that depends on the column (constraints, indexes, etc), and record enough
15286 : * information to let us recreate the objects.
15287 : */
15288 : static void
15289 945 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
15290 : Relation rel, AttrNumber attnum, const char *colName)
15291 : {
15292 : Relation depRel;
15293 : ScanKeyData key[3];
15294 : SysScanDesc scan;
15295 : HeapTuple depTup;
15296 :
15297 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15298 :
15299 945 : depRel = table_open(DependRelationId, RowExclusiveLock);
15300 :
15301 945 : ScanKeyInit(&key[0],
15302 : Anum_pg_depend_refclassid,
15303 : BTEqualStrategyNumber, F_OIDEQ,
15304 : ObjectIdGetDatum(RelationRelationId));
15305 945 : ScanKeyInit(&key[1],
15306 : Anum_pg_depend_refobjid,
15307 : BTEqualStrategyNumber, F_OIDEQ,
15308 : ObjectIdGetDatum(RelationGetRelid(rel)));
15309 945 : ScanKeyInit(&key[2],
15310 : Anum_pg_depend_refobjsubid,
15311 : BTEqualStrategyNumber, F_INT4EQ,
15312 : Int32GetDatum((int32) attnum));
15313 :
15314 945 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15315 : NULL, 3, key);
15316 :
15317 1923 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15318 : {
15319 1002 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15320 : ObjectAddress foundObject;
15321 :
15322 1002 : foundObject.classId = foundDep->classid;
15323 1002 : foundObject.objectId = foundDep->objid;
15324 1002 : foundObject.objectSubId = foundDep->objsubid;
15325 :
15326 1002 : switch (foundObject.classId)
15327 : {
15328 192 : case RelationRelationId:
15329 : {
15330 192 : char relKind = get_rel_relkind(foundObject.objectId);
15331 :
15332 192 : if (relKind == RELKIND_INDEX ||
15333 : relKind == RELKIND_PARTITIONED_INDEX)
15334 : {
15335 : Assert(foundObject.objectSubId == 0);
15336 167 : RememberIndexForRebuilding(foundObject.objectId, tab);
15337 : }
15338 25 : else if (relKind == RELKIND_SEQUENCE)
15339 : {
15340 : /*
15341 : * This must be a SERIAL column's sequence. We need
15342 : * not do anything to it.
15343 : */
15344 : Assert(foundObject.objectSubId == 0);
15345 : }
15346 : else
15347 : {
15348 : /* Not expecting any other direct dependencies... */
15349 0 : elog(ERROR, "unexpected object depending on column: %s",
15350 : getObjectDescription(&foundObject, false));
15351 : }
15352 192 : break;
15353 : }
15354 :
15355 516 : case ConstraintRelationId:
15356 : Assert(foundObject.objectSubId == 0);
15357 516 : RememberConstraintForRebuilding(foundObject.objectId, tab);
15358 516 : break;
15359 :
15360 0 : case ProcedureRelationId:
15361 :
15362 : /*
15363 : * A new-style SQL function can depend on a column, if that
15364 : * column is referenced in the parsed function body. Ideally
15365 : * we'd automatically update the function by deparsing and
15366 : * reparsing it, but that's risky and might well fail anyhow.
15367 : * FIXME someday.
15368 : *
15369 : * This is only a problem for AT_AlterColumnType, not
15370 : * AT_SetExpression.
15371 : */
15372 0 : if (subtype == AT_AlterColumnType)
15373 0 : ereport(ERROR,
15374 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15375 : errmsg("cannot alter type of a column used by a function or procedure"),
15376 : errdetail("%s depends on column \"%s\"",
15377 : getObjectDescription(&foundObject, false),
15378 : colName)));
15379 0 : break;
15380 :
15381 8 : case RewriteRelationId:
15382 :
15383 : /*
15384 : * View/rule bodies have pretty much the same issues as
15385 : * function bodies. FIXME someday.
15386 : */
15387 8 : if (subtype == AT_AlterColumnType)
15388 8 : ereport(ERROR,
15389 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15390 : errmsg("cannot alter type of a column used by a view or rule"),
15391 : errdetail("%s depends on column \"%s\"",
15392 : getObjectDescription(&foundObject, false),
15393 : colName)));
15394 0 : break;
15395 :
15396 0 : case TriggerRelationId:
15397 :
15398 : /*
15399 : * A trigger can depend on a column because the column is
15400 : * specified as an update target, or because the column is
15401 : * used in the trigger's WHEN condition. The first case would
15402 : * not require any extra work, but the second case would
15403 : * require updating the WHEN expression, which has the same
15404 : * issues as above. Since we can't easily tell which case
15405 : * applies, we punt for both. FIXME someday.
15406 : */
15407 0 : if (subtype == AT_AlterColumnType)
15408 0 : ereport(ERROR,
15409 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15410 : errmsg("cannot alter type of a column used in a trigger definition"),
15411 : errdetail("%s depends on column \"%s\"",
15412 : getObjectDescription(&foundObject, false),
15413 : colName)));
15414 0 : break;
15415 :
15416 0 : case PolicyRelationId:
15417 :
15418 : /*
15419 : * A policy can depend on a column because the column is
15420 : * specified in the policy's USING or WITH CHECK qual
15421 : * expressions. It might be possible to rewrite and recheck
15422 : * the policy expression, but punt for now. It's certainly
15423 : * easy enough to remove and recreate the policy; still, FIXME
15424 : * someday.
15425 : */
15426 0 : if (subtype == AT_AlterColumnType)
15427 0 : ereport(ERROR,
15428 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15429 : errmsg("cannot alter type of a column used in a policy definition"),
15430 : errdetail("%s depends on column \"%s\"",
15431 : getObjectDescription(&foundObject, false),
15432 : colName)));
15433 0 : break;
15434 :
15435 233 : case AttrDefaultRelationId:
15436 : {
15437 233 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
15438 :
15439 450 : if (col.objectId == RelationGetRelid(rel) &&
15440 233 : col.objectSubId == attnum)
15441 : {
15442 : /*
15443 : * Ignore the column's own default expression. The
15444 : * caller deals with it.
15445 : */
15446 : }
15447 : else
15448 : {
15449 : /*
15450 : * This must be a reference from the expression of a
15451 : * generated column elsewhere in the same table.
15452 : * Changing the type/generated expression of a column
15453 : * that is used by a generated column is not allowed
15454 : * by SQL standard, so just punt for now. It might be
15455 : * doable with some thinking and effort.
15456 : */
15457 16 : if (subtype == AT_AlterColumnType)
15458 16 : ereport(ERROR,
15459 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15460 : errmsg("cannot alter type of a column used by a generated column"),
15461 : errdetail("Column \"%s\" is used by generated column \"%s\".",
15462 : colName,
15463 : get_attname(col.objectId,
15464 : col.objectSubId,
15465 : false))));
15466 : }
15467 217 : break;
15468 : }
15469 :
15470 53 : case StatisticExtRelationId:
15471 :
15472 : /*
15473 : * Give the extended-stats machinery a chance to fix anything
15474 : * that this column type change would break.
15475 : */
15476 53 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
15477 53 : break;
15478 :
15479 0 : case PublicationRelRelationId:
15480 :
15481 : /*
15482 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15483 : * clause. Same issues as above. FIXME someday.
15484 : */
15485 0 : if (subtype == AT_AlterColumnType)
15486 0 : ereport(ERROR,
15487 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15488 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
15489 : errdetail("%s depends on column \"%s\"",
15490 : getObjectDescription(&foundObject, false),
15491 : colName)));
15492 0 : break;
15493 :
15494 0 : default:
15495 :
15496 : /*
15497 : * We don't expect any other sorts of objects to depend on a
15498 : * column.
15499 : */
15500 0 : elog(ERROR, "unexpected object depending on column: %s",
15501 : getObjectDescription(&foundObject, false));
15502 : break;
15503 : }
15504 : }
15505 :
15506 921 : systable_endscan(scan);
15507 921 : table_close(depRel, NoLock);
15508 921 : }
15509 :
15510 : /*
15511 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
15512 : * needs to be reset.
15513 : */
15514 : static void
15515 306 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
15516 : {
15517 306 : if (!get_index_isreplident(indoid))
15518 294 : return;
15519 :
15520 12 : if (tab->replicaIdentityIndex)
15521 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15522 :
15523 12 : tab->replicaIdentityIndex = get_rel_name(indoid);
15524 : }
15525 :
15526 : /*
15527 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
15528 : */
15529 : static void
15530 306 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
15531 : {
15532 306 : if (!get_index_isclustered(indoid))
15533 294 : return;
15534 :
15535 12 : if (tab->clusterOnIndex)
15536 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15537 :
15538 12 : tab->clusterOnIndex = get_rel_name(indoid);
15539 : }
15540 :
15541 : /*
15542 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15543 : * to be rebuilt (which we might already know).
15544 : */
15545 : static void
15546 524 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
15547 : {
15548 : /*
15549 : * This de-duplication check is critical for two independent reasons: we
15550 : * mustn't try to recreate the same constraint twice, and if a constraint
15551 : * depends on more than one column whose type is to be altered, we must
15552 : * capture its definition string before applying any of the column type
15553 : * changes. ruleutils.c will get confused if we ask again later.
15554 : */
15555 524 : if (!list_member_oid(tab->changedConstraintOids, conoid))
15556 : {
15557 : /* OK, capture the constraint's existing definition string */
15558 452 : char *defstring = pg_get_constraintdef_command(conoid);
15559 : Oid indoid;
15560 :
15561 : /*
15562 : * It is critical to create not-null constraints ahead of primary key
15563 : * indexes; otherwise, the not-null constraint would be created by the
15564 : * primary key, and the constraint name would be wrong.
15565 : */
15566 452 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15567 : {
15568 163 : tab->changedConstraintOids = lcons_oid(conoid,
15569 : tab->changedConstraintOids);
15570 163 : tab->changedConstraintDefs = lcons(defstring,
15571 : tab->changedConstraintDefs);
15572 : }
15573 : else
15574 : {
15575 :
15576 289 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
15577 : conoid);
15578 289 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
15579 : defstring);
15580 : }
15581 :
15582 : /*
15583 : * For the index of a constraint, if any, remember if it is used for
15584 : * the table's replica identity or if it is a clustered index, so that
15585 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15586 : * those properties.
15587 : */
15588 452 : indoid = get_constraint_index(conoid);
15589 452 : if (OidIsValid(indoid))
15590 : {
15591 152 : RememberReplicaIdentityForRebuilding(indoid, tab);
15592 152 : RememberClusterOnForRebuilding(indoid, tab);
15593 : }
15594 : }
15595 524 : }
15596 :
15597 : /*
15598 : * Subroutine for ATExecAlterColumnType: remember that an index needs
15599 : * to be rebuilt (which we might already know).
15600 : */
15601 : static void
15602 167 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
15603 : {
15604 : /*
15605 : * This de-duplication check is critical for two independent reasons: we
15606 : * mustn't try to recreate the same index twice, and if an index depends
15607 : * on more than one column whose type is to be altered, we must capture
15608 : * its definition string before applying any of the column type changes.
15609 : * ruleutils.c will get confused if we ask again later.
15610 : */
15611 167 : if (!list_member_oid(tab->changedIndexOids, indoid))
15612 : {
15613 : /*
15614 : * Before adding it as an index-to-rebuild, we'd better see if it
15615 : * belongs to a constraint, and if so rebuild the constraint instead.
15616 : * Typically this check fails, because constraint indexes normally
15617 : * have only dependencies on their constraint. But it's possible for
15618 : * such an index to also have direct dependencies on table columns,
15619 : * for example with a partial exclusion constraint.
15620 : */
15621 162 : Oid conoid = get_index_constraint(indoid);
15622 :
15623 162 : if (OidIsValid(conoid))
15624 : {
15625 8 : RememberConstraintForRebuilding(conoid, tab);
15626 : }
15627 : else
15628 : {
15629 : /* OK, capture the index's existing definition string */
15630 154 : char *defstring = pg_get_indexdef_string(indoid);
15631 :
15632 154 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
15633 : indoid);
15634 154 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
15635 : defstring);
15636 :
15637 : /*
15638 : * Remember if this index is used for the table's replica identity
15639 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15640 : * can queue up commands necessary to restore those properties.
15641 : */
15642 154 : RememberReplicaIdentityForRebuilding(indoid, tab);
15643 154 : RememberClusterOnForRebuilding(indoid, tab);
15644 : }
15645 : }
15646 167 : }
15647 :
15648 : /*
15649 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
15650 : * needs to be rebuilt (which we might already know).
15651 : */
15652 : static void
15653 53 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
15654 : {
15655 : /*
15656 : * This de-duplication check is critical for two independent reasons: we
15657 : * mustn't try to recreate the same statistics object twice, and if the
15658 : * statistics object depends on more than one column whose type is to be
15659 : * altered, we must capture its definition string before applying any of
15660 : * the type changes. ruleutils.c will get confused if we ask again later.
15661 : */
15662 53 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15663 : {
15664 : /* OK, capture the statistics object's existing definition string */
15665 53 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
15666 :
15667 53 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
15668 : stxoid);
15669 53 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
15670 : defstring);
15671 : }
15672 53 : }
15673 :
15674 : /*
15675 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15676 : * operations for a particular relation. We have to drop and recreate all the
15677 : * indexes and constraints that depend on the altered columns. We do the
15678 : * actual dropping here, but re-creation is managed by adding work queue
15679 : * entries to do those steps later.
15680 : */
15681 : static void
15682 889 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
15683 : {
15684 : ObjectAddress obj;
15685 : ObjectAddresses *objects;
15686 : ListCell *def_item;
15687 : ListCell *oid_item;
15688 :
15689 : /*
15690 : * Collect all the constraints and indexes to drop so we can process them
15691 : * in a single call. That way we don't have to worry about dependencies
15692 : * among them.
15693 : */
15694 889 : objects = new_object_addresses();
15695 :
15696 : /*
15697 : * Re-parse the index and constraint definitions, and attach them to the
15698 : * appropriate work queue entries. We do this before dropping because in
15699 : * the case of a constraint on another table, we might not yet have
15700 : * exclusive lock on the table the constraint is attached to, and we need
15701 : * to get that before reparsing/dropping. (That's possible at least for
15702 : * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15703 : * requires a dependency on the target table's composite type in the other
15704 : * table's constraint expressions.)
15705 : *
15706 : * We can't rely on the output of deparsing to tell us which relation to
15707 : * operate on, because concurrent activity might have made the name
15708 : * resolve differently. Instead, we've got to use the OID of the
15709 : * constraint or index we're processing to figure out which relation to
15710 : * operate on.
15711 : */
15712 1341 : forboth(oid_item, tab->changedConstraintOids,
15713 : def_item, tab->changedConstraintDefs)
15714 : {
15715 452 : Oid oldId = lfirst_oid(oid_item);
15716 : HeapTuple tup;
15717 : Form_pg_constraint con;
15718 : Oid relid;
15719 : Oid confrelid;
15720 : bool conislocal;
15721 :
15722 452 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15723 452 : if (!HeapTupleIsValid(tup)) /* should not happen */
15724 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15725 452 : con = (Form_pg_constraint) GETSTRUCT(tup);
15726 452 : if (OidIsValid(con->conrelid))
15727 443 : relid = con->conrelid;
15728 : else
15729 : {
15730 : /* must be a domain constraint */
15731 9 : relid = get_typ_typrelid(getBaseType(con->contypid));
15732 9 : if (!OidIsValid(relid))
15733 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15734 : }
15735 452 : confrelid = con->confrelid;
15736 452 : conislocal = con->conislocal;
15737 452 : ReleaseSysCache(tup);
15738 :
15739 452 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
15740 452 : add_exact_object_address(&obj, objects);
15741 :
15742 : /*
15743 : * If the constraint is inherited (only), we don't want to inject a
15744 : * new definition here; it'll get recreated when
15745 : * ATAddCheckNNConstraint recurses from adding the parent table's
15746 : * constraint. But we had to carry the info this far so that we can
15747 : * drop the constraint below.
15748 : */
15749 452 : if (!conislocal)
15750 34 : continue;
15751 :
15752 : /*
15753 : * When rebuilding another table's constraint that references the
15754 : * table we're modifying, we might not yet have any lock on the other
15755 : * table, so get one now. We'll need AccessExclusiveLock for the DROP
15756 : * CONSTRAINT step, so there's no value in asking for anything weaker.
15757 : */
15758 418 : if (relid != tab->relid)
15759 36 : LockRelationOid(relid, AccessExclusiveLock);
15760 :
15761 418 : ATPostAlterTypeParse(oldId, relid, confrelid,
15762 418 : (char *) lfirst(def_item),
15763 418 : wqueue, lockmode, tab->rewrite);
15764 : }
15765 1043 : forboth(oid_item, tab->changedIndexOids,
15766 : def_item, tab->changedIndexDefs)
15767 : {
15768 154 : Oid oldId = lfirst_oid(oid_item);
15769 : Oid relid;
15770 :
15771 154 : relid = IndexGetRelation(oldId, false);
15772 :
15773 : /*
15774 : * As above, make sure we have lock on the index's table if it's not
15775 : * the same table.
15776 : */
15777 154 : if (relid != tab->relid)
15778 12 : LockRelationOid(relid, AccessExclusiveLock);
15779 :
15780 154 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15781 154 : (char *) lfirst(def_item),
15782 154 : wqueue, lockmode, tab->rewrite);
15783 :
15784 154 : ObjectAddressSet(obj, RelationRelationId, oldId);
15785 154 : add_exact_object_address(&obj, objects);
15786 : }
15787 :
15788 : /* add dependencies for new statistics */
15789 942 : forboth(oid_item, tab->changedStatisticsOids,
15790 : def_item, tab->changedStatisticsDefs)
15791 : {
15792 53 : Oid oldId = lfirst_oid(oid_item);
15793 : Oid relid;
15794 :
15795 53 : relid = StatisticsGetRelation(oldId, false);
15796 :
15797 : /*
15798 : * As above, make sure we have lock on the statistics object's table
15799 : * if it's not the same table. However, we take
15800 : * ShareUpdateExclusiveLock here, aligning with the lock level used in
15801 : * CreateStatistics and RemoveStatisticsById.
15802 : *
15803 : * CAUTION: this should be done after all cases that grab
15804 : * AccessExclusiveLock, else we risk causing deadlock due to needing
15805 : * to promote our table lock.
15806 : */
15807 53 : if (relid != tab->relid)
15808 12 : LockRelationOid(relid, ShareUpdateExclusiveLock);
15809 :
15810 53 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15811 53 : (char *) lfirst(def_item),
15812 53 : wqueue, lockmode, tab->rewrite);
15813 :
15814 53 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15815 53 : add_exact_object_address(&obj, objects);
15816 : }
15817 :
15818 : /*
15819 : * Queue up command to restore replica identity index marking
15820 : */
15821 889 : if (tab->replicaIdentityIndex)
15822 : {
15823 12 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15824 12 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
15825 :
15826 12 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15827 12 : subcmd->name = tab->replicaIdentityIndex;
15828 12 : cmd->subtype = AT_ReplicaIdentity;
15829 12 : cmd->def = (Node *) subcmd;
15830 :
15831 : /* do it after indexes and constraints */
15832 12 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15833 12 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15834 : }
15835 :
15836 : /*
15837 : * Queue up command to restore marking of index used for cluster.
15838 : */
15839 889 : if (tab->clusterOnIndex)
15840 : {
15841 12 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15842 :
15843 12 : cmd->subtype = AT_ClusterOn;
15844 12 : cmd->name = tab->clusterOnIndex;
15845 :
15846 : /* do it after indexes and constraints */
15847 12 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15848 12 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15849 : }
15850 :
15851 : /*
15852 : * It should be okay to use DROP_RESTRICT here, since nothing else should
15853 : * be depending on these objects.
15854 : */
15855 889 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
15856 :
15857 889 : free_object_addresses(objects);
15858 :
15859 : /*
15860 : * The objects will get recreated during subsequent passes over the work
15861 : * queue.
15862 : */
15863 889 : }
15864 :
15865 : /*
15866 : * Parse the previously-saved definition string for a constraint, index or
15867 : * statistics object against the newly-established column data type(s), and
15868 : * queue up the resulting command parsetrees for execution.
15869 : *
15870 : * This might fail if, for example, you have a WHERE clause that uses an
15871 : * operator that's not available for the new column type.
15872 : */
15873 : static void
15874 625 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15875 : List **wqueue, LOCKMODE lockmode, bool rewrite)
15876 : {
15877 : List *raw_parsetree_list;
15878 : List *querytree_list;
15879 : ListCell *list_item;
15880 : Relation rel;
15881 :
15882 : /*
15883 : * We expect that we will get only ALTER TABLE and CREATE INDEX
15884 : * statements. Hence, there is no need to pass them through
15885 : * parse_analyze_*() or the rewriter, but instead we need to pass them
15886 : * through parse_utilcmd.c to make them ready for execution.
15887 : */
15888 625 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15889 625 : querytree_list = NIL;
15890 1250 : foreach(list_item, raw_parsetree_list)
15891 : {
15892 625 : RawStmt *rs = lfirst_node(RawStmt, list_item);
15893 625 : Node *stmt = rs->stmt;
15894 :
15895 625 : if (IsA(stmt, IndexStmt))
15896 154 : querytree_list = lappend(querytree_list,
15897 154 : transformIndexStmt(oldRelId,
15898 : (IndexStmt *) stmt,
15899 : cmd));
15900 471 : else if (IsA(stmt, AlterTableStmt))
15901 : {
15902 : List *beforeStmts;
15903 : List *afterStmts;
15904 :
15905 409 : stmt = (Node *) transformAlterTableStmt(oldRelId,
15906 : (AlterTableStmt *) stmt,
15907 : cmd,
15908 : &beforeStmts,
15909 : &afterStmts);
15910 409 : querytree_list = list_concat(querytree_list, beforeStmts);
15911 409 : querytree_list = lappend(querytree_list, stmt);
15912 409 : querytree_list = list_concat(querytree_list, afterStmts);
15913 : }
15914 62 : else if (IsA(stmt, CreateStatsStmt))
15915 53 : querytree_list = lappend(querytree_list,
15916 53 : transformStatsStmt(oldRelId,
15917 : (CreateStatsStmt *) stmt,
15918 : cmd));
15919 : else
15920 9 : querytree_list = lappend(querytree_list, stmt);
15921 : }
15922 :
15923 : /* Caller should already have acquired whatever lock we need. */
15924 625 : rel = relation_open(oldRelId, NoLock);
15925 :
15926 : /*
15927 : * Attach each generated command to the proper place in the work queue.
15928 : * Note this could result in creation of entirely new work-queue entries.
15929 : *
15930 : * Also note that we have to tweak the command subtypes, because it turns
15931 : * out that re-creation of indexes and constraints has to act a bit
15932 : * differently from initial creation.
15933 : */
15934 1250 : foreach(list_item, querytree_list)
15935 : {
15936 625 : Node *stm = (Node *) lfirst(list_item);
15937 : AlteredTableInfo *tab;
15938 :
15939 625 : tab = ATGetQueueEntry(wqueue, rel);
15940 :
15941 625 : if (IsA(stm, IndexStmt))
15942 : {
15943 154 : IndexStmt *stmt = (IndexStmt *) stm;
15944 : AlterTableCmd *newcmd;
15945 :
15946 154 : if (!rewrite)
15947 41 : TryReuseIndex(oldId, stmt);
15948 154 : stmt->reset_default_tblspc = true;
15949 : /* keep the index's comment */
15950 154 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15951 :
15952 154 : newcmd = makeNode(AlterTableCmd);
15953 154 : newcmd->subtype = AT_ReAddIndex;
15954 154 : newcmd->def = (Node *) stmt;
15955 154 : tab->subcmds[AT_PASS_OLD_INDEX] =
15956 154 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15957 : }
15958 471 : else if (IsA(stm, AlterTableStmt))
15959 : {
15960 409 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
15961 : ListCell *lcmd;
15962 :
15963 818 : foreach(lcmd, stmt->cmds)
15964 : {
15965 409 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
15966 :
15967 409 : if (cmd->subtype == AT_AddIndex)
15968 : {
15969 : IndexStmt *indstmt;
15970 : Oid indoid;
15971 :
15972 152 : indstmt = castNode(IndexStmt, cmd->def);
15973 152 : indoid = get_constraint_index(oldId);
15974 :
15975 152 : if (!rewrite)
15976 32 : TryReuseIndex(indoid, indstmt);
15977 : /* keep any comment on the index */
15978 152 : indstmt->idxcomment = GetComment(indoid,
15979 : RelationRelationId, 0);
15980 152 : indstmt->reset_default_tblspc = true;
15981 :
15982 152 : cmd->subtype = AT_ReAddIndex;
15983 152 : tab->subcmds[AT_PASS_OLD_INDEX] =
15984 152 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15985 :
15986 : /* recreate any comment on the constraint */
15987 152 : RebuildConstraintComment(tab,
15988 : AT_PASS_OLD_INDEX,
15989 : oldId,
15990 : rel,
15991 : NIL,
15992 152 : indstmt->idxname);
15993 : }
15994 257 : else if (cmd->subtype == AT_AddConstraint)
15995 : {
15996 257 : Constraint *con = castNode(Constraint, cmd->def);
15997 :
15998 257 : con->old_pktable_oid = refRelId;
15999 : /* rewriting neither side of a FK */
16000 257 : if (con->contype == CONSTR_FOREIGN &&
16001 48 : !rewrite && tab->rewrite == 0)
16002 4 : TryReuseForeignKey(oldId, con);
16003 257 : con->reset_default_tblspc = true;
16004 257 : cmd->subtype = AT_ReAddConstraint;
16005 257 : tab->subcmds[AT_PASS_OLD_CONSTR] =
16006 257 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
16007 :
16008 : /*
16009 : * Recreate any comment on the constraint. If we have
16010 : * recreated a primary key, then transformTableConstraint
16011 : * has added an unnamed not-null constraint here; skip
16012 : * this in that case.
16013 : */
16014 257 : if (con->conname)
16015 257 : RebuildConstraintComment(tab,
16016 : AT_PASS_OLD_CONSTR,
16017 : oldId,
16018 : rel,
16019 : NIL,
16020 257 : con->conname);
16021 : else
16022 : Assert(con->contype == CONSTR_NOTNULL);
16023 : }
16024 : else
16025 0 : elog(ERROR, "unexpected statement subtype: %d",
16026 : (int) cmd->subtype);
16027 : }
16028 : }
16029 62 : else if (IsA(stm, AlterDomainStmt))
16030 : {
16031 9 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
16032 :
16033 9 : if (stmt->subtype == AD_AddConstraint)
16034 : {
16035 9 : Constraint *con = castNode(Constraint, stmt->def);
16036 9 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
16037 :
16038 9 : cmd->subtype = AT_ReAddDomainConstraint;
16039 9 : cmd->def = (Node *) stmt;
16040 9 : tab->subcmds[AT_PASS_OLD_CONSTR] =
16041 9 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
16042 :
16043 : /* recreate any comment on the constraint */
16044 9 : RebuildConstraintComment(tab,
16045 : AT_PASS_OLD_CONSTR,
16046 : oldId,
16047 : NULL,
16048 : stmt->typeName,
16049 9 : con->conname);
16050 : }
16051 : else
16052 0 : elog(ERROR, "unexpected statement subtype: %d",
16053 : (int) stmt->subtype);
16054 : }
16055 53 : else if (IsA(stm, CreateStatsStmt))
16056 : {
16057 53 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
16058 : AlterTableCmd *newcmd;
16059 :
16060 : /* keep the statistics object's comment */
16061 53 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
16062 :
16063 53 : newcmd = makeNode(AlterTableCmd);
16064 53 : newcmd->subtype = AT_ReAddStatistics;
16065 53 : newcmd->def = (Node *) stmt;
16066 53 : tab->subcmds[AT_PASS_MISC] =
16067 53 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
16068 : }
16069 : else
16070 0 : elog(ERROR, "unexpected statement type: %d",
16071 : (int) nodeTag(stm));
16072 : }
16073 :
16074 625 : relation_close(rel, NoLock);
16075 625 : }
16076 :
16077 : /*
16078 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
16079 : * for a table or domain constraint that is being rebuilt.
16080 : *
16081 : * objid is the OID of the constraint.
16082 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
16083 : * as a string list) for a domain constraint.
16084 : * (We could dig that info, as well as the conname, out of the pg_constraint
16085 : * entry; but callers already have them so might as well pass them.)
16086 : */
16087 : static void
16088 418 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
16089 : Relation rel, List *domname,
16090 : const char *conname)
16091 : {
16092 : CommentStmt *cmd;
16093 : char *comment_str;
16094 : AlterTableCmd *newcmd;
16095 :
16096 : /* Look for comment for object wanted, and leave if none */
16097 418 : comment_str = GetComment(objid, ConstraintRelationId, 0);
16098 418 : if (comment_str == NULL)
16099 358 : return;
16100 :
16101 : /* Build CommentStmt node, copying all input data for safety */
16102 60 : cmd = makeNode(CommentStmt);
16103 60 : if (rel)
16104 : {
16105 52 : cmd->objtype = OBJECT_TABCONSTRAINT;
16106 52 : cmd->object = (Node *)
16107 52 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
16108 : makeString(pstrdup(RelationGetRelationName(rel))),
16109 : makeString(pstrdup(conname)));
16110 : }
16111 : else
16112 : {
16113 8 : cmd->objtype = OBJECT_DOMCONSTRAINT;
16114 8 : cmd->object = (Node *)
16115 8 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
16116 : makeString(pstrdup(conname)));
16117 : }
16118 60 : cmd->comment = comment_str;
16119 :
16120 : /* Append it to list of commands */
16121 60 : newcmd = makeNode(AlterTableCmd);
16122 60 : newcmd->subtype = AT_ReAddComment;
16123 60 : newcmd->def = (Node *) cmd;
16124 60 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
16125 : }
16126 :
16127 : /*
16128 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
16129 : * for the real analysis, then mutates the IndexStmt based on that verdict.
16130 : */
16131 : static void
16132 73 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
16133 : {
16134 73 : if (CheckIndexCompatible(oldId,
16135 73 : stmt->accessMethod,
16136 73 : stmt->indexParams,
16137 73 : stmt->excludeOpNames,
16138 73 : stmt->iswithoutoverlaps))
16139 : {
16140 69 : Relation irel = index_open(oldId, NoLock);
16141 :
16142 : /* If it's a partitioned index, there is no storage to share. */
16143 69 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
16144 : {
16145 49 : stmt->oldNumber = irel->rd_locator.relNumber;
16146 49 : stmt->oldCreateSubid = irel->rd_createSubid;
16147 49 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
16148 : }
16149 69 : index_close(irel, NoLock);
16150 : }
16151 73 : }
16152 :
16153 : /*
16154 : * Subroutine for ATPostAlterTypeParse().
16155 : *
16156 : * Stash the old P-F equality operator into the Constraint node, for possible
16157 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
16158 : * this constraint can be skipped.
16159 : */
16160 : static void
16161 4 : TryReuseForeignKey(Oid oldId, Constraint *con)
16162 : {
16163 : HeapTuple tup;
16164 : Datum adatum;
16165 : ArrayType *arr;
16166 : Oid *rawarr;
16167 : int numkeys;
16168 : int i;
16169 :
16170 : Assert(con->contype == CONSTR_FOREIGN);
16171 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
16172 :
16173 4 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
16174 4 : if (!HeapTupleIsValid(tup)) /* should not happen */
16175 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
16176 :
16177 4 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
16178 : Anum_pg_constraint_conpfeqop);
16179 4 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
16180 4 : numkeys = ARR_DIMS(arr)[0];
16181 : /* test follows the one in ri_FetchConstraintInfo() */
16182 4 : if (ARR_NDIM(arr) != 1 ||
16183 4 : ARR_HASNULL(arr) ||
16184 4 : ARR_ELEMTYPE(arr) != OIDOID)
16185 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
16186 4 : rawarr = (Oid *) ARR_DATA_PTR(arr);
16187 :
16188 : /* stash a List of the operator Oids in our Constraint node */
16189 8 : for (i = 0; i < numkeys; i++)
16190 4 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
16191 :
16192 4 : ReleaseSysCache(tup);
16193 4 : }
16194 :
16195 : /*
16196 : * ALTER COLUMN .. OPTIONS ( ... )
16197 : *
16198 : * Returns the address of the modified column
16199 : */
16200 : static ObjectAddress
16201 93 : ATExecAlterColumnGenericOptions(Relation rel,
16202 : const char *colName,
16203 : List *options,
16204 : LOCKMODE lockmode)
16205 : {
16206 : Relation ftrel;
16207 : Relation attrel;
16208 : ForeignServer *server;
16209 : ForeignDataWrapper *fdw;
16210 : HeapTuple tuple;
16211 : HeapTuple newtuple;
16212 : bool isnull;
16213 : Datum repl_val[Natts_pg_attribute];
16214 : bool repl_null[Natts_pg_attribute];
16215 : bool repl_repl[Natts_pg_attribute];
16216 : Datum datum;
16217 : Form_pg_foreign_table fttableform;
16218 : Form_pg_attribute atttableform;
16219 : AttrNumber attnum;
16220 : ObjectAddress address;
16221 :
16222 93 : if (options == NIL)
16223 0 : return InvalidObjectAddress;
16224 :
16225 : /* First, determine FDW validator associated to the foreign table. */
16226 93 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
16227 93 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
16228 93 : if (!HeapTupleIsValid(tuple))
16229 0 : ereport(ERROR,
16230 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16231 : errmsg("foreign table \"%s\" does not exist",
16232 : RelationGetRelationName(rel))));
16233 93 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16234 93 : server = GetForeignServer(fttableform->ftserver);
16235 93 : fdw = GetForeignDataWrapper(server->fdwid);
16236 :
16237 93 : table_close(ftrel, AccessShareLock);
16238 93 : ReleaseSysCache(tuple);
16239 :
16240 93 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
16241 93 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
16242 93 : if (!HeapTupleIsValid(tuple))
16243 0 : ereport(ERROR,
16244 : (errcode(ERRCODE_UNDEFINED_COLUMN),
16245 : errmsg("column \"%s\" of relation \"%s\" does not exist",
16246 : colName, RelationGetRelationName(rel))));
16247 :
16248 : /* Prevent them from altering a system attribute */
16249 93 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16250 93 : attnum = atttableform->attnum;
16251 93 : if (attnum <= 0)
16252 4 : ereport(ERROR,
16253 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16254 : errmsg("cannot alter system column \"%s\"", colName)));
16255 :
16256 :
16257 : /* Initialize buffers for new tuple values */
16258 89 : memset(repl_val, 0, sizeof(repl_val));
16259 89 : memset(repl_null, false, sizeof(repl_null));
16260 89 : memset(repl_repl, false, sizeof(repl_repl));
16261 :
16262 : /* Extract the current options */
16263 89 : datum = SysCacheGetAttr(ATTNAME,
16264 : tuple,
16265 : Anum_pg_attribute_attfdwoptions,
16266 : &isnull);
16267 89 : if (isnull)
16268 83 : datum = PointerGetDatum(NULL);
16269 :
16270 : /* Transform the options */
16271 89 : datum = transformGenericOptions(AttributeRelationId,
16272 : datum,
16273 : options,
16274 : fdw->fdwvalidator);
16275 :
16276 89 : if (DatumGetPointer(datum) != NULL)
16277 89 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16278 : else
16279 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16280 :
16281 89 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16282 :
16283 : /* Everything looks good - update the tuple */
16284 :
16285 89 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16286 : repl_val, repl_null, repl_repl);
16287 :
16288 89 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16289 :
16290 89 : InvokeObjectPostAlterHook(RelationRelationId,
16291 : RelationGetRelid(rel),
16292 : atttableform->attnum);
16293 89 : ObjectAddressSubSet(address, RelationRelationId,
16294 : RelationGetRelid(rel), attnum);
16295 :
16296 89 : ReleaseSysCache(tuple);
16297 :
16298 89 : table_close(attrel, RowExclusiveLock);
16299 :
16300 89 : heap_freetuple(newtuple);
16301 :
16302 89 : return address;
16303 : }
16304 :
16305 : /*
16306 : * ALTER TABLE OWNER
16307 : *
16308 : * recursing is true if we are recursing from a table to its indexes,
16309 : * sequences, or toast table. We don't allow the ownership of those things to
16310 : * be changed separately from the parent table. Also, we can skip permission
16311 : * checks (this is necessary not just an optimization, else we'd fail to
16312 : * handle toast tables properly).
16313 : *
16314 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16315 : * free-standing composite type.
16316 : */
16317 : void
16318 1350 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16319 : {
16320 : Relation target_rel;
16321 : Relation class_rel;
16322 : HeapTuple tuple;
16323 : Form_pg_class tuple_class;
16324 :
16325 : /*
16326 : * Get exclusive lock till end of transaction on the target table. Use
16327 : * relation_open so that we can work on indexes and sequences.
16328 : */
16329 1350 : target_rel = relation_open(relationOid, lockmode);
16330 :
16331 : /* Get its pg_class tuple, too */
16332 1350 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
16333 :
16334 1350 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16335 1350 : if (!HeapTupleIsValid(tuple))
16336 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
16337 1350 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16338 :
16339 : /* Can we change the ownership of this tuple? */
16340 1350 : switch (tuple_class->relkind)
16341 : {
16342 1136 : case RELKIND_RELATION:
16343 : case RELKIND_VIEW:
16344 : case RELKIND_MATVIEW:
16345 : case RELKIND_FOREIGN_TABLE:
16346 : case RELKIND_PARTITIONED_TABLE:
16347 : case RELKIND_PROPGRAPH:
16348 : /* ok to change owner */
16349 1136 : break;
16350 89 : case RELKIND_INDEX:
16351 89 : if (!recursing)
16352 : {
16353 : /*
16354 : * Because ALTER INDEX OWNER used to be allowed, and in fact
16355 : * is generated by old versions of pg_dump, we give a warning
16356 : * and do nothing rather than erroring out. Also, to avoid
16357 : * unnecessary chatter while restoring those old dumps, say
16358 : * nothing at all if the command would be a no-op anyway.
16359 : */
16360 0 : if (tuple_class->relowner != newOwnerId)
16361 0 : ereport(WARNING,
16362 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16363 : errmsg("cannot change owner of index \"%s\"",
16364 : NameStr(tuple_class->relname)),
16365 : errhint("Change the ownership of the index's table instead.")));
16366 : /* quick hack to exit via the no-op path */
16367 0 : newOwnerId = tuple_class->relowner;
16368 : }
16369 89 : break;
16370 12 : case RELKIND_PARTITIONED_INDEX:
16371 12 : if (recursing)
16372 12 : break;
16373 0 : ereport(ERROR,
16374 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16375 : errmsg("cannot change owner of index \"%s\"",
16376 : NameStr(tuple_class->relname)),
16377 : errhint("Change the ownership of the index's table instead.")));
16378 : break;
16379 65 : case RELKIND_SEQUENCE:
16380 65 : if (!recursing &&
16381 35 : tuple_class->relowner != newOwnerId)
16382 : {
16383 : /* if it's an owned sequence, disallow changing it by itself */
16384 : Oid tableId;
16385 : int32 colId;
16386 :
16387 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16388 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16389 0 : ereport(ERROR,
16390 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16391 : errmsg("cannot change owner of sequence \"%s\"",
16392 : NameStr(tuple_class->relname)),
16393 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
16394 : NameStr(tuple_class->relname),
16395 : get_rel_name(tableId))));
16396 : }
16397 65 : break;
16398 5 : case RELKIND_COMPOSITE_TYPE:
16399 5 : if (recursing)
16400 5 : break;
16401 0 : ereport(ERROR,
16402 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16403 : errmsg("\"%s\" is a composite type",
16404 : NameStr(tuple_class->relname)),
16405 : /* translator: %s is an SQL ALTER command */
16406 : errhint("Use %s instead.",
16407 : "ALTER TYPE")));
16408 : break;
16409 43 : case RELKIND_TOASTVALUE:
16410 43 : if (recursing)
16411 43 : break;
16412 : pg_fallthrough;
16413 : default:
16414 0 : ereport(ERROR,
16415 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16416 : errmsg("cannot change owner of relation \"%s\"",
16417 : NameStr(tuple_class->relname)),
16418 : errdetail_relkind_not_supported(tuple_class->relkind)));
16419 : }
16420 :
16421 : /*
16422 : * If the new owner is the same as the existing owner, consider the
16423 : * command to have succeeded. This is for dump restoration purposes.
16424 : */
16425 1350 : if (tuple_class->relowner != newOwnerId)
16426 : {
16427 : Datum repl_val[Natts_pg_class];
16428 : bool repl_null[Natts_pg_class];
16429 : bool repl_repl[Natts_pg_class];
16430 : Acl *newAcl;
16431 : Datum aclDatum;
16432 : bool isNull;
16433 : HeapTuple newtuple;
16434 :
16435 : /* skip permission checks when recursing to index or toast table */
16436 418 : if (!recursing)
16437 : {
16438 : /* Superusers can always do it */
16439 213 : if (!superuser())
16440 : {
16441 28 : Oid namespaceOid = tuple_class->relnamespace;
16442 : AclResult aclresult;
16443 :
16444 : /* Otherwise, must be owner of the existing object */
16445 28 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16446 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
16447 0 : RelationGetRelationName(target_rel));
16448 :
16449 : /* Must be able to become new owner */
16450 28 : check_can_set_role(GetUserId(), newOwnerId);
16451 :
16452 : /* New owner must have CREATE privilege on namespace */
16453 20 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16454 : ACL_CREATE);
16455 20 : if (aclresult != ACLCHECK_OK)
16456 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
16457 0 : get_namespace_name(namespaceOid));
16458 : }
16459 : }
16460 :
16461 410 : memset(repl_null, false, sizeof(repl_null));
16462 410 : memset(repl_repl, false, sizeof(repl_repl));
16463 :
16464 410 : repl_repl[Anum_pg_class_relowner - 1] = true;
16465 410 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16466 :
16467 : /*
16468 : * Determine the modified ACL for the new owner. This is only
16469 : * necessary when the ACL is non-null.
16470 : */
16471 410 : aclDatum = SysCacheGetAttr(RELOID, tuple,
16472 : Anum_pg_class_relacl,
16473 : &isNull);
16474 410 : if (!isNull)
16475 : {
16476 60 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16477 : tuple_class->relowner, newOwnerId);
16478 60 : repl_repl[Anum_pg_class_relacl - 1] = true;
16479 60 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16480 : }
16481 :
16482 410 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16483 :
16484 410 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16485 :
16486 410 : heap_freetuple(newtuple);
16487 :
16488 : /*
16489 : * We must similarly update any per-column ACLs to reflect the new
16490 : * owner; for neatness reasons that's split out as a subroutine.
16491 : */
16492 410 : change_owner_fix_column_acls(relationOid,
16493 : tuple_class->relowner,
16494 : newOwnerId);
16495 :
16496 : /*
16497 : * Update owner dependency reference, if any. A composite type has
16498 : * none, because it's tracked for the pg_type entry instead of here;
16499 : * indexes and TOAST tables don't have their own entries either.
16500 : */
16501 410 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16502 405 : tuple_class->relkind != RELKIND_INDEX &&
16503 316 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16504 304 : tuple_class->relkind != RELKIND_TOASTVALUE)
16505 261 : changeDependencyOnOwner(RelationRelationId, relationOid,
16506 : newOwnerId);
16507 :
16508 : /*
16509 : * Also change the ownership of the table's row type, if it has one
16510 : */
16511 410 : if (OidIsValid(tuple_class->reltype))
16512 245 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16513 :
16514 : /*
16515 : * If we are operating on a table or materialized view, also change
16516 : * the ownership of any indexes and sequences that belong to the
16517 : * relation, as well as its toast table (if it has one).
16518 : */
16519 410 : if (tuple_class->relkind == RELKIND_RELATION ||
16520 224 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16521 188 : tuple_class->relkind == RELKIND_MATVIEW ||
16522 188 : tuple_class->relkind == RELKIND_TOASTVALUE)
16523 : {
16524 : List *index_oid_list;
16525 : ListCell *i;
16526 :
16527 : /* Find all the indexes belonging to this relation */
16528 265 : index_oid_list = RelationGetIndexList(target_rel);
16529 :
16530 : /* For each index, recursively change its ownership */
16531 366 : foreach(i, index_oid_list)
16532 101 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16533 :
16534 265 : list_free(index_oid_list);
16535 : }
16536 :
16537 : /* If it has a toast table, recurse to change its ownership */
16538 410 : if (tuple_class->reltoastrelid != InvalidOid)
16539 43 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16540 : true, lockmode);
16541 :
16542 : /* If it has dependent sequences, recurse to change them too */
16543 410 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16544 : }
16545 :
16546 1342 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16547 :
16548 1342 : ReleaseSysCache(tuple);
16549 1342 : table_close(class_rel, RowExclusiveLock);
16550 1342 : relation_close(target_rel, NoLock);
16551 1342 : }
16552 :
16553 : /*
16554 : * change_owner_fix_column_acls
16555 : *
16556 : * Helper function for ATExecChangeOwner. Scan the columns of the table
16557 : * and fix any non-null column ACLs to reflect the new owner.
16558 : */
16559 : static void
16560 410 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16561 : {
16562 : Relation attRelation;
16563 : SysScanDesc scan;
16564 : ScanKeyData key[1];
16565 : HeapTuple attributeTuple;
16566 :
16567 410 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16568 410 : ScanKeyInit(&key[0],
16569 : Anum_pg_attribute_attrelid,
16570 : BTEqualStrategyNumber, F_OIDEQ,
16571 : ObjectIdGetDatum(relationOid));
16572 410 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16573 : true, NULL, 1, key);
16574 2913 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16575 : {
16576 2503 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16577 : Datum repl_val[Natts_pg_attribute];
16578 : bool repl_null[Natts_pg_attribute];
16579 : bool repl_repl[Natts_pg_attribute];
16580 : Acl *newAcl;
16581 : Datum aclDatum;
16582 : bool isNull;
16583 : HeapTuple newtuple;
16584 :
16585 : /* Ignore dropped columns */
16586 2503 : if (att->attisdropped)
16587 2502 : continue;
16588 :
16589 2503 : aclDatum = heap_getattr(attributeTuple,
16590 : Anum_pg_attribute_attacl,
16591 : RelationGetDescr(attRelation),
16592 : &isNull);
16593 : /* Null ACLs do not require changes */
16594 2503 : if (isNull)
16595 2502 : continue;
16596 :
16597 1 : memset(repl_null, false, sizeof(repl_null));
16598 1 : memset(repl_repl, false, sizeof(repl_repl));
16599 :
16600 1 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16601 : oldOwnerId, newOwnerId);
16602 1 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
16603 1 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16604 :
16605 1 : newtuple = heap_modify_tuple(attributeTuple,
16606 : RelationGetDescr(attRelation),
16607 : repl_val, repl_null, repl_repl);
16608 :
16609 1 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16610 :
16611 1 : heap_freetuple(newtuple);
16612 : }
16613 410 : systable_endscan(scan);
16614 410 : table_close(attRelation, RowExclusiveLock);
16615 410 : }
16616 :
16617 : /*
16618 : * change_owner_recurse_to_sequences
16619 : *
16620 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
16621 : * for sequences that are dependent on serial columns, and changes their
16622 : * ownership.
16623 : */
16624 : static void
16625 410 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16626 : {
16627 : Relation depRel;
16628 : SysScanDesc scan;
16629 : ScanKeyData key[2];
16630 : HeapTuple tup;
16631 :
16632 : /*
16633 : * SERIAL sequences are those having an auto dependency on one of the
16634 : * table's columns (we don't care *which* column, exactly).
16635 : */
16636 410 : depRel = table_open(DependRelationId, AccessShareLock);
16637 :
16638 410 : ScanKeyInit(&key[0],
16639 : Anum_pg_depend_refclassid,
16640 : BTEqualStrategyNumber, F_OIDEQ,
16641 : ObjectIdGetDatum(RelationRelationId));
16642 410 : ScanKeyInit(&key[1],
16643 : Anum_pg_depend_refobjid,
16644 : BTEqualStrategyNumber, F_OIDEQ,
16645 : ObjectIdGetDatum(relationOid));
16646 : /* we leave refobjsubid unspecified */
16647 :
16648 410 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16649 : NULL, 2, key);
16650 :
16651 1316 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
16652 : {
16653 906 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16654 : Relation seqRel;
16655 :
16656 : /* skip dependencies other than auto dependencies on columns */
16657 906 : if (depForm->refobjsubid == 0 ||
16658 385 : depForm->classid != RelationRelationId ||
16659 121 : depForm->objsubid != 0 ||
16660 121 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16661 785 : continue;
16662 :
16663 : /* Use relation_open just in case it's an index */
16664 121 : seqRel = relation_open(depForm->objid, lockmode);
16665 :
16666 : /* skip non-sequence relations */
16667 121 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16668 : {
16669 : /* No need to keep the lock */
16670 104 : relation_close(seqRel, lockmode);
16671 104 : continue;
16672 : }
16673 :
16674 : /* We don't need to close the sequence while we alter it. */
16675 17 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16676 :
16677 : /* Now we can close it. Keep the lock till end of transaction. */
16678 17 : relation_close(seqRel, NoLock);
16679 : }
16680 :
16681 410 : systable_endscan(scan);
16682 :
16683 410 : relation_close(depRel, AccessShareLock);
16684 410 : }
16685 :
16686 : /*
16687 : * ALTER TABLE CLUSTER ON
16688 : *
16689 : * The only thing we have to do is to change the indisclustered bits.
16690 : *
16691 : * Return the address of the new clustering index.
16692 : */
16693 : static ObjectAddress
16694 39 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16695 : {
16696 : Oid indexOid;
16697 : ObjectAddress address;
16698 :
16699 39 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16700 :
16701 39 : if (!OidIsValid(indexOid))
16702 0 : ereport(ERROR,
16703 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16704 : errmsg("index \"%s\" for table \"%s\" does not exist",
16705 : indexName, RelationGetRelationName(rel))));
16706 :
16707 : /* Check index is valid to cluster on */
16708 39 : check_index_is_clusterable(rel, indexOid, lockmode);
16709 :
16710 : /* And do the work */
16711 39 : mark_index_clustered(rel, indexOid, false);
16712 :
16713 39 : ObjectAddressSet(address,
16714 : RelationRelationId, indexOid);
16715 :
16716 39 : return address;
16717 : }
16718 :
16719 : /*
16720 : * ALTER TABLE SET WITHOUT CLUSTER
16721 : *
16722 : * We have to find any indexes on the table that have indisclustered bit
16723 : * set and turn it off.
16724 : */
16725 : static void
16726 8 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
16727 : {
16728 8 : mark_index_clustered(rel, InvalidOid, false);
16729 8 : }
16730 :
16731 : /*
16732 : * Preparation phase for SET ACCESS METHOD
16733 : *
16734 : * Check that the access method exists and determine whether a change is
16735 : * actually needed.
16736 : */
16737 : static void
16738 73 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
16739 : {
16740 : Oid amoid;
16741 :
16742 : /*
16743 : * Look up the access method name and check that it differs from the
16744 : * table's current AM. If DEFAULT was specified for a partitioned table
16745 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16746 : */
16747 73 : if (amname != NULL)
16748 49 : amoid = get_table_am_oid(amname, false);
16749 24 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16750 12 : amoid = InvalidOid;
16751 : else
16752 12 : amoid = get_table_am_oid(default_table_access_method, false);
16753 :
16754 : /* if it's a match, phase 3 doesn't need to do anything */
16755 73 : if (rel->rd_rel->relam == amoid)
16756 8 : return;
16757 :
16758 : /* Save info for Phase 3 to do the real work */
16759 65 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
16760 65 : tab->newAccessMethod = amoid;
16761 65 : tab->chgAccessMethod = true;
16762 : }
16763 :
16764 : /*
16765 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16766 : * storage that have an interest in preserving AM.
16767 : *
16768 : * Since these have no storage, setting the access method is a catalog only
16769 : * operation.
16770 : */
16771 : static void
16772 29 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
16773 : {
16774 : Relation pg_class;
16775 : Oid oldAccessMethodId;
16776 : HeapTuple tuple;
16777 : Form_pg_class rd_rel;
16778 29 : Oid reloid = RelationGetRelid(rel);
16779 :
16780 : /*
16781 : * Shouldn't be called on relations having storage; these are processed in
16782 : * phase 3.
16783 : */
16784 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16785 :
16786 : /* Get a modifiable copy of the relation's pg_class row. */
16787 29 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16788 :
16789 29 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16790 29 : if (!HeapTupleIsValid(tuple))
16791 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
16792 29 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16793 :
16794 : /* Update the pg_class row. */
16795 29 : oldAccessMethodId = rd_rel->relam;
16796 29 : rd_rel->relam = newAccessMethodId;
16797 :
16798 : /* Leave if no update required */
16799 29 : if (rd_rel->relam == oldAccessMethodId)
16800 : {
16801 0 : heap_freetuple(tuple);
16802 0 : table_close(pg_class, RowExclusiveLock);
16803 0 : return;
16804 : }
16805 :
16806 29 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16807 :
16808 : /*
16809 : * Update the dependency on the new access method. No dependency is added
16810 : * if the new access method is InvalidOid (default case). Be very careful
16811 : * that this has to compare the previous value stored in pg_class with the
16812 : * new one.
16813 : */
16814 29 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16815 13 : {
16816 : ObjectAddress relobj,
16817 : referenced;
16818 :
16819 : /*
16820 : * New access method is defined and there was no dependency
16821 : * previously, so record a new one.
16822 : */
16823 13 : ObjectAddressSet(relobj, RelationRelationId, reloid);
16824 13 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16825 13 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16826 : }
16827 16 : else if (OidIsValid(oldAccessMethodId) &&
16828 16 : !OidIsValid(rd_rel->relam))
16829 : {
16830 : /*
16831 : * There was an access method defined, and no new one, so just remove
16832 : * the existing dependency.
16833 : */
16834 8 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
16835 : AccessMethodRelationId,
16836 : DEPENDENCY_NORMAL);
16837 : }
16838 : else
16839 : {
16840 : Assert(OidIsValid(oldAccessMethodId) &&
16841 : OidIsValid(rd_rel->relam));
16842 :
16843 : /* Both are valid, so update the dependency */
16844 8 : changeDependencyFor(RelationRelationId, reloid,
16845 : AccessMethodRelationId,
16846 : oldAccessMethodId, rd_rel->relam);
16847 : }
16848 :
16849 : /* make the relam and dependency changes visible */
16850 29 : CommandCounterIncrement();
16851 :
16852 29 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16853 :
16854 29 : heap_freetuple(tuple);
16855 29 : table_close(pg_class, RowExclusiveLock);
16856 : }
16857 :
16858 : /*
16859 : * ALTER TABLE SET TABLESPACE
16860 : */
16861 : static void
16862 98 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16863 : {
16864 : Oid tablespaceId;
16865 :
16866 : /* Check that the tablespace exists */
16867 98 : tablespaceId = get_tablespace_oid(tablespacename, false);
16868 :
16869 : /* Check permissions except when moving to database's default */
16870 98 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16871 : {
16872 : AclResult aclresult;
16873 :
16874 43 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16875 43 : if (aclresult != ACLCHECK_OK)
16876 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16877 : }
16878 :
16879 : /* Save info for Phase 3 to do the real work */
16880 98 : if (OidIsValid(tab->newTableSpace))
16881 0 : ereport(ERROR,
16882 : (errcode(ERRCODE_SYNTAX_ERROR),
16883 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
16884 :
16885 98 : tab->newTableSpace = tablespaceId;
16886 98 : }
16887 :
16888 : /*
16889 : * Set, reset, or replace reloptions.
16890 : */
16891 : static void
16892 627 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
16893 : LOCKMODE lockmode)
16894 : {
16895 : Oid relid;
16896 : Relation pgclass;
16897 : HeapTuple tuple;
16898 : HeapTuple newtuple;
16899 : Datum datum;
16900 : Datum newOptions;
16901 : Datum repl_val[Natts_pg_class];
16902 : bool repl_null[Natts_pg_class];
16903 : bool repl_repl[Natts_pg_class];
16904 627 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16905 :
16906 627 : if (defList == NIL && operation != AT_ReplaceRelOptions)
16907 0 : return; /* nothing to do */
16908 :
16909 627 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
16910 :
16911 : /* Fetch heap tuple */
16912 627 : relid = RelationGetRelid(rel);
16913 627 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16914 627 : if (!HeapTupleIsValid(tuple))
16915 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16916 :
16917 627 : if (operation == AT_ReplaceRelOptions)
16918 : {
16919 : /*
16920 : * If we're supposed to replace the reloptions list, we just pretend
16921 : * there were none before.
16922 : */
16923 133 : datum = (Datum) 0;
16924 : }
16925 : else
16926 : {
16927 : bool isnull;
16928 :
16929 : /* Get the old reloptions */
16930 494 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16931 : &isnull);
16932 494 : if (isnull)
16933 305 : datum = (Datum) 0;
16934 : }
16935 :
16936 : /* Generate new proposed reloptions (text array) */
16937 627 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16938 : operation == AT_ResetRelOptions);
16939 :
16940 : /* Validate */
16941 623 : switch (rel->rd_rel->relkind)
16942 : {
16943 347 : case RELKIND_RELATION:
16944 : case RELKIND_MATVIEW:
16945 347 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16946 347 : break;
16947 4 : case RELKIND_PARTITIONED_TABLE:
16948 4 : (void) partitioned_table_reloptions(newOptions, true);
16949 0 : break;
16950 201 : case RELKIND_VIEW:
16951 201 : (void) view_reloptions(newOptions, true);
16952 189 : break;
16953 71 : case RELKIND_INDEX:
16954 : case RELKIND_PARTITIONED_INDEX:
16955 71 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16956 57 : break;
16957 0 : case RELKIND_TOASTVALUE:
16958 : /* fall through to error -- shouldn't ever get here */
16959 : default:
16960 0 : ereport(ERROR,
16961 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16962 : errmsg("cannot set options for relation \"%s\"",
16963 : RelationGetRelationName(rel)),
16964 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16965 : break;
16966 : }
16967 :
16968 : /* Special-case validation of view options */
16969 593 : if (rel->rd_rel->relkind == RELKIND_VIEW)
16970 : {
16971 189 : Query *view_query = get_view_query(rel);
16972 189 : List *view_options = untransformRelOptions(newOptions);
16973 : ListCell *cell;
16974 189 : bool check_option = false;
16975 :
16976 257 : foreach(cell, view_options)
16977 : {
16978 68 : DefElem *defel = (DefElem *) lfirst(cell);
16979 :
16980 68 : if (strcmp(defel->defname, "check_option") == 0)
16981 16 : check_option = true;
16982 : }
16983 :
16984 : /*
16985 : * If the check option is specified, look to see if the view is
16986 : * actually auto-updatable or not.
16987 : */
16988 189 : if (check_option)
16989 : {
16990 : const char *view_updatable_error =
16991 16 : view_query_is_auto_updatable(view_query, true);
16992 :
16993 16 : if (view_updatable_error)
16994 0 : ereport(ERROR,
16995 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16996 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16997 : errhint("%s", _(view_updatable_error))));
16998 : }
16999 : }
17000 :
17001 : /*
17002 : * All we need do here is update the pg_class row; the new options will be
17003 : * propagated into relcaches during post-commit cache inval.
17004 : */
17005 593 : memset(repl_val, 0, sizeof(repl_val));
17006 593 : memset(repl_null, false, sizeof(repl_null));
17007 593 : memset(repl_repl, false, sizeof(repl_repl));
17008 :
17009 593 : if (newOptions != (Datum) 0)
17010 395 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
17011 : else
17012 198 : repl_null[Anum_pg_class_reloptions - 1] = true;
17013 :
17014 593 : repl_repl[Anum_pg_class_reloptions - 1] = true;
17015 :
17016 593 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17017 : repl_val, repl_null, repl_repl);
17018 :
17019 593 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17020 593 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
17021 :
17022 593 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
17023 :
17024 593 : heap_freetuple(newtuple);
17025 :
17026 593 : ReleaseSysCache(tuple);
17027 :
17028 : /* repeat the whole exercise for the toast table, if there's one */
17029 593 : if (OidIsValid(rel->rd_rel->reltoastrelid))
17030 : {
17031 : Relation toastrel;
17032 181 : Oid toastid = rel->rd_rel->reltoastrelid;
17033 :
17034 181 : toastrel = table_open(toastid, lockmode);
17035 :
17036 : /* Fetch heap tuple */
17037 181 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
17038 181 : if (!HeapTupleIsValid(tuple))
17039 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
17040 :
17041 181 : if (operation == AT_ReplaceRelOptions)
17042 : {
17043 : /*
17044 : * If we're supposed to replace the reloptions list, we just
17045 : * pretend there were none before.
17046 : */
17047 0 : datum = (Datum) 0;
17048 : }
17049 : else
17050 : {
17051 : bool isnull;
17052 :
17053 : /* Get the old reloptions */
17054 181 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
17055 : &isnull);
17056 181 : if (isnull)
17057 157 : datum = (Datum) 0;
17058 : }
17059 :
17060 181 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
17061 : false, operation == AT_ResetRelOptions);
17062 :
17063 181 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
17064 :
17065 181 : memset(repl_val, 0, sizeof(repl_val));
17066 181 : memset(repl_null, false, sizeof(repl_null));
17067 181 : memset(repl_repl, false, sizeof(repl_repl));
17068 :
17069 181 : if (newOptions != (Datum) 0)
17070 28 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
17071 : else
17072 153 : repl_null[Anum_pg_class_reloptions - 1] = true;
17073 :
17074 181 : repl_repl[Anum_pg_class_reloptions - 1] = true;
17075 :
17076 181 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17077 : repl_val, repl_null, repl_repl);
17078 :
17079 181 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17080 :
17081 181 : InvokeObjectPostAlterHookArg(RelationRelationId,
17082 : RelationGetRelid(toastrel), 0,
17083 : InvalidOid, true);
17084 :
17085 181 : heap_freetuple(newtuple);
17086 :
17087 181 : ReleaseSysCache(tuple);
17088 :
17089 181 : table_close(toastrel, NoLock);
17090 : }
17091 :
17092 593 : table_close(pgclass, RowExclusiveLock);
17093 : }
17094 :
17095 : /*
17096 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
17097 : * rewriting to be done, so we just want to copy the data as fast as possible.
17098 : */
17099 : static void
17100 100 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
17101 : {
17102 : Relation rel;
17103 : Oid reltoastrelid;
17104 : RelFileNumber newrelfilenumber;
17105 : RelFileLocator newrlocator;
17106 100 : List *reltoastidxids = NIL;
17107 : ListCell *lc;
17108 :
17109 : /*
17110 : * Need lock here in case we are recursing to toast table or index
17111 : */
17112 100 : rel = relation_open(tableOid, lockmode);
17113 :
17114 : /* Check first if relation can be moved to new tablespace */
17115 100 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17116 : {
17117 5 : InvokeObjectPostAlterHook(RelationRelationId,
17118 : RelationGetRelid(rel), 0);
17119 5 : relation_close(rel, NoLock);
17120 5 : return;
17121 : }
17122 :
17123 95 : reltoastrelid = rel->rd_rel->reltoastrelid;
17124 : /* Fetch the list of indexes on toast relation if necessary */
17125 95 : if (OidIsValid(reltoastrelid))
17126 : {
17127 13 : Relation toastRel = relation_open(reltoastrelid, lockmode);
17128 :
17129 13 : reltoastidxids = RelationGetIndexList(toastRel);
17130 13 : relation_close(toastRel, lockmode);
17131 : }
17132 :
17133 : /*
17134 : * Relfilenumbers are not unique in databases across tablespaces, so we
17135 : * need to allocate a new one in the new tablespace.
17136 : */
17137 95 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
17138 95 : rel->rd_rel->relpersistence);
17139 :
17140 : /* Open old and new relation */
17141 95 : newrlocator = rel->rd_locator;
17142 95 : newrlocator.relNumber = newrelfilenumber;
17143 95 : newrlocator.spcOid = newTableSpace;
17144 :
17145 : /* hand off to AM to actually create new rel storage and copy the data */
17146 95 : if (rel->rd_rel->relkind == RELKIND_INDEX)
17147 : {
17148 36 : index_copy_data(rel, newrlocator);
17149 : }
17150 : else
17151 : {
17152 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
17153 59 : table_relation_copy_data(rel, &newrlocator);
17154 : }
17155 :
17156 : /*
17157 : * Update the pg_class row.
17158 : *
17159 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
17160 : * executed on pg_class or its indexes (the above copy wouldn't contain
17161 : * the updated pg_class entry), but that's forbidden with
17162 : * CheckRelationTableSpaceMove().
17163 : */
17164 95 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
17165 :
17166 95 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
17167 :
17168 95 : RelationAssumeNewRelfilelocator(rel);
17169 :
17170 95 : relation_close(rel, NoLock);
17171 :
17172 : /* Make sure the reltablespace change is visible */
17173 95 : CommandCounterIncrement();
17174 :
17175 : /* Move associated toast relation and/or indexes, too */
17176 95 : if (OidIsValid(reltoastrelid))
17177 13 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
17178 108 : foreach(lc, reltoastidxids)
17179 13 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
17180 :
17181 : /* Clean up */
17182 95 : list_free(reltoastidxids);
17183 : }
17184 :
17185 : /*
17186 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
17187 : * storage that have an interest in preserving tablespace.
17188 : *
17189 : * Since these have no storage the tablespace can be updated with a simple
17190 : * metadata only operation to update the tablespace.
17191 : */
17192 : static void
17193 24 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
17194 : {
17195 : /*
17196 : * Shouldn't be called on relations having storage; these are processed in
17197 : * phase 3.
17198 : */
17199 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
17200 :
17201 : /* check if relation can be moved to its new tablespace */
17202 24 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17203 : {
17204 0 : InvokeObjectPostAlterHook(RelationRelationId,
17205 : RelationGetRelid(rel),
17206 : 0);
17207 0 : return;
17208 : }
17209 :
17210 : /* Update can be done, so change reltablespace */
17211 20 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
17212 :
17213 20 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
17214 :
17215 : /* Make sure the reltablespace change is visible */
17216 20 : CommandCounterIncrement();
17217 : }
17218 :
17219 : /*
17220 : * Alter Table ALL ... SET TABLESPACE
17221 : *
17222 : * Allows a user to move all objects of some type in a given tablespace in the
17223 : * current database to another tablespace. Objects can be chosen based on the
17224 : * owner of the object also, to allow users to move only their objects.
17225 : * The user must have CREATE rights on the new tablespace, as usual. The main
17226 : * permissions handling is done by the lower-level table move function.
17227 : *
17228 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
17229 : * lock can't be acquired then we ereport(ERROR).
17230 : */
17231 : Oid
17232 15 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
17233 : {
17234 15 : List *relations = NIL;
17235 : ListCell *l;
17236 : ScanKeyData key[1];
17237 : Relation rel;
17238 : TableScanDesc scan;
17239 : HeapTuple tuple;
17240 : Oid orig_tablespaceoid;
17241 : Oid new_tablespaceoid;
17242 15 : List *role_oids = roleSpecsToIds(stmt->roles);
17243 :
17244 : /* Ensure we were not asked to move something we can't */
17245 15 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17246 6 : stmt->objtype != OBJECT_MATVIEW)
17247 0 : ereport(ERROR,
17248 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17249 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17250 :
17251 : /* Get the orig and new tablespace OIDs */
17252 15 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17253 15 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17254 :
17255 : /* Can't move shared relations in to or out of pg_global */
17256 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17257 15 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17258 : new_tablespaceoid == GLOBALTABLESPACE_OID)
17259 0 : ereport(ERROR,
17260 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17261 : errmsg("cannot move relations in to or out of pg_global tablespace")));
17262 :
17263 : /*
17264 : * Must have CREATE rights on the new tablespace, unless it is the
17265 : * database default tablespace (which all users implicitly have CREATE
17266 : * rights on).
17267 : */
17268 15 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17269 : {
17270 : AclResult aclresult;
17271 :
17272 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17273 : ACL_CREATE);
17274 0 : if (aclresult != ACLCHECK_OK)
17275 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
17276 0 : get_tablespace_name(new_tablespaceoid));
17277 : }
17278 :
17279 : /*
17280 : * Now that the checks are done, check if we should set either to
17281 : * InvalidOid because it is our database's default tablespace.
17282 : */
17283 15 : if (orig_tablespaceoid == MyDatabaseTableSpace)
17284 0 : orig_tablespaceoid = InvalidOid;
17285 :
17286 15 : if (new_tablespaceoid == MyDatabaseTableSpace)
17287 15 : new_tablespaceoid = InvalidOid;
17288 :
17289 : /* no-op */
17290 15 : if (orig_tablespaceoid == new_tablespaceoid)
17291 0 : return new_tablespaceoid;
17292 :
17293 : /*
17294 : * Walk the list of objects in the tablespace and move them. This will
17295 : * only find objects in our database, of course.
17296 : */
17297 15 : ScanKeyInit(&key[0],
17298 : Anum_pg_class_reltablespace,
17299 : BTEqualStrategyNumber, F_OIDEQ,
17300 : ObjectIdGetDatum(orig_tablespaceoid));
17301 :
17302 15 : rel = table_open(RelationRelationId, AccessShareLock);
17303 15 : scan = table_beginscan_catalog(rel, 1, key);
17304 66 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17305 : {
17306 51 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17307 51 : Oid relOid = relForm->oid;
17308 :
17309 : /*
17310 : * Do not move objects in pg_catalog as part of this, if an admin
17311 : * really wishes to do so, they can issue the individual ALTER
17312 : * commands directly.
17313 : *
17314 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
17315 : * (TOAST will be moved with the main table).
17316 : */
17317 51 : if (IsCatalogNamespace(relForm->relnamespace) ||
17318 102 : relForm->relisshared ||
17319 102 : isAnyTempNamespace(relForm->relnamespace) ||
17320 51 : IsToastNamespace(relForm->relnamespace))
17321 0 : continue;
17322 :
17323 : /* Only move the object type requested */
17324 51 : if ((stmt->objtype == OBJECT_TABLE &&
17325 30 : relForm->relkind != RELKIND_RELATION &&
17326 18 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17327 33 : (stmt->objtype == OBJECT_INDEX &&
17328 18 : relForm->relkind != RELKIND_INDEX &&
17329 3 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17330 30 : (stmt->objtype == OBJECT_MATVIEW &&
17331 3 : relForm->relkind != RELKIND_MATVIEW))
17332 21 : continue;
17333 :
17334 : /* Check if we are only moving objects owned by certain roles */
17335 30 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17336 0 : continue;
17337 :
17338 : /*
17339 : * Handle permissions-checking here since we are locking the tables
17340 : * and also to avoid doing a bunch of work only to fail part-way. Note
17341 : * that permissions will also be checked by AlterTableInternal().
17342 : *
17343 : * Caller must be considered an owner on the table to move it.
17344 : */
17345 30 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17346 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
17347 0 : NameStr(relForm->relname));
17348 :
17349 30 : if (stmt->nowait &&
17350 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
17351 0 : ereport(ERROR,
17352 : (errcode(ERRCODE_OBJECT_IN_USE),
17353 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
17354 : get_namespace_name(relForm->relnamespace),
17355 : NameStr(relForm->relname))));
17356 : else
17357 30 : LockRelationOid(relOid, AccessExclusiveLock);
17358 :
17359 : /* Add to our list of objects to move */
17360 30 : relations = lappend_oid(relations, relOid);
17361 : }
17362 :
17363 15 : table_endscan(scan);
17364 15 : table_close(rel, AccessShareLock);
17365 :
17366 15 : if (relations == NIL)
17367 6 : ereport(NOTICE,
17368 : (errcode(ERRCODE_NO_DATA_FOUND),
17369 : errmsg("no matching relations in tablespace \"%s\" found",
17370 : orig_tablespaceoid == InvalidOid ? "(database default)" :
17371 : get_tablespace_name(orig_tablespaceoid))));
17372 :
17373 : /* Everything is locked, loop through and move all of the relations. */
17374 45 : foreach(l, relations)
17375 : {
17376 30 : List *cmds = NIL;
17377 30 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
17378 :
17379 30 : cmd->subtype = AT_SetTableSpace;
17380 30 : cmd->name = stmt->new_tablespacename;
17381 :
17382 30 : cmds = lappend(cmds, cmd);
17383 :
17384 30 : EventTriggerAlterTableStart((Node *) stmt);
17385 : /* OID is set by AlterTableInternal */
17386 30 : AlterTableInternal(lfirst_oid(l), cmds, false);
17387 30 : EventTriggerAlterTableEnd();
17388 : }
17389 :
17390 15 : return new_tablespaceoid;
17391 : }
17392 :
17393 : static void
17394 36 : index_copy_data(Relation rel, RelFileLocator newrlocator)
17395 : {
17396 : SMgrRelation dstrel;
17397 :
17398 : /*
17399 : * Since we copy the file directly without looking at the shared buffers,
17400 : * we'd better first flush out any pages of the source relation that are
17401 : * in shared buffers. We assume no new changes will be made while we are
17402 : * holding exclusive lock on the rel.
17403 : */
17404 36 : FlushRelationBuffers(rel);
17405 :
17406 : /*
17407 : * Create and copy all forks of the relation, and schedule unlinking of
17408 : * old physical files.
17409 : *
17410 : * NOTE: any conflict in relfilenumber value will be caught in
17411 : * RelationCreateStorage().
17412 : */
17413 36 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17414 :
17415 : /* copy main fork */
17416 36 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
17417 36 : rel->rd_rel->relpersistence);
17418 :
17419 : /* copy those extra forks that exist */
17420 36 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17421 144 : forkNum <= MAX_FORKNUM; forkNum++)
17422 : {
17423 108 : if (smgrexists(RelationGetSmgr(rel), forkNum))
17424 : {
17425 0 : smgrcreate(dstrel, forkNum, false);
17426 :
17427 : /*
17428 : * WAL log creation if the relation is persistent, or this is the
17429 : * init fork of an unlogged relation.
17430 : */
17431 0 : if (RelationIsPermanent(rel) ||
17432 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17433 : forkNum == INIT_FORKNUM))
17434 0 : log_smgrcreate(&newrlocator, forkNum);
17435 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17436 0 : rel->rd_rel->relpersistence);
17437 : }
17438 : }
17439 :
17440 : /* drop old relation, and close new one */
17441 36 : RelationDropStorage(rel);
17442 36 : smgrclose(dstrel);
17443 36 : }
17444 :
17445 : /*
17446 : * ALTER TABLE ENABLE/DISABLE TRIGGER
17447 : *
17448 : * We just pass this off to trigger.c.
17449 : */
17450 : static void
17451 191 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17452 : char fires_when, bool skip_system, bool recurse,
17453 : LOCKMODE lockmode)
17454 : {
17455 191 : EnableDisableTrigger(rel, trigname, InvalidOid,
17456 : fires_when, skip_system, recurse,
17457 : lockmode);
17458 :
17459 191 : InvokeObjectPostAlterHook(RelationRelationId,
17460 : RelationGetRelid(rel), 0);
17461 191 : }
17462 :
17463 : /*
17464 : * ALTER TABLE ENABLE/DISABLE RULE
17465 : *
17466 : * We just pass this off to rewriteDefine.c.
17467 : */
17468 : static void
17469 29 : ATExecEnableDisableRule(Relation rel, const char *rulename,
17470 : char fires_when, LOCKMODE lockmode)
17471 : {
17472 29 : EnableDisableRule(rel, rulename, fires_when);
17473 :
17474 29 : InvokeObjectPostAlterHook(RelationRelationId,
17475 : RelationGetRelid(rel), 0);
17476 29 : }
17477 :
17478 : /*
17479 : * Preparation phase of [NO] INHERIT
17480 : *
17481 : * Check the relation defined as a child.
17482 : */
17483 : static void
17484 378 : ATPrepChangeInherit(Relation child_rel)
17485 : {
17486 378 : if (child_rel->rd_rel->reloftype)
17487 8 : ereport(ERROR,
17488 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17489 : errmsg("cannot change inheritance of typed table")));
17490 :
17491 370 : if (child_rel->rd_rel->relispartition)
17492 12 : ereport(ERROR,
17493 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17494 : errmsg("cannot change inheritance of a partition")));
17495 358 : }
17496 :
17497 : /*
17498 : * ALTER TABLE INHERIT
17499 : *
17500 : * Return the address of the new parent relation.
17501 : */
17502 : static ObjectAddress
17503 289 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17504 : {
17505 : Relation parent_rel;
17506 : List *children;
17507 : ObjectAddress address;
17508 : const char *trigger_name;
17509 :
17510 : /*
17511 : * A self-exclusive lock is needed here. See the similar case in
17512 : * MergeAttributes() for a full explanation.
17513 : */
17514 289 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17515 :
17516 : /*
17517 : * Must be owner of both parent and child -- child was checked by
17518 : * ATSimplePermissions call in ATPrepCmd
17519 : */
17520 289 : ATSimplePermissions(AT_AddInherit, parent_rel,
17521 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
17522 :
17523 : /* Permanent rels cannot inherit from temporary ones */
17524 289 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17525 4 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17526 0 : ereport(ERROR,
17527 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17528 : errmsg("cannot inherit from temporary relation \"%s\"",
17529 : RelationGetRelationName(parent_rel))));
17530 :
17531 : /* If parent rel is temp, it must belong to this session */
17532 289 : if (RELATION_IS_OTHER_TEMP(parent_rel))
17533 0 : ereport(ERROR,
17534 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17535 : errmsg("cannot inherit from temporary relation of another session")));
17536 :
17537 : /* Ditto for the child */
17538 289 : if (RELATION_IS_OTHER_TEMP(child_rel))
17539 0 : ereport(ERROR,
17540 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17541 : errmsg("cannot inherit to temporary relation of another session")));
17542 :
17543 : /* Prevent partitioned tables from becoming inheritance parents */
17544 289 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17545 4 : ereport(ERROR,
17546 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17547 : errmsg("cannot inherit from partitioned table \"%s\"",
17548 : parent->relname)));
17549 :
17550 : /* Likewise for partitions */
17551 285 : if (parent_rel->rd_rel->relispartition)
17552 4 : ereport(ERROR,
17553 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17554 : errmsg("cannot inherit from a partition")));
17555 :
17556 : /*
17557 : * Prevent circularity by seeing if proposed parent inherits from child.
17558 : * (In particular, this disallows making a rel inherit from itself.)
17559 : *
17560 : * This is not completely bulletproof because of race conditions: in
17561 : * multi-level inheritance trees, someone else could concurrently be
17562 : * making another inheritance link that closes the loop but does not join
17563 : * either of the rels we have locked. Preventing that seems to require
17564 : * exclusive locks on the entire inheritance tree, which is a cure worse
17565 : * than the disease. find_all_inheritors() will cope with circularity
17566 : * anyway, so don't sweat it too much.
17567 : *
17568 : * We use weakest lock we can on child's children, namely AccessShareLock.
17569 : */
17570 281 : children = find_all_inheritors(RelationGetRelid(child_rel),
17571 : AccessShareLock, NULL);
17572 :
17573 281 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
17574 8 : ereport(ERROR,
17575 : (errcode(ERRCODE_DUPLICATE_TABLE),
17576 : errmsg("circular inheritance not allowed"),
17577 : errdetail("\"%s\" is already a child of \"%s\".",
17578 : parent->relname,
17579 : RelationGetRelationName(child_rel))));
17580 :
17581 : /*
17582 : * If child_rel has row-level triggers with transition tables, we
17583 : * currently don't allow it to become an inheritance child. See also
17584 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
17585 : */
17586 273 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17587 273 : if (trigger_name != NULL)
17588 4 : ereport(ERROR,
17589 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17590 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17591 : trigger_name, RelationGetRelationName(child_rel)),
17592 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17593 :
17594 : /* OK to create inheritance */
17595 269 : CreateInheritance(child_rel, parent_rel, false);
17596 :
17597 209 : ObjectAddressSet(address, RelationRelationId,
17598 : RelationGetRelid(parent_rel));
17599 :
17600 : /* keep our lock on the parent relation until commit */
17601 209 : table_close(parent_rel, NoLock);
17602 :
17603 209 : return address;
17604 : }
17605 :
17606 : /*
17607 : * CreateInheritance
17608 : * Catalog manipulation portion of creating inheritance between a child
17609 : * table and a parent table.
17610 : *
17611 : * This verifies that all the columns and check constraints of the parent
17612 : * appear in the child and that they have the same data types and expressions.
17613 : *
17614 : * Common to ATExecAddInherit() and ATExecAttachPartition().
17615 : */
17616 : static void
17617 2185 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17618 : {
17619 : Relation catalogRelation;
17620 : SysScanDesc scan;
17621 : ScanKeyData key;
17622 : HeapTuple inheritsTuple;
17623 : int32 inhseqno;
17624 :
17625 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17626 2185 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17627 :
17628 : /*
17629 : * Check for duplicates in the list of parents, and determine the highest
17630 : * inhseqno already present; we'll use the next one for the new parent.
17631 : * Also, if proposed child is a partition, it cannot already be
17632 : * inheriting.
17633 : *
17634 : * Note: we do not reject the case where the child already inherits from
17635 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17636 : */
17637 2185 : ScanKeyInit(&key,
17638 : Anum_pg_inherits_inhrelid,
17639 : BTEqualStrategyNumber, F_OIDEQ,
17640 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17641 2185 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17642 : true, NULL, 1, &key);
17643 :
17644 : /* inhseqno sequences start at 1 */
17645 2185 : inhseqno = 0;
17646 2228 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17647 : {
17648 47 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17649 :
17650 47 : if (inh->inhparent == RelationGetRelid(parent_rel))
17651 4 : ereport(ERROR,
17652 : (errcode(ERRCODE_DUPLICATE_TABLE),
17653 : errmsg("relation \"%s\" would be inherited from more than once",
17654 : RelationGetRelationName(parent_rel))));
17655 :
17656 43 : if (inh->inhseqno > inhseqno)
17657 43 : inhseqno = inh->inhseqno;
17658 : }
17659 2181 : systable_endscan(scan);
17660 :
17661 : /* Match up the columns and bump attinhcount as needed */
17662 2181 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17663 :
17664 : /* Match up the constraints and bump coninhcount as needed */
17665 2093 : MergeConstraintsIntoExisting(child_rel, parent_rel);
17666 :
17667 : /*
17668 : * OK, it looks valid. Make the catalog entries that show inheritance.
17669 : */
17670 2053 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
17671 : RelationGetRelid(parent_rel),
17672 : inhseqno + 1,
17673 : catalogRelation,
17674 2053 : parent_rel->rd_rel->relkind ==
17675 : RELKIND_PARTITIONED_TABLE);
17676 :
17677 : /* Now we're done with pg_inherits */
17678 2053 : table_close(catalogRelation, RowExclusiveLock);
17679 2053 : }
17680 :
17681 : /*
17682 : * Obtain the source-text form of the constraint expression for a check
17683 : * constraint, given its pg_constraint tuple
17684 : */
17685 : static char *
17686 272 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
17687 : {
17688 : Form_pg_constraint con;
17689 : bool isnull;
17690 : Datum attr;
17691 : Datum expr;
17692 :
17693 272 : con = (Form_pg_constraint) GETSTRUCT(contup);
17694 272 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17695 272 : if (isnull)
17696 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
17697 :
17698 272 : expr = DirectFunctionCall2(pg_get_expr, attr,
17699 : ObjectIdGetDatum(con->conrelid));
17700 272 : return TextDatumGetCString(expr);
17701 : }
17702 :
17703 : /*
17704 : * Determine whether two check constraints are functionally equivalent
17705 : *
17706 : * The test we apply is to see whether they reverse-compile to the same
17707 : * source string. This insulates us from issues like whether attributes
17708 : * have the same physical column numbers in parent and child relations.
17709 : *
17710 : * Note that we ignore enforceability as there are cases where constraints
17711 : * with differing enforceability are allowed.
17712 : */
17713 : static bool
17714 136 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
17715 : {
17716 136 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
17717 136 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
17718 :
17719 136 : if (acon->condeferrable != bcon->condeferrable ||
17720 136 : acon->condeferred != bcon->condeferred ||
17721 136 : strcmp(decompile_conbin(a, tupleDesc),
17722 136 : decompile_conbin(b, tupleDesc)) != 0)
17723 4 : return false;
17724 : else
17725 132 : return true;
17726 : }
17727 :
17728 : /*
17729 : * Check columns in child table match up with columns in parent, and increment
17730 : * their attinhcount.
17731 : *
17732 : * Called by CreateInheritance
17733 : *
17734 : * Currently all parent columns must be found in child. Missing columns are an
17735 : * error. One day we might consider creating new columns like CREATE TABLE
17736 : * does. However, that is widely unpopular --- in the common use case of
17737 : * partitioned tables it's a foot-gun.
17738 : *
17739 : * The data type must match exactly. If the parent column is NOT NULL then
17740 : * the child must be as well. Defaults are not compared, however.
17741 : */
17742 : static void
17743 2181 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17744 : {
17745 : Relation attrrel;
17746 : TupleDesc parent_desc;
17747 :
17748 2181 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17749 2181 : parent_desc = RelationGetDescr(parent_rel);
17750 :
17751 7201 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17752 : {
17753 5108 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17754 5108 : char *parent_attname = NameStr(parent_att->attname);
17755 : HeapTuple tuple;
17756 :
17757 : /* Ignore dropped columns in the parent. */
17758 5108 : if (parent_att->attisdropped)
17759 180 : continue;
17760 :
17761 : /* Find same column in child (matching on column name). */
17762 4928 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17763 4928 : if (HeapTupleIsValid(tuple))
17764 : {
17765 4920 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17766 :
17767 4920 : if (parent_att->atttypid != child_att->atttypid ||
17768 4916 : parent_att->atttypmod != child_att->atttypmod)
17769 8 : ereport(ERROR,
17770 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17771 : errmsg("child table \"%s\" has different type for column \"%s\"",
17772 : RelationGetRelationName(child_rel), parent_attname)));
17773 :
17774 4912 : if (parent_att->attcollation != child_att->attcollation)
17775 4 : ereport(ERROR,
17776 : (errcode(ERRCODE_COLLATION_MISMATCH),
17777 : errmsg("child table \"%s\" has different collation for column \"%s\"",
17778 : RelationGetRelationName(child_rel), parent_attname)));
17779 :
17780 : /*
17781 : * If the parent has a not-null constraint that's not NO INHERIT,
17782 : * make sure the child has one too.
17783 : *
17784 : * Other constraints are checked elsewhere.
17785 : */
17786 4908 : if (parent_att->attnotnull && !child_att->attnotnull)
17787 : {
17788 : HeapTuple contup;
17789 :
17790 32 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17791 32 : parent_att->attnum);
17792 32 : if (HeapTupleIsValid(contup) &&
17793 32 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17794 20 : ereport(ERROR,
17795 : errcode(ERRCODE_DATATYPE_MISMATCH),
17796 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17797 : parent_attname, RelationGetRelationName(child_rel)));
17798 : }
17799 :
17800 : /*
17801 : * Child column must be generated if and only if parent column is.
17802 : */
17803 4888 : if (parent_att->attgenerated && !child_att->attgenerated)
17804 24 : ereport(ERROR,
17805 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17806 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17807 4864 : if (child_att->attgenerated && !parent_att->attgenerated)
17808 16 : ereport(ERROR,
17809 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17810 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17811 :
17812 4848 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17813 8 : ereport(ERROR,
17814 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17815 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17816 : errdetail("Parent column is %s, child column is %s.",
17817 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17818 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17819 :
17820 : /*
17821 : * Regular inheritance children are independent enough not to
17822 : * inherit identity columns. But partitions are integral part of
17823 : * a partitioned table and inherit identity column.
17824 : */
17825 4840 : if (ispartition)
17826 4363 : child_att->attidentity = parent_att->attidentity;
17827 :
17828 : /*
17829 : * OK, bump the child column's inheritance count. (If we fail
17830 : * later on, this change will just roll back.)
17831 : */
17832 4840 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
17833 : &child_att->attinhcount))
17834 0 : ereport(ERROR,
17835 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17836 : errmsg("too many inheritance parents"));
17837 :
17838 : /*
17839 : * In case of partitions, we must enforce that value of attislocal
17840 : * is same in all partitions. (Note: there are only inherited
17841 : * attributes in partitions)
17842 : */
17843 4840 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17844 : {
17845 : Assert(child_att->attinhcount == 1);
17846 4363 : child_att->attislocal = false;
17847 : }
17848 :
17849 4840 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17850 4840 : heap_freetuple(tuple);
17851 : }
17852 : else
17853 : {
17854 8 : ereport(ERROR,
17855 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17856 : errmsg("child table is missing column \"%s\"", parent_attname)));
17857 : }
17858 : }
17859 :
17860 2093 : table_close(attrrel, RowExclusiveLock);
17861 2093 : }
17862 :
17863 : /*
17864 : * Check constraints in child table match up with constraints in parent,
17865 : * and increment their coninhcount.
17866 : *
17867 : * Constraints that are marked ONLY in the parent are ignored.
17868 : *
17869 : * Called by CreateInheritance
17870 : *
17871 : * Currently all constraints in parent must be present in the child. One day we
17872 : * may consider adding new constraints like CREATE TABLE does.
17873 : *
17874 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
17875 : * constraints. As long as tables have more like 10 constraints it shouldn't be
17876 : * a problem though. Even 100 constraints ought not be the end of the world.
17877 : *
17878 : * XXX See MergeWithExistingConstraint too if you change this code.
17879 : */
17880 : static void
17881 2093 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
17882 : {
17883 : Relation constraintrel;
17884 : SysScanDesc parent_scan;
17885 : ScanKeyData parent_key;
17886 : HeapTuple parent_tuple;
17887 2093 : Oid parent_relid = RelationGetRelid(parent_rel);
17888 : AttrMap *attmap;
17889 :
17890 2093 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17891 :
17892 : /* Outer loop scans through the parent's constraint definitions */
17893 2093 : ScanKeyInit(&parent_key,
17894 : Anum_pg_constraint_conrelid,
17895 : BTEqualStrategyNumber, F_OIDEQ,
17896 : ObjectIdGetDatum(parent_relid));
17897 2093 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17898 : true, NULL, 1, &parent_key);
17899 :
17900 2093 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17901 : RelationGetDescr(child_rel),
17902 : true);
17903 :
17904 3704 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17905 : {
17906 1651 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17907 : SysScanDesc child_scan;
17908 : ScanKeyData child_key;
17909 : HeapTuple child_tuple;
17910 : AttrNumber parent_attno;
17911 1651 : bool found = false;
17912 :
17913 1651 : if (parent_con->contype != CONSTRAINT_CHECK &&
17914 1483 : parent_con->contype != CONSTRAINT_NOTNULL)
17915 770 : continue;
17916 :
17917 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17918 913 : if (parent_con->connoinherit)
17919 32 : continue;
17920 :
17921 881 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17922 729 : parent_attno = extractNotNullColumn(parent_tuple);
17923 : else
17924 152 : parent_attno = InvalidAttrNumber;
17925 :
17926 : /* Search for a child constraint matching this one */
17927 881 : ScanKeyInit(&child_key,
17928 : Anum_pg_constraint_conrelid,
17929 : BTEqualStrategyNumber, F_OIDEQ,
17930 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17931 881 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17932 : true, NULL, 1, &child_key);
17933 :
17934 1408 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17935 : {
17936 1392 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17937 : HeapTuple child_copy;
17938 :
17939 1392 : if (child_con->contype != parent_con->contype)
17940 274 : continue;
17941 :
17942 : /*
17943 : * CHECK constraint are matched by constraint name, NOT NULL ones
17944 : * by attribute number.
17945 : */
17946 1118 : if (child_con->contype == CONSTRAINT_CHECK)
17947 : {
17948 262 : if (strcmp(NameStr(parent_con->conname),
17949 199 : NameStr(child_con->conname)) != 0)
17950 63 : continue;
17951 : }
17952 919 : else if (child_con->contype == CONSTRAINT_NOTNULL)
17953 : {
17954 : Form_pg_attribute parent_attr;
17955 : Form_pg_attribute child_attr;
17956 : AttrNumber child_attno;
17957 :
17958 919 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17959 919 : child_attno = extractNotNullColumn(child_tuple);
17960 919 : if (parent_attno != attmap->attnums[child_attno - 1])
17961 190 : continue;
17962 :
17963 729 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17964 : /* there shouldn't be constraints on dropped columns */
17965 729 : if (parent_attr->attisdropped || child_attr->attisdropped)
17966 0 : elog(ERROR, "found not-null constraint on dropped columns");
17967 : }
17968 :
17969 865 : if (child_con->contype == CONSTRAINT_CHECK &&
17970 136 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17971 4 : ereport(ERROR,
17972 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17973 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17974 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17975 :
17976 : /*
17977 : * If the child constraint is "no inherit" then cannot merge
17978 : */
17979 861 : if (child_con->connoinherit)
17980 8 : ereport(ERROR,
17981 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17982 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17983 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17984 :
17985 : /*
17986 : * If the child constraint is "not valid" then cannot merge with a
17987 : * valid parent constraint
17988 : */
17989 853 : if (parent_con->convalidated && child_con->conenforced &&
17990 773 : !child_con->convalidated)
17991 8 : ereport(ERROR,
17992 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17993 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17994 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17995 :
17996 : /*
17997 : * A NOT ENFORCED child constraint cannot be merged with an
17998 : * ENFORCED parent constraint. However, the reverse is allowed,
17999 : * where the child constraint is ENFORCED.
18000 : */
18001 845 : if (parent_con->conenforced && !child_con->conenforced)
18002 4 : ereport(ERROR,
18003 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18004 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
18005 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
18006 :
18007 : /*
18008 : * OK, bump the child constraint's inheritance count. (If we fail
18009 : * later on, this change will just roll back.)
18010 : */
18011 841 : child_copy = heap_copytuple(child_tuple);
18012 841 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
18013 :
18014 841 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
18015 : &child_con->coninhcount))
18016 0 : ereport(ERROR,
18017 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
18018 : errmsg("too many inheritance parents"));
18019 :
18020 : /*
18021 : * In case of partitions, an inherited constraint must be
18022 : * inherited only once since it cannot have multiple parents and
18023 : * it is never considered local.
18024 : */
18025 841 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18026 : {
18027 : Assert(child_con->coninhcount == 1);
18028 750 : child_con->conislocal = false;
18029 : }
18030 :
18031 841 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
18032 841 : heap_freetuple(child_copy);
18033 :
18034 841 : found = true;
18035 841 : break;
18036 : }
18037 :
18038 857 : systable_endscan(child_scan);
18039 :
18040 857 : if (!found)
18041 : {
18042 16 : if (parent_con->contype == CONSTRAINT_NOTNULL)
18043 0 : ereport(ERROR,
18044 : errcode(ERRCODE_DATATYPE_MISMATCH),
18045 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18046 : get_attname(parent_relid,
18047 : extractNotNullColumn(parent_tuple),
18048 : false),
18049 : RelationGetRelationName(child_rel)));
18050 :
18051 16 : ereport(ERROR,
18052 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18053 : errmsg("child table is missing constraint \"%s\"",
18054 : NameStr(parent_con->conname))));
18055 : }
18056 : }
18057 :
18058 2053 : systable_endscan(parent_scan);
18059 2053 : table_close(constraintrel, RowExclusiveLock);
18060 2053 : }
18061 :
18062 : /*
18063 : * ALTER TABLE NO INHERIT
18064 : *
18065 : * Return value is the address of the relation that is no longer parent.
18066 : */
18067 : static ObjectAddress
18068 69 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
18069 : {
18070 : ObjectAddress address;
18071 : Relation parent_rel;
18072 :
18073 : /*
18074 : * AccessShareLock on the parent is probably enough, seeing that DROP
18075 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
18076 : * be inspecting the parent's schema.
18077 : */
18078 69 : parent_rel = table_openrv(parent, AccessShareLock);
18079 :
18080 : /*
18081 : * We don't bother to check ownership of the parent table --- ownership of
18082 : * the child is presumed enough rights.
18083 : */
18084 :
18085 : /* Off to RemoveInheritance() where most of the work happens */
18086 69 : RemoveInheritance(rel, parent_rel, false);
18087 :
18088 65 : ObjectAddressSet(address, RelationRelationId,
18089 : RelationGetRelid(parent_rel));
18090 :
18091 : /* keep our lock on the parent relation until commit */
18092 65 : table_close(parent_rel, NoLock);
18093 :
18094 65 : return address;
18095 : }
18096 :
18097 : /*
18098 : * MarkInheritDetached
18099 : *
18100 : * Set inhdetachpending for a partition, for ATExecDetachPartition
18101 : * in concurrent mode. While at it, verify that no other partition is
18102 : * already pending detach.
18103 : */
18104 : static void
18105 75 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
18106 : {
18107 : Relation catalogRelation;
18108 : SysScanDesc scan;
18109 : ScanKeyData key;
18110 : HeapTuple inheritsTuple;
18111 75 : bool found = false;
18112 :
18113 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
18114 :
18115 : /*
18116 : * Find pg_inherits entries by inhparent. (We need to scan them all in
18117 : * order to verify that no other partition is pending detach.)
18118 : */
18119 75 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
18120 75 : ScanKeyInit(&key,
18121 : Anum_pg_inherits_inhparent,
18122 : BTEqualStrategyNumber, F_OIDEQ,
18123 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18124 75 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
18125 : true, NULL, 1, &key);
18126 :
18127 294 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
18128 : {
18129 : Form_pg_inherits inhForm;
18130 :
18131 145 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
18132 145 : if (inhForm->inhdetachpending)
18133 1 : ereport(ERROR,
18134 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18135 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
18136 : get_rel_name(inhForm->inhrelid),
18137 : get_namespace_name(parent_rel->rd_rel->relnamespace),
18138 : RelationGetRelationName(parent_rel)),
18139 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
18140 :
18141 144 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
18142 : {
18143 : HeapTuple newtup;
18144 :
18145 74 : newtup = heap_copytuple(inheritsTuple);
18146 74 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
18147 :
18148 74 : CatalogTupleUpdate(catalogRelation,
18149 74 : &inheritsTuple->t_self,
18150 : newtup);
18151 74 : found = true;
18152 74 : heap_freetuple(newtup);
18153 : /* keep looking, to ensure we catch others pending detach */
18154 : }
18155 : }
18156 :
18157 : /* Done */
18158 74 : systable_endscan(scan);
18159 74 : table_close(catalogRelation, RowExclusiveLock);
18160 :
18161 74 : if (!found)
18162 0 : ereport(ERROR,
18163 : (errcode(ERRCODE_UNDEFINED_TABLE),
18164 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18165 : RelationGetRelationName(child_rel),
18166 : RelationGetRelationName(parent_rel))));
18167 74 : }
18168 :
18169 : /*
18170 : * RemoveInheritance
18171 : *
18172 : * Drop a parent from the child's parents. This just adjusts the attinhcount
18173 : * and attislocal of the columns and removes the pg_inherit and pg_depend
18174 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
18175 : *
18176 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
18177 : * up attislocal stays true, which means if a child is ever removed from a
18178 : * parent then its columns will never be automatically dropped which may
18179 : * surprise. But at least we'll never surprise by dropping columns someone
18180 : * isn't expecting to be dropped which would actually mean data loss.
18181 : *
18182 : * coninhcount and conislocal for inherited constraints are adjusted in
18183 : * exactly the same way.
18184 : *
18185 : * Common to ATExecDropInherit() and ATExecDetachPartition().
18186 : */
18187 : static void
18188 773 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
18189 : {
18190 : Relation catalogRelation;
18191 : SysScanDesc scan;
18192 : ScanKeyData key[3];
18193 : HeapTuple attributeTuple,
18194 : constraintTuple;
18195 : AttrMap *attmap;
18196 : List *connames;
18197 : List *nncolumns;
18198 : bool found;
18199 : bool is_partitioning;
18200 :
18201 773 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
18202 :
18203 773 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
18204 : RelationGetRelid(parent_rel),
18205 : expect_detached,
18206 773 : RelationGetRelationName(child_rel));
18207 773 : if (!found)
18208 : {
18209 16 : if (is_partitioning)
18210 12 : ereport(ERROR,
18211 : (errcode(ERRCODE_UNDEFINED_TABLE),
18212 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18213 : RelationGetRelationName(child_rel),
18214 : RelationGetRelationName(parent_rel))));
18215 : else
18216 4 : ereport(ERROR,
18217 : (errcode(ERRCODE_UNDEFINED_TABLE),
18218 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18219 : RelationGetRelationName(parent_rel),
18220 : RelationGetRelationName(child_rel))));
18221 : }
18222 :
18223 : /*
18224 : * Search through child columns looking for ones matching parent rel
18225 : */
18226 757 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
18227 757 : ScanKeyInit(&key[0],
18228 : Anum_pg_attribute_attrelid,
18229 : BTEqualStrategyNumber, F_OIDEQ,
18230 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18231 757 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
18232 : true, NULL, 1, key);
18233 7013 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
18234 : {
18235 6256 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
18236 :
18237 : /* Ignore if dropped or not inherited */
18238 6256 : if (att->attisdropped)
18239 28 : continue;
18240 6228 : if (att->attinhcount <= 0)
18241 4570 : continue;
18242 :
18243 1658 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
18244 1658 : NameStr(att->attname)))
18245 : {
18246 : /* Decrement inhcount and possibly set islocal to true */
18247 1622 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
18248 1622 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
18249 :
18250 1622 : copy_att->attinhcount--;
18251 1622 : if (copy_att->attinhcount == 0)
18252 1602 : copy_att->attislocal = true;
18253 :
18254 1622 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18255 1622 : heap_freetuple(copyTuple);
18256 : }
18257 : }
18258 757 : systable_endscan(scan);
18259 757 : table_close(catalogRelation, RowExclusiveLock);
18260 :
18261 : /*
18262 : * Likewise, find inherited check and not-null constraints and disinherit
18263 : * them. To do this, we first need a list of the names of the parent's
18264 : * check constraints. (We cheat a bit by only checking for name matches,
18265 : * assuming that the expressions will match.)
18266 : *
18267 : * For NOT NULL columns, we store column numbers to match, mapping them in
18268 : * to the child rel's attribute numbers.
18269 : */
18270 757 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
18271 : RelationGetDescr(parent_rel),
18272 : false);
18273 :
18274 757 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18275 757 : ScanKeyInit(&key[0],
18276 : Anum_pg_constraint_conrelid,
18277 : BTEqualStrategyNumber, F_OIDEQ,
18278 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18279 757 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18280 : true, NULL, 1, key);
18281 :
18282 757 : connames = NIL;
18283 757 : nncolumns = NIL;
18284 :
18285 1454 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18286 : {
18287 697 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18288 :
18289 697 : if (con->connoinherit)
18290 152 : continue;
18291 :
18292 545 : if (con->contype == CONSTRAINT_CHECK)
18293 76 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
18294 545 : if (con->contype == CONSTRAINT_NOTNULL)
18295 : {
18296 249 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18297 :
18298 249 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18299 : }
18300 : }
18301 :
18302 757 : systable_endscan(scan);
18303 :
18304 : /* Now scan the child's constraints to find matches */
18305 757 : ScanKeyInit(&key[0],
18306 : Anum_pg_constraint_conrelid,
18307 : BTEqualStrategyNumber, F_OIDEQ,
18308 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18309 757 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18310 : true, NULL, 1, key);
18311 :
18312 1450 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18313 : {
18314 693 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18315 693 : bool match = false;
18316 :
18317 : /*
18318 : * Match CHECK constraints by name, not-null constraints by column
18319 : * number, and ignore all others.
18320 : */
18321 693 : if (con->contype == CONSTRAINT_CHECK)
18322 : {
18323 228 : foreach_ptr(char, chkname, connames)
18324 : {
18325 80 : if (con->contype == CONSTRAINT_CHECK &&
18326 80 : strcmp(NameStr(con->conname), chkname) == 0)
18327 : {
18328 76 : match = true;
18329 76 : connames = foreach_delete_current(connames, chkname);
18330 76 : break;
18331 : }
18332 : }
18333 : }
18334 581 : else if (con->contype == CONSTRAINT_NOTNULL)
18335 : {
18336 289 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18337 :
18338 582 : foreach_int(prevattno, nncolumns)
18339 : {
18340 253 : if (prevattno == child_attno)
18341 : {
18342 249 : match = true;
18343 249 : nncolumns = foreach_delete_current(nncolumns, prevattno);
18344 249 : break;
18345 : }
18346 : }
18347 : }
18348 : else
18349 292 : continue;
18350 :
18351 401 : if (match)
18352 : {
18353 : /* Decrement inhcount and possibly set islocal to true */
18354 325 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
18355 325 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18356 :
18357 325 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
18358 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18359 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
18360 :
18361 325 : copy_con->coninhcount--;
18362 325 : if (copy_con->coninhcount == 0)
18363 313 : copy_con->conislocal = true;
18364 :
18365 325 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18366 325 : heap_freetuple(copyTuple);
18367 : }
18368 : }
18369 :
18370 : /* We should have matched all constraints */
18371 757 : if (connames != NIL || nncolumns != NIL)
18372 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18373 : list_length(connames) + list_length(nncolumns),
18374 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18375 :
18376 757 : systable_endscan(scan);
18377 757 : table_close(catalogRelation, RowExclusiveLock);
18378 :
18379 757 : drop_parent_dependency(RelationGetRelid(child_rel),
18380 : RelationRelationId,
18381 : RelationGetRelid(parent_rel),
18382 : child_dependency_type(is_partitioning));
18383 :
18384 : /*
18385 : * Post alter hook of this inherits. Since object_access_hook doesn't take
18386 : * multiple object identifiers, we relay oid of parent relation using
18387 : * auxiliary_id argument.
18388 : */
18389 757 : InvokeObjectPostAlterHookArg(InheritsRelationId,
18390 : RelationGetRelid(child_rel), 0,
18391 : RelationGetRelid(parent_rel), false);
18392 757 : }
18393 :
18394 : /*
18395 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18396 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18397 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18398 : * be TypeRelationId). There's no convenient way to do this, so go trawling
18399 : * through pg_depend.
18400 : */
18401 : static void
18402 765 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18403 : DependencyType deptype)
18404 : {
18405 : Relation catalogRelation;
18406 : SysScanDesc scan;
18407 : ScanKeyData key[3];
18408 : HeapTuple depTuple;
18409 :
18410 765 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18411 :
18412 765 : ScanKeyInit(&key[0],
18413 : Anum_pg_depend_classid,
18414 : BTEqualStrategyNumber, F_OIDEQ,
18415 : ObjectIdGetDatum(RelationRelationId));
18416 765 : ScanKeyInit(&key[1],
18417 : Anum_pg_depend_objid,
18418 : BTEqualStrategyNumber, F_OIDEQ,
18419 : ObjectIdGetDatum(relid));
18420 765 : ScanKeyInit(&key[2],
18421 : Anum_pg_depend_objsubid,
18422 : BTEqualStrategyNumber, F_INT4EQ,
18423 : Int32GetDatum(0));
18424 :
18425 765 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18426 : NULL, 3, key);
18427 :
18428 2348 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18429 : {
18430 1583 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18431 :
18432 1583 : if (dep->refclassid == refclassid &&
18433 793 : dep->refobjid == refobjid &&
18434 765 : dep->refobjsubid == 0 &&
18435 765 : dep->deptype == deptype)
18436 765 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18437 : }
18438 :
18439 765 : systable_endscan(scan);
18440 765 : table_close(catalogRelation, RowExclusiveLock);
18441 765 : }
18442 :
18443 : /*
18444 : * ALTER TABLE OF
18445 : *
18446 : * Attach a table to a composite type, as though it had been created with CREATE
18447 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18448 : * subject table must not have inheritance parents. These restrictions ensure
18449 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18450 : *
18451 : * The address of the type is returned.
18452 : */
18453 : static ObjectAddress
18454 42 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18455 : {
18456 42 : Oid relid = RelationGetRelid(rel);
18457 : Type typetuple;
18458 : Form_pg_type typeform;
18459 : Oid typeid;
18460 : Relation inheritsRelation,
18461 : relationRelation;
18462 : SysScanDesc scan;
18463 : ScanKeyData key;
18464 : AttrNumber table_attno,
18465 : type_attno;
18466 : TupleDesc typeTupleDesc,
18467 : tableTupleDesc;
18468 : ObjectAddress tableobj,
18469 : typeobj;
18470 : HeapTuple classtuple;
18471 :
18472 : /* Validate the type. */
18473 42 : typetuple = typenameType(NULL, ofTypename, NULL);
18474 42 : check_of_type(typetuple);
18475 42 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
18476 42 : typeid = typeform->oid;
18477 :
18478 : /* Fail if the table has any inheritance parents. */
18479 42 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18480 42 : ScanKeyInit(&key,
18481 : Anum_pg_inherits_inhrelid,
18482 : BTEqualStrategyNumber, F_OIDEQ,
18483 : ObjectIdGetDatum(relid));
18484 42 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18485 : true, NULL, 1, &key);
18486 42 : if (HeapTupleIsValid(systable_getnext(scan)))
18487 4 : ereport(ERROR,
18488 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18489 : errmsg("typed tables cannot inherit")));
18490 38 : systable_endscan(scan);
18491 38 : table_close(inheritsRelation, AccessShareLock);
18492 :
18493 : /*
18494 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
18495 : * require that the order also match. However, attnotnull need not match.
18496 : */
18497 38 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18498 38 : tableTupleDesc = RelationGetDescr(rel);
18499 38 : table_attno = 1;
18500 121 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18501 : {
18502 : Form_pg_attribute type_attr,
18503 : table_attr;
18504 : const char *type_attname,
18505 : *table_attname;
18506 :
18507 : /* Get the next non-dropped type attribute. */
18508 99 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18509 99 : if (type_attr->attisdropped)
18510 29 : continue;
18511 70 : type_attname = NameStr(type_attr->attname);
18512 :
18513 : /* Get the next non-dropped table attribute. */
18514 : do
18515 : {
18516 78 : if (table_attno > tableTupleDesc->natts)
18517 4 : ereport(ERROR,
18518 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18519 : errmsg("table is missing column \"%s\"",
18520 : type_attname)));
18521 74 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18522 74 : table_attno++;
18523 74 : } while (table_attr->attisdropped);
18524 66 : table_attname = NameStr(table_attr->attname);
18525 :
18526 : /* Compare name. */
18527 66 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18528 4 : ereport(ERROR,
18529 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18530 : errmsg("table has column \"%s\" where type requires \"%s\"",
18531 : table_attname, type_attname)));
18532 :
18533 : /* Compare type. */
18534 62 : if (table_attr->atttypid != type_attr->atttypid ||
18535 58 : table_attr->atttypmod != type_attr->atttypmod ||
18536 54 : table_attr->attcollation != type_attr->attcollation)
18537 8 : ereport(ERROR,
18538 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18539 : errmsg("table \"%s\" has different type for column \"%s\"",
18540 : RelationGetRelationName(rel), type_attname)));
18541 : }
18542 22 : ReleaseTupleDesc(typeTupleDesc);
18543 :
18544 : /* Any remaining columns at the end of the table had better be dropped. */
18545 22 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
18546 : {
18547 4 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18548 : table_attno - 1);
18549 :
18550 4 : if (!table_attr->attisdropped)
18551 4 : ereport(ERROR,
18552 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18553 : errmsg("table has extra column \"%s\"",
18554 : NameStr(table_attr->attname))));
18555 : }
18556 :
18557 : /* If the table was already typed, drop the existing dependency. */
18558 18 : if (rel->rd_rel->reloftype)
18559 4 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18560 : DEPENDENCY_NORMAL);
18561 :
18562 : /* Record a dependency on the new type. */
18563 18 : tableobj.classId = RelationRelationId;
18564 18 : tableobj.objectId = relid;
18565 18 : tableobj.objectSubId = 0;
18566 18 : typeobj.classId = TypeRelationId;
18567 18 : typeobj.objectId = typeid;
18568 18 : typeobj.objectSubId = 0;
18569 18 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18570 :
18571 : /* Update pg_class.reloftype */
18572 18 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18573 18 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18574 18 : if (!HeapTupleIsValid(classtuple))
18575 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18576 18 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18577 18 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18578 :
18579 18 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18580 :
18581 18 : heap_freetuple(classtuple);
18582 18 : table_close(relationRelation, RowExclusiveLock);
18583 :
18584 18 : ReleaseSysCache(typetuple);
18585 :
18586 18 : return typeobj;
18587 : }
18588 :
18589 : /*
18590 : * ALTER TABLE NOT OF
18591 : *
18592 : * Detach a typed table from its originating type. Just clear reloftype and
18593 : * remove the dependency.
18594 : */
18595 : static void
18596 4 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
18597 : {
18598 4 : Oid relid = RelationGetRelid(rel);
18599 : Relation relationRelation;
18600 : HeapTuple tuple;
18601 :
18602 4 : if (!OidIsValid(rel->rd_rel->reloftype))
18603 0 : ereport(ERROR,
18604 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18605 : errmsg("\"%s\" is not a typed table",
18606 : RelationGetRelationName(rel))));
18607 :
18608 : /*
18609 : * We don't bother to check ownership of the type --- ownership of the
18610 : * table is presumed enough rights. No lock required on the type, either.
18611 : */
18612 :
18613 4 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18614 : DEPENDENCY_NORMAL);
18615 :
18616 : /* Clear pg_class.reloftype */
18617 4 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18618 4 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18619 4 : if (!HeapTupleIsValid(tuple))
18620 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18621 4 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18622 4 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18623 :
18624 4 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18625 :
18626 4 : heap_freetuple(tuple);
18627 4 : table_close(relationRelation, RowExclusiveLock);
18628 4 : }
18629 :
18630 : /*
18631 : * relation_mark_replica_identity: Update a table's replica identity
18632 : *
18633 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18634 : * index. Otherwise, it must be InvalidOid.
18635 : *
18636 : * Caller had better hold an exclusive lock on the relation, as the results
18637 : * of running two of these concurrently wouldn't be pretty.
18638 : */
18639 : static void
18640 281 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18641 : bool is_internal)
18642 : {
18643 : Relation pg_index;
18644 : Relation pg_class;
18645 : HeapTuple pg_class_tuple;
18646 : HeapTuple pg_index_tuple;
18647 : Form_pg_class pg_class_form;
18648 : Form_pg_index pg_index_form;
18649 : ListCell *index;
18650 :
18651 : /*
18652 : * Check whether relreplident has changed, and update it if so.
18653 : */
18654 281 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18655 281 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
18656 : ObjectIdGetDatum(RelationGetRelid(rel)));
18657 281 : if (!HeapTupleIsValid(pg_class_tuple))
18658 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
18659 : RelationGetRelationName(rel));
18660 281 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18661 281 : if (pg_class_form->relreplident != ri_type)
18662 : {
18663 248 : pg_class_form->relreplident = ri_type;
18664 248 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18665 : }
18666 281 : table_close(pg_class, RowExclusiveLock);
18667 281 : heap_freetuple(pg_class_tuple);
18668 :
18669 : /*
18670 : * Update the per-index indisreplident flags correctly.
18671 : */
18672 281 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
18673 747 : foreach(index, RelationGetIndexList(rel))
18674 : {
18675 466 : Oid thisIndexOid = lfirst_oid(index);
18676 466 : bool dirty = false;
18677 :
18678 466 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18679 : ObjectIdGetDatum(thisIndexOid));
18680 466 : if (!HeapTupleIsValid(pg_index_tuple))
18681 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18682 466 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18683 :
18684 466 : if (thisIndexOid == indexOid)
18685 : {
18686 : /* Set the bit if not already set. */
18687 150 : if (!pg_index_form->indisreplident)
18688 : {
18689 138 : dirty = true;
18690 138 : pg_index_form->indisreplident = true;
18691 : }
18692 : }
18693 : else
18694 : {
18695 : /* Unset the bit if set. */
18696 316 : if (pg_index_form->indisreplident)
18697 : {
18698 34 : dirty = true;
18699 34 : pg_index_form->indisreplident = false;
18700 : }
18701 : }
18702 :
18703 466 : if (dirty)
18704 : {
18705 172 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18706 172 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18707 : InvalidOid, is_internal);
18708 :
18709 : /*
18710 : * Invalidate the relcache for the table, so that after we commit
18711 : * all sessions will refresh the table's replica identity index
18712 : * before attempting any UPDATE or DELETE on the table. (If we
18713 : * changed the table's pg_class row above, then a relcache inval
18714 : * is already queued due to that; but we might not have.)
18715 : */
18716 172 : CacheInvalidateRelcache(rel);
18717 : }
18718 466 : heap_freetuple(pg_index_tuple);
18719 : }
18720 :
18721 281 : table_close(pg_index, RowExclusiveLock);
18722 281 : }
18723 :
18724 : /*
18725 : * ALTER TABLE <name> REPLICA IDENTITY ...
18726 : */
18727 : static void
18728 313 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
18729 : {
18730 : Oid indexOid;
18731 : Relation indexRel;
18732 : int key;
18733 :
18734 313 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18735 : {
18736 4 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18737 4 : return;
18738 : }
18739 309 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18740 : {
18741 98 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18742 98 : return;
18743 : }
18744 211 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18745 : {
18746 29 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18747 29 : return;
18748 : }
18749 182 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18750 : {
18751 : /* fallthrough */ ;
18752 : }
18753 : else
18754 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18755 :
18756 : /* Check that the index exists */
18757 182 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18758 182 : if (!OidIsValid(indexOid))
18759 0 : ereport(ERROR,
18760 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18761 : errmsg("index \"%s\" for table \"%s\" does not exist",
18762 : stmt->name, RelationGetRelationName(rel))));
18763 :
18764 182 : indexRel = index_open(indexOid, ShareLock);
18765 :
18766 : /* Check that the index is on the relation we're altering. */
18767 182 : if (indexRel->rd_index == NULL ||
18768 182 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
18769 4 : ereport(ERROR,
18770 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18771 : errmsg("\"%s\" is not an index for table \"%s\"",
18772 : RelationGetRelationName(indexRel),
18773 : RelationGetRelationName(rel))));
18774 :
18775 : /*
18776 : * The AM must support uniqueness, and the index must in fact be unique.
18777 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18778 : * exclusion), we can use that too.
18779 : */
18780 178 : if ((!indexRel->rd_indam->amcanunique ||
18781 166 : !indexRel->rd_index->indisunique) &&
18782 16 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18783 8 : ereport(ERROR,
18784 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18785 : errmsg("cannot use non-unique index \"%s\" as replica identity",
18786 : RelationGetRelationName(indexRel))));
18787 : /* Deferred indexes are not guaranteed to be always unique. */
18788 170 : if (!indexRel->rd_index->indimmediate)
18789 8 : ereport(ERROR,
18790 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18791 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
18792 : RelationGetRelationName(indexRel))));
18793 : /* Expression indexes aren't supported. */
18794 162 : if (RelationGetIndexExpressions(indexRel) != NIL)
18795 4 : ereport(ERROR,
18796 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18797 : errmsg("cannot use expression index \"%s\" as replica identity",
18798 : RelationGetRelationName(indexRel))));
18799 : /* Predicate indexes aren't supported. */
18800 158 : if (RelationGetIndexPredicate(indexRel) != NIL)
18801 4 : ereport(ERROR,
18802 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18803 : errmsg("cannot use partial index \"%s\" as replica identity",
18804 : RelationGetRelationName(indexRel))));
18805 :
18806 : /* Check index for nullable columns. */
18807 346 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18808 : {
18809 196 : int16 attno = indexRel->rd_index->indkey.values[key];
18810 : Form_pg_attribute attr;
18811 :
18812 : /*
18813 : * Reject any other system columns. (Going forward, we'll disallow
18814 : * indexes containing such columns in the first place, but they might
18815 : * exist in older branches.)
18816 : */
18817 196 : if (attno <= 0)
18818 0 : ereport(ERROR,
18819 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18820 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18821 : RelationGetRelationName(indexRel), attno)));
18822 :
18823 196 : attr = TupleDescAttr(rel->rd_att, attno - 1);
18824 196 : if (!attr->attnotnull)
18825 4 : ereport(ERROR,
18826 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18827 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18828 : RelationGetRelationName(indexRel),
18829 : NameStr(attr->attname))));
18830 : }
18831 :
18832 : /* This index is suitable for use as a replica identity. Mark it. */
18833 150 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18834 :
18835 150 : index_close(indexRel, NoLock);
18836 : }
18837 :
18838 : /*
18839 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18840 : */
18841 : static void
18842 246 : ATExecSetRowSecurity(Relation rel, bool rls)
18843 : {
18844 : Relation pg_class;
18845 : Oid relid;
18846 : HeapTuple tuple;
18847 :
18848 246 : relid = RelationGetRelid(rel);
18849 :
18850 : /* Pull the record for this relation and update it */
18851 246 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18852 :
18853 246 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18854 :
18855 246 : if (!HeapTupleIsValid(tuple))
18856 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18857 :
18858 246 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18859 246 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18860 :
18861 246 : InvokeObjectPostAlterHook(RelationRelationId,
18862 : RelationGetRelid(rel), 0);
18863 :
18864 246 : table_close(pg_class, RowExclusiveLock);
18865 246 : heap_freetuple(tuple);
18866 246 : }
18867 :
18868 : /*
18869 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18870 : */
18871 : static void
18872 90 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
18873 : {
18874 : Relation pg_class;
18875 : Oid relid;
18876 : HeapTuple tuple;
18877 :
18878 90 : relid = RelationGetRelid(rel);
18879 :
18880 90 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18881 :
18882 90 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18883 :
18884 90 : if (!HeapTupleIsValid(tuple))
18885 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18886 :
18887 90 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18888 90 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18889 :
18890 90 : InvokeObjectPostAlterHook(RelationRelationId,
18891 : RelationGetRelid(rel), 0);
18892 :
18893 90 : table_close(pg_class, RowExclusiveLock);
18894 90 : heap_freetuple(tuple);
18895 90 : }
18896 :
18897 : /*
18898 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
18899 : */
18900 : static void
18901 31 : ATExecGenericOptions(Relation rel, List *options)
18902 : {
18903 : Relation ftrel;
18904 : ForeignServer *server;
18905 : ForeignDataWrapper *fdw;
18906 : HeapTuple tuple;
18907 : bool isnull;
18908 : Datum repl_val[Natts_pg_foreign_table];
18909 : bool repl_null[Natts_pg_foreign_table];
18910 : bool repl_repl[Natts_pg_foreign_table];
18911 : Datum datum;
18912 : Form_pg_foreign_table tableform;
18913 :
18914 31 : if (options == NIL)
18915 0 : return;
18916 :
18917 31 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18918 :
18919 31 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18920 : ObjectIdGetDatum(rel->rd_id));
18921 31 : if (!HeapTupleIsValid(tuple))
18922 0 : ereport(ERROR,
18923 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18924 : errmsg("foreign table \"%s\" does not exist",
18925 : RelationGetRelationName(rel))));
18926 31 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18927 31 : server = GetForeignServer(tableform->ftserver);
18928 31 : fdw = GetForeignDataWrapper(server->fdwid);
18929 :
18930 31 : memset(repl_val, 0, sizeof(repl_val));
18931 31 : memset(repl_null, false, sizeof(repl_null));
18932 31 : memset(repl_repl, false, sizeof(repl_repl));
18933 :
18934 : /* Extract the current options */
18935 31 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
18936 : tuple,
18937 : Anum_pg_foreign_table_ftoptions,
18938 : &isnull);
18939 31 : if (isnull)
18940 2 : datum = PointerGetDatum(NULL);
18941 :
18942 : /* Transform the options */
18943 31 : datum = transformGenericOptions(ForeignTableRelationId,
18944 : datum,
18945 : options,
18946 : fdw->fdwvalidator);
18947 :
18948 30 : if (DatumGetPointer(datum) != NULL)
18949 30 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18950 : else
18951 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18952 :
18953 30 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18954 :
18955 : /* Everything looks good - update the tuple */
18956 :
18957 30 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18958 : repl_val, repl_null, repl_repl);
18959 :
18960 30 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18961 :
18962 : /*
18963 : * Invalidate relcache so that all sessions will refresh any cached plans
18964 : * that might depend on the old options.
18965 : */
18966 30 : CacheInvalidateRelcache(rel);
18967 :
18968 30 : InvokeObjectPostAlterHook(ForeignTableRelationId,
18969 : RelationGetRelid(rel), 0);
18970 :
18971 30 : table_close(ftrel, RowExclusiveLock);
18972 :
18973 30 : heap_freetuple(tuple);
18974 : }
18975 :
18976 : /*
18977 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18978 : *
18979 : * Return value is the address of the modified column
18980 : */
18981 : static ObjectAddress
18982 47 : ATExecSetCompression(Relation rel,
18983 : const char *column,
18984 : Node *newValue,
18985 : LOCKMODE lockmode)
18986 : {
18987 : Relation attrel;
18988 : HeapTuple tuple;
18989 : Form_pg_attribute atttableform;
18990 : AttrNumber attnum;
18991 : char *compression;
18992 : char cmethod;
18993 : ObjectAddress address;
18994 :
18995 47 : compression = strVal(newValue);
18996 :
18997 47 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18998 :
18999 : /* copy the cache entry so we can scribble on it below */
19000 47 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
19001 47 : if (!HeapTupleIsValid(tuple))
19002 0 : ereport(ERROR,
19003 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19004 : errmsg("column \"%s\" of relation \"%s\" does not exist",
19005 : column, RelationGetRelationName(rel))));
19006 :
19007 : /* prevent them from altering a system attribute */
19008 47 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
19009 47 : attnum = atttableform->attnum;
19010 47 : if (attnum <= 0)
19011 0 : ereport(ERROR,
19012 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19013 : errmsg("cannot alter system column \"%s\"", column)));
19014 :
19015 : /*
19016 : * Check that column type is compressible, then get the attribute
19017 : * compression method code
19018 : */
19019 47 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
19020 :
19021 : /* update pg_attribute entry */
19022 43 : atttableform->attcompression = cmethod;
19023 43 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
19024 :
19025 43 : InvokeObjectPostAlterHook(RelationRelationId,
19026 : RelationGetRelid(rel),
19027 : attnum);
19028 :
19029 : /*
19030 : * Apply the change to indexes as well (only for simple index columns,
19031 : * matching behavior of index.c ConstructTupleDescriptor()).
19032 : */
19033 43 : SetIndexStorageProperties(rel, attrel, attnum,
19034 : false, 0,
19035 : true, cmethod,
19036 : lockmode);
19037 :
19038 43 : heap_freetuple(tuple);
19039 :
19040 43 : table_close(attrel, RowExclusiveLock);
19041 :
19042 : /* make changes visible */
19043 43 : CommandCounterIncrement();
19044 :
19045 43 : ObjectAddressSubSet(address, RelationRelationId,
19046 : RelationGetRelid(rel), attnum);
19047 43 : return address;
19048 : }
19049 :
19050 :
19051 : /*
19052 : * Preparation phase for SET LOGGED/UNLOGGED
19053 : *
19054 : * This verifies that we're not trying to change a temp table. Also,
19055 : * existing foreign key constraints are checked to avoid ending up with
19056 : * permanent tables referencing unlogged tables.
19057 : */
19058 : static void
19059 66 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
19060 : {
19061 : Relation pg_constraint;
19062 : HeapTuple tuple;
19063 : SysScanDesc scan;
19064 : ScanKeyData skey[1];
19065 :
19066 : /*
19067 : * Disallow changing status for a temp table. Also verify whether we can
19068 : * get away with doing nothing; in such cases we don't need to run the
19069 : * checks below, either.
19070 : */
19071 66 : switch (rel->rd_rel->relpersistence)
19072 : {
19073 0 : case RELPERSISTENCE_TEMP:
19074 0 : ereport(ERROR,
19075 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
19076 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
19077 : RelationGetRelationName(rel)),
19078 : errtable(rel)));
19079 : break;
19080 37 : case RELPERSISTENCE_PERMANENT:
19081 37 : if (toLogged)
19082 : /* nothing to do */
19083 8 : return;
19084 33 : break;
19085 29 : case RELPERSISTENCE_UNLOGGED:
19086 29 : if (!toLogged)
19087 : /* nothing to do */
19088 4 : return;
19089 25 : break;
19090 : }
19091 :
19092 : /*
19093 : * Check that the table is not part of any publication when changing to
19094 : * UNLOGGED, as UNLOGGED tables can't be published.
19095 : */
19096 91 : if (!toLogged &&
19097 33 : GetRelationIncludedPublications(RelationGetRelid(rel)) != NIL)
19098 0 : ereport(ERROR,
19099 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19100 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
19101 : RelationGetRelationName(rel)),
19102 : errdetail("Unlogged relations cannot be replicated.")));
19103 :
19104 : /*
19105 : * Check existing foreign key constraints to preserve the invariant that
19106 : * permanent tables cannot reference unlogged ones. Self-referencing
19107 : * foreign keys can safely be ignored.
19108 : */
19109 58 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
19110 :
19111 : /*
19112 : * Scan conrelid if changing to permanent, else confrelid. This also
19113 : * determines whether a useful index exists.
19114 : */
19115 58 : ScanKeyInit(&skey[0],
19116 : toLogged ? Anum_pg_constraint_conrelid :
19117 : Anum_pg_constraint_confrelid,
19118 : BTEqualStrategyNumber, F_OIDEQ,
19119 : ObjectIdGetDatum(RelationGetRelid(rel)));
19120 58 : scan = systable_beginscan(pg_constraint,
19121 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
19122 : true, NULL, 1, skey);
19123 :
19124 94 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19125 : {
19126 44 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
19127 :
19128 44 : if (con->contype == CONSTRAINT_FOREIGN)
19129 : {
19130 : Oid foreignrelid;
19131 : Relation foreignrel;
19132 :
19133 : /* the opposite end of what we used as scankey */
19134 20 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
19135 :
19136 : /* ignore if self-referencing */
19137 20 : if (RelationGetRelid(rel) == foreignrelid)
19138 8 : continue;
19139 :
19140 12 : foreignrel = relation_open(foreignrelid, AccessShareLock);
19141 :
19142 12 : if (toLogged)
19143 : {
19144 4 : if (!RelationIsPermanent(foreignrel))
19145 4 : ereport(ERROR,
19146 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
19147 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
19148 : RelationGetRelationName(rel),
19149 : RelationGetRelationName(foreignrel)),
19150 : errtableconstraint(rel, NameStr(con->conname))));
19151 : }
19152 : else
19153 : {
19154 8 : if (RelationIsPermanent(foreignrel))
19155 4 : ereport(ERROR,
19156 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
19157 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
19158 : RelationGetRelationName(rel),
19159 : RelationGetRelationName(foreignrel)),
19160 : errtableconstraint(rel, NameStr(con->conname))));
19161 : }
19162 :
19163 4 : relation_close(foreignrel, AccessShareLock);
19164 : }
19165 : }
19166 :
19167 50 : systable_endscan(scan);
19168 :
19169 50 : table_close(pg_constraint, AccessShareLock);
19170 :
19171 : /* force rewrite if necessary; see comment in ATRewriteTables */
19172 50 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
19173 50 : if (toLogged)
19174 21 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
19175 : else
19176 29 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
19177 50 : tab->chgPersistence = true;
19178 : }
19179 :
19180 : /*
19181 : * Execute ALTER TABLE SET SCHEMA
19182 : */
19183 : ObjectAddress
19184 88 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
19185 : {
19186 : Relation rel;
19187 : Oid relid;
19188 : Oid oldNspOid;
19189 : Oid nspOid;
19190 : RangeVar *newrv;
19191 : ObjectAddresses *objsMoved;
19192 : ObjectAddress myself;
19193 :
19194 88 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
19195 88 : stmt->missing_ok ? RVR_MISSING_OK : 0,
19196 : RangeVarCallbackForAlterRelation,
19197 : stmt);
19198 :
19199 83 : if (!OidIsValid(relid))
19200 : {
19201 12 : ereport(NOTICE,
19202 : (errmsg("relation \"%s\" does not exist, skipping",
19203 : stmt->relation->relname)));
19204 12 : return InvalidObjectAddress;
19205 : }
19206 :
19207 71 : rel = relation_open(relid, NoLock);
19208 :
19209 71 : oldNspOid = RelationGetNamespace(rel);
19210 :
19211 : /* If it's an owned sequence, disallow moving it by itself. */
19212 71 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19213 : {
19214 : Oid tableId;
19215 : int32 colId;
19216 :
19217 6 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19218 1 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
19219 4 : ereport(ERROR,
19220 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19221 : errmsg("cannot move an owned sequence into another schema"),
19222 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
19223 : RelationGetRelationName(rel),
19224 : get_rel_name(tableId))));
19225 : }
19226 :
19227 : /* Get and lock schema OID and check its permissions. */
19228 67 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19229 67 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
19230 :
19231 : /* common checks on switching namespaces */
19232 67 : CheckSetNamespace(oldNspOid, nspOid);
19233 :
19234 67 : objsMoved = new_object_addresses();
19235 67 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
19236 63 : free_object_addresses(objsMoved);
19237 :
19238 63 : ObjectAddressSet(myself, RelationRelationId, relid);
19239 :
19240 63 : if (oldschema)
19241 63 : *oldschema = oldNspOid;
19242 :
19243 : /* close rel, but keep lock until commit */
19244 63 : relation_close(rel, NoLock);
19245 :
19246 63 : return myself;
19247 : }
19248 :
19249 : /*
19250 : * The guts of relocating a table or materialized view to another namespace:
19251 : * besides moving the relation itself, its dependent objects are relocated to
19252 : * the new schema.
19253 : */
19254 : void
19255 68 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
19256 : ObjectAddresses *objsMoved)
19257 : {
19258 : Relation classRel;
19259 :
19260 : Assert(objsMoved != NULL);
19261 :
19262 : /* OK, modify the pg_class row and pg_depend entry */
19263 68 : classRel = table_open(RelationRelationId, RowExclusiveLock);
19264 :
19265 68 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19266 : nspOid, true, objsMoved);
19267 :
19268 : /* Fix the table's row type too, if it has one */
19269 64 : if (OidIsValid(rel->rd_rel->reltype))
19270 63 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19271 : false, /* isImplicitArray */
19272 : false, /* ignoreDependent */
19273 : false, /* errorOnTableType */
19274 : objsMoved);
19275 :
19276 : /* Fix other dependent stuff */
19277 64 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19278 64 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19279 : objsMoved, AccessExclusiveLock);
19280 64 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19281 : false, objsMoved);
19282 :
19283 64 : table_close(classRel, RowExclusiveLock);
19284 64 : }
19285 :
19286 : /*
19287 : * The guts of relocating a relation to another namespace: fix the pg_class
19288 : * entry, and the pg_depend entry if any. Caller must already have
19289 : * opened and write-locked pg_class.
19290 : */
19291 : void
19292 135 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
19293 : Oid oldNspOid, Oid newNspOid,
19294 : bool hasDependEntry,
19295 : ObjectAddresses *objsMoved)
19296 : {
19297 : HeapTuple classTup;
19298 : Form_pg_class classForm;
19299 : ObjectAddress thisobj;
19300 135 : bool already_done = false;
19301 :
19302 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19303 135 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19304 135 : if (!HeapTupleIsValid(classTup))
19305 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
19306 135 : classForm = (Form_pg_class) GETSTRUCT(classTup);
19307 :
19308 : Assert(classForm->relnamespace == oldNspOid);
19309 :
19310 135 : thisobj.classId = RelationRelationId;
19311 135 : thisobj.objectId = relOid;
19312 135 : thisobj.objectSubId = 0;
19313 :
19314 : /*
19315 : * If the object has already been moved, don't move it again. If it's
19316 : * already in the right place, don't move it, but still fire the object
19317 : * access hook.
19318 : */
19319 135 : already_done = object_address_present(&thisobj, objsMoved);
19320 135 : if (!already_done && oldNspOid != newNspOid)
19321 103 : {
19322 107 : ItemPointerData otid = classTup->t_self;
19323 :
19324 : /* check for duplicate name (more friendly than unique-index failure) */
19325 107 : if (get_relname_relid(NameStr(classForm->relname),
19326 : newNspOid) != InvalidOid)
19327 4 : ereport(ERROR,
19328 : (errcode(ERRCODE_DUPLICATE_TABLE),
19329 : errmsg("relation \"%s\" already exists in schema \"%s\"",
19330 : NameStr(classForm->relname),
19331 : get_namespace_name(newNspOid))));
19332 :
19333 : /* classTup is a copy, so OK to scribble on */
19334 103 : classForm->relnamespace = newNspOid;
19335 :
19336 103 : CatalogTupleUpdate(classRel, &otid, classTup);
19337 103 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19338 :
19339 :
19340 : /* Update dependency on schema if caller said so */
19341 179 : if (hasDependEntry &&
19342 76 : changeDependencyFor(RelationRelationId,
19343 : relOid,
19344 : NamespaceRelationId,
19345 : oldNspOid,
19346 : newNspOid) != 1)
19347 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
19348 : NameStr(classForm->relname));
19349 : }
19350 : else
19351 28 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19352 131 : if (!already_done)
19353 : {
19354 131 : add_exact_object_address(&thisobj, objsMoved);
19355 :
19356 131 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19357 : }
19358 :
19359 131 : heap_freetuple(classTup);
19360 131 : }
19361 :
19362 : /*
19363 : * Move all indexes for the specified relation to another namespace.
19364 : *
19365 : * Note: we assume adequate permission checking was done by the caller,
19366 : * and that the caller has a suitable lock on the owning relation.
19367 : */
19368 : static void
19369 64 : AlterIndexNamespaces(Relation classRel, Relation rel,
19370 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19371 : {
19372 : List *indexList;
19373 : ListCell *l;
19374 :
19375 64 : indexList = RelationGetIndexList(rel);
19376 :
19377 94 : foreach(l, indexList)
19378 : {
19379 30 : Oid indexOid = lfirst_oid(l);
19380 : ObjectAddress thisobj;
19381 :
19382 30 : thisobj.classId = RelationRelationId;
19383 30 : thisobj.objectId = indexOid;
19384 30 : thisobj.objectSubId = 0;
19385 :
19386 : /*
19387 : * Note: currently, the index will not have its own dependency on the
19388 : * namespace, so we don't need to do changeDependencyFor(). There's no
19389 : * row type in pg_type, either.
19390 : *
19391 : * XXX this objsMoved test may be pointless -- surely we have a single
19392 : * dependency link from a relation to each index?
19393 : */
19394 30 : if (!object_address_present(&thisobj, objsMoved))
19395 : {
19396 30 : AlterRelationNamespaceInternal(classRel, indexOid,
19397 : oldNspOid, newNspOid,
19398 : false, objsMoved);
19399 30 : add_exact_object_address(&thisobj, objsMoved);
19400 : }
19401 : }
19402 :
19403 64 : list_free(indexList);
19404 64 : }
19405 :
19406 : /*
19407 : * Move all identity and SERIAL-column sequences of the specified relation to another
19408 : * namespace.
19409 : *
19410 : * Note: we assume adequate permission checking was done by the caller,
19411 : * and that the caller has a suitable lock on the owning relation.
19412 : */
19413 : static void
19414 64 : AlterSeqNamespaces(Relation classRel, Relation rel,
19415 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19416 : LOCKMODE lockmode)
19417 : {
19418 : Relation depRel;
19419 : SysScanDesc scan;
19420 : ScanKeyData key[2];
19421 : HeapTuple tup;
19422 :
19423 : /*
19424 : * SERIAL sequences are those having an auto dependency on one of the
19425 : * table's columns (we don't care *which* column, exactly).
19426 : */
19427 64 : depRel = table_open(DependRelationId, AccessShareLock);
19428 :
19429 64 : ScanKeyInit(&key[0],
19430 : Anum_pg_depend_refclassid,
19431 : BTEqualStrategyNumber, F_OIDEQ,
19432 : ObjectIdGetDatum(RelationRelationId));
19433 64 : ScanKeyInit(&key[1],
19434 : Anum_pg_depend_refobjid,
19435 : BTEqualStrategyNumber, F_OIDEQ,
19436 : ObjectIdGetDatum(RelationGetRelid(rel)));
19437 : /* we leave refobjsubid unspecified */
19438 :
19439 64 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19440 : NULL, 2, key);
19441 :
19442 425 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
19443 : {
19444 361 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19445 : Relation seqRel;
19446 :
19447 : /* skip dependencies other than auto dependencies on columns */
19448 361 : if (depForm->refobjsubid == 0 ||
19449 252 : depForm->classid != RelationRelationId ||
19450 28 : depForm->objsubid != 0 ||
19451 28 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19452 333 : continue;
19453 :
19454 : /* Use relation_open just in case it's an index */
19455 28 : seqRel = relation_open(depForm->objid, lockmode);
19456 :
19457 : /* skip non-sequence relations */
19458 28 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19459 : {
19460 : /* No need to keep the lock */
19461 0 : relation_close(seqRel, lockmode);
19462 0 : continue;
19463 : }
19464 :
19465 : /* Fix the pg_class and pg_depend entries */
19466 28 : AlterRelationNamespaceInternal(classRel, depForm->objid,
19467 : oldNspOid, newNspOid,
19468 : true, objsMoved);
19469 :
19470 : /*
19471 : * Sequences used to have entries in pg_type, but no longer do. If we
19472 : * ever re-instate that, we'll need to move the pg_type entry to the
19473 : * new namespace, too (using AlterTypeNamespaceInternal).
19474 : */
19475 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19476 :
19477 : /* Now we can close it. Keep the lock till end of transaction. */
19478 28 : relation_close(seqRel, NoLock);
19479 : }
19480 :
19481 64 : systable_endscan(scan);
19482 :
19483 64 : relation_close(depRel, AccessShareLock);
19484 64 : }
19485 :
19486 :
19487 : /*
19488 : * This code supports
19489 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19490 : *
19491 : * Because we only support this for TEMP tables, it's sufficient to remember
19492 : * the state in a backend-local data structure.
19493 : */
19494 :
19495 : /*
19496 : * Register a newly-created relation's ON COMMIT action.
19497 : */
19498 : void
19499 120 : register_on_commit_action(Oid relid, OnCommitAction action)
19500 : {
19501 : OnCommitItem *oc;
19502 : MemoryContext oldcxt;
19503 :
19504 : /*
19505 : * We needn't bother registering the relation unless there is an ON COMMIT
19506 : * action we need to take.
19507 : */
19508 120 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19509 16 : return;
19510 :
19511 104 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
19512 :
19513 104 : oc = palloc_object(OnCommitItem);
19514 104 : oc->relid = relid;
19515 104 : oc->oncommit = action;
19516 104 : oc->creating_subid = GetCurrentSubTransactionId();
19517 104 : oc->deleting_subid = InvalidSubTransactionId;
19518 :
19519 : /*
19520 : * We use lcons() here so that ON COMMIT actions are processed in reverse
19521 : * order of registration. That might not be essential but it seems
19522 : * reasonable.
19523 : */
19524 104 : on_commits = lcons(oc, on_commits);
19525 :
19526 104 : MemoryContextSwitchTo(oldcxt);
19527 : }
19528 :
19529 : /*
19530 : * Unregister any ON COMMIT action when a relation is deleted.
19531 : *
19532 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19533 : */
19534 : void
19535 33134 : remove_on_commit_action(Oid relid)
19536 : {
19537 : ListCell *l;
19538 :
19539 33247 : foreach(l, on_commits)
19540 : {
19541 205 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19542 :
19543 205 : if (oc->relid == relid)
19544 : {
19545 92 : oc->deleting_subid = GetCurrentSubTransactionId();
19546 92 : break;
19547 : }
19548 : }
19549 33134 : }
19550 :
19551 : /*
19552 : * Perform ON COMMIT actions.
19553 : *
19554 : * This is invoked just before actually committing, since it's possible
19555 : * to encounter errors.
19556 : */
19557 : void
19558 593740 : PreCommit_on_commit_actions(void)
19559 : {
19560 : ListCell *l;
19561 593740 : List *oids_to_truncate = NIL;
19562 593740 : List *oids_to_drop = NIL;
19563 :
19564 594286 : foreach(l, on_commits)
19565 : {
19566 546 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19567 :
19568 : /* Ignore entry if already dropped in this xact */
19569 546 : if (oc->deleting_subid != InvalidSubTransactionId)
19570 49 : continue;
19571 :
19572 497 : switch (oc->oncommit)
19573 : {
19574 0 : case ONCOMMIT_NOOP:
19575 : case ONCOMMIT_PRESERVE_ROWS:
19576 : /* Do nothing (there shouldn't be such entries, actually) */
19577 0 : break;
19578 462 : case ONCOMMIT_DELETE_ROWS:
19579 :
19580 : /*
19581 : * If this transaction hasn't accessed any temporary
19582 : * relations, we can skip truncating ON COMMIT DELETE ROWS
19583 : * tables, as they must still be empty.
19584 : */
19585 462 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
19586 298 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19587 462 : break;
19588 35 : case ONCOMMIT_DROP:
19589 35 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19590 35 : break;
19591 : }
19592 : }
19593 :
19594 : /*
19595 : * Truncate relations before dropping so that all dependencies between
19596 : * relations are removed after they are worked on. Doing it like this
19597 : * might be a waste as it is possible that a relation being truncated will
19598 : * be dropped anyway due to its parent being dropped, but this makes the
19599 : * code more robust because of not having to re-check that the relation
19600 : * exists at truncation time.
19601 : */
19602 593740 : if (oids_to_truncate != NIL)
19603 254 : heap_truncate(oids_to_truncate);
19604 :
19605 593736 : if (oids_to_drop != NIL)
19606 : {
19607 31 : ObjectAddresses *targetObjects = new_object_addresses();
19608 :
19609 66 : foreach(l, oids_to_drop)
19610 : {
19611 : ObjectAddress object;
19612 :
19613 35 : object.classId = RelationRelationId;
19614 35 : object.objectId = lfirst_oid(l);
19615 35 : object.objectSubId = 0;
19616 :
19617 : Assert(!object_address_present(&object, targetObjects));
19618 :
19619 35 : add_exact_object_address(&object, targetObjects);
19620 : }
19621 :
19622 : /*
19623 : * Object deletion might involve toast table access (to clean up
19624 : * toasted catalog entries), so ensure we have a valid snapshot.
19625 : */
19626 31 : PushActiveSnapshot(GetTransactionSnapshot());
19627 :
19628 : /*
19629 : * Since this is an automatic drop, rather than one directly initiated
19630 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19631 : */
19632 31 : performMultipleDeletions(targetObjects, DROP_CASCADE,
19633 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
19634 :
19635 31 : PopActiveSnapshot();
19636 :
19637 : #ifdef USE_ASSERT_CHECKING
19638 :
19639 : /*
19640 : * Note that table deletion will call remove_on_commit_action, so the
19641 : * entry should get marked as deleted.
19642 : */
19643 : foreach(l, on_commits)
19644 : {
19645 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19646 :
19647 : if (oc->oncommit != ONCOMMIT_DROP)
19648 : continue;
19649 :
19650 : Assert(oc->deleting_subid != InvalidSubTransactionId);
19651 : }
19652 : #endif
19653 : }
19654 593736 : }
19655 :
19656 : /*
19657 : * Post-commit or post-abort cleanup for ON COMMIT management.
19658 : *
19659 : * All we do here is remove no-longer-needed OnCommitItem entries.
19660 : *
19661 : * During commit, remove entries that were deleted during this transaction;
19662 : * during abort, remove those created during this transaction.
19663 : */
19664 : void
19665 628801 : AtEOXact_on_commit_actions(bool isCommit)
19666 : {
19667 : ListCell *cur_item;
19668 :
19669 629371 : foreach(cur_item, on_commits)
19670 : {
19671 570 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19672 :
19673 642 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19674 72 : oc->creating_subid != InvalidSubTransactionId)
19675 : {
19676 : /* cur_item must be removed */
19677 104 : on_commits = foreach_delete_current(on_commits, cur_item);
19678 104 : pfree(oc);
19679 : }
19680 : else
19681 : {
19682 : /* cur_item must be preserved */
19683 466 : oc->creating_subid = InvalidSubTransactionId;
19684 466 : oc->deleting_subid = InvalidSubTransactionId;
19685 : }
19686 : }
19687 628801 : }
19688 :
19689 : /*
19690 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19691 : *
19692 : * During subabort, we can immediately remove entries created during this
19693 : * subtransaction. During subcommit, just relabel entries marked during
19694 : * this subtransaction as being the parent's responsibility.
19695 : */
19696 : void
19697 11887 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
19698 : SubTransactionId parentSubid)
19699 : {
19700 : ListCell *cur_item;
19701 :
19702 11887 : foreach(cur_item, on_commits)
19703 : {
19704 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19705 :
19706 0 : if (!isCommit && oc->creating_subid == mySubid)
19707 : {
19708 : /* cur_item must be removed */
19709 0 : on_commits = foreach_delete_current(on_commits, cur_item);
19710 0 : pfree(oc);
19711 : }
19712 : else
19713 : {
19714 : /* cur_item must be preserved */
19715 0 : if (oc->creating_subid == mySubid)
19716 0 : oc->creating_subid = parentSubid;
19717 0 : if (oc->deleting_subid == mySubid)
19718 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19719 : }
19720 : }
19721 11887 : }
19722 :
19723 : /*
19724 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19725 : * the relation to be locked only if (1) it's a plain or partitioned table,
19726 : * materialized view, or TOAST table and (2) the current user is the owner (or
19727 : * the superuser) or has been granted MAINTAIN. This meets the
19728 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19729 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19730 : */
19731 : void
19732 684 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
19733 : Oid relId, Oid oldRelId, void *arg)
19734 : {
19735 : char relkind;
19736 : AclResult aclresult;
19737 :
19738 : /* Nothing to do if the relation was not found. */
19739 684 : if (!OidIsValid(relId))
19740 3 : return;
19741 :
19742 : /*
19743 : * If the relation does exist, check whether it's an index. But note that
19744 : * the relation might have been dropped between the time we did the name
19745 : * lookup and now. In that case, there's nothing to do.
19746 : */
19747 681 : relkind = get_rel_relkind(relId);
19748 681 : if (!relkind)
19749 0 : return;
19750 681 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19751 106 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19752 18 : ereport(ERROR,
19753 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19754 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19755 :
19756 : /* Check permissions */
19757 663 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19758 663 : if (aclresult != ACLCHECK_OK)
19759 20 : aclcheck_error(aclresult,
19760 20 : get_relkind_objtype(get_rel_relkind(relId)),
19761 20 : relation->relname);
19762 : }
19763 :
19764 : /*
19765 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19766 : */
19767 : static void
19768 1421 : RangeVarCallbackForTruncate(const RangeVar *relation,
19769 : Oid relId, Oid oldRelId, void *arg)
19770 : {
19771 : HeapTuple tuple;
19772 :
19773 : /* Nothing to do if the relation was not found. */
19774 1421 : if (!OidIsValid(relId))
19775 0 : return;
19776 :
19777 1421 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19778 1421 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19779 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19780 :
19781 1421 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
19782 1418 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
19783 :
19784 1398 : ReleaseSysCache(tuple);
19785 : }
19786 :
19787 : /*
19788 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19789 : * the owner of the relation, or superuser.
19790 : */
19791 : void
19792 11806 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
19793 : Oid relId, Oid oldRelId, void *arg)
19794 : {
19795 : HeapTuple tuple;
19796 :
19797 : /* Nothing to do if the relation was not found. */
19798 11806 : if (!OidIsValid(relId))
19799 17 : return;
19800 :
19801 11789 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19802 11789 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19803 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19804 :
19805 11789 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19806 16 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
19807 16 : relation->relname);
19808 :
19809 23486 : if (!allowSystemTableMods &&
19810 11713 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19811 1 : ereport(ERROR,
19812 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19813 : errmsg("permission denied: \"%s\" is a system catalog",
19814 : relation->relname)));
19815 :
19816 11772 : ReleaseSysCache(tuple);
19817 : }
19818 :
19819 : /*
19820 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
19821 : * processing.
19822 : */
19823 : static void
19824 22771 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
19825 : void *arg)
19826 : {
19827 22771 : Node *stmt = (Node *) arg;
19828 : ObjectType reltype;
19829 : HeapTuple tuple;
19830 : Form_pg_class classform;
19831 : AclResult aclresult;
19832 : char relkind;
19833 :
19834 22771 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19835 22771 : if (!HeapTupleIsValid(tuple))
19836 169 : return; /* concurrently dropped */
19837 22602 : classform = (Form_pg_class) GETSTRUCT(tuple);
19838 22602 : relkind = classform->relkind;
19839 :
19840 : /* Must own relation. */
19841 22602 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19842 56 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
19843 :
19844 : /* No system table modifications unless explicitly allowed. */
19845 22546 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
19846 18 : ereport(ERROR,
19847 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19848 : errmsg("permission denied: \"%s\" is a system catalog",
19849 : rv->relname)));
19850 :
19851 : /*
19852 : * Extract the specified relation type from the statement parse tree.
19853 : *
19854 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
19855 : * have CREATE rights on the containing namespace.
19856 : */
19857 22528 : if (IsA(stmt, RenameStmt))
19858 : {
19859 312 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19860 : GetUserId(), ACL_CREATE);
19861 312 : if (aclresult != ACLCHECK_OK)
19862 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
19863 0 : get_namespace_name(classform->relnamespace));
19864 312 : reltype = ((RenameStmt *) stmt)->renameType;
19865 : }
19866 22216 : else if (IsA(stmt, AlterObjectSchemaStmt))
19867 72 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19868 :
19869 22144 : else if (IsA(stmt, AlterTableStmt))
19870 22144 : reltype = ((AlterTableStmt *) stmt)->objtype;
19871 : else
19872 : {
19873 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19874 : reltype = OBJECT_TABLE; /* placate compiler */
19875 : }
19876 :
19877 : /*
19878 : * For compatibility with prior releases, we allow ALTER TABLE to be used
19879 : * with most other types of relations (but not composite types). We allow
19880 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
19881 : * otherwise. Otherwise, the user must select the correct form of the
19882 : * command for the relation at issue.
19883 : */
19884 22528 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19885 0 : ereport(ERROR,
19886 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19887 : errmsg("\"%s\" is not a sequence", rv->relname)));
19888 :
19889 22528 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19890 0 : ereport(ERROR,
19891 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19892 : errmsg("\"%s\" is not a view", rv->relname)));
19893 :
19894 22528 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19895 0 : ereport(ERROR,
19896 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19897 : errmsg("\"%s\" is not a materialized view", rv->relname)));
19898 :
19899 22528 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19900 0 : ereport(ERROR,
19901 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19902 : errmsg("\"%s\" is not a foreign table", rv->relname)));
19903 :
19904 22528 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19905 0 : ereport(ERROR,
19906 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19907 : errmsg("\"%s\" is not a composite type", rv->relname)));
19908 :
19909 22528 : if (reltype == OBJECT_PROPGRAPH && relkind != RELKIND_PROPGRAPH)
19910 0 : ereport(ERROR,
19911 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19912 : errmsg("\"%s\" is not a property graph", rv->relname)));
19913 :
19914 22528 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19915 : relkind != RELKIND_PARTITIONED_INDEX
19916 25 : && !IsA(stmt, RenameStmt))
19917 4 : ereport(ERROR,
19918 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19919 : errmsg("\"%s\" is not an index", rv->relname)));
19920 :
19921 : /*
19922 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19923 : * TYPE for that.
19924 : */
19925 22524 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19926 0 : ereport(ERROR,
19927 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19928 : errmsg("\"%s\" is a composite type", rv->relname),
19929 : /* translator: %s is an SQL ALTER command */
19930 : errhint("Use %s instead.",
19931 : "ALTER TYPE")));
19932 :
19933 : /*
19934 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19935 : * to a different schema, such as indexes and TOAST tables.
19936 : */
19937 22524 : if (IsA(stmt, AlterObjectSchemaStmt))
19938 : {
19939 72 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19940 0 : ereport(ERROR,
19941 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19942 : errmsg("cannot change schema of index \"%s\"",
19943 : rv->relname),
19944 : errhint("Change the schema of the table instead.")));
19945 72 : else if (relkind == RELKIND_COMPOSITE_TYPE)
19946 0 : ereport(ERROR,
19947 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19948 : errmsg("cannot change schema of composite type \"%s\"",
19949 : rv->relname),
19950 : /* translator: %s is an SQL ALTER command */
19951 : errhint("Use %s instead.",
19952 : "ALTER TYPE")));
19953 72 : else if (relkind == RELKIND_TOASTVALUE)
19954 0 : ereport(ERROR,
19955 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19956 : errmsg("cannot change schema of TOAST table \"%s\"",
19957 : rv->relname),
19958 : errhint("Change the schema of the table instead.")));
19959 : }
19960 :
19961 22524 : ReleaseSysCache(tuple);
19962 : }
19963 :
19964 : /*
19965 : * Transform any expressions present in the partition key
19966 : *
19967 : * Returns a transformed PartitionSpec.
19968 : */
19969 : static PartitionSpec *
19970 3551 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
19971 : {
19972 : PartitionSpec *newspec;
19973 : ParseState *pstate;
19974 : ParseNamespaceItem *nsitem;
19975 : ListCell *l;
19976 :
19977 3551 : newspec = makeNode(PartitionSpec);
19978 :
19979 3551 : newspec->strategy = partspec->strategy;
19980 3551 : newspec->partParams = NIL;
19981 3551 : newspec->location = partspec->location;
19982 :
19983 : /* Check valid number of columns for strategy */
19984 5153 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19985 1602 : list_length(partspec->partParams) != 1)
19986 4 : ereport(ERROR,
19987 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19988 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19989 :
19990 : /*
19991 : * Create a dummy ParseState and insert the target relation as its sole
19992 : * rangetable entry. We need a ParseState for transformExpr.
19993 : */
19994 3547 : pstate = make_parsestate(NULL);
19995 3547 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19996 : NULL, false, true);
19997 3547 : addNSItemToQuery(pstate, nsitem, true, true, true);
19998 :
19999 : /* take care of any partition expressions */
20000 7396 : foreach(l, partspec->partParams)
20001 : {
20002 3865 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
20003 :
20004 3865 : if (pelem->expr)
20005 : {
20006 : /* Copy, to avoid scribbling on the input */
20007 232 : pelem = copyObject(pelem);
20008 :
20009 : /* Now do parse transformation of the expression */
20010 232 : pelem->expr = transformExpr(pstate, pelem->expr,
20011 : EXPR_KIND_PARTITION_EXPRESSION);
20012 :
20013 : /* we have to fix its collations too */
20014 216 : assign_expr_collations(pstate, pelem->expr);
20015 : }
20016 :
20017 3849 : newspec->partParams = lappend(newspec->partParams, pelem);
20018 : }
20019 :
20020 3531 : return newspec;
20021 : }
20022 :
20023 : /*
20024 : * Compute per-partition-column information from a list of PartitionElems.
20025 : * Expressions in the PartitionElems must be parse-analyzed already.
20026 : */
20027 : static void
20028 3531 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
20029 : List **partexprs, Oid *partopclass, Oid *partcollation,
20030 : PartitionStrategy strategy)
20031 : {
20032 : int attn;
20033 : ListCell *lc;
20034 : Oid am_oid;
20035 :
20036 3531 : attn = 0;
20037 7292 : foreach(lc, partParams)
20038 : {
20039 3849 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
20040 : Oid atttype;
20041 : Oid attcollation;
20042 :
20043 3849 : if (pelem->name != NULL)
20044 : {
20045 : /* Simple attribute reference */
20046 : HeapTuple atttuple;
20047 : Form_pg_attribute attform;
20048 :
20049 3633 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
20050 3633 : pelem->name);
20051 3633 : if (!HeapTupleIsValid(atttuple))
20052 8 : ereport(ERROR,
20053 : (errcode(ERRCODE_UNDEFINED_COLUMN),
20054 : errmsg("column \"%s\" named in partition key does not exist",
20055 : pelem->name),
20056 : parser_errposition(pstate, pelem->location)));
20057 3625 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
20058 :
20059 3625 : if (attform->attnum <= 0)
20060 4 : ereport(ERROR,
20061 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20062 : errmsg("cannot use system column \"%s\" in partition key",
20063 : pelem->name),
20064 : parser_errposition(pstate, pelem->location)));
20065 :
20066 : /*
20067 : * Stored generated columns cannot work: They are computed after
20068 : * BEFORE triggers, but partition routing is done before all
20069 : * triggers. Maybe virtual generated columns could be made to
20070 : * work, but then they would need to be handled as an expression
20071 : * below.
20072 : */
20073 3621 : if (attform->attgenerated)
20074 8 : ereport(ERROR,
20075 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20076 : errmsg("cannot use generated column in partition key"),
20077 : errdetail("Column \"%s\" is a generated column.",
20078 : pelem->name),
20079 : parser_errposition(pstate, pelem->location)));
20080 :
20081 3613 : partattrs[attn] = attform->attnum;
20082 3613 : atttype = attform->atttypid;
20083 3613 : attcollation = attform->attcollation;
20084 3613 : ReleaseSysCache(atttuple);
20085 : }
20086 : else
20087 : {
20088 : /* Expression */
20089 216 : Node *expr = pelem->expr;
20090 : char partattname[16];
20091 216 : Bitmapset *expr_attrs = NULL;
20092 : int i;
20093 :
20094 : Assert(expr != NULL);
20095 216 : atttype = exprType(expr);
20096 216 : attcollation = exprCollation(expr);
20097 :
20098 : /*
20099 : * The expression must be of a storable type (e.g., not RECORD).
20100 : * The test is the same as for whether a table column is of a safe
20101 : * type (which is why we needn't check for the non-expression
20102 : * case).
20103 : */
20104 216 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
20105 216 : CheckAttributeType(partattname,
20106 : atttype, attcollation,
20107 : NIL, CHKATYPE_IS_PARTKEY);
20108 :
20109 : /*
20110 : * Strip any top-level COLLATE clause. This ensures that we treat
20111 : * "x COLLATE y" and "(x COLLATE y)" alike.
20112 : */
20113 208 : while (IsA(expr, CollateExpr))
20114 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
20115 :
20116 : /*
20117 : * Examine all the columns in the partition key expression. When
20118 : * the whole-row reference is present, examine all the columns of
20119 : * the partitioned table.
20120 : */
20121 208 : pull_varattnos(expr, 1, &expr_attrs);
20122 208 : if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
20123 : {
20124 40 : expr_attrs = bms_add_range(expr_attrs,
20125 : 1 - FirstLowInvalidHeapAttributeNumber,
20126 20 : RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
20127 20 : expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
20128 : }
20129 :
20130 208 : i = -1;
20131 457 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
20132 : {
20133 281 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
20134 :
20135 : Assert(attno != 0);
20136 :
20137 : /*
20138 : * Cannot allow system column references, since that would
20139 : * make partition routing impossible: their values won't be
20140 : * known yet when we need to do that.
20141 : */
20142 281 : if (attno < 0)
20143 0 : ereport(ERROR,
20144 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20145 : errmsg("partition key expressions cannot contain system column references")));
20146 :
20147 : /*
20148 : * Stored generated columns cannot work: They are computed
20149 : * after BEFORE triggers, but partition routing is done before
20150 : * all triggers. Virtual generated columns could probably
20151 : * work, but it would require more work elsewhere (for example
20152 : * SET EXPRESSION would need to check whether the column is
20153 : * used in partition keys). Seems safer to prohibit for now.
20154 : */
20155 281 : if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
20156 32 : ereport(ERROR,
20157 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20158 : errmsg("cannot use generated column in partition key"),
20159 : errdetail("Column \"%s\" is a generated column.",
20160 : get_attname(RelationGetRelid(rel), attno, false)),
20161 : parser_errposition(pstate, pelem->location)));
20162 : }
20163 :
20164 176 : if (IsA(expr, Var) &&
20165 8 : ((Var *) expr)->varattno > 0)
20166 : {
20167 :
20168 : /*
20169 : * User wrote "(column)" or "(column COLLATE something)".
20170 : * Treat it like simple attribute anyway.
20171 : */
20172 4 : partattrs[attn] = ((Var *) expr)->varattno;
20173 : }
20174 : else
20175 : {
20176 172 : partattrs[attn] = 0; /* marks the column as expression */
20177 172 : *partexprs = lappend(*partexprs, expr);
20178 :
20179 : /*
20180 : * transformPartitionSpec() should have already rejected
20181 : * subqueries, aggregates, window functions, and SRFs, based
20182 : * on the EXPR_KIND_ for partition expressions.
20183 : */
20184 :
20185 : /*
20186 : * Preprocess the expression before checking for mutability.
20187 : * This is essential for the reasons described in
20188 : * contain_mutable_functions_after_planning. However, we call
20189 : * expression_planner for ourselves rather than using that
20190 : * function, because if constant-folding reduces the
20191 : * expression to a constant, we'd like to know that so we can
20192 : * complain below.
20193 : *
20194 : * Like contain_mutable_functions_after_planning, assume that
20195 : * expression_planner won't scribble on its input, so this
20196 : * won't affect the partexprs entry we saved above.
20197 : */
20198 172 : expr = (Node *) expression_planner((Expr *) expr);
20199 :
20200 : /*
20201 : * Partition expressions cannot contain mutable functions,
20202 : * because a given row must always map to the same partition
20203 : * as long as there is no change in the partition boundary
20204 : * structure.
20205 : */
20206 172 : if (contain_mutable_functions(expr))
20207 4 : ereport(ERROR,
20208 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20209 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
20210 :
20211 : /*
20212 : * While it is not exactly *wrong* for a partition expression
20213 : * to be a constant, it seems better to reject such keys.
20214 : */
20215 168 : if (IsA(expr, Const))
20216 8 : ereport(ERROR,
20217 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20218 : errmsg("cannot use constant expression as partition key")));
20219 : }
20220 : }
20221 :
20222 : /*
20223 : * Apply collation override if any
20224 : */
20225 3777 : if (pelem->collation)
20226 36 : attcollation = get_collation_oid(pelem->collation, false);
20227 :
20228 : /*
20229 : * Check we have a collation iff it's a collatable type. The only
20230 : * expected failures here are (1) COLLATE applied to a noncollatable
20231 : * type, or (2) partition expression had an unresolved collation. But
20232 : * we might as well code this to be a complete consistency check.
20233 : */
20234 3777 : if (type_is_collatable(atttype))
20235 : {
20236 436 : if (!OidIsValid(attcollation))
20237 0 : ereport(ERROR,
20238 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
20239 : errmsg("could not determine which collation to use for partition expression"),
20240 : errhint("Use the COLLATE clause to set the collation explicitly.")));
20241 : }
20242 : else
20243 : {
20244 3341 : if (OidIsValid(attcollation))
20245 0 : ereport(ERROR,
20246 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20247 : errmsg("collations are not supported by type %s",
20248 : format_type_be(atttype))));
20249 : }
20250 :
20251 3777 : partcollation[attn] = attcollation;
20252 :
20253 : /*
20254 : * Identify the appropriate operator class. For list and range
20255 : * partitioning, we use a btree operator class; hash partitioning uses
20256 : * a hash operator class.
20257 : */
20258 3777 : if (strategy == PARTITION_STRATEGY_HASH)
20259 215 : am_oid = HASH_AM_OID;
20260 : else
20261 3562 : am_oid = BTREE_AM_OID;
20262 :
20263 3777 : if (!pelem->opclass)
20264 : {
20265 3685 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20266 :
20267 3685 : if (!OidIsValid(partopclass[attn]))
20268 : {
20269 8 : if (strategy == PARTITION_STRATEGY_HASH)
20270 0 : ereport(ERROR,
20271 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20272 : errmsg("data type %s has no default operator class for access method \"%s\"",
20273 : format_type_be(atttype), "hash"),
20274 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20275 : else
20276 8 : ereport(ERROR,
20277 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20278 : errmsg("data type %s has no default operator class for access method \"%s\"",
20279 : format_type_be(atttype), "btree"),
20280 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20281 : }
20282 : }
20283 : else
20284 92 : partopclass[attn] = ResolveOpClass(pelem->opclass,
20285 : atttype,
20286 : am_oid == HASH_AM_OID ? "hash" : "btree",
20287 : am_oid);
20288 :
20289 3761 : attn++;
20290 : }
20291 3443 : }
20292 :
20293 : /*
20294 : * PartConstraintImpliedByRelConstraint
20295 : * Do scanrel's existing constraints imply the partition constraint?
20296 : *
20297 : * "Existing constraints" include its check constraints and column-level
20298 : * not-null constraints. partConstraint describes the partition constraint,
20299 : * in implicit-AND form.
20300 : */
20301 : bool
20302 2058 : PartConstraintImpliedByRelConstraint(Relation scanrel,
20303 : List *partConstraint)
20304 : {
20305 2058 : List *existConstraint = NIL;
20306 2058 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20307 : int i;
20308 :
20309 2058 : if (constr && constr->has_not_null)
20310 : {
20311 544 : int natts = scanrel->rd_att->natts;
20312 :
20313 1870 : for (i = 1; i <= natts; i++)
20314 : {
20315 1326 : CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20316 :
20317 : /* invalid not-null constraint must be ignored here */
20318 1326 : if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20319 : {
20320 747 : Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20321 747 : NullTest *ntest = makeNode(NullTest);
20322 :
20323 747 : ntest->arg = (Expr *) makeVar(1,
20324 : i,
20325 : wholeatt->atttypid,
20326 : wholeatt->atttypmod,
20327 : wholeatt->attcollation,
20328 : 0);
20329 747 : ntest->nulltesttype = IS_NOT_NULL;
20330 :
20331 : /*
20332 : * argisrow=false is correct even for a composite column,
20333 : * because attnotnull does not represent a SQL-spec IS NOT
20334 : * NULL test in such a case, just IS DISTINCT FROM NULL.
20335 : */
20336 747 : ntest->argisrow = false;
20337 747 : ntest->location = -1;
20338 747 : existConstraint = lappend(existConstraint, ntest);
20339 : }
20340 : }
20341 : }
20342 :
20343 2058 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20344 : }
20345 :
20346 : /*
20347 : * ConstraintImpliedByRelConstraint
20348 : * Do scanrel's existing constraints imply the given constraint?
20349 : *
20350 : * testConstraint is the constraint to validate. provenConstraint is a
20351 : * caller-provided list of conditions which this function may assume
20352 : * to be true. Both provenConstraint and testConstraint must be in
20353 : * implicit-AND form, must only contain immutable clauses, and must
20354 : * contain only Vars with varno = 1.
20355 : */
20356 : bool
20357 2874 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20358 : {
20359 2874 : List *existConstraint = list_copy(provenConstraint);
20360 2874 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20361 : int num_check,
20362 : i;
20363 :
20364 2874 : num_check = (constr != NULL) ? constr->num_check : 0;
20365 3210 : for (i = 0; i < num_check; i++)
20366 : {
20367 : Node *cexpr;
20368 :
20369 : /*
20370 : * If this constraint hasn't been fully validated yet, we must ignore
20371 : * it here.
20372 : */
20373 336 : if (!constr->check[i].ccvalid)
20374 12 : continue;
20375 :
20376 : /*
20377 : * NOT ENFORCED constraints are always marked as invalid, which should
20378 : * have been ignored.
20379 : */
20380 : Assert(constr->check[i].ccenforced);
20381 :
20382 324 : cexpr = stringToNode(constr->check[i].ccbin);
20383 :
20384 : /*
20385 : * Run each expression through const-simplification and
20386 : * canonicalization. It is necessary, because we will be comparing it
20387 : * to similarly-processed partition constraint expressions, and may
20388 : * fail to detect valid matches without this.
20389 : */
20390 324 : cexpr = eval_const_expressions(NULL, cexpr);
20391 324 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20392 :
20393 324 : existConstraint = list_concat(existConstraint,
20394 324 : make_ands_implicit((Expr *) cexpr));
20395 : }
20396 :
20397 : /*
20398 : * Try to make the proof. Since we are comparing CHECK constraints, we
20399 : * need to use weak implication, i.e., we assume existConstraint is
20400 : * not-false and try to prove the same for testConstraint.
20401 : *
20402 : * Note that predicate_implied_by assumes its first argument is known
20403 : * immutable. That should always be true for both NOT NULL and partition
20404 : * constraints, so we don't test it here.
20405 : */
20406 2874 : return predicate_implied_by(testConstraint, existConstraint, true);
20407 : }
20408 :
20409 : /*
20410 : * QueuePartitionConstraintValidation
20411 : *
20412 : * Add an entry to wqueue to have the given partition constraint validated by
20413 : * Phase 3, for the given relation, and all its children.
20414 : *
20415 : * We first verify whether the given constraint is implied by pre-existing
20416 : * relation constraints; if it is, there's no need to scan the table to
20417 : * validate, so don't queue in that case.
20418 : */
20419 : static void
20420 1733 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
20421 : List *partConstraint,
20422 : bool validate_default)
20423 : {
20424 : /*
20425 : * Based on the table's existing constraints, determine whether or not we
20426 : * may skip scanning the table.
20427 : */
20428 1733 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20429 : {
20430 55 : if (!validate_default)
20431 41 : ereport(DEBUG1,
20432 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20433 : RelationGetRelationName(scanrel))));
20434 : else
20435 14 : ereport(DEBUG1,
20436 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20437 : RelationGetRelationName(scanrel))));
20438 55 : return;
20439 : }
20440 :
20441 : /*
20442 : * Constraints proved insufficient. For plain relations, queue a
20443 : * validation item now; for partitioned tables, recurse to process each
20444 : * partition.
20445 : */
20446 1678 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20447 : {
20448 : AlteredTableInfo *tab;
20449 :
20450 : /* Grab a work queue entry. */
20451 1403 : tab = ATGetQueueEntry(wqueue, scanrel);
20452 : Assert(tab->partition_constraint == NULL);
20453 1403 : tab->partition_constraint = (Expr *) linitial(partConstraint);
20454 1403 : tab->validate_default = validate_default;
20455 : }
20456 275 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20457 : {
20458 244 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20459 : int i;
20460 :
20461 524 : for (i = 0; i < partdesc->nparts; i++)
20462 : {
20463 : Relation part_rel;
20464 : List *thisPartConstraint;
20465 :
20466 : /*
20467 : * This is the minimum lock we need to prevent deadlocks.
20468 : */
20469 280 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20470 :
20471 : /*
20472 : * Adjust the constraint for scanrel so that it matches this
20473 : * partition's attribute numbers.
20474 : */
20475 : thisPartConstraint =
20476 280 : map_partition_varattnos(partConstraint, 1,
20477 : part_rel, scanrel);
20478 :
20479 280 : QueuePartitionConstraintValidation(wqueue, part_rel,
20480 : thisPartConstraint,
20481 : validate_default);
20482 280 : table_close(part_rel, NoLock); /* keep lock till commit */
20483 : }
20484 : }
20485 : }
20486 :
20487 : /*
20488 : * attachPartitionTable: attach a new partition to the partitioned table
20489 : *
20490 : * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
20491 : * of an ALTER TABLE sequence.
20492 : * rel: partitioned relation;
20493 : * attachrel: relation of attached partition;
20494 : * bound: bounds of attached relation.
20495 : */
20496 : static void
20497 1916 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
20498 : {
20499 : /*
20500 : * Create an inheritance; the relevant checks are performed inside the
20501 : * function.
20502 : */
20503 1916 : CreateInheritance(attachrel, rel, true);
20504 :
20505 : /* Update the pg_class entry. */
20506 1844 : StorePartitionBound(attachrel, rel, bound);
20507 :
20508 : /* Ensure there exists a correct set of indexes in the partition. */
20509 1844 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20510 :
20511 : /* and triggers */
20512 1824 : CloneRowTriggersToPartition(rel, attachrel);
20513 :
20514 : /*
20515 : * Clone foreign key constraints. Callee is responsible for setting up
20516 : * for phase 3 constraint verification.
20517 : */
20518 1820 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
20519 1808 : }
20520 :
20521 : /*
20522 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20523 : *
20524 : * Return the address of the newly attached partition.
20525 : */
20526 : static ObjectAddress
20527 1604 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
20528 : AlterTableUtilityContext *context)
20529 : {
20530 : Relation attachrel,
20531 : catalog;
20532 : List *attachrel_children;
20533 : List *partConstraint;
20534 : SysScanDesc scan;
20535 : ScanKeyData skey;
20536 : AttrNumber attno;
20537 : int natts;
20538 : TupleDesc tupleDesc;
20539 : ObjectAddress address;
20540 : const char *trigger_name;
20541 : Oid defaultPartOid;
20542 : List *partBoundConstraint;
20543 1604 : List *exceptpuboids = NIL;
20544 1604 : ParseState *pstate = make_parsestate(NULL);
20545 :
20546 1604 : pstate->p_sourcetext = context->queryString;
20547 :
20548 : /*
20549 : * We must lock the default partition if one exists, because attaching a
20550 : * new partition will change its partition constraint.
20551 : */
20552 : defaultPartOid =
20553 1604 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20554 1604 : if (OidIsValid(defaultPartOid))
20555 117 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20556 :
20557 1604 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20558 :
20559 : /*
20560 : * XXX I think it'd be a good idea to grab locks on all tables referenced
20561 : * by FKs at this point also.
20562 : */
20563 :
20564 : /*
20565 : * Must be owner of both parent and source table -- parent was checked by
20566 : * ATSimplePermissions call in ATPrepCmd
20567 : */
20568 1600 : ATSimplePermissions(AT_AttachPartition, attachrel,
20569 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
20570 :
20571 : /* A partition can only have one parent */
20572 1596 : if (attachrel->rd_rel->relispartition)
20573 4 : ereport(ERROR,
20574 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20575 : errmsg("\"%s\" is already a partition",
20576 : RelationGetRelationName(attachrel))));
20577 :
20578 1592 : if (OidIsValid(attachrel->rd_rel->reloftype))
20579 4 : ereport(ERROR,
20580 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20581 : errmsg("cannot attach a typed table as partition")));
20582 :
20583 : /*
20584 : * Disallow attaching a partition if the table is referenced in a
20585 : * publication EXCEPT clause. Changing the partition hierarchy could alter
20586 : * the effective publication membership.
20587 : */
20588 1588 : exceptpuboids = GetRelationExcludedPublications(RelationGetRelid(attachrel));
20589 1588 : if (exceptpuboids != NIL)
20590 : {
20591 4 : bool first = true;
20592 : StringInfoData pubnames;
20593 :
20594 4 : initStringInfo(&pubnames);
20595 :
20596 12 : foreach_oid(pubid, exceptpuboids)
20597 : {
20598 4 : char *pubname = get_publication_name(pubid, false);
20599 :
20600 4 : if (!first)
20601 : {
20602 : /*
20603 : * translator: This is a separator in a list of publication
20604 : * names.
20605 : */
20606 0 : appendStringInfoString(&pubnames, _(", "));
20607 : }
20608 :
20609 4 : first = false;
20610 :
20611 4 : appendStringInfo(&pubnames, _("\"%s\""), pubname);
20612 : }
20613 :
20614 4 : ereport(ERROR,
20615 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20616 : errmsg_plural("cannot attach table \"%s\" as partition because it is referenced in publication %s EXCEPT clause",
20617 : "cannot attach table \"%s\" as partition because it is referenced in publications %s EXCEPT clause",
20618 : list_length(exceptpuboids),
20619 : RelationGetRelationName(attachrel),
20620 : pubnames.data),
20621 : errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
20622 : errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
20623 : }
20624 :
20625 1584 : list_free(exceptpuboids);
20626 :
20627 : /*
20628 : * Table being attached should not already be part of inheritance; either
20629 : * as a child table...
20630 : */
20631 1584 : catalog = table_open(InheritsRelationId, AccessShareLock);
20632 1584 : ScanKeyInit(&skey,
20633 : Anum_pg_inherits_inhrelid,
20634 : BTEqualStrategyNumber, F_OIDEQ,
20635 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20636 1584 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20637 : NULL, 1, &skey);
20638 1584 : if (HeapTupleIsValid(systable_getnext(scan)))
20639 4 : ereport(ERROR,
20640 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20641 : errmsg("cannot attach inheritance child as partition")));
20642 1580 : systable_endscan(scan);
20643 :
20644 : /* ...or as a parent table (except the case when it is partitioned) */
20645 1580 : ScanKeyInit(&skey,
20646 : Anum_pg_inherits_inhparent,
20647 : BTEqualStrategyNumber, F_OIDEQ,
20648 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20649 1580 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20650 : 1, &skey);
20651 1580 : if (HeapTupleIsValid(systable_getnext(scan)) &&
20652 184 : attachrel->rd_rel->relkind == RELKIND_RELATION)
20653 4 : ereport(ERROR,
20654 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20655 : errmsg("cannot attach inheritance parent as partition")));
20656 1576 : systable_endscan(scan);
20657 1576 : table_close(catalog, AccessShareLock);
20658 :
20659 : /*
20660 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
20661 : * particular, this disallows making a rel a partition of itself.)
20662 : *
20663 : * We do that by checking if rel is a member of the list of attachrel's
20664 : * partitions provided the latter is partitioned at all. We want to avoid
20665 : * having to construct this list again, so we request the strongest lock
20666 : * on all partitions. We need the strongest lock, because we may decide
20667 : * to scan them if we find out that the table being attached (or its leaf
20668 : * partitions) may contain rows that violate the partition constraint. If
20669 : * the table has a constraint that would prevent such rows, which by
20670 : * definition is present in all the partitions, we need not scan the
20671 : * table, nor its partitions. But we cannot risk a deadlock by taking a
20672 : * weaker lock now and the stronger one only when needed.
20673 : */
20674 1576 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20675 : AccessExclusiveLock, NULL);
20676 1576 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20677 8 : ereport(ERROR,
20678 : (errcode(ERRCODE_DUPLICATE_TABLE),
20679 : errmsg("circular inheritance not allowed"),
20680 : errdetail("\"%s\" is already a child of \"%s\".",
20681 : RelationGetRelationName(rel),
20682 : RelationGetRelationName(attachrel))));
20683 :
20684 : /* If the parent is permanent, so must be all of its partitions. */
20685 1568 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20686 1540 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20687 4 : ereport(ERROR,
20688 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20689 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20690 : RelationGetRelationName(rel))));
20691 :
20692 : /* Temp parent cannot have a partition that is itself not a temp */
20693 1564 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20694 28 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20695 12 : ereport(ERROR,
20696 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20697 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20698 : RelationGetRelationName(rel))));
20699 :
20700 : /* If the parent is temp, it must belong to this session */
20701 1552 : if (RELATION_IS_OTHER_TEMP(rel))
20702 0 : ereport(ERROR,
20703 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20704 : errmsg("cannot attach as partition of temporary relation of another session")));
20705 :
20706 : /* Ditto for the partition */
20707 1552 : if (RELATION_IS_OTHER_TEMP(attachrel))
20708 0 : ereport(ERROR,
20709 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20710 : errmsg("cannot attach temporary relation of another session as partition")));
20711 :
20712 : /*
20713 : * Check if attachrel has any identity columns or any columns that aren't
20714 : * in the parent.
20715 : */
20716 1552 : tupleDesc = RelationGetDescr(attachrel);
20717 1552 : natts = tupleDesc->natts;
20718 5362 : for (attno = 1; attno <= natts; attno++)
20719 : {
20720 3838 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20721 3838 : char *attributeName = NameStr(attribute->attname);
20722 :
20723 : /* Ignore dropped */
20724 3838 : if (attribute->attisdropped)
20725 404 : continue;
20726 :
20727 3434 : if (attribute->attidentity)
20728 16 : ereport(ERROR,
20729 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20730 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20731 : RelationGetRelationName(attachrel), attributeName),
20732 : errdetail("The new partition may not contain an identity column."));
20733 :
20734 : /* Try to find the column in parent (matching on column name) */
20735 3418 : if (!SearchSysCacheExists2(ATTNAME,
20736 : ObjectIdGetDatum(RelationGetRelid(rel)),
20737 : CStringGetDatum(attributeName)))
20738 12 : ereport(ERROR,
20739 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20740 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20741 : RelationGetRelationName(attachrel), attributeName,
20742 : RelationGetRelationName(rel)),
20743 : errdetail("The new partition may contain only the columns present in parent.")));
20744 : }
20745 :
20746 : /*
20747 : * If child_rel has row-level triggers with transition tables, we
20748 : * currently don't allow it to become a partition. See also prohibitions
20749 : * in ATExecAddInherit() and CreateTrigger().
20750 : */
20751 1524 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20752 1524 : if (trigger_name != NULL)
20753 4 : ereport(ERROR,
20754 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20755 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20756 : trigger_name, RelationGetRelationName(attachrel)),
20757 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
20758 :
20759 : /*
20760 : * Check that the new partition's bound is valid and does not overlap any
20761 : * of existing partitions of the parent - note that it does not return on
20762 : * error.
20763 : */
20764 1520 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
20765 : cmd->bound, pstate);
20766 :
20767 1496 : attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
20768 :
20769 : /*
20770 : * Generate a partition constraint from the partition bound specification.
20771 : * If the parent itself is a partition, make sure to include its
20772 : * constraint as well.
20773 : */
20774 1388 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20775 :
20776 : /*
20777 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20778 : * since it's needed later to construct the constraint expression for
20779 : * validating against the default partition, if any.
20780 : */
20781 1388 : partConstraint = list_concat_copy(partBoundConstraint,
20782 1388 : RelationGetPartitionQual(rel));
20783 :
20784 : /* Skip validation if there are no constraints to validate. */
20785 1388 : if (partConstraint)
20786 : {
20787 : /*
20788 : * Run the partition quals through const-simplification similar to
20789 : * check constraints. We skip canonicalize_qual, though, because
20790 : * partition quals should be in canonical form already.
20791 : */
20792 : partConstraint =
20793 1360 : (List *) eval_const_expressions(NULL,
20794 : (Node *) partConstraint);
20795 :
20796 : /* XXX this sure looks wrong */
20797 1360 : partConstraint = list_make1(make_ands_explicit(partConstraint));
20798 :
20799 : /*
20800 : * Adjust the generated constraint to match this partition's attribute
20801 : * numbers.
20802 : */
20803 1360 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20804 : rel);
20805 :
20806 : /* Validate partition constraints against the table being attached. */
20807 1360 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20808 : false);
20809 : }
20810 :
20811 : /*
20812 : * If we're attaching a partition other than the default partition and a
20813 : * default one exists, then that partition's partition constraint changes,
20814 : * so add an entry to the work queue to validate it, too. (We must not do
20815 : * this when the partition being attached is the default one; we already
20816 : * did it above!)
20817 : */
20818 1388 : if (OidIsValid(defaultPartOid))
20819 : {
20820 : Relation defaultrel;
20821 : List *defPartConstraint;
20822 :
20823 : Assert(!cmd->bound->is_default);
20824 :
20825 : /* we already hold a lock on the default partition */
20826 93 : defaultrel = table_open(defaultPartOid, NoLock);
20827 : defPartConstraint =
20828 93 : get_proposed_default_constraint(partBoundConstraint);
20829 :
20830 : /*
20831 : * Map the Vars in the constraint expression from rel's attnos to
20832 : * defaultrel's.
20833 : */
20834 : defPartConstraint =
20835 93 : map_partition_varattnos(defPartConstraint,
20836 : 1, defaultrel, rel);
20837 93 : QueuePartitionConstraintValidation(wqueue, defaultrel,
20838 : defPartConstraint, true);
20839 :
20840 : /* keep our lock until commit. */
20841 93 : table_close(defaultrel, NoLock);
20842 : }
20843 :
20844 1388 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20845 :
20846 : /*
20847 : * If the partition we just attached is partitioned itself, invalidate
20848 : * relcache for all descendent partitions too to ensure that their
20849 : * rd_partcheck expression trees are rebuilt; partitions already locked at
20850 : * the beginning of this function.
20851 : */
20852 1388 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20853 : {
20854 : ListCell *l;
20855 :
20856 711 : foreach(l, attachrel_children)
20857 : {
20858 482 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
20859 : }
20860 : }
20861 :
20862 : /* keep our lock until commit */
20863 1388 : table_close(attachrel, NoLock);
20864 :
20865 1388 : return address;
20866 : }
20867 :
20868 : /*
20869 : * AttachPartitionEnsureIndexes
20870 : * subroutine for ATExecAttachPartition to create/match indexes
20871 : *
20872 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20873 : * PARTITION: every partition must have an index attached to each index on the
20874 : * partitioned table.
20875 : */
20876 : static void
20877 1844 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
20878 : {
20879 : List *idxes;
20880 : List *attachRelIdxs;
20881 : Relation *attachrelIdxRels;
20882 : IndexInfo **attachInfos;
20883 : ListCell *cell;
20884 : MemoryContext cxt;
20885 : MemoryContext oldcxt;
20886 :
20887 1844 : cxt = AllocSetContextCreate(CurrentMemoryContext,
20888 : "AttachPartitionEnsureIndexes",
20889 : ALLOCSET_DEFAULT_SIZES);
20890 1844 : oldcxt = MemoryContextSwitchTo(cxt);
20891 :
20892 1844 : idxes = RelationGetIndexList(rel);
20893 1844 : attachRelIdxs = RelationGetIndexList(attachrel);
20894 1844 : attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
20895 1844 : attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
20896 :
20897 : /* Build arrays of all existing indexes and their IndexInfos */
20898 3957 : foreach_oid(cldIdxId, attachRelIdxs)
20899 : {
20900 269 : int i = foreach_current_index(cldIdxId);
20901 :
20902 269 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20903 269 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20904 : }
20905 :
20906 : /*
20907 : * If we're attaching a foreign table, we must fail if any of the indexes
20908 : * is a constraint index; otherwise, there's nothing to do here. Do this
20909 : * before starting work, to avoid wasting the effort of building a few
20910 : * non-unique indexes before coming across a unique one.
20911 : */
20912 1844 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20913 : {
20914 55 : foreach(cell, idxes)
20915 : {
20916 24 : Oid idx = lfirst_oid(cell);
20917 24 : Relation idxRel = index_open(idx, AccessShareLock);
20918 :
20919 24 : if (idxRel->rd_index->indisunique ||
20920 16 : idxRel->rd_index->indisprimary)
20921 8 : ereport(ERROR,
20922 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20923 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20924 : RelationGetRelationName(attachrel),
20925 : RelationGetRelationName(rel)),
20926 : errdetail("Partitioned table \"%s\" contains unique indexes.",
20927 : RelationGetRelationName(rel))));
20928 16 : index_close(idxRel, AccessShareLock);
20929 : }
20930 :
20931 31 : goto out;
20932 : }
20933 :
20934 : /*
20935 : * For each index on the partitioned table, find a matching one in the
20936 : * partition-to-be; if one is not found, create one.
20937 : */
20938 2278 : foreach(cell, idxes)
20939 : {
20940 485 : Oid idx = lfirst_oid(cell);
20941 485 : Relation idxRel = index_open(idx, AccessShareLock);
20942 : IndexInfo *info;
20943 : AttrMap *attmap;
20944 485 : bool found = false;
20945 : Oid constraintOid;
20946 :
20947 : /*
20948 : * Ignore indexes in the partitioned table other than partitioned
20949 : * indexes.
20950 : */
20951 485 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20952 : {
20953 0 : index_close(idxRel, AccessShareLock);
20954 0 : continue;
20955 : }
20956 :
20957 : /* construct an indexinfo to compare existing indexes against */
20958 485 : info = BuildIndexInfo(idxRel);
20959 485 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20960 : RelationGetDescr(rel),
20961 : false);
20962 485 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
20963 :
20964 : /*
20965 : * Scan the list of existing indexes in the partition-to-be, and mark
20966 : * the first matching, valid, unattached one we find, if any, as
20967 : * partition of the parent index. If we find one, we're done.
20968 : */
20969 525 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20970 : {
20971 197 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20972 197 : Oid cldConstrOid = InvalidOid;
20973 :
20974 : /* does this index have a parent? if so, can't use it */
20975 197 : if (attachrelIdxRels[i]->rd_rel->relispartition)
20976 8 : continue;
20977 :
20978 : /* If this index is invalid, can't use it */
20979 189 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
20980 4 : continue;
20981 :
20982 185 : if (CompareIndexInfo(attachInfos[i], info,
20983 185 : attachrelIdxRels[i]->rd_indcollation,
20984 185 : idxRel->rd_indcollation,
20985 185 : attachrelIdxRels[i]->rd_opfamily,
20986 185 : idxRel->rd_opfamily,
20987 : attmap))
20988 : {
20989 : /*
20990 : * If this index is being created in the parent because of a
20991 : * constraint, then the child needs to have a constraint also,
20992 : * so look for one. If there is no such constraint, this
20993 : * index is no good, so keep looking.
20994 : */
20995 161 : if (OidIsValid(constraintOid))
20996 : {
20997 : cldConstrOid =
20998 96 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
20999 : cldIdxId);
21000 : /* no dice */
21001 96 : if (!OidIsValid(cldConstrOid))
21002 4 : continue;
21003 :
21004 : /* Ensure they're both the same type of constraint */
21005 184 : if (get_constraint_type(constraintOid) !=
21006 92 : get_constraint_type(cldConstrOid))
21007 0 : continue;
21008 : }
21009 :
21010 : /* bingo. */
21011 157 : IndexSetParentIndex(attachrelIdxRels[i], idx);
21012 157 : if (OidIsValid(constraintOid))
21013 92 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
21014 : RelationGetRelid(attachrel));
21015 157 : found = true;
21016 :
21017 157 : CommandCounterIncrement();
21018 157 : break;
21019 : }
21020 : }
21021 :
21022 : /*
21023 : * If no suitable index was found in the partition-to-be, create one
21024 : * now. Note that if this is a PK, not-null constraints must already
21025 : * exist.
21026 : */
21027 485 : if (!found)
21028 : {
21029 : IndexStmt *stmt;
21030 : Oid conOid;
21031 :
21032 328 : stmt = generateClonedIndexStmt(NULL,
21033 : idxRel, attmap,
21034 : &conOid);
21035 328 : DefineIndex(NULL,
21036 : RelationGetRelid(attachrel), stmt, InvalidOid,
21037 : RelationGetRelid(idxRel),
21038 : conOid,
21039 : -1,
21040 : true, false, false, false, false);
21041 : }
21042 :
21043 473 : index_close(idxRel, AccessShareLock);
21044 : }
21045 :
21046 1824 : out:
21047 : /* Clean up. */
21048 2085 : for (int i = 0; i < list_length(attachRelIdxs); i++)
21049 261 : index_close(attachrelIdxRels[i], AccessShareLock);
21050 1824 : MemoryContextSwitchTo(oldcxt);
21051 1824 : MemoryContextDelete(cxt);
21052 1824 : }
21053 :
21054 : /*
21055 : * CloneRowTriggersToPartition
21056 : * subroutine for ATExecAttachPartition/DefineRelation to create row
21057 : * triggers on partitions
21058 : */
21059 : static void
21060 2084 : CloneRowTriggersToPartition(Relation parent, Relation partition)
21061 : {
21062 : Relation pg_trigger;
21063 : ScanKeyData key;
21064 : SysScanDesc scan;
21065 : HeapTuple tuple;
21066 : MemoryContext perTupCxt;
21067 :
21068 2084 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21069 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
21070 2084 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
21071 2084 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
21072 : true, NULL, 1, &key);
21073 :
21074 2084 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
21075 : "clone trig", ALLOCSET_SMALL_SIZES);
21076 :
21077 3313 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
21078 : {
21079 1233 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
21080 : CreateTrigStmt *trigStmt;
21081 1233 : Node *qual = NULL;
21082 : Datum value;
21083 : bool isnull;
21084 1233 : List *cols = NIL;
21085 1233 : List *trigargs = NIL;
21086 : MemoryContext oldcxt;
21087 :
21088 : /*
21089 : * Ignore statement-level triggers; those are not cloned.
21090 : */
21091 1233 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
21092 1102 : continue;
21093 :
21094 : /*
21095 : * Don't clone internal triggers, because the constraint cloning code
21096 : * will.
21097 : */
21098 1205 : if (trigForm->tgisinternal)
21099 1074 : continue;
21100 :
21101 : /*
21102 : * Complain if we find an unexpected trigger type.
21103 : */
21104 131 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
21105 107 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
21106 0 : elog(ERROR, "unexpected trigger \"%s\" found",
21107 : NameStr(trigForm->tgname));
21108 :
21109 : /* Use short-lived context for CREATE TRIGGER */
21110 131 : oldcxt = MemoryContextSwitchTo(perTupCxt);
21111 :
21112 : /*
21113 : * If there is a WHEN clause, generate a 'cooked' version of it that's
21114 : * appropriate for the partition.
21115 : */
21116 131 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
21117 : RelationGetDescr(pg_trigger), &isnull);
21118 131 : if (!isnull)
21119 : {
21120 4 : qual = stringToNode(TextDatumGetCString(value));
21121 4 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
21122 : partition, parent);
21123 4 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
21124 : partition, parent);
21125 : }
21126 :
21127 : /*
21128 : * If there is a column list, transform it to a list of column names.
21129 : * Note we don't need to map this list in any way ...
21130 : */
21131 131 : if (trigForm->tgattr.dim1 > 0)
21132 : {
21133 : int i;
21134 :
21135 8 : for (i = 0; i < trigForm->tgattr.dim1; i++)
21136 : {
21137 : Form_pg_attribute col;
21138 :
21139 4 : col = TupleDescAttr(parent->rd_att,
21140 4 : trigForm->tgattr.values[i] - 1);
21141 4 : cols = lappend(cols,
21142 4 : makeString(pstrdup(NameStr(col->attname))));
21143 : }
21144 : }
21145 :
21146 : /* Reconstruct trigger arguments list. */
21147 131 : if (trigForm->tgnargs > 0)
21148 : {
21149 : char *p;
21150 :
21151 36 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
21152 : RelationGetDescr(pg_trigger), &isnull);
21153 36 : if (isnull)
21154 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
21155 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
21156 :
21157 36 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
21158 :
21159 80 : for (int i = 0; i < trigForm->tgnargs; i++)
21160 : {
21161 44 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
21162 44 : p += strlen(p) + 1;
21163 : }
21164 : }
21165 :
21166 131 : trigStmt = makeNode(CreateTrigStmt);
21167 131 : trigStmt->replace = false;
21168 131 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
21169 131 : trigStmt->trigname = NameStr(trigForm->tgname);
21170 131 : trigStmt->relation = NULL;
21171 131 : trigStmt->funcname = NULL; /* passed separately */
21172 131 : trigStmt->args = trigargs;
21173 131 : trigStmt->row = true;
21174 131 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
21175 131 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
21176 131 : trigStmt->columns = cols;
21177 131 : trigStmt->whenClause = NULL; /* passed separately */
21178 131 : trigStmt->transitionRels = NIL; /* not supported at present */
21179 131 : trigStmt->deferrable = trigForm->tgdeferrable;
21180 131 : trigStmt->initdeferred = trigForm->tginitdeferred;
21181 131 : trigStmt->constrrel = NULL; /* passed separately */
21182 :
21183 131 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
21184 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
21185 : trigForm->tgfoid, trigForm->oid, qual,
21186 131 : false, true, trigForm->tgenabled);
21187 :
21188 127 : MemoryContextSwitchTo(oldcxt);
21189 127 : MemoryContextReset(perTupCxt);
21190 : }
21191 :
21192 2080 : MemoryContextDelete(perTupCxt);
21193 :
21194 2080 : systable_endscan(scan);
21195 2080 : table_close(pg_trigger, RowExclusiveLock);
21196 2080 : }
21197 :
21198 : /*
21199 : * ALTER TABLE DETACH PARTITION
21200 : *
21201 : * Return the address of the relation that is no longer a partition of rel.
21202 : *
21203 : * If concurrent mode is requested, we run in two transactions. A side-
21204 : * effect is that this command cannot run in a multi-part ALTER TABLE.
21205 : * Currently, that's enforced by the grammar.
21206 : *
21207 : * The strategy for concurrency is to first modify the partition's
21208 : * pg_inherit catalog row to make it visible to everyone that the
21209 : * partition is detached, lock the partition against writes, and commit
21210 : * the transaction; anyone who requests the partition descriptor from
21211 : * that point onwards has to ignore such a partition. In a second
21212 : * transaction, we wait until all transactions that could have seen the
21213 : * partition as attached are gone, then we remove the rest of partition
21214 : * metadata (pg_inherits and pg_class.relpartbounds).
21215 : */
21216 : static ObjectAddress
21217 373 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
21218 : RangeVar *name, bool concurrent)
21219 : {
21220 : Relation partRel;
21221 : ObjectAddress address;
21222 : Oid defaultPartOid;
21223 :
21224 : /*
21225 : * We must lock the default partition, because detaching this partition
21226 : * will change its partition constraint.
21227 : */
21228 : defaultPartOid =
21229 373 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21230 373 : if (OidIsValid(defaultPartOid))
21231 : {
21232 : /*
21233 : * Concurrent detaching when a default partition exists is not
21234 : * supported. The main problem is that the default partition
21235 : * constraint would change. And there's a definitional problem: what
21236 : * should happen to the tuples that are being inserted that belong to
21237 : * the partition being detached? Putting them on the partition being
21238 : * detached would be wrong, since they'd become "lost" after the
21239 : * detaching completes but we cannot put them in the default partition
21240 : * either until we alter its partition constraint.
21241 : *
21242 : * I think we could solve this problem if we effected the constraint
21243 : * change before committing the first transaction. But the lock would
21244 : * have to remain AEL and it would cause concurrent query planning to
21245 : * be blocked, so changing it that way would be even worse.
21246 : */
21247 74 : if (concurrent)
21248 8 : ereport(ERROR,
21249 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21250 : errmsg("cannot detach partitions concurrently when a default partition exists")));
21251 66 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
21252 : }
21253 :
21254 : /*
21255 : * In concurrent mode, the partition is locked with share-update-exclusive
21256 : * in the first transaction. This allows concurrent transactions to be
21257 : * doing DML to the partition.
21258 : */
21259 365 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
21260 : AccessExclusiveLock);
21261 :
21262 : /*
21263 : * Check inheritance conditions and either delete the pg_inherits row (in
21264 : * non-concurrent mode) or just set the inhdetachpending flag.
21265 : */
21266 357 : if (!concurrent)
21267 282 : RemoveInheritance(partRel, rel, false);
21268 : else
21269 75 : MarkInheritDetached(partRel, rel);
21270 :
21271 : /*
21272 : * Ensure that foreign keys still hold after this detach. This keeps
21273 : * locks on the referencing tables, which prevents concurrent transactions
21274 : * from adding rows that we wouldn't see. For this to work in concurrent
21275 : * mode, it is critical that the partition appears as no longer attached
21276 : * for the RI queries as soon as the first transaction commits.
21277 : */
21278 344 : ATDetachCheckNoForeignKeyRefs(partRel);
21279 :
21280 : /*
21281 : * Concurrent mode has to work harder; first we add a new constraint to
21282 : * the partition that matches the partition constraint. Then we close our
21283 : * existing transaction, and in a new one wait for all processes to catch
21284 : * up on the catalog updates we've done so far; at that point we can
21285 : * complete the operation.
21286 : */
21287 322 : if (concurrent)
21288 : {
21289 : Oid partrelid,
21290 : parentrelid;
21291 : LOCKTAG tag;
21292 : char *parentrelname;
21293 : char *partrelname;
21294 :
21295 : /*
21296 : * We're almost done now; the only traces that remain are the
21297 : * pg_inherits tuple and the partition's relpartbounds. Before we can
21298 : * remove those, we need to wait until all transactions that know that
21299 : * this is a partition are gone.
21300 : */
21301 :
21302 : /*
21303 : * Remember relation OIDs to re-acquire them later; and relation names
21304 : * too, for error messages if something is dropped in between.
21305 : */
21306 72 : partrelid = RelationGetRelid(partRel);
21307 72 : parentrelid = RelationGetRelid(rel);
21308 72 : parentrelname = MemoryContextStrdup(PortalContext,
21309 72 : RelationGetRelationName(rel));
21310 72 : partrelname = MemoryContextStrdup(PortalContext,
21311 72 : RelationGetRelationName(partRel));
21312 :
21313 : /* Invalidate relcache entries for the parent -- must be before close */
21314 72 : CacheInvalidateRelcache(rel);
21315 :
21316 72 : table_close(partRel, NoLock);
21317 72 : table_close(rel, NoLock);
21318 72 : tab->rel = NULL;
21319 :
21320 : /* Make updated catalog entry visible */
21321 72 : PopActiveSnapshot();
21322 72 : CommitTransactionCommand();
21323 :
21324 72 : StartTransactionCommand();
21325 :
21326 : /*
21327 : * Now wait. This ensures that all queries that were planned
21328 : * including the partition are finished before we remove the rest of
21329 : * catalog entries. We don't need or indeed want to acquire this
21330 : * lock, though -- that would block later queries.
21331 : *
21332 : * We don't need to concern ourselves with waiting for a lock on the
21333 : * partition itself, since we will acquire AccessExclusiveLock below.
21334 : */
21335 72 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21336 72 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
21337 :
21338 : /*
21339 : * Now acquire locks in both relations again. Note they may have been
21340 : * removed in the meantime, so care is required.
21341 : */
21342 47 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21343 47 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
21344 :
21345 : /* If the relations aren't there, something bad happened; bail out */
21346 47 : if (rel == NULL)
21347 : {
21348 0 : if (partRel != NULL) /* shouldn't happen */
21349 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21350 : partrelname);
21351 0 : ereport(ERROR,
21352 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21353 : errmsg("partitioned table \"%s\" was removed concurrently",
21354 : parentrelname)));
21355 : }
21356 47 : if (partRel == NULL)
21357 0 : ereport(ERROR,
21358 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21359 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
21360 :
21361 47 : tab->rel = rel;
21362 : }
21363 :
21364 : /*
21365 : * Detaching the partition might involve TOAST table access, so ensure we
21366 : * have a valid snapshot.
21367 : */
21368 297 : PushActiveSnapshot(GetTransactionSnapshot());
21369 :
21370 : /* Do the final part of detaching */
21371 297 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21372 :
21373 296 : PopActiveSnapshot();
21374 :
21375 296 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21376 :
21377 : /* keep our lock until commit */
21378 296 : table_close(partRel, NoLock);
21379 :
21380 296 : return address;
21381 : }
21382 :
21383 : /*
21384 : * Second part of ALTER TABLE .. DETACH.
21385 : *
21386 : * This is separate so that it can be run independently when the second
21387 : * transaction of the concurrent algorithm fails (crash or abort).
21388 : */
21389 : static void
21390 672 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21391 : Oid defaultPartOid)
21392 : {
21393 : Relation classRel;
21394 : List *fks;
21395 : ListCell *cell;
21396 : List *indexes;
21397 : Datum new_val[Natts_pg_class];
21398 : bool new_null[Natts_pg_class],
21399 : new_repl[Natts_pg_class];
21400 : HeapTuple tuple,
21401 : newtuple;
21402 672 : Relation trigrel = NULL;
21403 672 : List *fkoids = NIL;
21404 :
21405 672 : if (concurrent)
21406 : {
21407 : /*
21408 : * We can remove the pg_inherits row now. (In the non-concurrent case,
21409 : * this was already done).
21410 : */
21411 54 : RemoveInheritance(partRel, rel, true);
21412 : }
21413 :
21414 : /* Drop any triggers that were cloned on creation/attach. */
21415 672 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
21416 :
21417 : /*
21418 : * Detach any foreign keys that are inherited. This includes creating
21419 : * additional action triggers.
21420 : */
21421 672 : fks = copyObject(RelationGetFKeyList(partRel));
21422 672 : if (fks != NIL)
21423 60 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21424 :
21425 : /*
21426 : * It's possible that the partition being detached has a foreign key that
21427 : * references a partitioned table. When that happens, there are multiple
21428 : * pg_constraint rows for the partition: one points to the partitioned
21429 : * table itself, while the others point to each of its partitions. Only
21430 : * the topmost one is to be considered here; the child constraints must be
21431 : * left alone, because conceptually those aren't coming from our parent
21432 : * partitioned table, but from this partition itself.
21433 : *
21434 : * We implement this by collecting all the constraint OIDs in a first scan
21435 : * of the FK array, and skipping in the loop below those constraints whose
21436 : * parents are listed here.
21437 : */
21438 1460 : foreach_node(ForeignKeyCacheInfo, fk, fks)
21439 116 : fkoids = lappend_oid(fkoids, fk->conoid);
21440 :
21441 788 : foreach(cell, fks)
21442 : {
21443 116 : ForeignKeyCacheInfo *fk = lfirst(cell);
21444 : HeapTuple contup;
21445 : Form_pg_constraint conform;
21446 :
21447 116 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21448 116 : if (!HeapTupleIsValid(contup))
21449 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21450 116 : conform = (Form_pg_constraint) GETSTRUCT(contup);
21451 :
21452 : /*
21453 : * Consider only inherited foreign keys, and only if their parents
21454 : * aren't in the list.
21455 : */
21456 116 : if (conform->contype != CONSTRAINT_FOREIGN ||
21457 216 : !OidIsValid(conform->conparentid) ||
21458 100 : list_member_oid(fkoids, conform->conparentid))
21459 : {
21460 44 : ReleaseSysCache(contup);
21461 44 : continue;
21462 : }
21463 :
21464 : /*
21465 : * The constraint on this table must be marked no longer a child of
21466 : * the parent's constraint, as do its check triggers.
21467 : */
21468 72 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
21469 :
21470 : /*
21471 : * Also, look up the partition's "check" triggers corresponding to the
21472 : * ENFORCED constraint being detached and detach them from the parent
21473 : * triggers. NOT ENFORCED constraints do not have these triggers;
21474 : * therefore, this step is not needed.
21475 : */
21476 72 : if (fk->conenforced)
21477 : {
21478 : Oid insertTriggerOid,
21479 : updateTriggerOid;
21480 :
21481 72 : GetForeignKeyCheckTriggers(trigrel,
21482 : fk->conoid, fk->confrelid, fk->conrelid,
21483 : &insertTriggerOid, &updateTriggerOid);
21484 : Assert(OidIsValid(insertTriggerOid));
21485 72 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21486 : RelationGetRelid(partRel));
21487 : Assert(OidIsValid(updateTriggerOid));
21488 72 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21489 : RelationGetRelid(partRel));
21490 : }
21491 :
21492 : /*
21493 : * Lastly, create the action triggers on the referenced table, using
21494 : * addFkRecurseReferenced, which requires some elaborate setup (so put
21495 : * it in a separate block). While at it, if the table is partitioned,
21496 : * that function will recurse to create the pg_constraint rows and
21497 : * action triggers for each partition.
21498 : *
21499 : * Note there's no need to do addFkConstraint() here, because the
21500 : * pg_constraint row already exists.
21501 : */
21502 : {
21503 : Constraint *fkconstraint;
21504 : int numfks;
21505 : AttrNumber conkey[INDEX_MAX_KEYS];
21506 : AttrNumber confkey[INDEX_MAX_KEYS];
21507 : Oid conpfeqop[INDEX_MAX_KEYS];
21508 : Oid conppeqop[INDEX_MAX_KEYS];
21509 : Oid conffeqop[INDEX_MAX_KEYS];
21510 : int numfkdelsetcols;
21511 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21512 : Relation refdRel;
21513 :
21514 72 : DeconstructFkConstraintRow(contup,
21515 : &numfks,
21516 : conkey,
21517 : confkey,
21518 : conpfeqop,
21519 : conppeqop,
21520 : conffeqop,
21521 : &numfkdelsetcols,
21522 : confdelsetcols);
21523 :
21524 : /* Create a synthetic node we'll use throughout */
21525 72 : fkconstraint = makeNode(Constraint);
21526 72 : fkconstraint->contype = CONSTRAINT_FOREIGN;
21527 72 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
21528 72 : fkconstraint->deferrable = conform->condeferrable;
21529 72 : fkconstraint->initdeferred = conform->condeferred;
21530 72 : fkconstraint->is_enforced = conform->conenforced;
21531 72 : fkconstraint->skip_validation = true;
21532 72 : fkconstraint->initially_valid = conform->convalidated;
21533 : /* a few irrelevant fields omitted here */
21534 72 : fkconstraint->pktable = NULL;
21535 72 : fkconstraint->fk_attrs = NIL;
21536 72 : fkconstraint->pk_attrs = NIL;
21537 72 : fkconstraint->fk_matchtype = conform->confmatchtype;
21538 72 : fkconstraint->fk_upd_action = conform->confupdtype;
21539 72 : fkconstraint->fk_del_action = conform->confdeltype;
21540 72 : fkconstraint->fk_del_set_cols = NIL;
21541 72 : fkconstraint->old_conpfeqop = NIL;
21542 72 : fkconstraint->old_pktable_oid = InvalidOid;
21543 72 : fkconstraint->location = -1;
21544 :
21545 : /* set up colnames, used to generate the constraint name */
21546 176 : for (int i = 0; i < numfks; i++)
21547 : {
21548 : Form_pg_attribute att;
21549 :
21550 104 : att = TupleDescAttr(RelationGetDescr(partRel),
21551 104 : conkey[i] - 1);
21552 :
21553 104 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21554 104 : makeString(NameStr(att->attname)));
21555 : }
21556 :
21557 72 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
21558 :
21559 72 : addFkRecurseReferenced(fkconstraint, partRel,
21560 : refdRel,
21561 : conform->conindid,
21562 : fk->conoid,
21563 : numfks,
21564 : confkey,
21565 : conkey,
21566 : conpfeqop,
21567 : conppeqop,
21568 : conffeqop,
21569 : numfkdelsetcols,
21570 : confdelsetcols,
21571 : true,
21572 : InvalidOid, InvalidOid,
21573 72 : conform->conperiod);
21574 72 : table_close(refdRel, NoLock); /* keep lock till end of xact */
21575 : }
21576 :
21577 72 : ReleaseSysCache(contup);
21578 : }
21579 672 : list_free_deep(fks);
21580 672 : if (trigrel)
21581 60 : table_close(trigrel, RowExclusiveLock);
21582 :
21583 : /*
21584 : * Any sub-constraints that are in the referenced-side of a larger
21585 : * constraint have to be removed. This partition is no longer part of the
21586 : * key space of the constraint.
21587 : */
21588 727 : foreach(cell, GetParentedForeignKeyRefs(partRel))
21589 : {
21590 56 : Oid constrOid = lfirst_oid(cell);
21591 : ObjectAddress constraint;
21592 :
21593 56 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21594 56 : deleteDependencyRecordsForClass(ConstraintRelationId,
21595 : constrOid,
21596 : ConstraintRelationId,
21597 : DEPENDENCY_INTERNAL);
21598 56 : CommandCounterIncrement();
21599 :
21600 56 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21601 56 : performDeletion(&constraint, DROP_RESTRICT, 0);
21602 : }
21603 :
21604 : /* Now we can detach indexes */
21605 671 : indexes = RelationGetIndexList(partRel);
21606 967 : foreach(cell, indexes)
21607 : {
21608 296 : Oid idxid = lfirst_oid(cell);
21609 : Oid parentidx;
21610 : Relation idx;
21611 : Oid constrOid;
21612 : Oid parentConstrOid;
21613 :
21614 296 : if (!has_superclass(idxid))
21615 8 : continue;
21616 :
21617 288 : parentidx = get_partition_parent(idxid, false);
21618 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21619 :
21620 288 : idx = index_open(idxid, AccessExclusiveLock);
21621 288 : IndexSetParentIndex(idx, InvalidOid);
21622 :
21623 : /*
21624 : * If there's a constraint associated with the index, detach it too.
21625 : * Careful: it is possible for a constraint index in a partition to be
21626 : * the child of a non-constraint index, so verify whether the parent
21627 : * index does actually have a constraint.
21628 : */
21629 288 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
21630 : idxid);
21631 288 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
21632 : parentidx);
21633 288 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21634 135 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21635 :
21636 288 : index_close(idx, NoLock);
21637 : }
21638 :
21639 : /* Update pg_class tuple */
21640 671 : classRel = table_open(RelationRelationId, RowExclusiveLock);
21641 671 : tuple = SearchSysCacheCopy1(RELOID,
21642 : ObjectIdGetDatum(RelationGetRelid(partRel)));
21643 671 : if (!HeapTupleIsValid(tuple))
21644 0 : elog(ERROR, "cache lookup failed for relation %u",
21645 : RelationGetRelid(partRel));
21646 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21647 :
21648 : /* Clear relpartbound and reset relispartition */
21649 671 : memset(new_val, 0, sizeof(new_val));
21650 671 : memset(new_null, false, sizeof(new_null));
21651 671 : memset(new_repl, false, sizeof(new_repl));
21652 671 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21653 671 : new_null[Anum_pg_class_relpartbound - 1] = true;
21654 671 : new_repl[Anum_pg_class_relpartbound - 1] = true;
21655 671 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21656 : new_val, new_null, new_repl);
21657 :
21658 671 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21659 671 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21660 671 : heap_freetuple(newtuple);
21661 671 : table_close(classRel, RowExclusiveLock);
21662 :
21663 : /*
21664 : * Drop identity property from all identity columns of partition.
21665 : */
21666 2177 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21667 : {
21668 1506 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21669 :
21670 1506 : if (!attr->attisdropped && attr->attidentity)
21671 20 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21672 : AccessExclusiveLock, true, true);
21673 : }
21674 :
21675 671 : if (OidIsValid(defaultPartOid))
21676 : {
21677 : /*
21678 : * If the relation being detached is the default partition itself,
21679 : * remove it from the parent's pg_partitioned_table entry.
21680 : *
21681 : * If not, we must invalidate default partition's relcache entry, as
21682 : * in StorePartitionBound: its partition constraint depends on every
21683 : * other partition's partition constraint.
21684 : */
21685 159 : if (RelationGetRelid(partRel) == defaultPartOid)
21686 29 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
21687 : else
21688 130 : CacheInvalidateRelcacheByRelid(defaultPartOid);
21689 : }
21690 :
21691 : /*
21692 : * Invalidate the parent's relcache so that the partition is no longer
21693 : * included in its partition descriptor.
21694 : */
21695 671 : CacheInvalidateRelcache(rel);
21696 :
21697 : /*
21698 : * If the partition we just detached is partitioned itself, invalidate
21699 : * relcache for all descendent partitions too to ensure that their
21700 : * rd_partcheck expression trees are rebuilt; must lock partitions before
21701 : * doing so, using the same lockmode as what partRel has been locked with
21702 : * by the caller.
21703 : */
21704 671 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21705 : {
21706 : List *children;
21707 :
21708 41 : children = find_all_inheritors(RelationGetRelid(partRel),
21709 : AccessExclusiveLock, NULL);
21710 135 : foreach(cell, children)
21711 : {
21712 94 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
21713 : }
21714 : }
21715 671 : }
21716 :
21717 : /*
21718 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21719 : *
21720 : * To use when a DETACH PARTITION command previously did not run to
21721 : * completion; this completes the detaching process.
21722 : */
21723 : static ObjectAddress
21724 7 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
21725 : {
21726 : Relation partRel;
21727 : ObjectAddress address;
21728 7 : Snapshot snap = GetActiveSnapshot();
21729 :
21730 7 : partRel = table_openrv(name, AccessExclusiveLock);
21731 :
21732 : /*
21733 : * Wait until existing snapshots are gone. This is important if the
21734 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21735 : * user could immediately run DETACH FINALIZE without actually waiting for
21736 : * existing transactions. We must not complete the detach action until
21737 : * all such queries are complete (otherwise we would present them with an
21738 : * inconsistent view of catalogs).
21739 : */
21740 7 : WaitForOlderSnapshots(snap->xmin, false);
21741 :
21742 7 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21743 :
21744 7 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21745 :
21746 7 : table_close(partRel, NoLock);
21747 :
21748 7 : return address;
21749 : }
21750 :
21751 : /*
21752 : * DropClonedTriggersFromPartition
21753 : * subroutine for ATExecDetachPartition to remove any triggers that were
21754 : * cloned to the partition when it was created-as-partition or attached.
21755 : * This undoes what CloneRowTriggersToPartition did.
21756 : */
21757 : static void
21758 672 : DropClonedTriggersFromPartition(Oid partitionId)
21759 : {
21760 : ScanKeyData skey;
21761 : SysScanDesc scan;
21762 : HeapTuple trigtup;
21763 : Relation tgrel;
21764 : ObjectAddresses *objects;
21765 :
21766 672 : objects = new_object_addresses();
21767 :
21768 : /*
21769 : * Scan pg_trigger to search for all triggers on this rel.
21770 : */
21771 672 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21772 : F_OIDEQ, ObjectIdGetDatum(partitionId));
21773 672 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21774 672 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21775 : true, NULL, 1, &skey);
21776 1008 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21777 : {
21778 336 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21779 : ObjectAddress trig;
21780 :
21781 : /* Ignore triggers that weren't cloned */
21782 336 : if (!OidIsValid(pg_trigger->tgparentid))
21783 296 : continue;
21784 :
21785 : /*
21786 : * Ignore internal triggers that are implementation objects of foreign
21787 : * keys, because these will be detached when the foreign keys
21788 : * themselves are.
21789 : */
21790 280 : if (OidIsValid(pg_trigger->tgconstrrelid))
21791 240 : continue;
21792 :
21793 : /*
21794 : * This is ugly, but necessary: remove the dependency markings on the
21795 : * trigger so that it can be removed.
21796 : */
21797 40 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21798 : TriggerRelationId,
21799 : DEPENDENCY_PARTITION_PRI);
21800 40 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21801 : RelationRelationId,
21802 : DEPENDENCY_PARTITION_SEC);
21803 :
21804 : /* remember this trigger to remove it below */
21805 40 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21806 40 : add_exact_object_address(&trig, objects);
21807 : }
21808 :
21809 : /* make the dependency removal visible to the deletion below */
21810 672 : CommandCounterIncrement();
21811 672 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
21812 :
21813 : /* done */
21814 672 : free_object_addresses(objects);
21815 672 : systable_endscan(scan);
21816 672 : table_close(tgrel, RowExclusiveLock);
21817 672 : }
21818 :
21819 : /*
21820 : * Before acquiring lock on an index, acquire the same lock on the owning
21821 : * table.
21822 : */
21823 : struct AttachIndexCallbackState
21824 : {
21825 : Oid partitionOid;
21826 : Oid parentTblOid;
21827 : bool lockedParentTbl;
21828 : };
21829 :
21830 : static void
21831 254 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21832 : void *arg)
21833 : {
21834 : struct AttachIndexCallbackState *state;
21835 : Form_pg_class classform;
21836 : HeapTuple tuple;
21837 :
21838 254 : state = (struct AttachIndexCallbackState *) arg;
21839 :
21840 254 : if (!state->lockedParentTbl)
21841 : {
21842 241 : LockRelationOid(state->parentTblOid, AccessShareLock);
21843 241 : state->lockedParentTbl = true;
21844 : }
21845 :
21846 : /*
21847 : * If we previously locked some other heap, and the name we're looking up
21848 : * no longer refers to an index on that relation, release the now-useless
21849 : * lock. XXX maybe we should do *after* we verify whether the index does
21850 : * not actually belong to the same relation ...
21851 : */
21852 254 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21853 : {
21854 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
21855 0 : state->partitionOid = InvalidOid;
21856 : }
21857 :
21858 : /* Didn't find a relation, so no need for locking or permission checks. */
21859 254 : if (!OidIsValid(relOid))
21860 4 : return;
21861 :
21862 250 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21863 250 : if (!HeapTupleIsValid(tuple))
21864 0 : return; /* concurrently dropped, so nothing to do */
21865 250 : classform = (Form_pg_class) GETSTRUCT(tuple);
21866 250 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21867 192 : classform->relkind != RELKIND_INDEX)
21868 4 : ereport(ERROR,
21869 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21870 : errmsg("\"%s\" is not an index", rv->relname)));
21871 246 : ReleaseSysCache(tuple);
21872 :
21873 : /*
21874 : * Since we need only examine the heap's tupledesc, an access share lock
21875 : * on it (preventing any DDL) is sufficient.
21876 : */
21877 246 : state->partitionOid = IndexGetRelation(relOid, false);
21878 246 : LockRelationOid(state->partitionOid, AccessShareLock);
21879 : }
21880 :
21881 : /*
21882 : * ALTER INDEX i1 ATTACH PARTITION i2
21883 : */
21884 : static ObjectAddress
21885 241 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
21886 : {
21887 : Relation partIdx;
21888 : Relation partTbl;
21889 : Relation parentTbl;
21890 : ObjectAddress address;
21891 : Oid partIdxId;
21892 : Oid currParent;
21893 : struct AttachIndexCallbackState state;
21894 :
21895 : /*
21896 : * We need to obtain lock on the index 'name' to modify it, but we also
21897 : * need to read its owning table's tuple descriptor -- so we need to lock
21898 : * both. To avoid deadlocks, obtain lock on the table before doing so on
21899 : * the index. Furthermore, we need to examine the parent table of the
21900 : * partition, so lock that one too.
21901 : */
21902 241 : state.partitionOid = InvalidOid;
21903 241 : state.parentTblOid = parentIdx->rd_index->indrelid;
21904 241 : state.lockedParentTbl = false;
21905 : partIdxId =
21906 241 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
21907 : RangeVarCallbackForAttachIndex,
21908 : &state);
21909 : /* Not there? */
21910 233 : if (!OidIsValid(partIdxId))
21911 0 : ereport(ERROR,
21912 : (errcode(ERRCODE_UNDEFINED_OBJECT),
21913 : errmsg("index \"%s\" does not exist", name->relname)));
21914 :
21915 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21916 233 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
21917 :
21918 : /* we already hold locks on both tables, so this is safe: */
21919 233 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21920 233 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21921 :
21922 233 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21923 :
21924 : /* Silently do nothing if already in the right state */
21925 466 : currParent = partIdx->rd_rel->relispartition ?
21926 233 : get_partition_parent(partIdxId, false) : InvalidOid;
21927 233 : if (currParent != RelationGetRelid(parentIdx))
21928 : {
21929 : IndexInfo *childInfo;
21930 : IndexInfo *parentInfo;
21931 : AttrMap *attmap;
21932 : bool found;
21933 : int i;
21934 : PartitionDesc partDesc;
21935 : Oid constraintOid,
21936 217 : cldConstrId = InvalidOid;
21937 :
21938 : /*
21939 : * If this partition already has an index attached, refuse the
21940 : * operation.
21941 : */
21942 217 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21943 :
21944 213 : if (OidIsValid(currParent))
21945 0 : ereport(ERROR,
21946 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21947 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21948 : RelationGetRelationName(partIdx),
21949 : RelationGetRelationName(parentIdx)),
21950 : errdetail("Index \"%s\" is already attached to another index.",
21951 : RelationGetRelationName(partIdx))));
21952 :
21953 : /* Make sure it indexes a partition of the other index's table */
21954 213 : partDesc = RelationGetPartitionDesc(parentTbl, true);
21955 213 : found = false;
21956 324 : for (i = 0; i < partDesc->nparts; i++)
21957 : {
21958 320 : if (partDesc->oids[i] == state.partitionOid)
21959 : {
21960 209 : found = true;
21961 209 : break;
21962 : }
21963 : }
21964 213 : if (!found)
21965 4 : ereport(ERROR,
21966 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21967 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21968 : RelationGetRelationName(partIdx),
21969 : RelationGetRelationName(parentIdx)),
21970 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21971 : RelationGetRelationName(partIdx),
21972 : RelationGetRelationName(parentTbl))));
21973 :
21974 : /* Ensure the indexes are compatible */
21975 209 : childInfo = BuildIndexInfo(partIdx);
21976 209 : parentInfo = BuildIndexInfo(parentIdx);
21977 209 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21978 : RelationGetDescr(parentTbl),
21979 : false);
21980 209 : if (!CompareIndexInfo(childInfo, parentInfo,
21981 209 : partIdx->rd_indcollation,
21982 209 : parentIdx->rd_indcollation,
21983 209 : partIdx->rd_opfamily,
21984 209 : parentIdx->rd_opfamily,
21985 : attmap))
21986 28 : ereport(ERROR,
21987 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21988 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21989 : RelationGetRelationName(partIdx),
21990 : RelationGetRelationName(parentIdx)),
21991 : errdetail("The index definitions do not match.")));
21992 :
21993 : /*
21994 : * If there is a constraint in the parent, make sure there is one in
21995 : * the child too.
21996 : */
21997 181 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21998 : RelationGetRelid(parentIdx));
21999 :
22000 181 : if (OidIsValid(constraintOid))
22001 : {
22002 65 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
22003 : partIdxId);
22004 65 : if (!OidIsValid(cldConstrId))
22005 4 : ereport(ERROR,
22006 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
22007 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22008 : RelationGetRelationName(partIdx),
22009 : RelationGetRelationName(parentIdx)),
22010 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
22011 : RelationGetRelationName(parentIdx),
22012 : RelationGetRelationName(parentTbl),
22013 : RelationGetRelationName(partIdx))));
22014 : }
22015 :
22016 : /*
22017 : * If it's a primary key, make sure the columns in the partition are
22018 : * NOT NULL.
22019 : */
22020 177 : if (parentIdx->rd_index->indisprimary)
22021 55 : verifyPartitionIndexNotNull(childInfo, partTbl);
22022 :
22023 : /* All good -- do it */
22024 177 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
22025 177 : if (OidIsValid(constraintOid))
22026 61 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
22027 : RelationGetRelid(partTbl));
22028 :
22029 177 : free_attrmap(attmap);
22030 :
22031 177 : validatePartitionedIndex(parentIdx, parentTbl);
22032 : }
22033 :
22034 193 : relation_close(parentTbl, AccessShareLock);
22035 : /* keep these locks till commit */
22036 193 : relation_close(partTbl, NoLock);
22037 193 : relation_close(partIdx, NoLock);
22038 :
22039 193 : return address;
22040 : }
22041 :
22042 : /*
22043 : * Verify whether the given partition already contains an index attached
22044 : * to the given partitioned index. If so, raise an error.
22045 : */
22046 : static void
22047 217 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
22048 : {
22049 : Oid existingIdx;
22050 :
22051 217 : existingIdx = index_get_partition(partitionTbl,
22052 : RelationGetRelid(parentIdx));
22053 217 : if (OidIsValid(existingIdx))
22054 4 : ereport(ERROR,
22055 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
22056 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22057 : RelationGetRelationName(partIdx),
22058 : RelationGetRelationName(parentIdx)),
22059 : errdetail("Another index \"%s\" is already attached for partition \"%s\".",
22060 : get_rel_name(existingIdx),
22061 : RelationGetRelationName(partitionTbl))));
22062 213 : }
22063 :
22064 : /*
22065 : * Verify whether the set of attached partition indexes to a parent index on
22066 : * a partitioned table is complete. If it is, mark the parent index valid.
22067 : *
22068 : * This should be called each time a partition index is attached.
22069 : */
22070 : static void
22071 205 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
22072 : {
22073 : Relation inheritsRel;
22074 : SysScanDesc scan;
22075 : ScanKeyData key;
22076 205 : int tuples = 0;
22077 : HeapTuple inhTup;
22078 205 : bool updated = false;
22079 :
22080 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
22081 :
22082 : /*
22083 : * Scan pg_inherits for this parent index. Count each valid index we find
22084 : * (verifying the pg_index entry for each), and if we reach the total
22085 : * amount we expect, we can mark this parent index as valid.
22086 : */
22087 205 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
22088 205 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
22089 : BTEqualStrategyNumber, F_OIDEQ,
22090 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
22091 205 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
22092 : NULL, 1, &key);
22093 523 : while ((inhTup = systable_getnext(scan)) != NULL)
22094 : {
22095 318 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
22096 : HeapTuple indTup;
22097 : Form_pg_index indexForm;
22098 :
22099 318 : indTup = SearchSysCache1(INDEXRELID,
22100 : ObjectIdGetDatum(inhForm->inhrelid));
22101 318 : if (!HeapTupleIsValid(indTup))
22102 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
22103 318 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
22104 318 : if (indexForm->indisvalid)
22105 280 : tuples += 1;
22106 318 : ReleaseSysCache(indTup);
22107 : }
22108 :
22109 : /* Done with pg_inherits */
22110 205 : systable_endscan(scan);
22111 205 : table_close(inheritsRel, AccessShareLock);
22112 :
22113 : /*
22114 : * If we found as many inherited indexes as the partitioned table has
22115 : * partitions, we're good; update pg_index to set indisvalid.
22116 : */
22117 205 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
22118 : {
22119 : Relation idxRel;
22120 : HeapTuple indTup;
22121 : Form_pg_index indexForm;
22122 :
22123 106 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
22124 106 : indTup = SearchSysCacheCopy1(INDEXRELID,
22125 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
22126 106 : if (!HeapTupleIsValid(indTup))
22127 0 : elog(ERROR, "cache lookup failed for index %u",
22128 : RelationGetRelid(partedIdx));
22129 106 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
22130 :
22131 106 : indexForm->indisvalid = true;
22132 106 : updated = true;
22133 :
22134 106 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
22135 :
22136 106 : table_close(idxRel, RowExclusiveLock);
22137 106 : heap_freetuple(indTup);
22138 : }
22139 :
22140 : /*
22141 : * If this index is in turn a partition of a larger index, validating it
22142 : * might cause the parent to become valid also. Try that.
22143 : */
22144 205 : if (updated && partedIdx->rd_rel->relispartition)
22145 : {
22146 : Oid parentIdxId,
22147 : parentTblId;
22148 : Relation parentIdx,
22149 : parentTbl;
22150 :
22151 : /* make sure we see the validation we just did */
22152 28 : CommandCounterIncrement();
22153 :
22154 28 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
22155 28 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
22156 28 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
22157 28 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
22158 : Assert(!parentIdx->rd_index->indisvalid);
22159 :
22160 28 : validatePartitionedIndex(parentIdx, parentTbl);
22161 :
22162 28 : relation_close(parentIdx, AccessExclusiveLock);
22163 28 : relation_close(parentTbl, AccessExclusiveLock);
22164 : }
22165 205 : }
22166 :
22167 : /*
22168 : * When attaching an index as a partition of a partitioned index which is a
22169 : * primary key, verify that all the columns in the partition are marked NOT
22170 : * NULL.
22171 : */
22172 : static void
22173 55 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
22174 : {
22175 111 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
22176 : {
22177 56 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
22178 56 : iinfo->ii_IndexAttrNumbers[i] - 1);
22179 :
22180 56 : if (!att->attnotnull)
22181 0 : ereport(ERROR,
22182 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
22183 : errmsg("invalid primary key definition"),
22184 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
22185 : NameStr(att->attname),
22186 : RelationGetRelationName(partition)));
22187 : }
22188 55 : }
22189 :
22190 : /*
22191 : * Return an OID list of constraints that reference the given relation
22192 : * that are marked as having a parent constraints.
22193 : */
22194 : static List *
22195 1016 : GetParentedForeignKeyRefs(Relation partition)
22196 : {
22197 : Relation pg_constraint;
22198 : HeapTuple tuple;
22199 : SysScanDesc scan;
22200 : ScanKeyData key[2];
22201 1016 : List *constraints = NIL;
22202 :
22203 : /*
22204 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
22205 : * scan.
22206 : */
22207 1443 : if (RelationGetIndexList(partition) == NIL ||
22208 427 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
22209 : INDEX_ATTR_BITMAP_KEY)))
22210 757 : return NIL;
22211 :
22212 : /* Search for constraints referencing this table */
22213 259 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
22214 259 : ScanKeyInit(&key[0],
22215 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
22216 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
22217 259 : ScanKeyInit(&key[1],
22218 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
22219 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
22220 :
22221 : /* XXX This is a seqscan, as we don't have a usable index */
22222 259 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
22223 380 : while ((tuple = systable_getnext(scan)) != NULL)
22224 : {
22225 121 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
22226 :
22227 : /*
22228 : * We only need to process constraints that are part of larger ones.
22229 : */
22230 121 : if (!OidIsValid(constrForm->conparentid))
22231 0 : continue;
22232 :
22233 121 : constraints = lappend_oid(constraints, constrForm->oid);
22234 : }
22235 :
22236 259 : systable_endscan(scan);
22237 259 : table_close(pg_constraint, AccessShareLock);
22238 :
22239 259 : return constraints;
22240 : }
22241 :
22242 : /*
22243 : * During DETACH PARTITION, verify that any foreign keys pointing to the
22244 : * partitioned table would not become invalid. An error is raised if any
22245 : * referenced values exist.
22246 : */
22247 : static void
22248 344 : ATDetachCheckNoForeignKeyRefs(Relation partition)
22249 : {
22250 : List *constraints;
22251 : ListCell *cell;
22252 :
22253 344 : constraints = GetParentedForeignKeyRefs(partition);
22254 :
22255 387 : foreach(cell, constraints)
22256 : {
22257 65 : Oid constrOid = lfirst_oid(cell);
22258 : HeapTuple tuple;
22259 : Form_pg_constraint constrForm;
22260 : Relation rel;
22261 65 : Trigger trig = {0};
22262 :
22263 65 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
22264 65 : if (!HeapTupleIsValid(tuple))
22265 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22266 65 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
22267 :
22268 : Assert(OidIsValid(constrForm->conparentid));
22269 : Assert(constrForm->confrelid == RelationGetRelid(partition));
22270 :
22271 : /* prevent data changes into the referencing table until commit */
22272 65 : rel = table_open(constrForm->conrelid, ShareLock);
22273 :
22274 65 : trig.tgoid = InvalidOid;
22275 65 : trig.tgname = NameStr(constrForm->conname);
22276 65 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22277 65 : trig.tgisinternal = true;
22278 65 : trig.tgconstrrelid = RelationGetRelid(partition);
22279 65 : trig.tgconstrindid = constrForm->conindid;
22280 65 : trig.tgconstraint = constrForm->oid;
22281 65 : trig.tgdeferrable = false;
22282 65 : trig.tginitdeferred = false;
22283 : /* we needn't fill in remaining fields */
22284 :
22285 65 : RI_PartitionRemove_Check(&trig, rel, partition);
22286 :
22287 43 : ReleaseSysCache(tuple);
22288 :
22289 43 : table_close(rel, NoLock);
22290 : }
22291 322 : }
22292 :
22293 : /*
22294 : * resolve column compression specification to compression method.
22295 : */
22296 : static char
22297 169473 : GetAttributeCompression(Oid atttypid, const char *compression)
22298 : {
22299 : char cmethod;
22300 :
22301 169473 : if (compression == NULL || strcmp(compression, "default") == 0)
22302 169336 : return InvalidCompressionMethod;
22303 :
22304 : /*
22305 : * To specify a nondefault method, the column data type must be toastable.
22306 : * Note this says nothing about whether the column's attstorage setting
22307 : * permits compression; we intentionally allow attstorage and
22308 : * attcompression to be independent. But with a non-toastable type,
22309 : * attstorage could not be set to a value that would permit compression.
22310 : *
22311 : * We don't actually need to enforce this, since nothing bad would happen
22312 : * if attcompression were non-default; it would never be consulted. But
22313 : * it seems more user-friendly to complain about a certainly-useless
22314 : * attempt to set the property.
22315 : */
22316 137 : if (!TypeIsToastable(atttypid))
22317 4 : ereport(ERROR,
22318 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22319 : errmsg("column data type %s does not support compression",
22320 : format_type_be(atttypid))));
22321 :
22322 133 : cmethod = CompressionNameToMethod(compression);
22323 133 : if (!CompressionMethodIsValid(cmethod))
22324 8 : ereport(ERROR,
22325 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22326 : errmsg("invalid compression method \"%s\"", compression)));
22327 :
22328 125 : return cmethod;
22329 : }
22330 :
22331 : /*
22332 : * resolve column storage specification
22333 : */
22334 : static char
22335 204 : GetAttributeStorage(Oid atttypid, const char *storagemode)
22336 : {
22337 204 : char cstorage = 0;
22338 :
22339 204 : if (pg_strcasecmp(storagemode, "plain") == 0)
22340 37 : cstorage = TYPSTORAGE_PLAIN;
22341 167 : else if (pg_strcasecmp(storagemode, "external") == 0)
22342 104 : cstorage = TYPSTORAGE_EXTERNAL;
22343 63 : else if (pg_strcasecmp(storagemode, "extended") == 0)
22344 26 : cstorage = TYPSTORAGE_EXTENDED;
22345 37 : else if (pg_strcasecmp(storagemode, "main") == 0)
22346 33 : cstorage = TYPSTORAGE_MAIN;
22347 4 : else if (pg_strcasecmp(storagemode, "default") == 0)
22348 4 : cstorage = get_typstorage(atttypid);
22349 : else
22350 0 : ereport(ERROR,
22351 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22352 : errmsg("invalid storage type \"%s\"",
22353 : storagemode)));
22354 :
22355 : /*
22356 : * safety check: do not allow toasted storage modes unless column datatype
22357 : * is TOAST-aware.
22358 : */
22359 204 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22360 4 : ereport(ERROR,
22361 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22362 : errmsg("column data type %s can only have storage PLAIN",
22363 : format_type_be(atttypid))));
22364 :
22365 200 : return cstorage;
22366 : }
22367 :
22368 : /*
22369 : * buildExpressionExecutionStates: build the needed expression execution states
22370 : * for new partition (newPartRel) checks and initialize expressions for
22371 : * generated columns. All expressions should be created in "tab"
22372 : * (AlteredTableInfo structure).
22373 : */
22374 : static void
22375 420 : buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
22376 : {
22377 : /*
22378 : * Build the needed expression execution states. Here, we expect only NOT
22379 : * NULL and CHECK constraint.
22380 : */
22381 856 : foreach_ptr(NewConstraint, con, tab->constraints)
22382 : {
22383 16 : switch (con->contype)
22384 : {
22385 16 : case CONSTR_CHECK:
22386 :
22387 : /*
22388 : * We already expanded virtual expression in
22389 : * createTableConstraints.
22390 : */
22391 16 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22392 16 : break;
22393 0 : case CONSTR_NOTNULL:
22394 : /* Nothing to do here. */
22395 0 : break;
22396 0 : default:
22397 0 : elog(ERROR, "unrecognized constraint type: %d",
22398 : (int) con->contype);
22399 : }
22400 : }
22401 :
22402 : /* Expression already planned in createTableConstraints */
22403 884 : foreach_ptr(NewColumnValue, ex, tab->newvals)
22404 44 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22405 420 : }
22406 :
22407 : /*
22408 : * evaluateGeneratedExpressionsAndCheckConstraints: evaluate any generated
22409 : * expressions for "tab" (AlteredTableInfo structure) whose inputs come from
22410 : * the new tuple (insertslot) of the new partition (newPartRel).
22411 : */
22412 : static void
22413 664 : evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab,
22414 : Relation newPartRel,
22415 : TupleTableSlot *insertslot,
22416 : ExprContext *econtext)
22417 : {
22418 664 : econtext->ecxt_scantuple = insertslot;
22419 :
22420 1392 : foreach_ptr(NewColumnValue, ex, tab->newvals)
22421 : {
22422 64 : if (!ex->is_generated)
22423 0 : continue;
22424 :
22425 64 : insertslot->tts_values[ex->attnum - 1]
22426 64 : = ExecEvalExpr(ex->exprstate,
22427 : econtext,
22428 64 : &insertslot->tts_isnull[ex->attnum - 1]);
22429 : }
22430 :
22431 1352 : foreach_ptr(NewConstraint, con, tab->constraints)
22432 : {
22433 24 : switch (con->contype)
22434 : {
22435 24 : case CONSTR_CHECK:
22436 24 : if (!ExecCheck(con->qualstate, econtext))
22437 0 : ereport(ERROR,
22438 : errcode(ERRCODE_CHECK_VIOLATION),
22439 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22440 : con->name, RelationGetRelationName(newPartRel)),
22441 : errtableconstraint(newPartRel, con->name));
22442 24 : break;
22443 0 : case CONSTR_NOTNULL:
22444 : case CONSTR_FOREIGN:
22445 : /* Nothing to do here */
22446 0 : break;
22447 0 : default:
22448 0 : elog(ERROR, "unrecognized constraint type: %d",
22449 : (int) con->contype);
22450 : }
22451 : }
22452 664 : }
22453 :
22454 : /*
22455 : * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
22456 : */
22457 : static List *
22458 440 : getAttributesList(Relation parent_rel)
22459 : {
22460 : AttrNumber parent_attno;
22461 : TupleDesc modelDesc;
22462 440 : List *colList = NIL;
22463 :
22464 440 : modelDesc = RelationGetDescr(parent_rel);
22465 :
22466 1581 : for (parent_attno = 1; parent_attno <= modelDesc->natts;
22467 1141 : parent_attno++)
22468 : {
22469 1141 : Form_pg_attribute attribute = TupleDescAttr(modelDesc,
22470 : parent_attno - 1);
22471 : ColumnDef *def;
22472 :
22473 : /* Ignore dropped columns in the parent. */
22474 1141 : if (attribute->attisdropped)
22475 0 : continue;
22476 :
22477 1141 : def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22478 : attribute->atttypmod, attribute->attcollation);
22479 :
22480 1141 : def->is_not_null = attribute->attnotnull;
22481 :
22482 : /* Copy identity. */
22483 1141 : def->identity = attribute->attidentity;
22484 :
22485 : /* Copy attgenerated. */
22486 1141 : def->generated = attribute->attgenerated;
22487 :
22488 1141 : def->storage = attribute->attstorage;
22489 :
22490 : /* Likewise, copy compression. */
22491 1141 : if (CompressionMethodIsValid(attribute->attcompression))
22492 12 : def->compression =
22493 12 : pstrdup(GetCompressionMethodName(attribute->attcompression));
22494 : else
22495 1129 : def->compression = NULL;
22496 :
22497 : /* Add to column list. */
22498 1141 : colList = lappend(colList, def);
22499 : }
22500 :
22501 440 : return colList;
22502 : }
22503 :
22504 : /*
22505 : * createTableConstraints:
22506 : * create check constraints, default values, and generated values for newRel
22507 : * based on parent_rel. tab is pending-work queue for newRel, we may need it in
22508 : * MergePartitionsMoveRows.
22509 : */
22510 : static void
22511 420 : createTableConstraints(List **wqueue, AlteredTableInfo *tab,
22512 : Relation parent_rel, Relation newRel)
22513 : {
22514 : TupleDesc tupleDesc;
22515 : TupleConstr *constr;
22516 : AttrMap *attmap;
22517 : AttrNumber parent_attno;
22518 : int ccnum;
22519 420 : List *constraints = NIL;
22520 420 : List *cookedConstraints = NIL;
22521 :
22522 420 : tupleDesc = RelationGetDescr(parent_rel);
22523 420 : constr = tupleDesc->constr;
22524 :
22525 420 : if (!constr)
22526 264 : return;
22527 :
22528 : /*
22529 : * Construct a map from the parent relation's attnos to the child rel's.
22530 : * This re-checks type match, etc, although it shouldn't be possible to
22531 : * have a failure since both tables are locked.
22532 : */
22533 156 : attmap = build_attrmap_by_name(RelationGetDescr(newRel),
22534 : tupleDesc,
22535 : false);
22536 :
22537 : /* Cycle for default values. */
22538 592 : for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22539 : {
22540 436 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
22541 : parent_attno - 1);
22542 :
22543 : /* Ignore dropped columns in the parent. */
22544 436 : if (attribute->attisdropped)
22545 0 : continue;
22546 :
22547 : /* Copy the default, if present, and it should be copied. */
22548 436 : if (attribute->atthasdef)
22549 : {
22550 100 : Node *this_default = NULL;
22551 : bool found_whole_row;
22552 : AttrNumber num;
22553 : Node *def;
22554 : NewColumnValue *newval;
22555 :
22556 100 : if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22557 4 : this_default = build_generation_expression(parent_rel, attribute->attnum);
22558 : else
22559 : {
22560 96 : this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
22561 96 : if (this_default == NULL)
22562 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
22563 : attribute->attnum, RelationGetRelationName(parent_rel));
22564 : }
22565 :
22566 100 : num = attmap->attnums[parent_attno - 1];
22567 100 : def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22568 :
22569 100 : if (found_whole_row && attribute->attgenerated != '\0')
22570 0 : elog(ERROR, "cannot convert whole-row table reference");
22571 :
22572 : /* Add a pre-cooked default expression. */
22573 100 : StoreAttrDefault(newRel, num, def, true);
22574 :
22575 : /*
22576 : * Stored generated column expressions in parent_rel might
22577 : * reference the tableoid. newRel, parent_rel tableoid clear is
22578 : * not the same. If so, these stored generated columns require
22579 : * recomputation for newRel within MergePartitionsMoveRows.
22580 : */
22581 100 : if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22582 : {
22583 44 : newval = palloc0_object(NewColumnValue);
22584 44 : newval->attnum = num;
22585 44 : newval->expr = expression_planner((Expr *) def);
22586 44 : newval->is_generated = (attribute->attgenerated != '\0');
22587 44 : tab->newvals = lappend(tab->newvals, newval);
22588 : }
22589 : }
22590 : }
22591 :
22592 : /* Cycle for CHECK constraints. */
22593 224 : for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22594 : {
22595 68 : char *ccname = constr->check[ccnum].ccname;
22596 68 : char *ccbin = constr->check[ccnum].ccbin;
22597 68 : bool ccenforced = constr->check[ccnum].ccenforced;
22598 68 : bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22599 68 : bool ccvalid = constr->check[ccnum].ccvalid;
22600 : Node *ccbin_node;
22601 : bool found_whole_row;
22602 : Constraint *constr;
22603 :
22604 : /*
22605 : * The partitioned table can not have a NO INHERIT check constraint
22606 : * (see StoreRelCheck function for details).
22607 : */
22608 : Assert(!ccnoinherit);
22609 :
22610 68 : ccbin_node = map_variable_attnos(stringToNode(ccbin),
22611 : 1, 0,
22612 : attmap,
22613 : InvalidOid, &found_whole_row);
22614 :
22615 : /*
22616 : * For the moment we have to reject whole-row variables (as for CREATE
22617 : * TABLE LIKE and inheritances).
22618 : */
22619 68 : if (found_whole_row)
22620 0 : elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
22621 : ccname,
22622 : RelationGetRelationName(parent_rel));
22623 :
22624 68 : constr = makeNode(Constraint);
22625 68 : constr->contype = CONSTR_CHECK;
22626 68 : constr->conname = pstrdup(ccname);
22627 68 : constr->deferrable = false;
22628 68 : constr->initdeferred = false;
22629 68 : constr->is_enforced = ccenforced;
22630 68 : constr->skip_validation = !ccvalid;
22631 68 : constr->initially_valid = ccvalid;
22632 68 : constr->is_no_inherit = ccnoinherit;
22633 68 : constr->raw_expr = NULL;
22634 68 : constr->cooked_expr = nodeToString(ccbin_node);
22635 68 : constr->location = -1;
22636 68 : constraints = lappend(constraints, constr);
22637 : }
22638 :
22639 : /* Install all CHECK constraints. */
22640 156 : cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
22641 : false, true, true, NULL);
22642 :
22643 : /* Make the additional catalog changes visible. */
22644 156 : CommandCounterIncrement();
22645 :
22646 : /*
22647 : * parent_rel check constraint expression may reference tableoid, so later
22648 : * in MergePartitionsMoveRows, we need to evaluate the check constraint
22649 : * again for the newRel. We can check whether the check constraint
22650 : * contains a tableoid reference via pull_varattnos.
22651 : */
22652 380 : foreach_ptr(CookedConstraint, ccon, cookedConstraints)
22653 : {
22654 68 : if (!ccon->skip_validation)
22655 : {
22656 : Node *qual;
22657 44 : Bitmapset *attnums = NULL;
22658 :
22659 : Assert(ccon->contype == CONSTR_CHECK);
22660 44 : qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
22661 44 : pull_varattnos(qual, 1, &attnums);
22662 :
22663 : /*
22664 : * Add a check only if it contains a tableoid
22665 : * (TableOidAttributeNumber).
22666 : */
22667 44 : if (bms_is_member(TableOidAttributeNumber - FirstLowInvalidHeapAttributeNumber,
22668 : attnums))
22669 : {
22670 : NewConstraint *newcon;
22671 :
22672 16 : newcon = palloc0_object(NewConstraint);
22673 16 : newcon->name = ccon->name;
22674 16 : newcon->contype = CONSTR_CHECK;
22675 16 : newcon->qual = qual;
22676 :
22677 16 : tab->constraints = lappend(tab->constraints, newcon);
22678 : }
22679 : }
22680 : }
22681 :
22682 : /* Don't need the cookedConstraints anymore. */
22683 156 : list_free_deep(cookedConstraints);
22684 :
22685 : /* Reproduce not-null constraints. */
22686 156 : if (constr->has_not_null)
22687 : {
22688 : List *nnconstraints;
22689 :
22690 : /*
22691 : * The "include_noinh" argument is false because a partitioned table
22692 : * can't have NO INHERIT constraint.
22693 : */
22694 108 : nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
22695 : false, false);
22696 :
22697 : Assert(list_length(nnconstraints) > 0);
22698 :
22699 : /*
22700 : * We already set pg_attribute.attnotnull in createPartitionTable. No
22701 : * need call set_attnotnull again.
22702 : */
22703 108 : AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
22704 : }
22705 : }
22706 :
22707 : /*
22708 : * createPartitionTable:
22709 : *
22710 : * Create a new partition (newPartName) for the partitioned table (parent_rel).
22711 : * ownerId is determined by the partition on which the operation is performed,
22712 : * so it is passed separately. The new partition will inherit the access method
22713 : * and persistence type from the parent table.
22714 : *
22715 : * Returns the created relation (locked in AccessExclusiveLock mode).
22716 : */
22717 : static Relation
22718 440 : createPartitionTable(List **wqueue, RangeVar *newPartName,
22719 : Relation parent_rel, Oid ownerId)
22720 : {
22721 : Relation newRel;
22722 : Oid newRelId;
22723 : Oid existingRelid;
22724 : TupleDesc descriptor;
22725 440 : List *colList = NIL;
22726 : Oid relamId;
22727 : Oid namespaceId;
22728 : AlteredTableInfo *new_partrel_tab;
22729 440 : Form_pg_class parent_relform = parent_rel->rd_rel;
22730 :
22731 : /* If the existing rel is temp, it must belong to this session. */
22732 440 : if (RELATION_IS_OTHER_TEMP(parent_rel))
22733 0 : ereport(ERROR,
22734 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
22735 : errmsg("cannot create as partition of temporary relation of another session"));
22736 :
22737 : /* Look up inheritance ancestors and generate the relation schema. */
22738 440 : colList = getAttributesList(parent_rel);
22739 :
22740 : /* Create a tuple descriptor from the relation schema. */
22741 440 : descriptor = BuildDescForRelation(colList);
22742 :
22743 : /* Look up the access method for the new relation. */
22744 440 : relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
22745 :
22746 : /* Look up the namespace in which we are supposed to create the relation. */
22747 : namespaceId =
22748 440 : RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
22749 440 : if (OidIsValid(existingRelid))
22750 0 : ereport(ERROR,
22751 : errcode(ERRCODE_DUPLICATE_TABLE),
22752 : errmsg("relation \"%s\" already exists", newPartName->relname));
22753 :
22754 : /*
22755 : * We intended to create the partition with the same persistence as the
22756 : * parent table, but we still need to recheck because that might be
22757 : * affected by the search_path. If the parent is permanent, so must be
22758 : * all of its partitions.
22759 : */
22760 440 : if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22761 404 : newPartName->relpersistence == RELPERSISTENCE_TEMP)
22762 8 : ereport(ERROR,
22763 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
22764 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22765 : RelationGetRelationName(parent_rel)));
22766 :
22767 : /* Permanent rels cannot be partitions belonging to a temporary parent. */
22768 432 : if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22769 408 : parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22770 12 : ereport(ERROR,
22771 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
22772 : errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22773 : RelationGetRelationName(parent_rel)));
22774 :
22775 : /* Create the relation. */
22776 420 : newRelId = heap_create_with_catalog(newPartName->relname,
22777 : namespaceId,
22778 : parent_relform->reltablespace,
22779 : InvalidOid,
22780 : InvalidOid,
22781 : InvalidOid,
22782 : ownerId,
22783 : relamId,
22784 : descriptor,
22785 : NIL,
22786 : RELKIND_RELATION,
22787 420 : newPartName->relpersistence,
22788 : false,
22789 : false,
22790 : ONCOMMIT_NOOP,
22791 : (Datum) 0,
22792 : true,
22793 : allowSystemTableMods,
22794 : true,
22795 : InvalidOid,
22796 : NULL);
22797 :
22798 : /*
22799 : * We must bump the command counter to make the newly-created relation
22800 : * tuple visible for opening.
22801 : */
22802 420 : CommandCounterIncrement();
22803 :
22804 : /*
22805 : * Open the new partition with no lock, because we already have an
22806 : * AccessExclusiveLock placed there after creation.
22807 : */
22808 420 : newRel = table_open(newRelId, NoLock);
22809 :
22810 : /* Find or create a work queue entry for the newly created table. */
22811 420 : new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
22812 :
22813 : /* Create constraints, default values, and generated values. */
22814 420 : createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
22815 :
22816 : /*
22817 : * Need to call CommandCounterIncrement, so a fresh relcache entry has
22818 : * newly installed constraint info.
22819 : */
22820 420 : CommandCounterIncrement();
22821 :
22822 420 : return newRel;
22823 : }
22824 :
22825 : /*
22826 : * MergePartitionsMoveRows: scan partitions to be merged (mergingPartitions)
22827 : * of the partitioned table and move rows into the new partition
22828 : * (newPartRel). We also verify check constraints against these rows.
22829 : */
22830 : static void
22831 88 : MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
22832 : {
22833 : CommandId mycid;
22834 : EState *estate;
22835 : AlteredTableInfo *tab;
22836 : ListCell *ltab;
22837 :
22838 : /* The FSM is empty, so don't bother using it. */
22839 88 : uint32 ti_options = TABLE_INSERT_SKIP_FSM;
22840 : BulkInsertState bistate; /* state of bulk inserts for partition */
22841 : TupleTableSlot *dstslot;
22842 :
22843 : /* Find the work queue entry for the new partition table: newPartRel. */
22844 88 : tab = ATGetQueueEntry(wqueue, newPartRel);
22845 :
22846 : /* Generate the constraint and default execution states. */
22847 88 : estate = CreateExecutorState();
22848 :
22849 88 : buildExpressionExecutionStates(tab, newPartRel, estate);
22850 :
22851 88 : mycid = GetCurrentCommandId(true);
22852 :
22853 : /* Prepare a BulkInsertState for table_tuple_insert. */
22854 88 : bistate = GetBulkInsertState();
22855 :
22856 : /* Create the necessary tuple slot. */
22857 88 : dstslot = table_slot_create(newPartRel, NULL);
22858 :
22859 380 : foreach_oid(merging_oid, mergingPartitions)
22860 : {
22861 : ExprContext *econtext;
22862 : TupleTableSlot *srcslot;
22863 : TupleConversionMap *tuple_map;
22864 : TableScanDesc scan;
22865 : MemoryContext oldCxt;
22866 : Snapshot snapshot;
22867 : Relation mergingPartition;
22868 :
22869 204 : econtext = GetPerTupleExprContext(estate);
22870 :
22871 : /*
22872 : * Partition is already locked in the transformPartitionCmdForMerge
22873 : * function.
22874 : */
22875 204 : mergingPartition = table_open(merging_oid, NoLock);
22876 :
22877 : /* Create a source tuple slot for the partition being merged. */
22878 204 : srcslot = table_slot_create(mergingPartition, NULL);
22879 :
22880 : /*
22881 : * Map computing for moving attributes of the merged partition to the
22882 : * new partition.
22883 : */
22884 204 : tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
22885 : RelationGetDescr(newPartRel));
22886 :
22887 : /* Scan through the rows. */
22888 204 : snapshot = RegisterSnapshot(GetLatestSnapshot());
22889 204 : scan = table_beginscan(mergingPartition, snapshot, 0, NULL,
22890 : SO_NONE);
22891 :
22892 : /*
22893 : * Switch to per-tuple memory context and reset it for each tuple
22894 : * produced, so we don't leak memory.
22895 : */
22896 204 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
22897 :
22898 445 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
22899 : {
22900 : TupleTableSlot *insertslot;
22901 :
22902 241 : CHECK_FOR_INTERRUPTS();
22903 :
22904 241 : if (tuple_map)
22905 : {
22906 : /* Need to use a map to copy attributes. */
22907 28 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22908 : }
22909 : else
22910 : {
22911 213 : slot_getallattrs(srcslot);
22912 :
22913 : /* Copy attributes directly. */
22914 213 : insertslot = dstslot;
22915 :
22916 213 : ExecClearTuple(insertslot);
22917 :
22918 213 : memcpy(insertslot->tts_values, srcslot->tts_values,
22919 213 : sizeof(Datum) * srcslot->tts_nvalid);
22920 213 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22921 213 : sizeof(bool) * srcslot->tts_nvalid);
22922 :
22923 213 : ExecStoreVirtualTuple(insertslot);
22924 : }
22925 :
22926 : /*
22927 : * Constraints and GENERATED expressions might reference the
22928 : * tableoid column, so fill tts_tableOid with the desired value.
22929 : * (We must do this each time, because it gets overwritten with
22930 : * newrel's OID during storing.)
22931 : */
22932 241 : insertslot->tts_tableOid = RelationGetRelid(newPartRel);
22933 :
22934 : /*
22935 : * Now, evaluate any generated expressions whose inputs come from
22936 : * the new tuple. We assume these columns won't reference each
22937 : * other, so that there's no ordering dependency.
22938 : */
22939 241 : evaluateGeneratedExpressionsAndCheckConstraints(tab, newPartRel,
22940 : insertslot, econtext);
22941 :
22942 : /* Write the tuple out to the new relation. */
22943 241 : table_tuple_insert(newPartRel, insertslot, mycid,
22944 : ti_options, bistate);
22945 :
22946 241 : ResetExprContext(econtext);
22947 : }
22948 :
22949 204 : MemoryContextSwitchTo(oldCxt);
22950 204 : table_endscan(scan);
22951 204 : UnregisterSnapshot(snapshot);
22952 :
22953 204 : if (tuple_map)
22954 20 : free_conversion_map(tuple_map);
22955 :
22956 204 : ExecDropSingleTupleTableSlot(srcslot);
22957 204 : table_close(mergingPartition, NoLock);
22958 : }
22959 :
22960 88 : FreeExecutorState(estate);
22961 88 : ExecDropSingleTupleTableSlot(dstslot);
22962 88 : FreeBulkInsertState(bistate);
22963 :
22964 88 : table_finish_bulk_insert(newPartRel, ti_options);
22965 :
22966 : /*
22967 : * We don't need to process this newPartRel since we already processed it
22968 : * here, so delete the ALTER TABLE queue for it.
22969 : */
22970 176 : foreach(ltab, *wqueue)
22971 : {
22972 176 : tab = (AlteredTableInfo *) lfirst(ltab);
22973 176 : if (tab->relid == RelationGetRelid(newPartRel))
22974 : {
22975 88 : *wqueue = list_delete_cell(*wqueue, ltab);
22976 88 : break;
22977 : }
22978 : }
22979 88 : }
22980 :
22981 : /*
22982 : * detachPartitionTable: detach partition "child_rel" from partitioned table
22983 : * "parent_rel" with default partition identifier "defaultPartOid"
22984 : */
22985 : static void
22986 368 : detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
22987 : {
22988 : /* Remove the pg_inherits row first. */
22989 368 : RemoveInheritance(child_rel, parent_rel, false);
22990 :
22991 : /*
22992 : * Detaching the partition might involve TOAST table access, so ensure we
22993 : * have a valid snapshot.
22994 : */
22995 368 : PushActiveSnapshot(GetTransactionSnapshot());
22996 :
22997 : /* Do the final part of detaching. */
22998 368 : DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
22999 :
23000 368 : PopActiveSnapshot();
23001 368 : }
23002 :
23003 : /*
23004 : * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
23005 : */
23006 : static void
23007 116 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
23008 : PartitionCmd *cmd, AlterTableUtilityContext *context)
23009 : {
23010 : Relation newPartRel;
23011 116 : List *mergingPartitions = NIL;
23012 : Oid defaultPartOid;
23013 : Oid existingRelid;
23014 116 : Oid ownerId = InvalidOid;
23015 : Oid save_userid;
23016 : int save_sec_context;
23017 : int save_nestlevel;
23018 :
23019 : /*
23020 : * Check ownership of merged partitions - partitions with different owners
23021 : * cannot be merged. Also, collect the OIDs of these partitions during the
23022 : * check.
23023 : */
23024 488 : foreach_node(RangeVar, name, cmd->partlist)
23025 : {
23026 : Relation mergingPartition;
23027 :
23028 : /*
23029 : * We are going to detach and remove this partition. We already took
23030 : * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
23031 : * NoLock is fine.
23032 : */
23033 264 : mergingPartition = table_openrv_extended(name, NoLock, false);
23034 : Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
23035 :
23036 264 : if (OidIsValid(ownerId))
23037 : {
23038 : /* Do the partitions being merged have different owners? */
23039 148 : if (ownerId != mergingPartition->rd_rel->relowner)
23040 4 : ereport(ERROR,
23041 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
23042 : errmsg("partitions being merged have different owners"));
23043 : }
23044 : else
23045 116 : ownerId = mergingPartition->rd_rel->relowner;
23046 :
23047 : /* Store the next merging partition into the list. */
23048 260 : mergingPartitions = lappend_oid(mergingPartitions,
23049 : RelationGetRelid(mergingPartition));
23050 :
23051 260 : table_close(mergingPartition, NoLock);
23052 : }
23053 :
23054 : /* Look up the existing relation by the new partition name. */
23055 112 : RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
23056 :
23057 : /*
23058 : * Check if this name is already taken. This helps us to detect the
23059 : * situation when one of the merging partitions has the same name as the
23060 : * new partition. Otherwise, this would fail later on anyway, but
23061 : * catching this here allows us to emit a nicer error message.
23062 : */
23063 112 : if (OidIsValid(existingRelid))
23064 : {
23065 17 : if (list_member_oid(mergingPartitions, existingRelid))
23066 : {
23067 : /*
23068 : * The new partition has the same name as one of the merging
23069 : * partitions.
23070 : */
23071 : char tmpRelName[NAMEDATALEN];
23072 :
23073 : /* Generate a temporary name. */
23074 13 : sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23075 :
23076 : /*
23077 : * Rename the existing partition with a temporary name, leaving it
23078 : * free for the new partition. We don't need to care about this
23079 : * in the future because we're going to eventually drop the
23080 : * existing partition anyway.
23081 : */
23082 13 : RenameRelationInternal(existingRelid, tmpRelName, true, false);
23083 :
23084 : /*
23085 : * We must bump the command counter to make the new partition
23086 : * tuple visible for rename.
23087 : */
23088 13 : CommandCounterIncrement();
23089 : }
23090 : else
23091 : {
23092 4 : ereport(ERROR,
23093 : errcode(ERRCODE_DUPLICATE_TABLE),
23094 : errmsg("relation \"%s\" already exists", cmd->name->relname));
23095 : }
23096 : }
23097 :
23098 : defaultPartOid =
23099 108 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
23100 :
23101 : /* Detach all merging partitions. */
23102 460 : foreach_oid(mergingPartitionOid, mergingPartitions)
23103 : {
23104 : Relation child_rel;
23105 :
23106 244 : child_rel = table_open(mergingPartitionOid, NoLock);
23107 :
23108 244 : detachPartitionTable(rel, child_rel, defaultPartOid);
23109 :
23110 244 : table_close(child_rel, NoLock);
23111 : }
23112 :
23113 : /*
23114 : * Perform a preliminary check to determine whether it's safe to drop all
23115 : * merging partitions before we actually do so later. After merging rows
23116 : * into the new partitions via MergePartitionsMoveRows, all old partitions
23117 : * need to be dropped. However, since the drop behavior is DROP_RESTRICT
23118 : * and the merge process (MergePartitionsMoveRows) can be time-consuming,
23119 : * performing an early check on the drop eligibility of old partitions is
23120 : * preferable.
23121 : */
23122 448 : foreach_oid(mergingPartitionOid, mergingPartitions)
23123 : {
23124 : ObjectAddress object;
23125 :
23126 : /* Get oid of the later to be dropped relation. */
23127 240 : object.objectId = mergingPartitionOid;
23128 240 : object.classId = RelationRelationId;
23129 240 : object.objectSubId = 0;
23130 :
23131 240 : performDeletionCheck(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
23132 : }
23133 :
23134 : /*
23135 : * Create a table for the new partition, using the partitioned table as a
23136 : * model.
23137 : */
23138 : Assert(OidIsValid(ownerId));
23139 104 : newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
23140 :
23141 : /*
23142 : * Switch to the table owner's userid, so that any index functions are run
23143 : * as that user. Also, lockdown security-restricted operations and
23144 : * arrange to make GUC variable changes local to this command.
23145 : *
23146 : * Need to do it after determining the namespace in the
23147 : * createPartitionTable() call.
23148 : */
23149 88 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
23150 88 : SetUserIdAndSecContext(ownerId,
23151 : save_sec_context | SECURITY_RESTRICTED_OPERATION);
23152 88 : save_nestlevel = NewGUCNestLevel();
23153 88 : RestrictSearchPath();
23154 :
23155 : /* Copy data from merged partitions to the new partition. */
23156 88 : MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
23157 :
23158 : /* Drop the current partitions before attaching the new one. */
23159 380 : foreach_oid(mergingPartitionOid, mergingPartitions)
23160 : {
23161 : ObjectAddress object;
23162 :
23163 204 : object.objectId = mergingPartitionOid;
23164 204 : object.classId = RelationRelationId;
23165 204 : object.objectSubId = 0;
23166 :
23167 204 : performDeletion(&object, DROP_RESTRICT, 0);
23168 : }
23169 :
23170 88 : list_free(mergingPartitions);
23171 :
23172 : /*
23173 : * Attach a new partition to the partitioned table. wqueue = NULL:
23174 : * verification for each cloned constraint is not needed.
23175 : */
23176 88 : attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
23177 :
23178 : /* Keep the lock until commit. */
23179 88 : table_close(newPartRel, NoLock);
23180 :
23181 : /* Roll back any GUC changes executed by index functions. */
23182 88 : AtEOXact_GUC(false, save_nestlevel);
23183 :
23184 : /* Restore the userid and security context. */
23185 88 : SetUserIdAndSecContext(save_userid, save_sec_context);
23186 88 : }
23187 :
23188 : /*
23189 : * Struct with the context of the new partition for inserting rows from the
23190 : * split partition.
23191 : */
23192 : typedef struct SplitPartitionContext
23193 : {
23194 : ExprState *partqualstate; /* expression for checking a slot for a
23195 : * partition (NULL for DEFAULT partition) */
23196 : BulkInsertState bistate; /* state of bulk inserts for partition */
23197 : TupleTableSlot *dstslot; /* slot for inserting row into partition */
23198 : AlteredTableInfo *tab; /* structure with generated column expressions
23199 : * and check constraint expressions. */
23200 : Relation partRel; /* relation for partition */
23201 : } SplitPartitionContext;
23202 :
23203 : /*
23204 : * createSplitPartitionContext: create context for partition and fill it
23205 : */
23206 : static SplitPartitionContext *
23207 332 : createSplitPartitionContext(Relation partRel)
23208 : {
23209 : SplitPartitionContext *pc;
23210 :
23211 332 : pc = palloc0_object(SplitPartitionContext);
23212 332 : pc->partRel = partRel;
23213 :
23214 : /*
23215 : * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
23216 : * don't bother using it.
23217 : */
23218 332 : pc->bistate = GetBulkInsertState();
23219 :
23220 : /* Create a destination tuple slot for the new partition. */
23221 332 : pc->dstslot = table_slot_create(pc->partRel, NULL);
23222 :
23223 332 : return pc;
23224 : }
23225 :
23226 : /*
23227 : * deleteSplitPartitionContext: delete context for partition
23228 : */
23229 : static void
23230 332 : deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, uint32 ti_options)
23231 : {
23232 : ListCell *ltab;
23233 :
23234 332 : ExecDropSingleTupleTableSlot(pc->dstslot);
23235 332 : FreeBulkInsertState(pc->bistate);
23236 :
23237 332 : table_finish_bulk_insert(pc->partRel, ti_options);
23238 :
23239 : /*
23240 : * We don't need to process this pc->partRel so delete the ALTER TABLE
23241 : * queue of it.
23242 : */
23243 664 : foreach(ltab, *wqueue)
23244 : {
23245 664 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
23246 :
23247 664 : if (tab->relid == RelationGetRelid(pc->partRel))
23248 : {
23249 332 : *wqueue = list_delete_cell(*wqueue, ltab);
23250 332 : break;
23251 : }
23252 : }
23253 :
23254 332 : pfree(pc);
23255 332 : }
23256 :
23257 : /*
23258 : * SplitPartitionMoveRows: scan split partition (splitRel) of partitioned table
23259 : * (rel) and move rows into new partitions.
23260 : *
23261 : * New partitions description:
23262 : * partlist: list of pointers to SinglePartitionSpec structures. It contains
23263 : * the partition specification details for all new partitions.
23264 : * newPartRels: list of Relations, new partitions created in
23265 : * ATExecSplitPartition.
23266 : */
23267 : static void
23268 120 : SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel,
23269 : List *partlist, List *newPartRels)
23270 : {
23271 : /* The FSM is empty, so don't bother using it. */
23272 120 : uint32 ti_options = TABLE_INSERT_SKIP_FSM;
23273 : CommandId mycid;
23274 : EState *estate;
23275 : ListCell *listptr,
23276 : *listptr2;
23277 : TupleTableSlot *srcslot;
23278 : ExprContext *econtext;
23279 : TableScanDesc scan;
23280 : Snapshot snapshot;
23281 : MemoryContext oldCxt;
23282 120 : List *partContexts = NIL;
23283 : TupleConversionMap *tuple_map;
23284 120 : SplitPartitionContext *defaultPartCtx = NULL,
23285 : *pc;
23286 :
23287 120 : mycid = GetCurrentCommandId(true);
23288 :
23289 120 : estate = CreateExecutorState();
23290 :
23291 452 : forboth(listptr, partlist, listptr2, newPartRels)
23292 : {
23293 332 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
23294 :
23295 332 : pc = createSplitPartitionContext((Relation) lfirst(listptr2));
23296 :
23297 : /* Find the work queue entry for the new partition table: newPartRel. */
23298 332 : pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23299 :
23300 332 : buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23301 :
23302 332 : if (sps->bound->is_default)
23303 : {
23304 : /*
23305 : * We should not create a structure to check the partition
23306 : * constraint for the new DEFAULT partition.
23307 : */
23308 28 : defaultPartCtx = pc;
23309 : }
23310 : else
23311 : {
23312 : List *partConstraint;
23313 :
23314 : /* Build expression execution states for partition check quals. */
23315 304 : partConstraint = get_qual_from_partbound(rel, sps->bound);
23316 : partConstraint =
23317 304 : (List *) eval_const_expressions(NULL,
23318 : (Node *) partConstraint);
23319 : /* Make a boolean expression for ExecCheck(). */
23320 304 : partConstraint = list_make1(make_ands_explicit(partConstraint));
23321 :
23322 : /*
23323 : * Map the vars in the constraint expression from rel's attnos to
23324 : * splitRel's.
23325 : */
23326 304 : partConstraint = map_partition_varattnos(partConstraint,
23327 : 1, splitRel, rel);
23328 :
23329 304 : pc->partqualstate =
23330 304 : ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
23331 : Assert(pc->partqualstate != NULL);
23332 : }
23333 :
23334 : /* Store partition context into a list. */
23335 332 : partContexts = lappend(partContexts, pc);
23336 : }
23337 :
23338 120 : econtext = GetPerTupleExprContext(estate);
23339 :
23340 : /* Create the necessary tuple slot. */
23341 120 : srcslot = table_slot_create(splitRel, NULL);
23342 :
23343 : /*
23344 : * Map computing for moving attributes of the split partition to the new
23345 : * partition (for the first new partition, but other new partitions can
23346 : * use the same map).
23347 : */
23348 120 : pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
23349 120 : tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
23350 120 : RelationGetDescr(pc->partRel));
23351 :
23352 : /* Scan through the rows. */
23353 120 : snapshot = RegisterSnapshot(GetLatestSnapshot());
23354 120 : scan = table_beginscan(splitRel, snapshot, 0, NULL,
23355 : SO_NONE);
23356 :
23357 : /*
23358 : * Switch to per-tuple memory context and reset it for each tuple
23359 : * produced, so we don't leak memory.
23360 : */
23361 120 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
23362 :
23363 543 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
23364 : {
23365 423 : bool found = false;
23366 : TupleTableSlot *insertslot;
23367 :
23368 423 : CHECK_FOR_INTERRUPTS();
23369 :
23370 423 : econtext->ecxt_scantuple = srcslot;
23371 :
23372 : /* Search partition for the current slot, srcslot. */
23373 1138 : foreach(listptr, partContexts)
23374 : {
23375 1062 : pc = (SplitPartitionContext *) lfirst(listptr);
23376 :
23377 : /* skip DEFAULT partition */
23378 1062 : if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23379 : {
23380 347 : found = true;
23381 347 : break;
23382 : }
23383 : }
23384 423 : if (!found)
23385 : {
23386 : /* Use the DEFAULT partition if it exists. */
23387 76 : if (defaultPartCtx)
23388 76 : pc = defaultPartCtx;
23389 : else
23390 0 : ereport(ERROR,
23391 : errcode(ERRCODE_CHECK_VIOLATION),
23392 : errmsg("can not find partition for split partition row"),
23393 : errtable(splitRel));
23394 : }
23395 :
23396 423 : if (tuple_map)
23397 : {
23398 : /* Need to use a map to copy attributes. */
23399 16 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23400 : }
23401 : else
23402 : {
23403 : /* Extract data from the old tuple. */
23404 407 : slot_getallattrs(srcslot);
23405 :
23406 : /* Copy attributes directly. */
23407 407 : insertslot = pc->dstslot;
23408 :
23409 407 : ExecClearTuple(insertslot);
23410 :
23411 407 : memcpy(insertslot->tts_values, srcslot->tts_values,
23412 407 : sizeof(Datum) * srcslot->tts_nvalid);
23413 407 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23414 407 : sizeof(bool) * srcslot->tts_nvalid);
23415 :
23416 407 : ExecStoreVirtualTuple(insertslot);
23417 : }
23418 :
23419 : /*
23420 : * Constraints and GENERATED expressions might reference the tableoid
23421 : * column, so fill tts_tableOid with the desired value. (We must do
23422 : * this each time, because it gets overwritten with newrel's OID
23423 : * during storing.)
23424 : */
23425 423 : insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23426 :
23427 : /*
23428 : * Now, evaluate any generated expressions whose inputs come from the
23429 : * new tuple. We assume these columns won't reference each other, so
23430 : * that there's no ordering dependency.
23431 : */
23432 423 : evaluateGeneratedExpressionsAndCheckConstraints(pc->tab, pc->partRel,
23433 : insertslot, econtext);
23434 :
23435 : /* Write the tuple out to the new relation. */
23436 423 : table_tuple_insert(pc->partRel, insertslot, mycid,
23437 423 : ti_options, pc->bistate);
23438 :
23439 423 : ResetExprContext(econtext);
23440 : }
23441 :
23442 120 : MemoryContextSwitchTo(oldCxt);
23443 :
23444 120 : table_endscan(scan);
23445 120 : UnregisterSnapshot(snapshot);
23446 :
23447 120 : if (tuple_map)
23448 4 : free_conversion_map(tuple_map);
23449 :
23450 120 : ExecDropSingleTupleTableSlot(srcslot);
23451 :
23452 120 : FreeExecutorState(estate);
23453 :
23454 572 : foreach_ptr(SplitPartitionContext, spc, partContexts)
23455 332 : deleteSplitPartitionContext(spc, wqueue, ti_options);
23456 120 : }
23457 :
23458 : /*
23459 : * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
23460 : */
23461 : static void
23462 128 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
23463 : PartitionCmd *cmd, AlterTableUtilityContext *context)
23464 : {
23465 : Relation splitRel;
23466 : Oid splitRelOid;
23467 : ListCell *listptr,
23468 : *listptr2;
23469 128 : bool isSameName = false;
23470 : char tmpRelName[NAMEDATALEN];
23471 128 : List *newPartRels = NIL;
23472 : ObjectAddress object;
23473 : Oid defaultPartOid;
23474 : Oid save_userid;
23475 : int save_sec_context;
23476 : int save_nestlevel;
23477 :
23478 128 : defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
23479 :
23480 : /*
23481 : * Partition is already locked in the transformPartitionCmdForSplit
23482 : * function.
23483 : */
23484 128 : splitRel = table_openrv(cmd->name, NoLock);
23485 :
23486 128 : splitRelOid = RelationGetRelid(splitRel);
23487 :
23488 : /* Check descriptions of new partitions. */
23489 592 : foreach_node(SinglePartitionSpec, sps, cmd->partlist)
23490 : {
23491 : Oid existingRelid;
23492 :
23493 : /* Look up the existing relation by the new partition name. */
23494 344 : RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
23495 :
23496 : /*
23497 : * This would fail later on anyway if the relation already exists. But
23498 : * by catching it here, we can emit a nicer error message.
23499 : */
23500 344 : if (existingRelid == splitRelOid && !isSameName)
23501 : /* One new partition can have the same name as a split partition. */
23502 29 : isSameName = true;
23503 315 : else if (OidIsValid(existingRelid))
23504 4 : ereport(ERROR,
23505 : errcode(ERRCODE_DUPLICATE_TABLE),
23506 : errmsg("relation \"%s\" already exists", sps->name->relname));
23507 : }
23508 :
23509 : /* Detach the split partition. */
23510 124 : detachPartitionTable(rel, splitRel, defaultPartOid);
23511 :
23512 : /*
23513 : * Perform a preliminary check to determine whether it's safe to drop the
23514 : * split partition before we actually do so later. After merging rows into
23515 : * the new partitions via SplitPartitionMoveRows, all old partitions need
23516 : * to be dropped. However, since the drop behavior is DROP_RESTRICT and
23517 : * the merge process (SplitPartitionMoveRows) can be time-consuming,
23518 : * performing an early check on the drop eligibility of old partitions is
23519 : * preferable.
23520 : */
23521 124 : object.objectId = splitRelOid;
23522 124 : object.classId = RelationRelationId;
23523 124 : object.objectSubId = 0;
23524 124 : performDeletionCheck(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
23525 :
23526 : /*
23527 : * If a new partition has the same name as the split partition, then we
23528 : * should rename the split partition to reuse its name.
23529 : */
23530 124 : if (isSameName)
23531 : {
23532 : /*
23533 : * We must bump the command counter to make the split partition tuple
23534 : * visible for renaming.
23535 : */
23536 29 : CommandCounterIncrement();
23537 : /* Rename partition. */
23538 29 : sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23539 29 : RenameRelationInternal(splitRelOid, tmpRelName, true, false);
23540 :
23541 : /*
23542 : * We must bump the command counter to make the split partition tuple
23543 : * visible after renaming.
23544 : */
23545 29 : CommandCounterIncrement();
23546 : }
23547 :
23548 : /* Create new partitions (like a split partition), without indexes. */
23549 576 : foreach_node(SinglePartitionSpec, sps, cmd->partlist)
23550 : {
23551 : Relation newPartRel;
23552 :
23553 336 : newPartRel = createPartitionTable(wqueue, sps->name, rel,
23554 336 : splitRel->rd_rel->relowner);
23555 332 : newPartRels = lappend(newPartRels, newPartRel);
23556 : }
23557 :
23558 : /*
23559 : * Switch to the table owner's userid, so that any index functions are run
23560 : * as that user. Also, lockdown security-restricted operations and
23561 : * arrange to make GUC variable changes local to this command.
23562 : *
23563 : * Need to do it after determining the namespace in the
23564 : * createPartitionTable() call.
23565 : */
23566 120 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
23567 120 : SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23568 : save_sec_context | SECURITY_RESTRICTED_OPERATION);
23569 120 : save_nestlevel = NewGUCNestLevel();
23570 120 : RestrictSearchPath();
23571 :
23572 : /* Copy data from the split partition to the new partitions. */
23573 120 : SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
23574 : /* Keep the lock until commit. */
23575 120 : table_close(splitRel, NoLock);
23576 :
23577 : /* Attach new partitions to the partitioned table. */
23578 452 : forboth(listptr, cmd->partlist, listptr2, newPartRels)
23579 : {
23580 332 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
23581 332 : Relation newPartRel = (Relation) lfirst(listptr2);
23582 :
23583 : /*
23584 : * wqueue = NULL: verification for each cloned constraint is not
23585 : * needed.
23586 : */
23587 332 : attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23588 : /* Keep the lock until commit. */
23589 332 : table_close(newPartRel, NoLock);
23590 : }
23591 :
23592 : /* Drop the split partition. */
23593 120 : object.classId = RelationRelationId;
23594 120 : object.objectId = splitRelOid;
23595 120 : object.objectSubId = 0;
23596 : /* Probably DROP_CASCADE is not needed. */
23597 120 : performDeletion(&object, DROP_RESTRICT, 0);
23598 :
23599 : /* Roll back any GUC changes executed by index functions. */
23600 120 : AtEOXact_GUC(false, save_nestlevel);
23601 :
23602 : /* Restore the userid and security context. */
23603 120 : SetUserIdAndSecContext(save_userid, save_sec_context);
23604 120 : }
|