Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * tablecmds.c
4 : * Commands for creating and altering table structures and settings
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/commands/tablecmds.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/attmap.h"
18 : #include "access/genam.h"
19 : #include "access/gist.h"
20 : #include "access/heapam.h"
21 : #include "access/heapam_xlog.h"
22 : #include "access/multixact.h"
23 : #include "access/reloptions.h"
24 : #include "access/relscan.h"
25 : #include "access/sysattr.h"
26 : #include "access/tableam.h"
27 : #include "access/toast_compression.h"
28 : #include "access/xact.h"
29 : #include "access/xlog.h"
30 : #include "access/xloginsert.h"
31 : #include "catalog/catalog.h"
32 : #include "catalog/heap.h"
33 : #include "catalog/index.h"
34 : #include "catalog/namespace.h"
35 : #include "catalog/objectaccess.h"
36 : #include "catalog/partition.h"
37 : #include "catalog/pg_am.h"
38 : #include "catalog/pg_attrdef.h"
39 : #include "catalog/pg_collation.h"
40 : #include "catalog/pg_constraint.h"
41 : #include "catalog/pg_depend.h"
42 : #include "catalog/pg_foreign_table.h"
43 : #include "catalog/pg_inherits.h"
44 : #include "catalog/pg_largeobject.h"
45 : #include "catalog/pg_namespace.h"
46 : #include "catalog/pg_opclass.h"
47 : #include "catalog/pg_policy.h"
48 : #include "catalog/pg_proc.h"
49 : #include "catalog/pg_publication_rel.h"
50 : #include "catalog/pg_rewrite.h"
51 : #include "catalog/pg_statistic_ext.h"
52 : #include "catalog/pg_tablespace.h"
53 : #include "catalog/pg_trigger.h"
54 : #include "catalog/pg_type.h"
55 : #include "catalog/storage.h"
56 : #include "catalog/storage_xlog.h"
57 : #include "catalog/toasting.h"
58 : #include "commands/cluster.h"
59 : #include "commands/comment.h"
60 : #include "commands/defrem.h"
61 : #include "commands/event_trigger.h"
62 : #include "commands/sequence.h"
63 : #include "commands/tablecmds.h"
64 : #include "commands/tablespace.h"
65 : #include "commands/trigger.h"
66 : #include "commands/typecmds.h"
67 : #include "commands/user.h"
68 : #include "commands/vacuum.h"
69 : #include "common/int.h"
70 : #include "executor/executor.h"
71 : #include "foreign/fdwapi.h"
72 : #include "foreign/foreign.h"
73 : #include "miscadmin.h"
74 : #include "nodes/makefuncs.h"
75 : #include "nodes/nodeFuncs.h"
76 : #include "nodes/parsenodes.h"
77 : #include "optimizer/optimizer.h"
78 : #include "parser/parse_coerce.h"
79 : #include "parser/parse_collate.h"
80 : #include "parser/parse_expr.h"
81 : #include "parser/parse_relation.h"
82 : #include "parser/parse_type.h"
83 : #include "parser/parse_utilcmd.h"
84 : #include "parser/parser.h"
85 : #include "partitioning/partbounds.h"
86 : #include "partitioning/partdesc.h"
87 : #include "pgstat.h"
88 : #include "rewrite/rewriteDefine.h"
89 : #include "rewrite/rewriteHandler.h"
90 : #include "rewrite/rewriteManip.h"
91 : #include "storage/bufmgr.h"
92 : #include "storage/lmgr.h"
93 : #include "storage/lock.h"
94 : #include "storage/predicate.h"
95 : #include "storage/smgr.h"
96 : #include "tcop/utility.h"
97 : #include "utils/acl.h"
98 : #include "utils/builtins.h"
99 : #include "utils/fmgroids.h"
100 : #include "utils/inval.h"
101 : #include "utils/lsyscache.h"
102 : #include "utils/memutils.h"
103 : #include "utils/partcache.h"
104 : #include "utils/relcache.h"
105 : #include "utils/ruleutils.h"
106 : #include "utils/snapmgr.h"
107 : #include "utils/syscache.h"
108 : #include "utils/timestamp.h"
109 : #include "utils/typcache.h"
110 : #include "utils/usercontext.h"
111 :
112 : /*
113 : * ON COMMIT action list
114 : */
115 : typedef struct OnCommitItem
116 : {
117 : Oid relid; /* relid of relation */
118 : OnCommitAction oncommit; /* what to do at end of xact */
119 :
120 : /*
121 : * If this entry was created during the current transaction,
122 : * creating_subid is the ID of the creating subxact; if created in a prior
123 : * transaction, creating_subid is zero. If deleted during the current
124 : * transaction, deleting_subid is the ID of the deleting subxact; if no
125 : * deletion request is pending, deleting_subid is zero.
126 : */
127 : SubTransactionId creating_subid;
128 : SubTransactionId deleting_subid;
129 : } OnCommitItem;
130 :
131 : static List *on_commits = NIL;
132 :
133 :
134 : /*
135 : * State information for ALTER TABLE
136 : *
137 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
138 : * structs, one for each table modified by the operation (the named table
139 : * plus any child tables that are affected). We save lists of subcommands
140 : * to apply to this table (possibly modified by parse transformation steps);
141 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
142 : * necessary information is stored in the constraints and newvals lists.
143 : *
144 : * Phase 2 is divided into multiple passes; subcommands are executed in
145 : * a pass determined by subcommand type.
146 : */
147 :
148 : typedef enum AlterTablePass
149 : {
150 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
151 : AT_PASS_DROP, /* DROP (all flavors) */
152 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
153 : AT_PASS_ADD_COL, /* ADD COLUMN */
154 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
155 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
156 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
157 : /* We could support a RENAME COLUMN pass here, but not currently used */
158 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
159 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
160 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
161 : AT_PASS_ADD_INDEX, /* ADD indexes */
162 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
163 : AT_PASS_MISC, /* other stuff */
164 : } AlterTablePass;
165 :
166 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
167 :
168 : typedef struct AlteredTableInfo
169 : {
170 : /* Information saved before any work commences: */
171 : Oid relid; /* Relation to work on */
172 : char relkind; /* Its relkind */
173 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
174 :
175 : /*
176 : * Transiently set during Phase 2, normally set to NULL.
177 : *
178 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
179 : * returns control. This can be exploited by ATExecCmd subroutines to
180 : * close/reopen across transaction boundaries.
181 : */
182 : Relation rel;
183 :
184 : /* Information saved by Phase 1 for Phase 2: */
185 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
186 : /* Information saved by Phases 1/2 for Phase 3: */
187 : List *constraints; /* List of NewConstraint */
188 : List *newvals; /* List of NewColumnValue */
189 : List *afterStmts; /* List of utility command parsetrees */
190 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
191 : int rewrite; /* Reason for forced rewrite, if any */
192 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
193 : Oid newAccessMethod; /* new access method; 0 means no change,
194 : * if above is true */
195 : Oid newTableSpace; /* new tablespace; 0 means no change */
196 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
197 : char newrelpersistence; /* if above is true */
198 : Expr *partition_constraint; /* for attach partition validation */
199 : /* true, if validating default due to some other attach/detach */
200 : bool validate_default;
201 : /* Objects to rebuild after completing ALTER TYPE operations */
202 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
203 : List *changedConstraintDefs; /* string definitions of same */
204 : List *changedIndexOids; /* OIDs of indexes to rebuild */
205 : List *changedIndexDefs; /* string definitions of same */
206 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
207 : char *clusterOnIndex; /* index to use for CLUSTER */
208 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
209 : List *changedStatisticsDefs; /* string definitions of same */
210 : } AlteredTableInfo;
211 :
212 : /* Struct describing one new constraint to check in Phase 3 scan */
213 : /* Note: new not-null constraints are handled elsewhere */
214 : typedef struct NewConstraint
215 : {
216 : char *name; /* Constraint name, or NULL if none */
217 : ConstrType contype; /* CHECK or FOREIGN */
218 : Oid refrelid; /* PK rel, if FOREIGN */
219 : Oid refindid; /* OID of PK's index, if FOREIGN */
220 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
221 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
222 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
223 : ExprState *qualstate; /* Execution state for CHECK expr */
224 : } NewConstraint;
225 :
226 : /*
227 : * Struct describing one new column value that needs to be computed during
228 : * Phase 3 copy (this could be either a new column with a non-null default, or
229 : * a column that we're changing the type of). Columns without such an entry
230 : * are just copied from the old table during ATRewriteTable. Note that the
231 : * expr is an expression over *old* table values, except when is_generated
232 : * is true; then it is an expression over columns of the *new* tuple.
233 : */
234 : typedef struct NewColumnValue
235 : {
236 : AttrNumber attnum; /* which column */
237 : Expr *expr; /* expression to compute */
238 : ExprState *exprstate; /* execution state */
239 : bool is_generated; /* is it a GENERATED expression? */
240 : } NewColumnValue;
241 :
242 : /*
243 : * Error-reporting support for RemoveRelations
244 : */
245 : struct dropmsgstrings
246 : {
247 : char kind;
248 : int nonexistent_code;
249 : const char *nonexistent_msg;
250 : const char *skipping_msg;
251 : const char *nota_msg;
252 : const char *drophint_msg;
253 : };
254 :
255 : static const struct dropmsgstrings dropmsgstringarray[] = {
256 : {RELKIND_RELATION,
257 : ERRCODE_UNDEFINED_TABLE,
258 : gettext_noop("table \"%s\" does not exist"),
259 : gettext_noop("table \"%s\" does not exist, skipping"),
260 : gettext_noop("\"%s\" is not a table"),
261 : gettext_noop("Use DROP TABLE to remove a table.")},
262 : {RELKIND_SEQUENCE,
263 : ERRCODE_UNDEFINED_TABLE,
264 : gettext_noop("sequence \"%s\" does not exist"),
265 : gettext_noop("sequence \"%s\" does not exist, skipping"),
266 : gettext_noop("\"%s\" is not a sequence"),
267 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
268 : {RELKIND_VIEW,
269 : ERRCODE_UNDEFINED_TABLE,
270 : gettext_noop("view \"%s\" does not exist"),
271 : gettext_noop("view \"%s\" does not exist, skipping"),
272 : gettext_noop("\"%s\" is not a view"),
273 : gettext_noop("Use DROP VIEW to remove a view.")},
274 : {RELKIND_MATVIEW,
275 : ERRCODE_UNDEFINED_TABLE,
276 : gettext_noop("materialized view \"%s\" does not exist"),
277 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
278 : gettext_noop("\"%s\" is not a materialized view"),
279 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
280 : {RELKIND_INDEX,
281 : ERRCODE_UNDEFINED_OBJECT,
282 : gettext_noop("index \"%s\" does not exist"),
283 : gettext_noop("index \"%s\" does not exist, skipping"),
284 : gettext_noop("\"%s\" is not an index"),
285 : gettext_noop("Use DROP INDEX to remove an index.")},
286 : {RELKIND_COMPOSITE_TYPE,
287 : ERRCODE_UNDEFINED_OBJECT,
288 : gettext_noop("type \"%s\" does not exist"),
289 : gettext_noop("type \"%s\" does not exist, skipping"),
290 : gettext_noop("\"%s\" is not a type"),
291 : gettext_noop("Use DROP TYPE to remove a type.")},
292 : {RELKIND_FOREIGN_TABLE,
293 : ERRCODE_UNDEFINED_OBJECT,
294 : gettext_noop("foreign table \"%s\" does not exist"),
295 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
296 : gettext_noop("\"%s\" is not a foreign table"),
297 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
298 : {RELKIND_PARTITIONED_TABLE,
299 : ERRCODE_UNDEFINED_TABLE,
300 : gettext_noop("table \"%s\" does not exist"),
301 : gettext_noop("table \"%s\" does not exist, skipping"),
302 : gettext_noop("\"%s\" is not a table"),
303 : gettext_noop("Use DROP TABLE to remove a table.")},
304 : {RELKIND_PARTITIONED_INDEX,
305 : ERRCODE_UNDEFINED_OBJECT,
306 : gettext_noop("index \"%s\" does not exist"),
307 : gettext_noop("index \"%s\" does not exist, skipping"),
308 : gettext_noop("\"%s\" is not an index"),
309 : gettext_noop("Use DROP INDEX to remove an index.")},
310 : {'\0', 0, NULL, NULL, NULL, NULL}
311 : };
312 :
313 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
314 : struct DropRelationCallbackState
315 : {
316 : /* These fields are set by RemoveRelations: */
317 : char expected_relkind;
318 : LOCKMODE heap_lockmode;
319 : /* These fields are state to track which subsidiary locks are held: */
320 : Oid heapOid;
321 : Oid partParentOid;
322 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
323 : char actual_relkind;
324 : char actual_relpersistence;
325 : };
326 :
327 : /* Alter table target-type flags for ATSimplePermissions */
328 : #define ATT_TABLE 0x0001
329 : #define ATT_VIEW 0x0002
330 : #define ATT_MATVIEW 0x0004
331 : #define ATT_INDEX 0x0008
332 : #define ATT_COMPOSITE_TYPE 0x0010
333 : #define ATT_FOREIGN_TABLE 0x0020
334 : #define ATT_PARTITIONED_INDEX 0x0040
335 : #define ATT_SEQUENCE 0x0080
336 : #define ATT_PARTITIONED_TABLE 0x0100
337 :
338 : /*
339 : * ForeignTruncateInfo
340 : *
341 : * Information related to truncation of foreign tables. This is used for
342 : * the elements in a hash table. It uses the server OID as lookup key,
343 : * and includes a per-server list of all foreign tables involved in the
344 : * truncation.
345 : */
346 : typedef struct ForeignTruncateInfo
347 : {
348 : Oid serverid;
349 : List *rels;
350 : } ForeignTruncateInfo;
351 :
352 : /* Partial or complete FK creation in addFkConstraint() */
353 : typedef enum addFkConstraintSides
354 : {
355 : addFkReferencedSide,
356 : addFkReferencingSide,
357 : addFkBothSides,
358 : } addFkConstraintSides;
359 :
360 : /*
361 : * Partition tables are expected to be dropped when the parent partitioned
362 : * table gets dropped. Hence for partitioning we use AUTO dependency.
363 : * Otherwise, for regular inheritance use NORMAL dependency.
364 : */
365 : #define child_dependency_type(child_is_partition) \
366 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
367 :
368 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
369 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
370 : static void truncate_check_activity(Relation rel);
371 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
372 : Oid relId, Oid oldRelId, void *arg);
373 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
374 : bool is_partition, List **supconstr,
375 : List **supnotnulls);
376 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
377 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
378 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
379 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
380 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
381 : static void StoreCatalogInheritance(Oid relationId, List *supers,
382 : bool child_is_partition);
383 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
384 : int32 seqNumber, Relation inhRelation,
385 : bool child_is_partition);
386 : static int findAttrByName(const char *attributeName, const List *columns);
387 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
388 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
389 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
390 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391 : LOCKMODE lockmode);
392 : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
393 : ATAlterConstraint *cmdcon,
394 : bool recurse, LOCKMODE lockmode);
395 : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
396 : Relation tgrel, Relation rel, HeapTuple contuple,
397 : bool recurse, LOCKMODE lockmode);
398 : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
399 : Relation conrel, Relation tgrel, Relation rel,
400 : HeapTuple contuple, bool recurse,
401 : List **otherrelids, LOCKMODE lockmode);
402 : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
403 : Relation conrel, Relation rel,
404 : HeapTuple contuple, LOCKMODE lockmode);
405 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
406 : bool deferrable, bool initdeferred,
407 : List **otherrelids);
408 : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
409 : Relation conrel, Relation tgrel, Relation rel,
410 : HeapTuple contuple, bool recurse,
411 : List **otherrelids, LOCKMODE lockmode);
412 : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
413 : HeapTuple contuple);
414 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
415 : Relation rel, char *constrName,
416 : bool recurse, bool recursing, LOCKMODE lockmode);
417 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
418 : HeapTuple contuple, LOCKMODE lockmode);
419 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
420 : char *constrName, HeapTuple contuple,
421 : bool recurse, bool recursing, LOCKMODE lockmode);
422 : static int transformColumnNameList(Oid relId, List *colList,
423 : int16 *attnums, Oid *atttypids, Oid *attcollids);
424 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
425 : List **attnamelist,
426 : int16 *attnums, Oid *atttypids, Oid *attcollids,
427 : Oid *opclasses, bool *pk_has_without_overlaps);
428 : static Oid transformFkeyCheckAttrs(Relation pkrel,
429 : int numattrs, int16 *attnums,
430 : bool with_period, Oid *opclasses,
431 : bool *pk_has_without_overlaps);
432 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
433 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
434 : Oid *funcid);
435 : static void validateForeignKeyConstraint(char *conname,
436 : Relation rel, Relation pkrel,
437 : Oid pkindOid, Oid constraintOid, bool hasperiod);
438 : static void CheckAlterTableIsSafe(Relation rel);
439 : static void ATController(AlterTableStmt *parsetree,
440 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
441 : AlterTableUtilityContext *context);
442 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
443 : bool recurse, bool recursing, LOCKMODE lockmode,
444 : AlterTableUtilityContext *context);
445 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
446 : AlterTableUtilityContext *context);
447 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
448 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
449 : AlterTableUtilityContext *context);
450 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
451 : Relation rel, AlterTableCmd *cmd,
452 : bool recurse, LOCKMODE lockmode,
453 : AlterTablePass cur_pass,
454 : AlterTableUtilityContext *context);
455 : static void ATRewriteTables(AlterTableStmt *parsetree,
456 : List **wqueue, LOCKMODE lockmode,
457 : AlterTableUtilityContext *context);
458 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
459 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
460 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
461 : static void ATSimpleRecursion(List **wqueue, Relation rel,
462 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
463 : AlterTableUtilityContext *context);
464 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
465 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
466 : LOCKMODE lockmode,
467 : AlterTableUtilityContext *context);
468 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
469 : DropBehavior behavior);
470 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
471 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
472 : AlterTableUtilityContext *context);
473 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
474 : Relation rel, AlterTableCmd **cmd,
475 : bool recurse, bool recursing,
476 : LOCKMODE lockmode, AlterTablePass cur_pass,
477 : AlterTableUtilityContext *context);
478 : static bool check_for_column_name_collision(Relation rel, const char *colname,
479 : bool if_not_exists);
480 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
481 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
482 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
483 : LOCKMODE lockmode);
484 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
485 : LOCKMODE lockmode);
486 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
487 : char *constrname, char *colName,
488 : bool recurse, bool recursing,
489 : LOCKMODE lockmode);
490 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
491 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
492 : List *testConstraint, List *provenConstraint);
493 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
494 : Node *newDefault, LOCKMODE lockmode);
495 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
496 : Node *newDefault);
497 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
498 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
499 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
500 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
501 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
502 : bool recurse, bool recursing);
503 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
504 : Node *newExpr, LOCKMODE lockmode);
505 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
506 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
507 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
508 : Node *newValue, LOCKMODE lockmode);
509 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
510 : Node *options, bool isReset, LOCKMODE lockmode);
511 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
512 : Node *newValue, LOCKMODE lockmode);
513 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
514 : AlterTableCmd *cmd, LOCKMODE lockmode,
515 : AlterTableUtilityContext *context);
516 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
517 : DropBehavior behavior,
518 : bool recurse, bool recursing,
519 : bool missing_ok, LOCKMODE lockmode,
520 : ObjectAddresses *addrs);
521 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
522 : bool recurse, LOCKMODE lockmode,
523 : AlterTableUtilityContext *context);
524 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
525 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
526 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
527 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
528 : static ObjectAddress ATExecAddConstraint(List **wqueue,
529 : AlteredTableInfo *tab, Relation rel,
530 : Constraint *newConstraint, bool recurse, bool is_readd,
531 : LOCKMODE lockmode);
532 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
533 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
534 : IndexStmt *stmt, LOCKMODE lockmode);
535 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
536 : AlteredTableInfo *tab, Relation rel,
537 : Constraint *constr,
538 : bool recurse, bool recursing, bool is_readd,
539 : LOCKMODE lockmode);
540 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
541 : Relation rel, Constraint *fkconstraint,
542 : bool recurse, bool recursing,
543 : LOCKMODE lockmode);
544 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
545 : int numfksetcols, const int16 *fksetcolsattnums,
546 : List *fksetcols);
547 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
548 : char *constraintname,
549 : Constraint *fkconstraint, Relation rel,
550 : Relation pkrel, Oid indexOid,
551 : Oid parentConstr,
552 : int numfks, int16 *pkattnum, int16 *fkattnum,
553 : Oid *pfeqoperators, Oid *ppeqoperators,
554 : Oid *ffeqoperators, int numfkdelsetcols,
555 : int16 *fkdelsetcols, bool is_internal,
556 : bool with_period);
557 : static void addFkRecurseReferenced(Constraint *fkconstraint,
558 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
559 : int numfks, int16 *pkattnum, int16 *fkattnum,
560 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
561 : int numfkdelsetcols, int16 *fkdelsetcols,
562 : bool old_check_ok,
563 : Oid parentDelTrigger, Oid parentUpdTrigger,
564 : bool with_period);
565 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
566 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
567 : int numfks, int16 *pkattnum, int16 *fkattnum,
568 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
569 : int numfkdelsetcols, int16 *fkdelsetcols,
570 : bool old_check_ok, LOCKMODE lockmode,
571 : Oid parentInsTrigger, Oid parentUpdTrigger,
572 : bool with_period);
573 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
574 : Relation partitionRel);
575 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
576 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
577 : Relation partRel);
578 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
579 : Constraint *fkconstraint, Oid constraintOid,
580 : Oid indexOid,
581 : Oid parentInsTrigger, Oid parentUpdTrigger,
582 : Oid *insertTrigOid, Oid *updateTrigOid);
583 : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
584 : Constraint *fkconstraint, Oid constraintOid,
585 : Oid indexOid,
586 : Oid parentDelTrigger, Oid parentUpdTrigger,
587 : Oid *deleteTrigOid, Oid *updateTrigOid);
588 : static bool tryAttachPartitionForeignKey(List **wqueue,
589 : ForeignKeyCacheInfo *fk,
590 : Relation partition,
591 : Oid parentConstrOid, int numfks,
592 : AttrNumber *mapped_conkey, AttrNumber *confkey,
593 : Oid *conpfeqop,
594 : Oid parentInsTrigger,
595 : Oid parentUpdTrigger,
596 : Relation trigrel);
597 : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
598 : Oid partConstrOid, Oid parentConstrOid,
599 : Oid parentInsTrigger, Oid parentUpdTrigger,
600 : Relation trigrel);
601 : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
602 : Oid conoid, Oid conrelid);
603 : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
604 : Oid confrelid, Oid conrelid);
605 : static void GetForeignKeyActionTriggers(Relation trigrel,
606 : Oid conoid, Oid confrelid, Oid conrelid,
607 : Oid *deleteTriggerOid,
608 : Oid *updateTriggerOid);
609 : static void GetForeignKeyCheckTriggers(Relation trigrel,
610 : Oid conoid, Oid confrelid, Oid conrelid,
611 : Oid *insertTriggerOid,
612 : Oid *updateTriggerOid);
613 : static void ATExecDropConstraint(Relation rel, const char *constrName,
614 : DropBehavior behavior, bool recurse,
615 : bool missing_ok, LOCKMODE lockmode);
616 : static ObjectAddress dropconstraint_internal(Relation rel,
617 : HeapTuple constraintTup, DropBehavior behavior,
618 : bool recurse, bool recursing,
619 : bool missing_ok, LOCKMODE lockmode);
620 : static void ATPrepAlterColumnType(List **wqueue,
621 : AlteredTableInfo *tab, Relation rel,
622 : bool recurse, bool recursing,
623 : AlterTableCmd *cmd, LOCKMODE lockmode,
624 : AlterTableUtilityContext *context);
625 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
626 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
627 : AlterTableCmd *cmd, LOCKMODE lockmode);
628 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
629 : Relation rel, AttrNumber attnum, const char *colName);
630 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
631 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
632 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
633 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
634 : LOCKMODE lockmode);
635 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
636 : char *cmd, List **wqueue, LOCKMODE lockmode,
637 : bool rewrite);
638 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
639 : Oid objid, Relation rel, List *domname,
640 : const char *conname);
641 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
642 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
643 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
644 : List *options, LOCKMODE lockmode);
645 : static void change_owner_fix_column_acls(Oid relationOid,
646 : Oid oldOwnerId, Oid newOwnerId);
647 : static void change_owner_recurse_to_sequences(Oid relationOid,
648 : Oid newOwnerId, LOCKMODE lockmode);
649 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
650 : LOCKMODE lockmode);
651 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
652 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
653 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
654 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
655 : bool toLogged);
656 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
657 : const char *tablespacename, LOCKMODE lockmode);
658 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
659 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
660 : static void ATExecSetRelOptions(Relation rel, List *defList,
661 : AlterTableType operation,
662 : LOCKMODE lockmode);
663 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
664 : char fires_when, bool skip_system, bool recurse,
665 : LOCKMODE lockmode);
666 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
667 : char fires_when, LOCKMODE lockmode);
668 : static void ATPrepAddInherit(Relation child_rel);
669 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
670 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
671 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
672 : DependencyType deptype);
673 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
674 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
675 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
676 : static void ATExecGenericOptions(Relation rel, List *options);
677 : static void ATExecSetRowSecurity(Relation rel, bool rls);
678 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
679 : static ObjectAddress ATExecSetCompression(Relation rel,
680 : const char *column, Node *newValue, LOCKMODE lockmode);
681 :
682 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
683 : static const char *storage_name(char c);
684 :
685 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
686 : Oid oldRelOid, void *arg);
687 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
688 : Oid oldrelid, void *arg);
689 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
690 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
691 : List **partexprs, Oid *partopclass, Oid *partcollation,
692 : PartitionStrategy strategy);
693 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
694 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
695 : bool expect_detached);
696 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
697 : PartitionCmd *cmd,
698 : AlterTableUtilityContext *context);
699 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
700 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
701 : List *partConstraint,
702 : bool validate_default);
703 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
704 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
705 : static void DropClonedTriggersFromPartition(Oid partitionId);
706 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
707 : Relation rel, RangeVar *name,
708 : bool concurrent);
709 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
710 : bool concurrent, Oid defaultPartOid);
711 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
712 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
713 : RangeVar *name);
714 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
715 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
716 : Relation partitionTbl);
717 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
718 : static List *GetParentedForeignKeyRefs(Relation partition);
719 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
720 : static char GetAttributeCompression(Oid atttypid, const char *compression);
721 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
722 :
723 :
724 : /* ----------------------------------------------------------------
725 : * DefineRelation
726 : * Creates a new relation.
727 : *
728 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
729 : * The other arguments are used to extend the behavior for other cases:
730 : * relkind: relkind to assign to the new relation
731 : * ownerId: if not InvalidOid, use this as the new relation's owner.
732 : * typaddress: if not null, it's set to the pg_type entry's address.
733 : * queryString: for error reporting
734 : *
735 : * Note that permissions checks are done against current user regardless of
736 : * ownerId. A nonzero ownerId is used when someone is creating a relation
737 : * "on behalf of" someone else, so we still want to see that the current user
738 : * has permissions to do it.
739 : *
740 : * If successful, returns the address of the new relation.
741 : * ----------------------------------------------------------------
742 : */
743 : ObjectAddress
744 59826 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
745 : ObjectAddress *typaddress, const char *queryString)
746 : {
747 : char relname[NAMEDATALEN];
748 : Oid namespaceId;
749 : Oid relationId;
750 : Oid tablespaceId;
751 : Relation rel;
752 : TupleDesc descriptor;
753 : List *inheritOids;
754 : List *old_constraints;
755 : List *old_notnulls;
756 : List *rawDefaults;
757 : List *cookedDefaults;
758 : List *nncols;
759 : Datum reloptions;
760 : ListCell *listptr;
761 : AttrNumber attnum;
762 : bool partitioned;
763 59826 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
764 : Oid ofTypeId;
765 : ObjectAddress address;
766 : LOCKMODE parentLockmode;
767 59826 : Oid accessMethodId = InvalidOid;
768 :
769 : /*
770 : * Truncate relname to appropriate length (probably a waste of time, as
771 : * parser should have done this already).
772 : */
773 59826 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
774 :
775 : /*
776 : * Check consistency of arguments
777 : */
778 59826 : if (stmt->oncommit != ONCOMMIT_NOOP
779 182 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
780 12 : ereport(ERROR,
781 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
782 : errmsg("ON COMMIT can only be used on temporary tables")));
783 :
784 59814 : if (stmt->partspec != NULL)
785 : {
786 4904 : if (relkind != RELKIND_RELATION)
787 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
788 :
789 4904 : relkind = RELKIND_PARTITIONED_TABLE;
790 4904 : partitioned = true;
791 : }
792 : else
793 54910 : partitioned = false;
794 :
795 59814 : if (relkind == RELKIND_PARTITIONED_TABLE &&
796 4904 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
797 6 : ereport(ERROR,
798 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
799 : errmsg("partitioned tables cannot be unlogged")));
800 :
801 : /*
802 : * Look up the namespace in which we are supposed to create the relation,
803 : * check we have permission to create there, lock it against concurrent
804 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
805 : * namespace is selected.
806 : */
807 : namespaceId =
808 59808 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
809 :
810 : /*
811 : * Security check: disallow creating temp tables from security-restricted
812 : * code. This is needed because calling code might not expect untrusted
813 : * tables to appear in pg_temp at the front of its search path.
814 : */
815 59808 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
816 3082 : && InSecurityRestrictedOperation())
817 0 : ereport(ERROR,
818 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
819 : errmsg("cannot create temporary table within security-restricted operation")));
820 :
821 : /*
822 : * Determine the lockmode to use when scanning parents. A self-exclusive
823 : * lock is needed here.
824 : *
825 : * For regular inheritance, if two backends attempt to add children to the
826 : * same parent simultaneously, and that parent has no pre-existing
827 : * children, then both will attempt to update the parent's relhassubclass
828 : * field, leading to a "tuple concurrently updated" error. Also, this
829 : * interlocks against a concurrent ANALYZE on the parent table, which
830 : * might otherwise be attempting to clear the parent's relhassubclass
831 : * field, if its previous children were recently dropped.
832 : *
833 : * If the child table is a partition, then we instead grab an exclusive
834 : * lock on the parent because its partition descriptor will be changed by
835 : * addition of the new partition.
836 : */
837 59808 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
838 : ShareUpdateExclusiveLock);
839 :
840 : /* Determine the list of OIDs of the parents. */
841 59808 : inheritOids = NIL;
842 70096 : foreach(listptr, stmt->inhRelations)
843 : {
844 10288 : RangeVar *rv = (RangeVar *) lfirst(listptr);
845 : Oid parentOid;
846 :
847 10288 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
848 :
849 : /*
850 : * Reject duplications in the list of parents.
851 : */
852 10288 : if (list_member_oid(inheritOids, parentOid))
853 0 : ereport(ERROR,
854 : (errcode(ERRCODE_DUPLICATE_TABLE),
855 : errmsg("relation \"%s\" would be inherited from more than once",
856 : get_rel_name(parentOid))));
857 :
858 10288 : inheritOids = lappend_oid(inheritOids, parentOid);
859 : }
860 :
861 : /*
862 : * Select tablespace to use: an explicitly indicated one, or (in the case
863 : * of a partitioned table) the parent's, if it has one.
864 : */
865 59808 : if (stmt->tablespacename)
866 : {
867 118 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
868 :
869 112 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
870 6 : ereport(ERROR,
871 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
872 : errmsg("cannot specify default tablespace for partitioned relations")));
873 : }
874 59690 : else if (stmt->partbound)
875 : {
876 : Assert(list_length(inheritOids) == 1);
877 7896 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
878 : }
879 : else
880 51794 : tablespaceId = InvalidOid;
881 :
882 : /* still nothing? use the default */
883 59796 : if (!OidIsValid(tablespaceId))
884 59668 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
885 : partitioned);
886 :
887 : /* Check permissions except when using database's default */
888 59790 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
889 : {
890 : AclResult aclresult;
891 :
892 146 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
893 : ACL_CREATE);
894 146 : if (aclresult != ACLCHECK_OK)
895 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
896 6 : get_tablespace_name(tablespaceId));
897 : }
898 :
899 : /* In all cases disallow placing user relations in pg_global */
900 59784 : if (tablespaceId == GLOBALTABLESPACE_OID)
901 18 : ereport(ERROR,
902 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
903 : errmsg("only shared relations can be placed in pg_global tablespace")));
904 :
905 : /* Identify user ID that will own the table */
906 59766 : if (!OidIsValid(ownerId))
907 59526 : ownerId = GetUserId();
908 :
909 : /*
910 : * Parse and validate reloptions, if any.
911 : */
912 59766 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
913 : true, false);
914 :
915 59748 : switch (relkind)
916 : {
917 14932 : case RELKIND_VIEW:
918 14932 : (void) view_reloptions(reloptions, true);
919 14914 : break;
920 4880 : case RELKIND_PARTITIONED_TABLE:
921 4880 : (void) partitioned_table_reloptions(reloptions, true);
922 4874 : break;
923 39936 : default:
924 39936 : (void) heap_reloptions(relkind, reloptions, true);
925 : }
926 :
927 59628 : if (stmt->ofTypename)
928 : {
929 : AclResult aclresult;
930 :
931 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
932 :
933 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
934 86 : if (aclresult != ACLCHECK_OK)
935 6 : aclcheck_error_type(aclresult, ofTypeId);
936 : }
937 : else
938 59542 : ofTypeId = InvalidOid;
939 :
940 : /*
941 : * Look up inheritance ancestors and generate relation schema, including
942 : * inherited attributes. (Note that stmt->tableElts is destructively
943 : * modified by MergeAttributes.)
944 : */
945 59382 : stmt->tableElts =
946 59622 : MergeAttributes(stmt->tableElts, inheritOids,
947 59622 : stmt->relation->relpersistence,
948 59622 : stmt->partbound != NULL,
949 : &old_constraints, &old_notnulls);
950 :
951 : /*
952 : * Create a tuple descriptor from the relation schema. Note that this
953 : * deals with column names, types, and in-descriptor NOT NULL flags, but
954 : * not default values, NOT NULL or CHECK constraints; we handle those
955 : * below.
956 : */
957 59382 : descriptor = BuildDescForRelation(stmt->tableElts);
958 :
959 : /*
960 : * Find columns with default values and prepare for insertion of the
961 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
962 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
963 : * while raw defaults go into a list of RawColumnDefault structs that will
964 : * be processed by AddRelationNewConstraints. (We can't deal with raw
965 : * expressions until we can do transformExpr.)
966 : */
967 59334 : rawDefaults = NIL;
968 59334 : cookedDefaults = NIL;
969 59334 : attnum = 0;
970 :
971 292718 : foreach(listptr, stmt->tableElts)
972 : {
973 233384 : ColumnDef *colDef = lfirst(listptr);
974 :
975 233384 : attnum++;
976 233384 : if (colDef->raw_default != NULL)
977 : {
978 : RawColumnDefault *rawEnt;
979 :
980 : Assert(colDef->cooked_default == NULL);
981 :
982 3104 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
983 3104 : rawEnt->attnum = attnum;
984 3104 : rawEnt->raw_default = colDef->raw_default;
985 3104 : rawEnt->generated = colDef->generated;
986 3104 : rawDefaults = lappend(rawDefaults, rawEnt);
987 : }
988 230280 : else if (colDef->cooked_default != NULL)
989 : {
990 : CookedConstraint *cooked;
991 :
992 388 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
993 388 : cooked->contype = CONSTR_DEFAULT;
994 388 : cooked->conoid = InvalidOid; /* until created */
995 388 : cooked->name = NULL;
996 388 : cooked->attnum = attnum;
997 388 : cooked->expr = colDef->cooked_default;
998 388 : cooked->is_enforced = true;
999 388 : cooked->skip_validation = false;
1000 388 : cooked->is_local = true; /* not used for defaults */
1001 388 : cooked->inhcount = 0; /* ditto */
1002 388 : cooked->is_no_inherit = false;
1003 388 : cookedDefaults = lappend(cookedDefaults, cooked);
1004 : }
1005 : }
1006 :
1007 : /*
1008 : * For relations with table AM and partitioned tables, select access
1009 : * method to use: an explicitly indicated one, or (in the case of a
1010 : * partitioned table) the parent's, if it has one.
1011 : */
1012 59334 : if (stmt->accessMethod != NULL)
1013 : {
1014 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1015 122 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1016 : }
1017 59212 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1018 : {
1019 37556 : if (stmt->partbound)
1020 : {
1021 : Assert(list_length(inheritOids) == 1);
1022 7714 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1023 : }
1024 :
1025 37556 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1026 32668 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1027 : }
1028 :
1029 : /*
1030 : * Create the relation. Inherited defaults and CHECK constraints are
1031 : * passed in for immediate handling --- since they don't need parsing,
1032 : * they can be stored immediately.
1033 : */
1034 59316 : relationId = heap_create_with_catalog(relname,
1035 : namespaceId,
1036 : tablespaceId,
1037 : InvalidOid,
1038 : InvalidOid,
1039 : ofTypeId,
1040 : ownerId,
1041 : accessMethodId,
1042 : descriptor,
1043 : list_concat(cookedDefaults,
1044 : old_constraints),
1045 : relkind,
1046 59316 : stmt->relation->relpersistence,
1047 : false,
1048 : false,
1049 : stmt->oncommit,
1050 : reloptions,
1051 : true,
1052 : allowSystemTableMods,
1053 : false,
1054 : InvalidOid,
1055 : typaddress);
1056 :
1057 : /*
1058 : * We must bump the command counter to make the newly-created relation
1059 : * tuple visible for opening.
1060 : */
1061 59280 : CommandCounterIncrement();
1062 :
1063 : /*
1064 : * Open the new relation and acquire exclusive lock on it. This isn't
1065 : * really necessary for locking out other backends (since they can't see
1066 : * the new rel anyway until we commit), but it keeps the lock manager from
1067 : * complaining about deadlock risks.
1068 : */
1069 59280 : rel = relation_open(relationId, AccessExclusiveLock);
1070 :
1071 : /*
1072 : * Now add any newly specified column default and generation expressions
1073 : * to the new relation. These are passed to us in the form of raw
1074 : * parsetrees; we need to transform them to executable expression trees
1075 : * before they can be added. The most convenient way to do that is to
1076 : * apply the parser's transformExpr routine, but transformExpr doesn't
1077 : * work unless we have a pre-existing relation. So, the transformation has
1078 : * to be postponed to this final step of CREATE TABLE.
1079 : *
1080 : * This needs to be before processing the partitioning clauses because
1081 : * those could refer to generated columns.
1082 : */
1083 59280 : if (rawDefaults)
1084 2650 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1085 : true, true, false, queryString);
1086 :
1087 : /*
1088 : * Make column generation expressions visible for use by partitioning.
1089 : */
1090 59118 : CommandCounterIncrement();
1091 :
1092 : /* Process and store partition bound, if any. */
1093 59118 : if (stmt->partbound)
1094 : {
1095 : PartitionBoundSpec *bound;
1096 : ParseState *pstate;
1097 7818 : Oid parentId = linitial_oid(inheritOids),
1098 : defaultPartOid;
1099 : Relation parent,
1100 7818 : defaultRel = NULL;
1101 : ParseNamespaceItem *nsitem;
1102 :
1103 : /* Already have strong enough lock on the parent */
1104 7818 : parent = table_open(parentId, NoLock);
1105 :
1106 : /*
1107 : * We are going to try to validate the partition bound specification
1108 : * against the partition key of parentRel, so it better have one.
1109 : */
1110 7818 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1111 18 : ereport(ERROR,
1112 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1113 : errmsg("\"%s\" is not partitioned",
1114 : RelationGetRelationName(parent))));
1115 :
1116 : /*
1117 : * The partition constraint of the default partition depends on the
1118 : * partition bounds of every other partition. It is possible that
1119 : * another backend might be about to execute a query on the default
1120 : * partition table, and that the query relies on previously cached
1121 : * default partition constraints. We must therefore take a table lock
1122 : * strong enough to prevent all queries on the default partition from
1123 : * proceeding until we commit and send out a shared-cache-inval notice
1124 : * that will make them update their index lists.
1125 : *
1126 : * Order of locking: The relation being added won't be visible to
1127 : * other backends until it is committed, hence here in
1128 : * DefineRelation() the order of locking the default partition and the
1129 : * relation being added does not matter. But at all other places we
1130 : * need to lock the default relation before we lock the relation being
1131 : * added or removed i.e. we should take the lock in same order at all
1132 : * the places such that lock parent, lock default partition and then
1133 : * lock the partition so as to avoid a deadlock.
1134 : */
1135 : defaultPartOid =
1136 7800 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1137 : true));
1138 7800 : if (OidIsValid(defaultPartOid))
1139 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1140 :
1141 : /* Transform the bound values */
1142 7800 : pstate = make_parsestate(NULL);
1143 7800 : pstate->p_sourcetext = queryString;
1144 :
1145 : /*
1146 : * Add an nsitem containing this relation, so that transformExpr
1147 : * called on partition bound expressions is able to report errors
1148 : * using a proper context.
1149 : */
1150 7800 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1151 : NULL, false, false);
1152 7800 : addNSItemToQuery(pstate, nsitem, false, true, true);
1153 :
1154 7800 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1155 :
1156 : /*
1157 : * Check first that the new partition's bound is valid and does not
1158 : * overlap with any of existing partitions of the parent.
1159 : */
1160 7596 : check_new_partition_bound(relname, parent, bound, pstate);
1161 :
1162 : /*
1163 : * If the default partition exists, its partition constraints will
1164 : * change after the addition of this new partition such that it won't
1165 : * allow any row that qualifies for this new partition. So, check that
1166 : * the existing data in the default partition satisfies the constraint
1167 : * as it will exist after adding this partition.
1168 : */
1169 7482 : if (OidIsValid(defaultPartOid))
1170 : {
1171 348 : check_default_partition_contents(parent, defaultRel, bound);
1172 : /* Keep the lock until commit. */
1173 330 : table_close(defaultRel, NoLock);
1174 : }
1175 :
1176 : /* Update the pg_class entry. */
1177 7464 : StorePartitionBound(rel, parent, bound);
1178 :
1179 7464 : table_close(parent, NoLock);
1180 : }
1181 :
1182 : /* Store inheritance information for new rel. */
1183 58764 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1184 :
1185 : /*
1186 : * Process the partitioning specification (if any) and store the partition
1187 : * key information into the catalog.
1188 : */
1189 58764 : if (partitioned)
1190 : {
1191 : ParseState *pstate;
1192 : int partnatts;
1193 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1194 : Oid partopclass[PARTITION_MAX_KEYS];
1195 : Oid partcollation[PARTITION_MAX_KEYS];
1196 4874 : List *partexprs = NIL;
1197 :
1198 4874 : pstate = make_parsestate(NULL);
1199 4874 : pstate->p_sourcetext = queryString;
1200 :
1201 4874 : partnatts = list_length(stmt->partspec->partParams);
1202 :
1203 : /* Protect fixed-size arrays here and in executor */
1204 4874 : if (partnatts > PARTITION_MAX_KEYS)
1205 0 : ereport(ERROR,
1206 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1207 : errmsg("cannot partition using more than %d columns",
1208 : PARTITION_MAX_KEYS)));
1209 :
1210 : /*
1211 : * We need to transform the raw parsetrees corresponding to partition
1212 : * expressions into executable expression trees. Like column defaults
1213 : * and CHECK constraints, we could not have done the transformation
1214 : * earlier.
1215 : */
1216 4874 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1217 :
1218 4844 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1219 : partattrs, &partexprs, partopclass,
1220 4844 : partcollation, stmt->partspec->strategy);
1221 :
1222 4748 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1223 : partexprs,
1224 : partopclass, partcollation);
1225 :
1226 : /* make it all visible */
1227 4748 : CommandCounterIncrement();
1228 : }
1229 :
1230 : /*
1231 : * If we're creating a partition, create now all the indexes, triggers,
1232 : * FKs defined in the parent.
1233 : *
1234 : * We can't do it earlier, because DefineIndex wants to know the partition
1235 : * key which we just stored.
1236 : */
1237 58638 : if (stmt->partbound)
1238 : {
1239 7458 : Oid parentId = linitial_oid(inheritOids);
1240 : Relation parent;
1241 : List *idxlist;
1242 : ListCell *cell;
1243 :
1244 : /* Already have strong enough lock on the parent */
1245 7458 : parent = table_open(parentId, NoLock);
1246 7458 : idxlist = RelationGetIndexList(parent);
1247 :
1248 : /*
1249 : * For each index in the parent table, create one in the partition
1250 : */
1251 8844 : foreach(cell, idxlist)
1252 : {
1253 1404 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1254 : AttrMap *attmap;
1255 : IndexStmt *idxstmt;
1256 : Oid constraintOid;
1257 :
1258 1404 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1259 : {
1260 36 : if (idxRel->rd_index->indisunique)
1261 12 : ereport(ERROR,
1262 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1263 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1264 : RelationGetRelationName(parent)),
1265 : errdetail("Table \"%s\" contains indexes that are unique.",
1266 : RelationGetRelationName(parent))));
1267 : else
1268 : {
1269 24 : index_close(idxRel, AccessShareLock);
1270 24 : continue;
1271 : }
1272 : }
1273 :
1274 1368 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1275 : RelationGetDescr(parent),
1276 : false);
1277 : idxstmt =
1278 1368 : generateClonedIndexStmt(NULL, idxRel,
1279 : attmap, &constraintOid);
1280 1368 : DefineIndex(RelationGetRelid(rel),
1281 : idxstmt,
1282 : InvalidOid,
1283 : RelationGetRelid(idxRel),
1284 : constraintOid,
1285 : -1,
1286 : false, false, false, false, false);
1287 :
1288 1362 : index_close(idxRel, AccessShareLock);
1289 : }
1290 :
1291 7440 : list_free(idxlist);
1292 :
1293 : /*
1294 : * If there are any row-level triggers, clone them to the new
1295 : * partition.
1296 : */
1297 7440 : if (parent->trigdesc != NULL)
1298 420 : CloneRowTriggersToPartition(parent, rel);
1299 :
1300 : /*
1301 : * And foreign keys too. Note that because we're freshly creating the
1302 : * table, there is no need to verify these new constraints.
1303 : */
1304 7440 : CloneForeignKeyConstraints(NULL, parent, rel);
1305 :
1306 7440 : table_close(parent, NoLock);
1307 : }
1308 :
1309 : /*
1310 : * Now add any newly specified CHECK constraints to the new relation. Same
1311 : * as for defaults above, but these need to come after partitioning is set
1312 : * up.
1313 : */
1314 58620 : if (stmt->constraints)
1315 722 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1316 : true, true, false, queryString);
1317 :
1318 : /*
1319 : * Finally, merge the not-null constraints that are declared directly with
1320 : * those that come from parent relations (making sure to count inheritance
1321 : * appropriately for each), create them, and set the attnotnull flag on
1322 : * columns that don't yet have it.
1323 : */
1324 58590 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1325 : old_notnulls);
1326 131808 : foreach_int(attrnum, nncols)
1327 14784 : set_attnotnull(NULL, rel, attrnum, NoLock);
1328 :
1329 58512 : ObjectAddressSet(address, RelationRelationId, relationId);
1330 :
1331 : /*
1332 : * Clean up. We keep lock on new relation (although it shouldn't be
1333 : * visible to anyone else anyway, until commit).
1334 : */
1335 58512 : relation_close(rel, NoLock);
1336 :
1337 58512 : return address;
1338 : }
1339 :
1340 : /*
1341 : * BuildDescForRelation
1342 : *
1343 : * Given a list of ColumnDef nodes, build a TupleDesc.
1344 : *
1345 : * Note: This is only for the limited purpose of table and view creation. Not
1346 : * everything is filled in. A real tuple descriptor should be obtained from
1347 : * the relcache.
1348 : */
1349 : TupleDesc
1350 62298 : BuildDescForRelation(const List *columns)
1351 : {
1352 : int natts;
1353 : AttrNumber attnum;
1354 : ListCell *l;
1355 : TupleDesc desc;
1356 : char *attname;
1357 : Oid atttypid;
1358 : int32 atttypmod;
1359 : Oid attcollation;
1360 : int attdim;
1361 :
1362 : /*
1363 : * allocate a new tuple descriptor
1364 : */
1365 62298 : natts = list_length(columns);
1366 62298 : desc = CreateTemplateTupleDesc(natts);
1367 :
1368 62298 : attnum = 0;
1369 :
1370 298862 : foreach(l, columns)
1371 : {
1372 236624 : ColumnDef *entry = lfirst(l);
1373 : AclResult aclresult;
1374 : Form_pg_attribute att;
1375 :
1376 : /*
1377 : * for each entry in the list, get the name and type information from
1378 : * the list and have TupleDescInitEntry fill in the attribute
1379 : * information we need.
1380 : */
1381 236624 : attnum++;
1382 :
1383 236624 : attname = entry->colname;
1384 236624 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1385 :
1386 236624 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1387 236624 : if (aclresult != ACLCHECK_OK)
1388 42 : aclcheck_error_type(aclresult, atttypid);
1389 :
1390 236582 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1391 236582 : attdim = list_length(entry->typeName->arrayBounds);
1392 236582 : if (attdim > PG_INT16_MAX)
1393 0 : ereport(ERROR,
1394 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1395 : errmsg("too many array dimensions"));
1396 :
1397 236582 : if (entry->typeName->setof)
1398 0 : ereport(ERROR,
1399 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1400 : errmsg("column \"%s\" cannot be declared SETOF",
1401 : attname)));
1402 :
1403 236582 : TupleDescInitEntry(desc, attnum, attname,
1404 : atttypid, atttypmod, attdim);
1405 236582 : att = TupleDescAttr(desc, attnum - 1);
1406 :
1407 : /* Override TupleDescInitEntry's settings as requested */
1408 236582 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1409 :
1410 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1411 236582 : att->attnotnull = entry->is_not_null;
1412 236582 : att->attislocal = entry->is_local;
1413 236582 : att->attinhcount = entry->inhcount;
1414 236582 : att->attidentity = entry->identity;
1415 236582 : att->attgenerated = entry->generated;
1416 236582 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1417 236570 : if (entry->storage)
1418 19782 : att->attstorage = entry->storage;
1419 216788 : else if (entry->storage_name)
1420 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1421 :
1422 236564 : populate_compact_attribute(desc, attnum - 1);
1423 : }
1424 :
1425 62238 : return desc;
1426 : }
1427 :
1428 : /*
1429 : * Emit the right error or warning message for a "DROP" command issued on a
1430 : * non-existent relation
1431 : */
1432 : static void
1433 1074 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1434 : {
1435 : const struct dropmsgstrings *rentry;
1436 :
1437 1194 : if (rel->schemaname != NULL &&
1438 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1439 : {
1440 42 : if (!missing_ok)
1441 : {
1442 0 : ereport(ERROR,
1443 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1444 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1445 : }
1446 : else
1447 : {
1448 42 : ereport(NOTICE,
1449 : (errmsg("schema \"%s\" does not exist, skipping",
1450 : rel->schemaname)));
1451 : }
1452 42 : return;
1453 : }
1454 :
1455 1352 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1456 : {
1457 1352 : if (rentry->kind == rightkind)
1458 : {
1459 1032 : if (!missing_ok)
1460 : {
1461 132 : ereport(ERROR,
1462 : (errcode(rentry->nonexistent_code),
1463 : errmsg(rentry->nonexistent_msg, rel->relname)));
1464 : }
1465 : else
1466 : {
1467 900 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1468 900 : break;
1469 : }
1470 : }
1471 : }
1472 :
1473 : Assert(rentry->kind != '\0'); /* Should be impossible */
1474 : }
1475 :
1476 : /*
1477 : * Emit the right error message for a "DROP" command issued on a
1478 : * relation of the wrong type
1479 : */
1480 : static void
1481 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1482 : {
1483 : const struct dropmsgstrings *rentry;
1484 : const struct dropmsgstrings *wentry;
1485 :
1486 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1487 0 : if (rentry->kind == rightkind)
1488 0 : break;
1489 : Assert(rentry->kind != '\0');
1490 :
1491 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1492 0 : if (wentry->kind == wrongkind)
1493 0 : break;
1494 : /* wrongkind could be something we don't have in our table... */
1495 :
1496 0 : ereport(ERROR,
1497 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1498 : errmsg(rentry->nota_msg, relname),
1499 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1500 : }
1501 :
1502 : /*
1503 : * RemoveRelations
1504 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1505 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1506 : */
1507 : void
1508 16990 : RemoveRelations(DropStmt *drop)
1509 : {
1510 : ObjectAddresses *objects;
1511 : char relkind;
1512 : ListCell *cell;
1513 16990 : int flags = 0;
1514 16990 : LOCKMODE lockmode = AccessExclusiveLock;
1515 :
1516 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1517 16990 : if (drop->concurrent)
1518 : {
1519 : /*
1520 : * Note that for temporary relations this lock may get upgraded later
1521 : * on, but as no other session can access a temporary relation, this
1522 : * is actually fine.
1523 : */
1524 220 : lockmode = ShareUpdateExclusiveLock;
1525 : Assert(drop->removeType == OBJECT_INDEX);
1526 220 : if (list_length(drop->objects) != 1)
1527 6 : ereport(ERROR,
1528 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1529 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1530 214 : if (drop->behavior == DROP_CASCADE)
1531 0 : ereport(ERROR,
1532 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1533 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1534 : }
1535 :
1536 : /*
1537 : * First we identify all the relations, then we delete them in a single
1538 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1539 : * RESTRICT errors if one of the relations depends on another.
1540 : */
1541 :
1542 : /* Determine required relkind */
1543 16984 : switch (drop->removeType)
1544 : {
1545 14688 : case OBJECT_TABLE:
1546 14688 : relkind = RELKIND_RELATION;
1547 14688 : break;
1548 :
1549 902 : case OBJECT_INDEX:
1550 902 : relkind = RELKIND_INDEX;
1551 902 : break;
1552 :
1553 176 : case OBJECT_SEQUENCE:
1554 176 : relkind = RELKIND_SEQUENCE;
1555 176 : break;
1556 :
1557 936 : case OBJECT_VIEW:
1558 936 : relkind = RELKIND_VIEW;
1559 936 : break;
1560 :
1561 120 : case OBJECT_MATVIEW:
1562 120 : relkind = RELKIND_MATVIEW;
1563 120 : break;
1564 :
1565 162 : case OBJECT_FOREIGN_TABLE:
1566 162 : relkind = RELKIND_FOREIGN_TABLE;
1567 162 : break;
1568 :
1569 0 : default:
1570 0 : elog(ERROR, "unrecognized drop object type: %d",
1571 : (int) drop->removeType);
1572 : relkind = 0; /* keep compiler quiet */
1573 : break;
1574 : }
1575 :
1576 : /* Lock and validate each relation; build a list of object addresses */
1577 16984 : objects = new_object_addresses();
1578 :
1579 37850 : foreach(cell, drop->objects)
1580 : {
1581 21024 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1582 : Oid relOid;
1583 : ObjectAddress obj;
1584 : struct DropRelationCallbackState state;
1585 :
1586 : /*
1587 : * These next few steps are a great deal like relation_openrv, but we
1588 : * don't bother building a relcache entry since we don't need it.
1589 : *
1590 : * Check for shared-cache-inval messages before trying to access the
1591 : * relation. This is needed to cover the case where the name
1592 : * identifies a rel that has been dropped and recreated since the
1593 : * start of our transaction: if we don't flush the old syscache entry,
1594 : * then we'll latch onto that entry and suffer an error later.
1595 : */
1596 21024 : AcceptInvalidationMessages();
1597 :
1598 : /* Look up the appropriate relation using namespace search. */
1599 21024 : state.expected_relkind = relkind;
1600 42048 : state.heap_lockmode = drop->concurrent ?
1601 21024 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1602 : /* We must initialize these fields to show that no locks are held: */
1603 21024 : state.heapOid = InvalidOid;
1604 21024 : state.partParentOid = InvalidOid;
1605 :
1606 21024 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1607 : RangeVarCallbackForDropRelation,
1608 : &state);
1609 :
1610 : /* Not there? */
1611 21004 : if (!OidIsValid(relOid))
1612 : {
1613 1074 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1614 942 : continue;
1615 : }
1616 :
1617 : /*
1618 : * Decide if concurrent mode needs to be used here or not. The
1619 : * callback retrieved the rel's persistence for us.
1620 : */
1621 19930 : if (drop->concurrent &&
1622 208 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1623 : {
1624 : Assert(list_length(drop->objects) == 1 &&
1625 : drop->removeType == OBJECT_INDEX);
1626 190 : flags |= PERFORM_DELETION_CONCURRENTLY;
1627 : }
1628 :
1629 : /*
1630 : * Concurrent index drop cannot be used with partitioned indexes,
1631 : * either.
1632 : */
1633 19930 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1634 190 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1635 6 : ereport(ERROR,
1636 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1637 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1638 : rel->relname)));
1639 :
1640 : /*
1641 : * If we're told to drop a partitioned index, we must acquire lock on
1642 : * all the children of its parent partitioned table before proceeding.
1643 : * Otherwise we'd try to lock the child index partitions before their
1644 : * tables, leading to potential deadlock against other sessions that
1645 : * will lock those objects in the other order.
1646 : */
1647 19924 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1648 76 : (void) find_all_inheritors(state.heapOid,
1649 : state.heap_lockmode,
1650 : NULL);
1651 :
1652 : /* OK, we're ready to delete this one */
1653 19924 : obj.classId = RelationRelationId;
1654 19924 : obj.objectId = relOid;
1655 19924 : obj.objectSubId = 0;
1656 :
1657 19924 : add_exact_object_address(&obj, objects);
1658 : }
1659 :
1660 16826 : performMultipleDeletions(objects, drop->behavior, flags);
1661 :
1662 16684 : free_object_addresses(objects);
1663 16684 : }
1664 :
1665 : /*
1666 : * Before acquiring a table lock, check whether we have sufficient rights.
1667 : * In the case of DROP INDEX, also try to lock the table before the index.
1668 : * Also, if the table to be dropped is a partition, we try to lock the parent
1669 : * first.
1670 : */
1671 : static void
1672 21296 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1673 : void *arg)
1674 : {
1675 : HeapTuple tuple;
1676 : struct DropRelationCallbackState *state;
1677 : char expected_relkind;
1678 : bool is_partition;
1679 : Form_pg_class classform;
1680 : LOCKMODE heap_lockmode;
1681 21296 : bool invalid_system_index = false;
1682 :
1683 21296 : state = (struct DropRelationCallbackState *) arg;
1684 21296 : heap_lockmode = state->heap_lockmode;
1685 :
1686 : /*
1687 : * If we previously locked some other index's heap, and the name we're
1688 : * looking up no longer refers to that relation, release the now-useless
1689 : * lock.
1690 : */
1691 21296 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1692 : {
1693 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1694 0 : state->heapOid = InvalidOid;
1695 : }
1696 :
1697 : /*
1698 : * Similarly, if we previously locked some other partition's heap, and the
1699 : * name we're looking up no longer refers to that relation, release the
1700 : * now-useless lock.
1701 : */
1702 21296 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1703 : {
1704 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1705 0 : state->partParentOid = InvalidOid;
1706 : }
1707 :
1708 : /* Didn't find a relation, so no need for locking or permission checks. */
1709 21296 : if (!OidIsValid(relOid))
1710 1080 : return;
1711 :
1712 20216 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1713 20216 : if (!HeapTupleIsValid(tuple))
1714 0 : return; /* concurrently dropped, so nothing to do */
1715 20216 : classform = (Form_pg_class) GETSTRUCT(tuple);
1716 20216 : is_partition = classform->relispartition;
1717 :
1718 : /* Pass back some data to save lookups in RemoveRelations */
1719 20216 : state->actual_relkind = classform->relkind;
1720 20216 : state->actual_relpersistence = classform->relpersistence;
1721 :
1722 : /*
1723 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1724 : * but RemoveRelations() can only pass one relkind for a given relation.
1725 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1726 : * That means we must be careful before giving the wrong type error when
1727 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1728 : * exists with indexes.
1729 : */
1730 20216 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1731 2904 : expected_relkind = RELKIND_RELATION;
1732 17312 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1733 82 : expected_relkind = RELKIND_INDEX;
1734 : else
1735 17230 : expected_relkind = classform->relkind;
1736 :
1737 20216 : if (state->expected_relkind != expected_relkind)
1738 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1739 0 : state->expected_relkind);
1740 :
1741 : /* Allow DROP to either table owner or schema owner */
1742 20216 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1743 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1744 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1745 18 : get_relkind_objtype(classform->relkind),
1746 18 : rel->relname);
1747 :
1748 : /*
1749 : * Check the case of a system index that might have been invalidated by a
1750 : * failed concurrent process and allow its drop. For the time being, this
1751 : * only concerns indexes of toast relations that became invalid during a
1752 : * REINDEX CONCURRENTLY process.
1753 : */
1754 20198 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1755 : {
1756 : HeapTuple locTuple;
1757 : Form_pg_index indexform;
1758 : bool indisvalid;
1759 :
1760 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1761 0 : if (!HeapTupleIsValid(locTuple))
1762 : {
1763 0 : ReleaseSysCache(tuple);
1764 0 : return;
1765 : }
1766 :
1767 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1768 0 : indisvalid = indexform->indisvalid;
1769 0 : ReleaseSysCache(locTuple);
1770 :
1771 : /* Mark object as being an invalid index of system catalogs */
1772 0 : if (!indisvalid)
1773 0 : invalid_system_index = true;
1774 : }
1775 :
1776 : /* In the case of an invalid index, it is fine to bypass this check */
1777 20198 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1778 2 : ereport(ERROR,
1779 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1780 : errmsg("permission denied: \"%s\" is a system catalog",
1781 : rel->relname)));
1782 :
1783 20196 : ReleaseSysCache(tuple);
1784 :
1785 : /*
1786 : * In DROP INDEX, attempt to acquire lock on the parent table before
1787 : * locking the index. index_drop() will need this anyway, and since
1788 : * regular queries lock tables before their indexes, we risk deadlock if
1789 : * we do it the other way around. No error if we don't find a pg_index
1790 : * entry, though --- the relation may have been dropped. Note that this
1791 : * code will execute for either plain or partitioned indexes.
1792 : */
1793 20196 : if (expected_relkind == RELKIND_INDEX &&
1794 : relOid != oldRelOid)
1795 : {
1796 890 : state->heapOid = IndexGetRelation(relOid, true);
1797 890 : if (OidIsValid(state->heapOid))
1798 890 : LockRelationOid(state->heapOid, heap_lockmode);
1799 : }
1800 :
1801 : /*
1802 : * Similarly, if the relation is a partition, we must acquire lock on its
1803 : * parent before locking the partition. That's because queries lock the
1804 : * parent before its partitions, so we risk deadlock if we do it the other
1805 : * way around.
1806 : */
1807 20196 : if (is_partition && relOid != oldRelOid)
1808 : {
1809 612 : state->partParentOid = get_partition_parent(relOid, true);
1810 612 : if (OidIsValid(state->partParentOid))
1811 612 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1812 : }
1813 : }
1814 :
1815 : /*
1816 : * ExecuteTruncate
1817 : * Executes a TRUNCATE command.
1818 : *
1819 : * This is a multi-relation truncate. We first open and grab exclusive
1820 : * lock on all relations involved, checking permissions and otherwise
1821 : * verifying that the relation is OK for truncation. Note that if relations
1822 : * are foreign tables, at this stage, we have not yet checked that their
1823 : * foreign data in external data sources are OK for truncation. These are
1824 : * checked when foreign data are actually truncated later. In CASCADE mode,
1825 : * relations having FK references to the targeted relations are automatically
1826 : * added to the group; in RESTRICT mode, we check that all FK references are
1827 : * internal to the group that's being truncated. Finally all the relations
1828 : * are truncated and reindexed.
1829 : */
1830 : void
1831 1666 : ExecuteTruncate(TruncateStmt *stmt)
1832 : {
1833 1666 : List *rels = NIL;
1834 1666 : List *relids = NIL;
1835 1666 : List *relids_logged = NIL;
1836 : ListCell *cell;
1837 :
1838 : /*
1839 : * Open, exclusive-lock, and check all the explicitly-specified relations
1840 : */
1841 3544 : foreach(cell, stmt->relations)
1842 : {
1843 1934 : RangeVar *rv = lfirst(cell);
1844 : Relation rel;
1845 1934 : bool recurse = rv->inh;
1846 : Oid myrelid;
1847 1934 : LOCKMODE lockmode = AccessExclusiveLock;
1848 :
1849 1934 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1850 : 0, RangeVarCallbackForTruncate,
1851 : NULL);
1852 :
1853 : /* don't throw error for "TRUNCATE foo, foo" */
1854 1896 : if (list_member_oid(relids, myrelid))
1855 2 : continue;
1856 :
1857 : /* open the relation, we already hold a lock on it */
1858 1894 : rel = table_open(myrelid, NoLock);
1859 :
1860 : /*
1861 : * RangeVarGetRelidExtended() has done most checks with its callback,
1862 : * but other checks with the now-opened Relation remain.
1863 : */
1864 1894 : truncate_check_activity(rel);
1865 :
1866 1888 : rels = lappend(rels, rel);
1867 1888 : relids = lappend_oid(relids, myrelid);
1868 :
1869 : /* Log this relation only if needed for logical decoding */
1870 1888 : if (RelationIsLogicallyLogged(rel))
1871 68 : relids_logged = lappend_oid(relids_logged, myrelid);
1872 :
1873 1888 : if (recurse)
1874 : {
1875 : ListCell *child;
1876 : List *children;
1877 :
1878 1826 : children = find_all_inheritors(myrelid, lockmode, NULL);
1879 :
1880 5438 : foreach(child, children)
1881 : {
1882 3612 : Oid childrelid = lfirst_oid(child);
1883 :
1884 3612 : if (list_member_oid(relids, childrelid))
1885 1826 : continue;
1886 :
1887 : /* find_all_inheritors already got lock */
1888 1786 : rel = table_open(childrelid, NoLock);
1889 :
1890 : /*
1891 : * It is possible that the parent table has children that are
1892 : * temp tables of other backends. We cannot safely access
1893 : * such tables (because of buffering issues), and the best
1894 : * thing to do is to silently ignore them. Note that this
1895 : * check is the same as one of the checks done in
1896 : * truncate_check_activity() called below, still it is kept
1897 : * here for simplicity.
1898 : */
1899 1786 : if (RELATION_IS_OTHER_TEMP(rel))
1900 : {
1901 8 : table_close(rel, lockmode);
1902 8 : continue;
1903 : }
1904 :
1905 : /*
1906 : * Inherited TRUNCATE commands perform access permission
1907 : * checks on the parent table only. So we skip checking the
1908 : * children's permissions and don't call
1909 : * truncate_check_perms() here.
1910 : */
1911 1778 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1912 1778 : truncate_check_activity(rel);
1913 :
1914 1778 : rels = lappend(rels, rel);
1915 1778 : relids = lappend_oid(relids, childrelid);
1916 :
1917 : /* Log this relation only if needed for logical decoding */
1918 1778 : if (RelationIsLogicallyLogged(rel))
1919 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1920 : }
1921 : }
1922 62 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1923 12 : ereport(ERROR,
1924 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1925 : errmsg("cannot truncate only a partitioned table"),
1926 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1927 : }
1928 :
1929 1610 : ExecuteTruncateGuts(rels, relids, relids_logged,
1930 1610 : stmt->behavior, stmt->restart_seqs, false);
1931 :
1932 : /* And close the rels */
1933 5028 : foreach(cell, rels)
1934 : {
1935 3500 : Relation rel = (Relation) lfirst(cell);
1936 :
1937 3500 : table_close(rel, NoLock);
1938 : }
1939 1528 : }
1940 :
1941 : /*
1942 : * ExecuteTruncateGuts
1943 : *
1944 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1945 : * command (see above) as well as replication subscribers that execute a
1946 : * replicated TRUNCATE action.
1947 : *
1948 : * explicit_rels is the list of Relations to truncate that the command
1949 : * specified. relids is the list of Oids corresponding to explicit_rels.
1950 : * relids_logged is the list of Oids (a subset of relids) that require
1951 : * WAL-logging. This is all a bit redundant, but the existing callers have
1952 : * this information handy in this form.
1953 : */
1954 : void
1955 1648 : ExecuteTruncateGuts(List *explicit_rels,
1956 : List *relids,
1957 : List *relids_logged,
1958 : DropBehavior behavior, bool restart_seqs,
1959 : bool run_as_table_owner)
1960 : {
1961 : List *rels;
1962 1648 : List *seq_relids = NIL;
1963 1648 : HTAB *ft_htab = NULL;
1964 : EState *estate;
1965 : ResultRelInfo *resultRelInfos;
1966 : ResultRelInfo *resultRelInfo;
1967 : SubTransactionId mySubid;
1968 : ListCell *cell;
1969 : Oid *logrelids;
1970 :
1971 : /*
1972 : * Check the explicitly-specified relations.
1973 : *
1974 : * In CASCADE mode, suck in all referencing relations as well. This
1975 : * requires multiple iterations to find indirectly-dependent relations. At
1976 : * each phase, we need to exclusive-lock new rels before looking for their
1977 : * dependencies, else we might miss something. Also, we check each rel as
1978 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1979 : * time on a rel we have no permissions for.
1980 : */
1981 1648 : rels = list_copy(explicit_rels);
1982 1648 : if (behavior == DROP_CASCADE)
1983 : {
1984 : for (;;)
1985 40 : {
1986 : List *newrelids;
1987 :
1988 80 : newrelids = heap_truncate_find_FKs(relids);
1989 80 : if (newrelids == NIL)
1990 40 : break; /* nothing else to add */
1991 :
1992 134 : foreach(cell, newrelids)
1993 : {
1994 94 : Oid relid = lfirst_oid(cell);
1995 : Relation rel;
1996 :
1997 94 : rel = table_open(relid, AccessExclusiveLock);
1998 94 : ereport(NOTICE,
1999 : (errmsg("truncate cascades to table \"%s\"",
2000 : RelationGetRelationName(rel))));
2001 94 : truncate_check_rel(relid, rel->rd_rel);
2002 94 : truncate_check_perms(relid, rel->rd_rel);
2003 94 : truncate_check_activity(rel);
2004 94 : rels = lappend(rels, rel);
2005 94 : relids = lappend_oid(relids, relid);
2006 :
2007 : /* Log this relation only if needed for logical decoding */
2008 94 : if (RelationIsLogicallyLogged(rel))
2009 0 : relids_logged = lappend_oid(relids_logged, relid);
2010 : }
2011 : }
2012 : }
2013 :
2014 : /*
2015 : * Check foreign key references. In CASCADE mode, this should be
2016 : * unnecessary since we just pulled in all the references; but as a
2017 : * cross-check, do it anyway if in an Assert-enabled build.
2018 : */
2019 : #ifdef USE_ASSERT_CHECKING
2020 : heap_truncate_check_FKs(rels, false);
2021 : #else
2022 1648 : if (behavior == DROP_RESTRICT)
2023 1608 : heap_truncate_check_FKs(rels, false);
2024 : #endif
2025 :
2026 : /*
2027 : * If we are asked to restart sequences, find all the sequences, lock them
2028 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2029 : * We want to do this early since it's pointless to do all the truncation
2030 : * work only to fail on sequence permissions.
2031 : */
2032 1574 : if (restart_seqs)
2033 : {
2034 52 : foreach(cell, rels)
2035 : {
2036 26 : Relation rel = (Relation) lfirst(cell);
2037 26 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2038 : ListCell *seqcell;
2039 :
2040 62 : foreach(seqcell, seqlist)
2041 : {
2042 36 : Oid seq_relid = lfirst_oid(seqcell);
2043 : Relation seq_rel;
2044 :
2045 36 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2046 :
2047 : /* This check must match AlterSequence! */
2048 36 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2049 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2050 0 : RelationGetRelationName(seq_rel));
2051 :
2052 36 : seq_relids = lappend_oid(seq_relids, seq_relid);
2053 :
2054 36 : relation_close(seq_rel, NoLock);
2055 : }
2056 : }
2057 : }
2058 :
2059 : /* Prepare to catch AFTER triggers. */
2060 1574 : AfterTriggerBeginQuery();
2061 :
2062 : /*
2063 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2064 : * each relation. We don't need to call ExecOpenIndices, though.
2065 : *
2066 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2067 : * though we don't have a range table and don't populate the
2068 : * es_result_relations array. That's a bit bogus, but it's enough to make
2069 : * ExecGetTriggerResultRel() find them.
2070 : */
2071 1574 : estate = CreateExecutorState();
2072 : resultRelInfos = (ResultRelInfo *)
2073 1574 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2074 1574 : resultRelInfo = resultRelInfos;
2075 5246 : foreach(cell, rels)
2076 : {
2077 3672 : Relation rel = (Relation) lfirst(cell);
2078 :
2079 3672 : InitResultRelInfo(resultRelInfo,
2080 : rel,
2081 : 0, /* dummy rangetable index */
2082 : NULL,
2083 : 0);
2084 3672 : estate->es_opened_result_relations =
2085 3672 : lappend(estate->es_opened_result_relations, resultRelInfo);
2086 3672 : resultRelInfo++;
2087 : }
2088 :
2089 : /*
2090 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2091 : * truncating (this is because one of them might throw an error). Also, if
2092 : * we were to allow them to prevent statement execution, that would need
2093 : * to be handled here.
2094 : */
2095 1574 : resultRelInfo = resultRelInfos;
2096 5246 : foreach(cell, rels)
2097 : {
2098 : UserContext ucxt;
2099 :
2100 3672 : if (run_as_table_owner)
2101 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2102 : &ucxt);
2103 3672 : ExecBSTruncateTriggers(estate, resultRelInfo);
2104 3672 : if (run_as_table_owner)
2105 70 : RestoreUserContext(&ucxt);
2106 3672 : resultRelInfo++;
2107 : }
2108 :
2109 : /*
2110 : * OK, truncate each table.
2111 : */
2112 1574 : mySubid = GetCurrentSubTransactionId();
2113 :
2114 5246 : foreach(cell, rels)
2115 : {
2116 3672 : Relation rel = (Relation) lfirst(cell);
2117 :
2118 : /* Skip partitioned tables as there is nothing to do */
2119 3672 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2120 698 : continue;
2121 :
2122 : /*
2123 : * Build the lists of foreign tables belonging to each foreign server
2124 : * and pass each list to the foreign data wrapper's callback function,
2125 : * so that each server can truncate its all foreign tables in bulk.
2126 : * Each list is saved as a single entry in a hash table that uses the
2127 : * server OID as lookup key.
2128 : */
2129 2974 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2130 : {
2131 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2132 : bool found;
2133 : ForeignTruncateInfo *ft_info;
2134 :
2135 : /* First time through, initialize hashtable for foreign tables */
2136 34 : if (!ft_htab)
2137 : {
2138 : HASHCTL hctl;
2139 :
2140 30 : memset(&hctl, 0, sizeof(HASHCTL));
2141 30 : hctl.keysize = sizeof(Oid);
2142 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2143 30 : hctl.hcxt = CurrentMemoryContext;
2144 :
2145 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2146 : 32, /* start small and extend */
2147 : &hctl,
2148 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2149 : }
2150 :
2151 : /* Find or create cached entry for the foreign table */
2152 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2153 34 : if (!found)
2154 30 : ft_info->rels = NIL;
2155 :
2156 : /*
2157 : * Save the foreign table in the entry of the server that the
2158 : * foreign table belongs to.
2159 : */
2160 34 : ft_info->rels = lappend(ft_info->rels, rel);
2161 34 : continue;
2162 : }
2163 :
2164 : /*
2165 : * Normally, we need a transaction-safe truncation here. However, if
2166 : * the table was either created in the current (sub)transaction or has
2167 : * a new relfilenumber in the current (sub)transaction, then we can
2168 : * just truncate it in-place, because a rollback would cause the whole
2169 : * table or the current physical file to be thrown away anyway.
2170 : */
2171 2940 : if (rel->rd_createSubid == mySubid ||
2172 2914 : rel->rd_newRelfilelocatorSubid == mySubid)
2173 : {
2174 : /* Immediate, non-rollbackable truncation is OK */
2175 90 : heap_truncate_one_rel(rel);
2176 : }
2177 : else
2178 : {
2179 : Oid heap_relid;
2180 : Oid toast_relid;
2181 2850 : ReindexParams reindex_params = {0};
2182 :
2183 : /*
2184 : * This effectively deletes all rows in the table, and may be done
2185 : * in a serializable transaction. In that case we must record a
2186 : * rw-conflict in to this transaction from each transaction
2187 : * holding a predicate lock on the table.
2188 : */
2189 2850 : CheckTableForSerializableConflictIn(rel);
2190 :
2191 : /*
2192 : * Need the full transaction-safe pushups.
2193 : *
2194 : * Create a new empty storage file for the relation, and assign it
2195 : * as the relfilenumber value. The old storage file is scheduled
2196 : * for deletion at commit.
2197 : */
2198 2850 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2199 :
2200 2850 : heap_relid = RelationGetRelid(rel);
2201 :
2202 : /*
2203 : * The same for the toast table, if any.
2204 : */
2205 2850 : toast_relid = rel->rd_rel->reltoastrelid;
2206 2850 : if (OidIsValid(toast_relid))
2207 : {
2208 1764 : Relation toastrel = relation_open(toast_relid,
2209 : AccessExclusiveLock);
2210 :
2211 1764 : RelationSetNewRelfilenumber(toastrel,
2212 1764 : toastrel->rd_rel->relpersistence);
2213 1764 : table_close(toastrel, NoLock);
2214 : }
2215 :
2216 : /*
2217 : * Reconstruct the indexes to match, and we're done.
2218 : */
2219 2850 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2220 : &reindex_params);
2221 : }
2222 :
2223 2940 : pgstat_count_truncate(rel);
2224 : }
2225 :
2226 : /* Now go through the hash table, and truncate foreign tables */
2227 1574 : if (ft_htab)
2228 : {
2229 : ForeignTruncateInfo *ft_info;
2230 : HASH_SEQ_STATUS seq;
2231 :
2232 30 : hash_seq_init(&seq, ft_htab);
2233 :
2234 30 : PG_TRY();
2235 : {
2236 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2237 : {
2238 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2239 :
2240 : /* truncate_check_rel() has checked that already */
2241 : Assert(routine->ExecForeignTruncate != NULL);
2242 :
2243 30 : routine->ExecForeignTruncate(ft_info->rels,
2244 : behavior,
2245 : restart_seqs);
2246 : }
2247 : }
2248 8 : PG_FINALLY();
2249 : {
2250 30 : hash_destroy(ft_htab);
2251 : }
2252 30 : PG_END_TRY();
2253 : }
2254 :
2255 : /*
2256 : * Restart owned sequences if we were asked to.
2257 : */
2258 1602 : foreach(cell, seq_relids)
2259 : {
2260 36 : Oid seq_relid = lfirst_oid(cell);
2261 :
2262 36 : ResetSequence(seq_relid);
2263 : }
2264 :
2265 : /*
2266 : * Write a WAL record to allow this set of actions to be logically
2267 : * decoded.
2268 : *
2269 : * Assemble an array of relids so we can write a single WAL record for the
2270 : * whole action.
2271 : */
2272 1566 : if (relids_logged != NIL)
2273 : {
2274 : xl_heap_truncate xlrec;
2275 54 : int i = 0;
2276 :
2277 : /* should only get here if wal_level >= logical */
2278 : Assert(XLogLogicalInfoActive());
2279 :
2280 54 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2281 144 : foreach(cell, relids_logged)
2282 90 : logrelids[i++] = lfirst_oid(cell);
2283 :
2284 54 : xlrec.dbId = MyDatabaseId;
2285 54 : xlrec.nrelids = list_length(relids_logged);
2286 54 : xlrec.flags = 0;
2287 54 : if (behavior == DROP_CASCADE)
2288 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2289 54 : if (restart_seqs)
2290 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2291 :
2292 54 : XLogBeginInsert();
2293 54 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2294 54 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2295 :
2296 54 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2297 :
2298 54 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2299 : }
2300 :
2301 : /*
2302 : * Process all AFTER STATEMENT TRUNCATE triggers.
2303 : */
2304 1566 : resultRelInfo = resultRelInfos;
2305 5230 : foreach(cell, rels)
2306 : {
2307 : UserContext ucxt;
2308 :
2309 3664 : if (run_as_table_owner)
2310 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2311 : &ucxt);
2312 3664 : ExecASTruncateTriggers(estate, resultRelInfo);
2313 3664 : if (run_as_table_owner)
2314 70 : RestoreUserContext(&ucxt);
2315 3664 : resultRelInfo++;
2316 : }
2317 :
2318 : /* Handle queued AFTER triggers */
2319 1566 : AfterTriggerEndQuery(estate);
2320 :
2321 : /* We can clean up the EState now */
2322 1566 : FreeExecutorState(estate);
2323 :
2324 : /*
2325 : * Close any rels opened by CASCADE (can't do this while EState still
2326 : * holds refs)
2327 : */
2328 1566 : rels = list_difference_ptr(rels, explicit_rels);
2329 1660 : foreach(cell, rels)
2330 : {
2331 94 : Relation rel = (Relation) lfirst(cell);
2332 :
2333 94 : table_close(rel, NoLock);
2334 : }
2335 1566 : }
2336 :
2337 : /*
2338 : * Check that a given relation is safe to truncate. Subroutine for
2339 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2340 : */
2341 : static void
2342 3902 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2343 : {
2344 3902 : char *relname = NameStr(reltuple->relname);
2345 :
2346 : /*
2347 : * Only allow truncate on regular tables, foreign tables using foreign
2348 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2349 : * latter are only being included here for the following checks; no
2350 : * physical truncation will occur in their case.).
2351 : */
2352 3902 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2353 : {
2354 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2355 38 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2356 :
2357 36 : if (!fdwroutine->ExecForeignTruncate)
2358 2 : ereport(ERROR,
2359 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2360 : errmsg("cannot truncate foreign table \"%s\"",
2361 : relname)));
2362 : }
2363 3864 : else if (reltuple->relkind != RELKIND_RELATION &&
2364 718 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2365 0 : ereport(ERROR,
2366 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2367 : errmsg("\"%s\" is not a table", relname)));
2368 :
2369 : /*
2370 : * Most system catalogs can't be truncated at all, or at least not unless
2371 : * allow_system_table_mods=on. As an exception, however, we allow
2372 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2373 : * to change its relfilenode to match the old cluster, and allowing a
2374 : * TRUNCATE command to be executed is the easiest way of doing that.
2375 : */
2376 3898 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2377 50 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2378 2 : ereport(ERROR,
2379 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2380 : errmsg("permission denied: \"%s\" is a system catalog",
2381 : relname)));
2382 :
2383 3896 : InvokeObjectTruncateHook(relid);
2384 3896 : }
2385 :
2386 : /*
2387 : * Check that current user has the permission to truncate given relation.
2388 : */
2389 : static void
2390 2118 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2391 : {
2392 2118 : char *relname = NameStr(reltuple->relname);
2393 : AclResult aclresult;
2394 :
2395 : /* Permissions checks */
2396 2118 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2397 2118 : if (aclresult != ACLCHECK_OK)
2398 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2399 : relname);
2400 2086 : }
2401 :
2402 : /*
2403 : * Set of extra sanity checks to check if a given relation is safe to
2404 : * truncate. This is split with truncate_check_rel() as
2405 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2406 : */
2407 : static void
2408 3766 : truncate_check_activity(Relation rel)
2409 : {
2410 : /*
2411 : * Don't allow truncate on temp tables of other backends ... their local
2412 : * buffer manager is not going to cope.
2413 : */
2414 3766 : if (RELATION_IS_OTHER_TEMP(rel))
2415 0 : ereport(ERROR,
2416 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2417 : errmsg("cannot truncate temporary tables of other sessions")));
2418 :
2419 : /*
2420 : * Also check for active uses of the relation in the current transaction,
2421 : * including open scans and pending AFTER trigger events.
2422 : */
2423 3766 : CheckTableNotInUse(rel, "TRUNCATE");
2424 3760 : }
2425 :
2426 : /*
2427 : * storage_name
2428 : * returns the name corresponding to a typstorage/attstorage enum value
2429 : */
2430 : static const char *
2431 24 : storage_name(char c)
2432 : {
2433 24 : switch (c)
2434 : {
2435 0 : case TYPSTORAGE_PLAIN:
2436 0 : return "PLAIN";
2437 0 : case TYPSTORAGE_EXTERNAL:
2438 0 : return "EXTERNAL";
2439 12 : case TYPSTORAGE_EXTENDED:
2440 12 : return "EXTENDED";
2441 12 : case TYPSTORAGE_MAIN:
2442 12 : return "MAIN";
2443 0 : default:
2444 0 : return "???";
2445 : }
2446 : }
2447 :
2448 : /*----------
2449 : * MergeAttributes
2450 : * Returns new schema given initial schema and superclasses.
2451 : *
2452 : * Input arguments:
2453 : * 'columns' is the column/attribute definition for the table. (It's a list
2454 : * of ColumnDef's.) It is destructively changed.
2455 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2456 : * 'relpersistence' is the persistence type of the table.
2457 : * 'is_partition' tells if the table is a partition.
2458 : *
2459 : * Output arguments:
2460 : * 'supconstr' receives a list of CookedConstraint representing
2461 : * CHECK constraints belonging to parent relations, updated as
2462 : * necessary to be valid for the child.
2463 : * 'supnotnulls' receives a list of CookedConstraint representing
2464 : * not-null constraints based on those from parent relations.
2465 : *
2466 : * Return value:
2467 : * Completed schema list.
2468 : *
2469 : * Notes:
2470 : * The order in which the attributes are inherited is very important.
2471 : * Intuitively, the inherited attributes should come first. If a table
2472 : * inherits from multiple parents, the order of those attributes are
2473 : * according to the order of the parents specified in CREATE TABLE.
2474 : *
2475 : * Here's an example:
2476 : *
2477 : * create table person (name text, age int4, location point);
2478 : * create table emp (salary int4, manager text) inherits(person);
2479 : * create table student (gpa float8) inherits (person);
2480 : * create table stud_emp (percent int4) inherits (emp, student);
2481 : *
2482 : * The order of the attributes of stud_emp is:
2483 : *
2484 : * person {1:name, 2:age, 3:location}
2485 : * / \
2486 : * {6:gpa} student emp {4:salary, 5:manager}
2487 : * \ /
2488 : * stud_emp {7:percent}
2489 : *
2490 : * If the same attribute name appears multiple times, then it appears
2491 : * in the result table in the proper location for its first appearance.
2492 : *
2493 : * Constraints (including not-null constraints) for the child table
2494 : * are the union of all relevant constraints, from both the child schema
2495 : * and parent tables. In addition, in legacy inheritance, each column that
2496 : * appears in a primary key in any of the parents also gets a NOT NULL
2497 : * constraint (partitioning doesn't need this, because the PK itself gets
2498 : * inherited.)
2499 : *
2500 : * The default value for a child column is defined as:
2501 : * (1) If the child schema specifies a default, that value is used.
2502 : * (2) If neither the child nor any parent specifies a default, then
2503 : * the column will not have a default.
2504 : * (3) If conflicting defaults are inherited from different parents
2505 : * (and not overridden by the child), an error is raised.
2506 : * (4) Otherwise the inherited default is used.
2507 : *
2508 : * Note that the default-value infrastructure is used for generated
2509 : * columns' expressions too, so most of the preceding paragraph applies
2510 : * to generation expressions too. We insist that a child column be
2511 : * generated if and only if its parent(s) are, but it need not have
2512 : * the same generation expression.
2513 : *----------
2514 : */
2515 : static List *
2516 59622 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2517 : bool is_partition, List **supconstr, List **supnotnulls)
2518 : {
2519 59622 : List *inh_columns = NIL;
2520 59622 : List *constraints = NIL;
2521 59622 : List *nnconstraints = NIL;
2522 59622 : bool have_bogus_defaults = false;
2523 : int child_attno;
2524 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2525 59622 : List *saved_columns = NIL;
2526 : ListCell *lc;
2527 :
2528 : /*
2529 : * Check for and reject tables with too many columns. We perform this
2530 : * check relatively early for two reasons: (a) we don't run the risk of
2531 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2532 : * okay if we're processing <= 1600 columns, but could take minutes to
2533 : * execute if the user attempts to create a table with hundreds of
2534 : * thousands of columns.
2535 : *
2536 : * Note that we also need to check that we do not exceed this figure after
2537 : * including columns from inherited relations.
2538 : */
2539 59622 : if (list_length(columns) > MaxHeapAttributeNumber)
2540 0 : ereport(ERROR,
2541 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2542 : errmsg("tables can have at most %d columns",
2543 : MaxHeapAttributeNumber)));
2544 :
2545 : /*
2546 : * Check for duplicate names in the explicit list of attributes.
2547 : *
2548 : * Although we might consider merging such entries in the same way that we
2549 : * handle name conflicts for inherited attributes, it seems to make more
2550 : * sense to assume such conflicts are errors.
2551 : *
2552 : * We don't use foreach() here because we have two nested loops over the
2553 : * columns list, with possible element deletions in the inner one. If we
2554 : * used foreach_delete_current() it could only fix up the state of one of
2555 : * the loops, so it seems cleaner to use looping over list indexes for
2556 : * both loops. Note that any deletion will happen beyond where the outer
2557 : * loop is, so its index never needs adjustment.
2558 : */
2559 274102 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2560 : {
2561 214504 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2562 :
2563 214504 : if (!is_partition && coldef->typeName == NULL)
2564 : {
2565 : /*
2566 : * Typed table column option that does not belong to a column from
2567 : * the type. This works because the columns from the type come
2568 : * first in the list. (We omit this check for partition column
2569 : * lists; those are processed separately below.)
2570 : */
2571 6 : ereport(ERROR,
2572 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2573 : errmsg("column \"%s\" does not exist",
2574 : coldef->colname)));
2575 : }
2576 :
2577 : /* restpos scans all entries beyond coldef; incr is in loop body */
2578 6380160 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2579 : {
2580 6165680 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2581 :
2582 6165680 : if (strcmp(coldef->colname, restdef->colname) == 0)
2583 : {
2584 50 : if (coldef->is_from_type)
2585 : {
2586 : /*
2587 : * merge the column options into the column from the type
2588 : */
2589 32 : coldef->is_not_null = restdef->is_not_null;
2590 32 : coldef->raw_default = restdef->raw_default;
2591 32 : coldef->cooked_default = restdef->cooked_default;
2592 32 : coldef->constraints = restdef->constraints;
2593 32 : coldef->is_from_type = false;
2594 32 : columns = list_delete_nth_cell(columns, restpos);
2595 : }
2596 : else
2597 18 : ereport(ERROR,
2598 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2599 : errmsg("column \"%s\" specified more than once",
2600 : coldef->colname)));
2601 : }
2602 : else
2603 6165630 : restpos++;
2604 : }
2605 : }
2606 :
2607 : /*
2608 : * In case of a partition, there are no new column definitions, only dummy
2609 : * ColumnDefs created for column constraints. Set them aside for now and
2610 : * process them at the end.
2611 : */
2612 59598 : if (is_partition)
2613 : {
2614 7884 : saved_columns = columns;
2615 7884 : columns = NIL;
2616 : }
2617 :
2618 : /*
2619 : * Scan the parents left-to-right, and merge their attributes to form a
2620 : * list of inherited columns (inh_columns).
2621 : */
2622 59598 : child_attno = 0;
2623 69778 : foreach(lc, supers)
2624 : {
2625 10264 : Oid parent = lfirst_oid(lc);
2626 : Relation relation;
2627 : TupleDesc tupleDesc;
2628 : TupleConstr *constr;
2629 : AttrMap *newattmap;
2630 : List *inherited_defaults;
2631 : List *cols_with_defaults;
2632 : List *nnconstrs;
2633 : ListCell *lc1;
2634 : ListCell *lc2;
2635 10264 : Bitmapset *nncols = NULL;
2636 :
2637 : /* caller already got lock */
2638 10264 : relation = table_open(parent, NoLock);
2639 :
2640 : /*
2641 : * Check for active uses of the parent partitioned table in the
2642 : * current transaction, such as being used in some manner by an
2643 : * enclosing command.
2644 : */
2645 10264 : if (is_partition)
2646 7884 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2647 :
2648 : /*
2649 : * We do not allow partitioned tables and partitions to participate in
2650 : * regular inheritance.
2651 : */
2652 10258 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2653 6 : ereport(ERROR,
2654 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2655 : errmsg("cannot inherit from partitioned table \"%s\"",
2656 : RelationGetRelationName(relation))));
2657 10252 : if (relation->rd_rel->relispartition && !is_partition)
2658 6 : ereport(ERROR,
2659 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2660 : errmsg("cannot inherit from partition \"%s\"",
2661 : RelationGetRelationName(relation))));
2662 :
2663 10246 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2664 7880 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2665 7860 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2666 0 : ereport(ERROR,
2667 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2668 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2669 : RelationGetRelationName(relation))));
2670 :
2671 : /*
2672 : * If the parent is permanent, so must be all of its partitions. Note
2673 : * that inheritance allows that case.
2674 : */
2675 10246 : if (is_partition &&
2676 7878 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2677 : relpersistence == RELPERSISTENCE_TEMP)
2678 6 : ereport(ERROR,
2679 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2680 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2681 : RelationGetRelationName(relation))));
2682 :
2683 : /* Permanent rels cannot inherit from temporary ones */
2684 10240 : if (relpersistence != RELPERSISTENCE_TEMP &&
2685 9892 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2686 24 : ereport(ERROR,
2687 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2688 : errmsg(!is_partition
2689 : ? "cannot inherit from temporary relation \"%s\""
2690 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2691 : RelationGetRelationName(relation))));
2692 :
2693 : /* If existing rel is temp, it must belong to this session */
2694 10216 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2695 294 : !relation->rd_islocaltemp)
2696 0 : ereport(ERROR,
2697 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2698 : errmsg(!is_partition
2699 : ? "cannot inherit from temporary relation of another session"
2700 : : "cannot create as partition of temporary relation of another session")));
2701 :
2702 : /*
2703 : * We should have an UNDER permission flag for this, but for now,
2704 : * demand that creator of a child table own the parent.
2705 : */
2706 10216 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2707 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2708 0 : RelationGetRelationName(relation));
2709 :
2710 10216 : tupleDesc = RelationGetDescr(relation);
2711 10216 : constr = tupleDesc->constr;
2712 :
2713 : /*
2714 : * newattmap->attnums[] will contain the child-table attribute numbers
2715 : * for the attributes of this parent table. (They are not the same
2716 : * for parents after the first one, nor if we have dropped columns.)
2717 : */
2718 10216 : newattmap = make_attrmap(tupleDesc->natts);
2719 :
2720 : /* We can't process inherited defaults until newattmap is complete. */
2721 10216 : inherited_defaults = cols_with_defaults = NIL;
2722 :
2723 : /*
2724 : * Request attnotnull on columns that have a not-null constraint
2725 : * that's not marked NO INHERIT.
2726 : */
2727 10216 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2728 : true, false);
2729 22744 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2730 2312 : nncols = bms_add_member(nncols, cc->attnum);
2731 :
2732 30762 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2733 20546 : parent_attno++)
2734 : {
2735 20582 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2736 : parent_attno - 1);
2737 20582 : char *attributeName = NameStr(attribute->attname);
2738 : int exist_attno;
2739 : ColumnDef *newdef;
2740 : ColumnDef *mergeddef;
2741 :
2742 : /*
2743 : * Ignore dropped columns in the parent.
2744 : */
2745 20582 : if (attribute->attisdropped)
2746 192 : continue; /* leave newattmap->attnums entry as zero */
2747 :
2748 : /*
2749 : * Create new column definition
2750 : */
2751 20390 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2752 : attribute->atttypmod, attribute->attcollation);
2753 20390 : newdef->storage = attribute->attstorage;
2754 20390 : newdef->generated = attribute->attgenerated;
2755 20390 : if (CompressionMethodIsValid(attribute->attcompression))
2756 30 : newdef->compression =
2757 30 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2758 :
2759 : /*
2760 : * Regular inheritance children are independent enough not to
2761 : * inherit identity columns. But partitions are integral part of
2762 : * a partitioned table and inherit identity column.
2763 : */
2764 20390 : if (is_partition)
2765 15988 : newdef->identity = attribute->attidentity;
2766 :
2767 : /*
2768 : * Does it match some previously considered column from another
2769 : * parent?
2770 : */
2771 20390 : exist_attno = findAttrByName(attributeName, inh_columns);
2772 20390 : if (exist_attno > 0)
2773 : {
2774 : /*
2775 : * Yes, try to merge the two column definitions.
2776 : */
2777 350 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2778 :
2779 314 : newattmap->attnums[parent_attno - 1] = exist_attno;
2780 :
2781 : /*
2782 : * Partitions have only one parent, so conflict should never
2783 : * occur.
2784 : */
2785 : Assert(!is_partition);
2786 : }
2787 : else
2788 : {
2789 : /*
2790 : * No, create a new inherited column
2791 : */
2792 20040 : newdef->inhcount = 1;
2793 20040 : newdef->is_local = false;
2794 20040 : inh_columns = lappend(inh_columns, newdef);
2795 :
2796 20040 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2797 20040 : mergeddef = newdef;
2798 : }
2799 :
2800 : /*
2801 : * mark attnotnull if parent has it
2802 : */
2803 20354 : if (bms_is_member(parent_attno, nncols))
2804 2312 : mergeddef->is_not_null = true;
2805 :
2806 : /*
2807 : * Locate default/generation expression if any
2808 : */
2809 20354 : if (attribute->atthasdef)
2810 : {
2811 : Node *this_default;
2812 :
2813 690 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2814 690 : if (this_default == NULL)
2815 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2816 : parent_attno, RelationGetRelationName(relation));
2817 :
2818 : /*
2819 : * If it's a GENERATED default, it might contain Vars that
2820 : * need to be mapped to the inherited column(s)' new numbers.
2821 : * We can't do that till newattmap is ready, so just remember
2822 : * all the inherited default expressions for the moment.
2823 : */
2824 690 : inherited_defaults = lappend(inherited_defaults, this_default);
2825 690 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2826 : }
2827 : }
2828 :
2829 : /*
2830 : * Now process any inherited default expressions, adjusting attnos
2831 : * using the completed newattmap map.
2832 : */
2833 10870 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2834 : {
2835 690 : Node *this_default = (Node *) lfirst(lc1);
2836 690 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2837 : bool found_whole_row;
2838 :
2839 : /* Adjust Vars to match new table's column numbering */
2840 690 : this_default = map_variable_attnos(this_default,
2841 : 1, 0,
2842 : newattmap,
2843 : InvalidOid, &found_whole_row);
2844 :
2845 : /*
2846 : * For the moment we have to reject whole-row variables. We could
2847 : * convert them, if we knew the new table's rowtype OID, but that
2848 : * hasn't been assigned yet. (A variable could only appear in a
2849 : * generation expression, so the error message is correct.)
2850 : */
2851 690 : if (found_whole_row)
2852 0 : ereport(ERROR,
2853 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2854 : errmsg("cannot convert whole-row table reference"),
2855 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2856 : def->colname,
2857 : RelationGetRelationName(relation))));
2858 :
2859 : /*
2860 : * If we already had a default from some prior parent, check to
2861 : * see if they are the same. If so, no problem; if not, mark the
2862 : * column as having a bogus default. Below, we will complain if
2863 : * the bogus default isn't overridden by the child columns.
2864 : */
2865 : Assert(def->raw_default == NULL);
2866 690 : if (def->cooked_default == NULL)
2867 648 : def->cooked_default = this_default;
2868 42 : else if (!equal(def->cooked_default, this_default))
2869 : {
2870 36 : def->cooked_default = &bogus_marker;
2871 36 : have_bogus_defaults = true;
2872 : }
2873 : }
2874 :
2875 : /*
2876 : * Now copy the CHECK constraints of this parent, adjusting attnos
2877 : * using the completed newattmap map. Identically named constraints
2878 : * are merged if possible, else we throw error.
2879 : */
2880 10180 : if (constr && constr->num_check > 0)
2881 : {
2882 334 : ConstrCheck *check = constr->check;
2883 :
2884 968 : for (int i = 0; i < constr->num_check; i++)
2885 : {
2886 634 : char *name = check[i].ccname;
2887 : Node *expr;
2888 : bool found_whole_row;
2889 :
2890 : /* ignore if the constraint is non-inheritable */
2891 634 : if (check[i].ccnoinherit)
2892 48 : continue;
2893 :
2894 : /* Adjust Vars to match new table's column numbering */
2895 586 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2896 : 1, 0,
2897 : newattmap,
2898 : InvalidOid, &found_whole_row);
2899 :
2900 : /*
2901 : * For the moment we have to reject whole-row variables. We
2902 : * could convert them, if we knew the new table's rowtype OID,
2903 : * but that hasn't been assigned yet.
2904 : */
2905 586 : if (found_whole_row)
2906 0 : ereport(ERROR,
2907 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2908 : errmsg("cannot convert whole-row table reference"),
2909 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2910 : name,
2911 : RelationGetRelationName(relation))));
2912 :
2913 586 : constraints = MergeCheckConstraint(constraints, name, expr,
2914 586 : check[i].ccenforced);
2915 : }
2916 : }
2917 :
2918 : /*
2919 : * Also copy the not-null constraints from this parent. The
2920 : * attnotnull markings were already installed above.
2921 : */
2922 22672 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2923 : {
2924 : Assert(nn->contype == CONSTR_NOTNULL);
2925 :
2926 2312 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2927 :
2928 2312 : nnconstraints = lappend(nnconstraints, nn);
2929 : }
2930 :
2931 10180 : free_attrmap(newattmap);
2932 :
2933 : /*
2934 : * Close the parent rel, but keep our lock on it until xact commit.
2935 : * That will prevent someone else from deleting or ALTERing the parent
2936 : * before the child is committed.
2937 : */
2938 10180 : table_close(relation, NoLock);
2939 : }
2940 :
2941 : /*
2942 : * If we had no inherited attributes, the result columns are just the
2943 : * explicitly declared columns. Otherwise, we need to merge the declared
2944 : * columns into the inherited column list. Although, we never have any
2945 : * explicitly declared columns if the table is a partition.
2946 : */
2947 59514 : if (inh_columns != NIL)
2948 : {
2949 9770 : int newcol_attno = 0;
2950 :
2951 10718 : foreach(lc, columns)
2952 : {
2953 1026 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2954 1026 : char *attributeName = newdef->colname;
2955 : int exist_attno;
2956 :
2957 : /*
2958 : * Partitions have only one parent and have no column definitions
2959 : * of their own, so conflict should never occur.
2960 : */
2961 : Assert(!is_partition);
2962 :
2963 1026 : newcol_attno++;
2964 :
2965 : /*
2966 : * Does it match some inherited column?
2967 : */
2968 1026 : exist_attno = findAttrByName(attributeName, inh_columns);
2969 1026 : if (exist_attno > 0)
2970 : {
2971 : /*
2972 : * Yes, try to merge the two column definitions.
2973 : */
2974 354 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2975 : }
2976 : else
2977 : {
2978 : /*
2979 : * No, attach new column unchanged to result columns.
2980 : */
2981 672 : inh_columns = lappend(inh_columns, newdef);
2982 : }
2983 : }
2984 :
2985 9692 : columns = inh_columns;
2986 :
2987 : /*
2988 : * Check that we haven't exceeded the legal # of columns after merging
2989 : * in inherited columns.
2990 : */
2991 9692 : if (list_length(columns) > MaxHeapAttributeNumber)
2992 0 : ereport(ERROR,
2993 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2994 : errmsg("tables can have at most %d columns",
2995 : MaxHeapAttributeNumber)));
2996 : }
2997 :
2998 : /*
2999 : * Now that we have the column definition list for a partition, we can
3000 : * check whether the columns referenced in the column constraint specs
3001 : * actually exist. Also, merge column defaults.
3002 : */
3003 59436 : if (is_partition)
3004 : {
3005 8060 : foreach(lc, saved_columns)
3006 : {
3007 242 : ColumnDef *restdef = lfirst(lc);
3008 242 : bool found = false;
3009 : ListCell *l;
3010 :
3011 900 : foreach(l, columns)
3012 : {
3013 694 : ColumnDef *coldef = lfirst(l);
3014 :
3015 694 : if (strcmp(coldef->colname, restdef->colname) == 0)
3016 : {
3017 242 : found = true;
3018 :
3019 : /*
3020 : * Check for conflicts related to generated columns.
3021 : *
3022 : * Same rules as above: generated-ness has to match the
3023 : * parent, but the contents of the generation expression
3024 : * can be different.
3025 : */
3026 242 : if (coldef->generated)
3027 : {
3028 134 : if (restdef->raw_default && !restdef->generated)
3029 12 : ereport(ERROR,
3030 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3031 : errmsg("column \"%s\" inherits from generated column but specifies default",
3032 : restdef->colname)));
3033 122 : if (restdef->identity)
3034 0 : ereport(ERROR,
3035 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3036 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3037 : restdef->colname)));
3038 : }
3039 : else
3040 : {
3041 108 : if (restdef->generated)
3042 12 : ereport(ERROR,
3043 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3044 : errmsg("child column \"%s\" specifies generation expression",
3045 : restdef->colname),
3046 : errhint("A child table column cannot be generated unless its parent column is.")));
3047 : }
3048 :
3049 218 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3050 12 : ereport(ERROR,
3051 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3052 : errmsg("column \"%s\" inherits from generated column of different kind",
3053 : restdef->colname),
3054 : errdetail("Parent column is %s, child column is %s.",
3055 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3056 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3057 :
3058 : /*
3059 : * Override the parent's default value for this column
3060 : * (coldef->cooked_default) with the partition's local
3061 : * definition (restdef->raw_default), if there's one. It
3062 : * should be physically impossible to get a cooked default
3063 : * in the local definition or a raw default in the
3064 : * inherited definition, but make sure they're nulls, for
3065 : * future-proofing.
3066 : */
3067 : Assert(restdef->cooked_default == NULL);
3068 : Assert(coldef->raw_default == NULL);
3069 206 : if (restdef->raw_default)
3070 : {
3071 134 : coldef->raw_default = restdef->raw_default;
3072 134 : coldef->cooked_default = NULL;
3073 : }
3074 : }
3075 : }
3076 :
3077 : /* complain for constraints on columns not in parent */
3078 206 : if (!found)
3079 0 : ereport(ERROR,
3080 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3081 : errmsg("column \"%s\" does not exist",
3082 : restdef->colname)));
3083 : }
3084 : }
3085 :
3086 : /*
3087 : * If we found any conflicting parent default values, check to make sure
3088 : * they were overridden by the child.
3089 : */
3090 59400 : if (have_bogus_defaults)
3091 : {
3092 90 : foreach(lc, columns)
3093 : {
3094 72 : ColumnDef *def = lfirst(lc);
3095 :
3096 72 : if (def->cooked_default == &bogus_marker)
3097 : {
3098 18 : if (def->generated)
3099 12 : ereport(ERROR,
3100 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3101 : errmsg("column \"%s\" inherits conflicting generation expressions",
3102 : def->colname),
3103 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3104 : else
3105 6 : ereport(ERROR,
3106 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3107 : errmsg("column \"%s\" inherits conflicting default values",
3108 : def->colname),
3109 : errhint("To resolve the conflict, specify a default explicitly.")));
3110 : }
3111 : }
3112 : }
3113 :
3114 59382 : *supconstr = constraints;
3115 59382 : *supnotnulls = nnconstraints;
3116 :
3117 59382 : return columns;
3118 : }
3119 :
3120 :
3121 : /*
3122 : * MergeCheckConstraint
3123 : * Try to merge an inherited CHECK constraint with previous ones
3124 : *
3125 : * If we inherit identically-named constraints from multiple parents, we must
3126 : * merge them, or throw an error if they don't have identical definitions.
3127 : *
3128 : * constraints is a list of CookedConstraint structs for previous constraints.
3129 : *
3130 : * If the new constraint matches an existing one, then the existing
3131 : * constraint's inheritance count is updated. If there is a conflict (same
3132 : * name but different expression), throw an error. If the constraint neither
3133 : * matches nor conflicts with an existing one, a new constraint is appended to
3134 : * the list.
3135 : */
3136 : static List *
3137 586 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3138 : {
3139 : ListCell *lc;
3140 : CookedConstraint *newcon;
3141 :
3142 1486 : foreach(lc, constraints)
3143 : {
3144 1026 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3145 :
3146 : Assert(ccon->contype == CONSTR_CHECK);
3147 :
3148 : /* Non-matching names never conflict */
3149 1026 : if (strcmp(ccon->name, name) != 0)
3150 900 : continue;
3151 :
3152 126 : if (equal(expr, ccon->expr))
3153 : {
3154 : /* OK to merge constraint with existing */
3155 126 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3156 : &ccon->inhcount))
3157 0 : ereport(ERROR,
3158 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3159 : errmsg("too many inheritance parents"));
3160 :
3161 : /*
3162 : * When enforceability differs, the merged constraint should be
3163 : * marked as ENFORCED because one of the parents is ENFORCED.
3164 : */
3165 126 : if (!ccon->is_enforced && is_enforced)
3166 : {
3167 24 : ccon->is_enforced = true;
3168 24 : ccon->skip_validation = false;
3169 : }
3170 :
3171 126 : return constraints;
3172 : }
3173 :
3174 0 : ereport(ERROR,
3175 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3176 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3177 : name)));
3178 : }
3179 :
3180 : /*
3181 : * Constraint couldn't be merged with an existing one and also didn't
3182 : * conflict with an existing one, so add it as a new one to the list.
3183 : */
3184 460 : newcon = palloc0_object(CookedConstraint);
3185 460 : newcon->contype = CONSTR_CHECK;
3186 460 : newcon->name = pstrdup(name);
3187 460 : newcon->expr = expr;
3188 460 : newcon->inhcount = 1;
3189 460 : newcon->is_enforced = is_enforced;
3190 460 : newcon->skip_validation = !is_enforced;
3191 460 : return lappend(constraints, newcon);
3192 : }
3193 :
3194 : /*
3195 : * MergeChildAttribute
3196 : * Merge given child attribute definition into given inherited attribute.
3197 : *
3198 : * Input arguments:
3199 : * 'inh_columns' is the list of inherited ColumnDefs.
3200 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3201 : * 'newcol_attno' is the attribute number in child table's schema definition
3202 : * 'newdef' is the column/attribute definition from the child table.
3203 : *
3204 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3205 : * ColumnDef remains unchanged.
3206 : *
3207 : * Notes:
3208 : * - The attribute is merged according to the rules laid out in the prologue
3209 : * of MergeAttributes().
3210 : * - If matching inherited attribute exists but the child attribute can not be
3211 : * merged into it, the function throws respective errors.
3212 : * - A partition can not have its own column definitions. Hence this function
3213 : * is applicable only to a regular inheritance child.
3214 : */
3215 : static void
3216 354 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3217 : {
3218 354 : char *attributeName = newdef->colname;
3219 : ColumnDef *inhdef;
3220 : Oid inhtypeid,
3221 : newtypeid;
3222 : int32 inhtypmod,
3223 : newtypmod;
3224 : Oid inhcollid,
3225 : newcollid;
3226 :
3227 354 : if (exist_attno == newcol_attno)
3228 320 : ereport(NOTICE,
3229 : (errmsg("merging column \"%s\" with inherited definition",
3230 : attributeName)));
3231 : else
3232 34 : ereport(NOTICE,
3233 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3234 : errdetail("User-specified column moved to the position of the inherited column.")));
3235 :
3236 354 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3237 :
3238 : /*
3239 : * Must have the same type and typmod
3240 : */
3241 354 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3242 354 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3243 354 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3244 12 : ereport(ERROR,
3245 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3246 : errmsg("column \"%s\" has a type conflict",
3247 : attributeName),
3248 : errdetail("%s versus %s",
3249 : format_type_with_typemod(inhtypeid, inhtypmod),
3250 : format_type_with_typemod(newtypeid, newtypmod))));
3251 :
3252 : /*
3253 : * Must have the same collation
3254 : */
3255 342 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3256 342 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3257 342 : if (inhcollid != newcollid)
3258 6 : ereport(ERROR,
3259 : (errcode(ERRCODE_COLLATION_MISMATCH),
3260 : errmsg("column \"%s\" has a collation conflict",
3261 : attributeName),
3262 : errdetail("\"%s\" versus \"%s\"",
3263 : get_collation_name(inhcollid),
3264 : get_collation_name(newcollid))));
3265 :
3266 : /*
3267 : * Identity is never inherited by a regular inheritance child. Pick
3268 : * child's identity definition if there's one.
3269 : */
3270 336 : inhdef->identity = newdef->identity;
3271 :
3272 : /*
3273 : * Copy storage parameter
3274 : */
3275 336 : if (inhdef->storage == 0)
3276 0 : inhdef->storage = newdef->storage;
3277 336 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3278 6 : ereport(ERROR,
3279 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3280 : errmsg("column \"%s\" has a storage parameter conflict",
3281 : attributeName),
3282 : errdetail("%s versus %s",
3283 : storage_name(inhdef->storage),
3284 : storage_name(newdef->storage))));
3285 :
3286 : /*
3287 : * Copy compression parameter
3288 : */
3289 330 : if (inhdef->compression == NULL)
3290 324 : inhdef->compression = newdef->compression;
3291 6 : else if (newdef->compression != NULL)
3292 : {
3293 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3294 6 : ereport(ERROR,
3295 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3296 : errmsg("column \"%s\" has a compression method conflict",
3297 : attributeName),
3298 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3299 : }
3300 :
3301 : /*
3302 : * Merge of not-null constraints = OR 'em together
3303 : */
3304 324 : inhdef->is_not_null |= newdef->is_not_null;
3305 :
3306 : /*
3307 : * Check for conflicts related to generated columns.
3308 : *
3309 : * If the parent column is generated, the child column will be made a
3310 : * generated column if it isn't already. If it is a generated column,
3311 : * we'll take its generation expression in preference to the parent's. We
3312 : * must check that the child column doesn't specify a default value or
3313 : * identity, which matches the rules for a single column in
3314 : * parse_utilcmd.c.
3315 : *
3316 : * Conversely, if the parent column is not generated, the child column
3317 : * can't be either. (We used to allow that, but it results in being able
3318 : * to override the generation expression via UPDATEs through the parent.)
3319 : */
3320 324 : if (inhdef->generated)
3321 : {
3322 62 : if (newdef->raw_default && !newdef->generated)
3323 12 : ereport(ERROR,
3324 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3325 : errmsg("column \"%s\" inherits from generated column but specifies default",
3326 : inhdef->colname)));
3327 50 : if (newdef->identity)
3328 12 : ereport(ERROR,
3329 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3330 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3331 : inhdef->colname)));
3332 : }
3333 : else
3334 : {
3335 262 : if (newdef->generated)
3336 12 : ereport(ERROR,
3337 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3338 : errmsg("child column \"%s\" specifies generation expression",
3339 : inhdef->colname),
3340 : errhint("A child table column cannot be generated unless its parent column is.")));
3341 : }
3342 :
3343 288 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3344 12 : ereport(ERROR,
3345 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3346 : errmsg("column \"%s\" inherits from generated column of different kind",
3347 : inhdef->colname),
3348 : errdetail("Parent column is %s, child column is %s.",
3349 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3350 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3351 :
3352 : /*
3353 : * If new def has a default, override previous default
3354 : */
3355 276 : if (newdef->raw_default != NULL)
3356 : {
3357 30 : inhdef->raw_default = newdef->raw_default;
3358 30 : inhdef->cooked_default = newdef->cooked_default;
3359 : }
3360 :
3361 : /* Mark the column as locally defined */
3362 276 : inhdef->is_local = true;
3363 276 : }
3364 :
3365 : /*
3366 : * MergeInheritedAttribute
3367 : * Merge given parent attribute definition into specified attribute
3368 : * inherited from the previous parents.
3369 : *
3370 : * Input arguments:
3371 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3372 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3373 : * 'newdef' is the new parent column/attribute definition to be merged.
3374 : *
3375 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3376 : *
3377 : * Notes:
3378 : * - The attribute is merged according to the rules laid out in the prologue
3379 : * of MergeAttributes().
3380 : * - If matching inherited attribute exists but the new attribute can not be
3381 : * merged into it, the function throws respective errors.
3382 : * - A partition inherits from only a single parent. Hence this function is
3383 : * applicable only to a regular inheritance.
3384 : */
3385 : static ColumnDef *
3386 350 : MergeInheritedAttribute(List *inh_columns,
3387 : int exist_attno,
3388 : const ColumnDef *newdef)
3389 : {
3390 350 : char *attributeName = newdef->colname;
3391 : ColumnDef *prevdef;
3392 : Oid prevtypeid,
3393 : newtypeid;
3394 : int32 prevtypmod,
3395 : newtypmod;
3396 : Oid prevcollid,
3397 : newcollid;
3398 :
3399 350 : ereport(NOTICE,
3400 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3401 : attributeName)));
3402 350 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3403 :
3404 : /*
3405 : * Must have the same type and typmod
3406 : */
3407 350 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3408 350 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3409 350 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3410 0 : ereport(ERROR,
3411 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3412 : errmsg("inherited column \"%s\" has a type conflict",
3413 : attributeName),
3414 : errdetail("%s versus %s",
3415 : format_type_with_typemod(prevtypeid, prevtypmod),
3416 : format_type_with_typemod(newtypeid, newtypmod))));
3417 :
3418 : /*
3419 : * Must have the same collation
3420 : */
3421 350 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3422 350 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3423 350 : if (prevcollid != newcollid)
3424 0 : ereport(ERROR,
3425 : (errcode(ERRCODE_COLLATION_MISMATCH),
3426 : errmsg("inherited column \"%s\" has a collation conflict",
3427 : attributeName),
3428 : errdetail("\"%s\" versus \"%s\"",
3429 : get_collation_name(prevcollid),
3430 : get_collation_name(newcollid))));
3431 :
3432 : /*
3433 : * Copy/check storage parameter
3434 : */
3435 350 : if (prevdef->storage == 0)
3436 0 : prevdef->storage = newdef->storage;
3437 350 : else if (prevdef->storage != newdef->storage)
3438 6 : ereport(ERROR,
3439 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3440 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3441 : attributeName),
3442 : errdetail("%s versus %s",
3443 : storage_name(prevdef->storage),
3444 : storage_name(newdef->storage))));
3445 :
3446 : /*
3447 : * Copy/check compression parameter
3448 : */
3449 344 : if (prevdef->compression == NULL)
3450 332 : prevdef->compression = newdef->compression;
3451 12 : else if (newdef->compression != NULL)
3452 : {
3453 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3454 6 : ereport(ERROR,
3455 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3456 : errmsg("column \"%s\" has a compression method conflict",
3457 : attributeName),
3458 : errdetail("%s versus %s",
3459 : prevdef->compression, newdef->compression)));
3460 : }
3461 :
3462 : /*
3463 : * Check for GENERATED conflicts
3464 : */
3465 338 : if (prevdef->generated != newdef->generated)
3466 24 : ereport(ERROR,
3467 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3468 : errmsg("inherited column \"%s\" has a generation conflict",
3469 : attributeName)));
3470 :
3471 : /*
3472 : * Default and other constraints are handled by the caller.
3473 : */
3474 :
3475 314 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3476 : &prevdef->inhcount))
3477 0 : ereport(ERROR,
3478 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3479 : errmsg("too many inheritance parents"));
3480 :
3481 314 : return prevdef;
3482 : }
3483 :
3484 : /*
3485 : * StoreCatalogInheritance
3486 : * Updates the system catalogs with proper inheritance information.
3487 : *
3488 : * supers is a list of the OIDs of the new relation's direct ancestors.
3489 : */
3490 : static void
3491 58764 : StoreCatalogInheritance(Oid relationId, List *supers,
3492 : bool child_is_partition)
3493 : {
3494 : Relation relation;
3495 : int32 seqNumber;
3496 : ListCell *entry;
3497 :
3498 : /*
3499 : * sanity checks
3500 : */
3501 : Assert(OidIsValid(relationId));
3502 :
3503 58764 : if (supers == NIL)
3504 49438 : return;
3505 :
3506 : /*
3507 : * Store INHERITS information in pg_inherits using direct ancestors only.
3508 : * Also enter dependencies on the direct ancestors, and make sure they are
3509 : * marked with relhassubclass = true.
3510 : *
3511 : * (Once upon a time, both direct and indirect ancestors were found here
3512 : * and then entered into pg_ipl. Since that catalog doesn't exist
3513 : * anymore, there's no need to look for indirect ancestors.)
3514 : */
3515 9326 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3516 :
3517 9326 : seqNumber = 1;
3518 18966 : foreach(entry, supers)
3519 : {
3520 9640 : Oid parentOid = lfirst_oid(entry);
3521 :
3522 9640 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3523 : child_is_partition);
3524 9640 : seqNumber++;
3525 : }
3526 :
3527 9326 : table_close(relation, RowExclusiveLock);
3528 : }
3529 :
3530 : /*
3531 : * Make catalog entries showing relationId as being an inheritance child
3532 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3533 : */
3534 : static void
3535 11972 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3536 : int32 seqNumber, Relation inhRelation,
3537 : bool child_is_partition)
3538 : {
3539 : ObjectAddress childobject,
3540 : parentobject;
3541 :
3542 : /* store the pg_inherits row */
3543 11972 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3544 :
3545 : /*
3546 : * Store a dependency too
3547 : */
3548 11972 : parentobject.classId = RelationRelationId;
3549 11972 : parentobject.objectId = parentOid;
3550 11972 : parentobject.objectSubId = 0;
3551 11972 : childobject.classId = RelationRelationId;
3552 11972 : childobject.objectId = relationId;
3553 11972 : childobject.objectSubId = 0;
3554 :
3555 11972 : recordDependencyOn(&childobject, &parentobject,
3556 : child_dependency_type(child_is_partition));
3557 :
3558 : /*
3559 : * Post creation hook of this inheritance. Since object_access_hook
3560 : * doesn't take multiple object identifiers, we relay oid of parent
3561 : * relation using auxiliary_id argument.
3562 : */
3563 11972 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3564 : relationId, 0,
3565 : parentOid, false);
3566 :
3567 : /*
3568 : * Mark the parent as having subclasses.
3569 : */
3570 11972 : SetRelationHasSubclass(parentOid, true);
3571 11972 : }
3572 :
3573 : /*
3574 : * Look for an existing column entry with the given name.
3575 : *
3576 : * Returns the index (starting with 1) if attribute already exists in columns,
3577 : * 0 if it doesn't.
3578 : */
3579 : static int
3580 21416 : findAttrByName(const char *attributeName, const List *columns)
3581 : {
3582 : ListCell *lc;
3583 21416 : int i = 1;
3584 :
3585 38328 : foreach(lc, columns)
3586 : {
3587 17616 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3588 704 : return i;
3589 :
3590 16912 : i++;
3591 : }
3592 20712 : return 0;
3593 : }
3594 :
3595 :
3596 : /*
3597 : * SetRelationHasSubclass
3598 : * Set the value of the relation's relhassubclass field in pg_class.
3599 : *
3600 : * It's always safe to set this field to true, because all SQL commands are
3601 : * ready to see true and then find no children. On the other hand, commands
3602 : * generally assume zero children if this is false.
3603 : *
3604 : * Caller must hold any self-exclusive lock until end of transaction. If the
3605 : * new value is false, caller must have acquired that lock before reading the
3606 : * evidence that justified the false value. That way, it properly waits if
3607 : * another backend is simultaneously concluding no need to change the tuple
3608 : * (new and old values are true).
3609 : *
3610 : * NOTE: an important side-effect of this operation is that an SI invalidation
3611 : * message is sent out to all backends --- including me --- causing plans
3612 : * referencing the relation to be rebuilt with the new list of children.
3613 : * This must happen even if we find that no change is needed in the pg_class
3614 : * row.
3615 : */
3616 : void
3617 14976 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3618 : {
3619 : Relation relationRelation;
3620 : HeapTuple tuple;
3621 : Form_pg_class classtuple;
3622 :
3623 : Assert(CheckRelationOidLockedByMe(relationId,
3624 : ShareUpdateExclusiveLock, false) ||
3625 : CheckRelationOidLockedByMe(relationId,
3626 : ShareRowExclusiveLock, true));
3627 :
3628 : /*
3629 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3630 : */
3631 14976 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3632 14976 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3633 14976 : if (!HeapTupleIsValid(tuple))
3634 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3635 14976 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3636 :
3637 14976 : if (classtuple->relhassubclass != relhassubclass)
3638 : {
3639 7560 : classtuple->relhassubclass = relhassubclass;
3640 7560 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3641 : }
3642 : else
3643 : {
3644 : /* no need to change tuple, but force relcache rebuild anyway */
3645 7416 : CacheInvalidateRelcacheByTuple(tuple);
3646 : }
3647 :
3648 14976 : heap_freetuple(tuple);
3649 14976 : table_close(relationRelation, RowExclusiveLock);
3650 14976 : }
3651 :
3652 : /*
3653 : * CheckRelationTableSpaceMove
3654 : * Check if relation can be moved to new tablespace.
3655 : *
3656 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3657 : *
3658 : * Returns true if the relation can be moved to the new tablespace; raises
3659 : * an error if it is not possible to do the move; returns false if the move
3660 : * would have no effect.
3661 : */
3662 : bool
3663 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3664 : {
3665 : Oid oldTableSpaceId;
3666 :
3667 : /*
3668 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3669 : * stored as 0.
3670 : */
3671 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3672 226 : if (newTableSpaceId == oldTableSpaceId ||
3673 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3674 10 : return false;
3675 :
3676 : /*
3677 : * We cannot support moving mapped relations into different tablespaces.
3678 : * (In particular this eliminates all shared catalogs.)
3679 : */
3680 216 : if (RelationIsMapped(rel))
3681 0 : ereport(ERROR,
3682 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3683 : errmsg("cannot move system relation \"%s\"",
3684 : RelationGetRelationName(rel))));
3685 :
3686 : /* Cannot move a non-shared relation into pg_global */
3687 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3688 12 : ereport(ERROR,
3689 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3690 : errmsg("only shared relations can be placed in pg_global tablespace")));
3691 :
3692 : /*
3693 : * Do not allow moving temp tables of other backends ... their local
3694 : * buffer manager is not going to cope.
3695 : */
3696 204 : if (RELATION_IS_OTHER_TEMP(rel))
3697 0 : ereport(ERROR,
3698 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3699 : errmsg("cannot move temporary tables of other sessions")));
3700 :
3701 204 : return true;
3702 : }
3703 :
3704 : /*
3705 : * SetRelationTableSpace
3706 : * Set new reltablespace and relfilenumber in pg_class entry.
3707 : *
3708 : * newTableSpaceId is the new tablespace for the relation, and
3709 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3710 : * InvalidRelFileNumber, this field is not updated.
3711 : *
3712 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3713 : *
3714 : * The caller of this routine had better check if a relation can be
3715 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3716 : * first, and is responsible for making the change visible with
3717 : * CommandCounterIncrement().
3718 : */
3719 : void
3720 204 : SetRelationTableSpace(Relation rel,
3721 : Oid newTableSpaceId,
3722 : RelFileNumber newRelFilenumber)
3723 : {
3724 : Relation pg_class;
3725 : HeapTuple tuple;
3726 : ItemPointerData otid;
3727 : Form_pg_class rd_rel;
3728 204 : Oid reloid = RelationGetRelid(rel);
3729 :
3730 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3731 :
3732 : /* Get a modifiable copy of the relation's pg_class row. */
3733 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3734 :
3735 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3736 204 : if (!HeapTupleIsValid(tuple))
3737 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3738 204 : otid = tuple->t_self;
3739 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3740 :
3741 : /* Update the pg_class row. */
3742 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3743 204 : InvalidOid : newTableSpaceId;
3744 204 : if (RelFileNumberIsValid(newRelFilenumber))
3745 160 : rd_rel->relfilenode = newRelFilenumber;
3746 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3747 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3748 :
3749 : /*
3750 : * Record dependency on tablespace. This is only required for relations
3751 : * that have no physical storage.
3752 : */
3753 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3754 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3755 : rd_rel->reltablespace);
3756 :
3757 204 : heap_freetuple(tuple);
3758 204 : table_close(pg_class, RowExclusiveLock);
3759 204 : }
3760 :
3761 : /*
3762 : * renameatt_check - basic sanity checks before attribute rename
3763 : */
3764 : static void
3765 1014 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3766 : {
3767 1014 : char relkind = classform->relkind;
3768 :
3769 1014 : if (classform->reloftype && !recursing)
3770 6 : ereport(ERROR,
3771 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3772 : errmsg("cannot rename column of typed table")));
3773 :
3774 : /*
3775 : * Renaming the columns of sequences or toast tables doesn't actually
3776 : * break anything from the system's point of view, since internal
3777 : * references are by attnum. But it doesn't seem right to allow users to
3778 : * change names that are hardcoded into the system, hence the following
3779 : * restriction.
3780 : */
3781 1008 : if (relkind != RELKIND_RELATION &&
3782 84 : relkind != RELKIND_VIEW &&
3783 84 : relkind != RELKIND_MATVIEW &&
3784 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3785 36 : relkind != RELKIND_INDEX &&
3786 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3787 0 : relkind != RELKIND_FOREIGN_TABLE &&
3788 : relkind != RELKIND_PARTITIONED_TABLE)
3789 0 : ereport(ERROR,
3790 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3791 : errmsg("cannot rename columns of relation \"%s\"",
3792 : NameStr(classform->relname)),
3793 : errdetail_relkind_not_supported(relkind)));
3794 :
3795 : /*
3796 : * permissions checking. only the owner of a class can change its schema.
3797 : */
3798 1008 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3799 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3800 0 : NameStr(classform->relname));
3801 1008 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3802 2 : ereport(ERROR,
3803 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3804 : errmsg("permission denied: \"%s\" is a system catalog",
3805 : NameStr(classform->relname))));
3806 1006 : }
3807 :
3808 : /*
3809 : * renameatt_internal - workhorse for renameatt
3810 : *
3811 : * Return value is the attribute number in the 'myrelid' relation.
3812 : */
3813 : static AttrNumber
3814 552 : renameatt_internal(Oid myrelid,
3815 : const char *oldattname,
3816 : const char *newattname,
3817 : bool recurse,
3818 : bool recursing,
3819 : int expected_parents,
3820 : DropBehavior behavior)
3821 : {
3822 : Relation targetrelation;
3823 : Relation attrelation;
3824 : HeapTuple atttup;
3825 : Form_pg_attribute attform;
3826 : AttrNumber attnum;
3827 :
3828 : /*
3829 : * Grab an exclusive lock on the target table, which we will NOT release
3830 : * until end of transaction.
3831 : */
3832 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3833 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3834 :
3835 : /*
3836 : * if the 'recurse' flag is set then we are supposed to rename this
3837 : * attribute in all classes that inherit from 'relname' (as well as in
3838 : * 'relname').
3839 : *
3840 : * any permissions or problems with duplicate attributes will cause the
3841 : * whole transaction to abort, which is what we want -- all or nothing.
3842 : */
3843 552 : if (recurse)
3844 : {
3845 : List *child_oids,
3846 : *child_numparents;
3847 : ListCell *lo,
3848 : *li;
3849 :
3850 : /*
3851 : * we need the number of parents for each child so that the recursive
3852 : * calls to renameatt() can determine whether there are any parents
3853 : * outside the inheritance hierarchy being processed.
3854 : */
3855 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3856 : &child_numparents);
3857 :
3858 : /*
3859 : * find_all_inheritors does the recursive search of the inheritance
3860 : * hierarchy, so all we have to do is process all of the relids in the
3861 : * list that it returns.
3862 : */
3863 734 : forboth(lo, child_oids, li, child_numparents)
3864 : {
3865 516 : Oid childrelid = lfirst_oid(lo);
3866 516 : int numparents = lfirst_int(li);
3867 :
3868 516 : if (childrelid == myrelid)
3869 248 : continue;
3870 : /* note we need not recurse again */
3871 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3872 : }
3873 : }
3874 : else
3875 : {
3876 : /*
3877 : * If we are told not to recurse, there had better not be any child
3878 : * tables; else the rename would put them out of step.
3879 : *
3880 : * expected_parents will only be 0 if we are not already recursing.
3881 : */
3882 340 : if (expected_parents == 0 &&
3883 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3884 12 : ereport(ERROR,
3885 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3886 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3887 : oldattname)));
3888 : }
3889 :
3890 : /* rename attributes in typed tables of composite type */
3891 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3892 : {
3893 : List *child_oids;
3894 : ListCell *lo;
3895 :
3896 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3897 24 : RelationGetRelationName(targetrelation),
3898 : behavior);
3899 :
3900 24 : foreach(lo, child_oids)
3901 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3902 : }
3903 :
3904 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3905 :
3906 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3907 504 : if (!HeapTupleIsValid(atttup))
3908 24 : ereport(ERROR,
3909 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3910 : errmsg("column \"%s\" does not exist",
3911 : oldattname)));
3912 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3913 :
3914 480 : attnum = attform->attnum;
3915 480 : if (attnum <= 0)
3916 0 : ereport(ERROR,
3917 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3918 : errmsg("cannot rename system column \"%s\"",
3919 : oldattname)));
3920 :
3921 : /*
3922 : * if the attribute is inherited, forbid the renaming. if this is a
3923 : * top-level call to renameatt(), then expected_parents will be 0, so the
3924 : * effect of this code will be to prohibit the renaming if the attribute
3925 : * is inherited at all. if this is a recursive call to renameatt(),
3926 : * expected_parents will be the number of parents the current relation has
3927 : * within the inheritance hierarchy being processed, so we'll prohibit the
3928 : * renaming only if there are additional parents from elsewhere.
3929 : */
3930 480 : if (attform->attinhcount > expected_parents)
3931 30 : ereport(ERROR,
3932 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3933 : errmsg("cannot rename inherited column \"%s\"",
3934 : oldattname)));
3935 :
3936 : /* new name should not already exist */
3937 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3938 :
3939 : /* apply the update */
3940 438 : namestrcpy(&(attform->attname), newattname);
3941 :
3942 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3943 :
3944 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3945 :
3946 438 : heap_freetuple(atttup);
3947 :
3948 438 : table_close(attrelation, RowExclusiveLock);
3949 :
3950 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3951 :
3952 438 : return attnum;
3953 : }
3954 :
3955 : /*
3956 : * Perform permissions and integrity checks before acquiring a relation lock.
3957 : */
3958 : static void
3959 416 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3960 : void *arg)
3961 : {
3962 : HeapTuple tuple;
3963 : Form_pg_class form;
3964 :
3965 416 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3966 416 : if (!HeapTupleIsValid(tuple))
3967 38 : return; /* concurrently dropped */
3968 378 : form = (Form_pg_class) GETSTRUCT(tuple);
3969 378 : renameatt_check(relid, form, false);
3970 370 : ReleaseSysCache(tuple);
3971 : }
3972 :
3973 : /*
3974 : * renameatt - changes the name of an attribute in a relation
3975 : *
3976 : * The returned ObjectAddress is that of the renamed column.
3977 : */
3978 : ObjectAddress
3979 316 : renameatt(RenameStmt *stmt)
3980 : {
3981 : Oid relid;
3982 : AttrNumber attnum;
3983 : ObjectAddress address;
3984 :
3985 : /* lock level taken here should match renameatt_internal */
3986 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3987 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
3988 : RangeVarCallbackForRenameAttribute,
3989 : NULL);
3990 :
3991 302 : if (!OidIsValid(relid))
3992 : {
3993 24 : ereport(NOTICE,
3994 : (errmsg("relation \"%s\" does not exist, skipping",
3995 : stmt->relation->relname)));
3996 24 : return InvalidObjectAddress;
3997 : }
3998 :
3999 : attnum =
4000 278 : renameatt_internal(relid,
4001 278 : stmt->subname, /* old att name */
4002 278 : stmt->newname, /* new att name */
4003 278 : stmt->relation->inh, /* recursive? */
4004 : false, /* recursing? */
4005 : 0, /* expected inhcount */
4006 : stmt->behavior);
4007 :
4008 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4009 :
4010 194 : return address;
4011 : }
4012 :
4013 : /*
4014 : * same logic as renameatt_internal
4015 : */
4016 : static ObjectAddress
4017 90 : rename_constraint_internal(Oid myrelid,
4018 : Oid mytypid,
4019 : const char *oldconname,
4020 : const char *newconname,
4021 : bool recurse,
4022 : bool recursing,
4023 : int expected_parents)
4024 : {
4025 90 : Relation targetrelation = NULL;
4026 : Oid constraintOid;
4027 : HeapTuple tuple;
4028 : Form_pg_constraint con;
4029 : ObjectAddress address;
4030 :
4031 : Assert(!myrelid || !mytypid);
4032 :
4033 90 : if (mytypid)
4034 : {
4035 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4036 : }
4037 : else
4038 : {
4039 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4040 :
4041 : /*
4042 : * don't tell it whether we're recursing; we allow changing typed
4043 : * tables here
4044 : */
4045 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4046 :
4047 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4048 : }
4049 :
4050 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4051 90 : if (!HeapTupleIsValid(tuple))
4052 0 : elog(ERROR, "cache lookup failed for constraint %u",
4053 : constraintOid);
4054 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4055 :
4056 90 : if (myrelid &&
4057 84 : (con->contype == CONSTRAINT_CHECK ||
4058 24 : con->contype == CONSTRAINT_NOTNULL) &&
4059 66 : !con->connoinherit)
4060 : {
4061 54 : if (recurse)
4062 : {
4063 : List *child_oids,
4064 : *child_numparents;
4065 : ListCell *lo,
4066 : *li;
4067 :
4068 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4069 : &child_numparents);
4070 :
4071 84 : forboth(lo, child_oids, li, child_numparents)
4072 : {
4073 48 : Oid childrelid = lfirst_oid(lo);
4074 48 : int numparents = lfirst_int(li);
4075 :
4076 48 : if (childrelid == myrelid)
4077 36 : continue;
4078 :
4079 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4080 : }
4081 : }
4082 : else
4083 : {
4084 24 : if (expected_parents == 0 &&
4085 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4086 6 : ereport(ERROR,
4087 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4088 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4089 : oldconname)));
4090 : }
4091 :
4092 48 : if (con->coninhcount > expected_parents)
4093 6 : ereport(ERROR,
4094 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4095 : errmsg("cannot rename inherited constraint \"%s\"",
4096 : oldconname)));
4097 : }
4098 :
4099 78 : if (con->conindid
4100 18 : && (con->contype == CONSTRAINT_PRIMARY
4101 6 : || con->contype == CONSTRAINT_UNIQUE
4102 0 : || con->contype == CONSTRAINT_EXCLUSION))
4103 : /* rename the index; this renames the constraint as well */
4104 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4105 : else
4106 60 : RenameConstraintById(constraintOid, newconname);
4107 :
4108 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4109 :
4110 78 : ReleaseSysCache(tuple);
4111 :
4112 78 : if (targetrelation)
4113 : {
4114 : /*
4115 : * Invalidate relcache so as others can see the new constraint name.
4116 : */
4117 72 : CacheInvalidateRelcache(targetrelation);
4118 :
4119 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4120 : }
4121 :
4122 78 : return address;
4123 : }
4124 :
4125 : ObjectAddress
4126 84 : RenameConstraint(RenameStmt *stmt)
4127 : {
4128 84 : Oid relid = InvalidOid;
4129 84 : Oid typid = InvalidOid;
4130 :
4131 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4132 : {
4133 : Relation rel;
4134 : HeapTuple tup;
4135 :
4136 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4137 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4138 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4139 6 : if (!HeapTupleIsValid(tup))
4140 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4141 6 : checkDomainOwner(tup);
4142 6 : ReleaseSysCache(tup);
4143 6 : table_close(rel, NoLock);
4144 : }
4145 : else
4146 : {
4147 : /* lock level taken here should match rename_constraint_internal */
4148 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4149 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4150 : RangeVarCallbackForRenameAttribute,
4151 : NULL);
4152 78 : if (!OidIsValid(relid))
4153 : {
4154 6 : ereport(NOTICE,
4155 : (errmsg("relation \"%s\" does not exist, skipping",
4156 : stmt->relation->relname)));
4157 6 : return InvalidObjectAddress;
4158 : }
4159 : }
4160 :
4161 : return
4162 78 : rename_constraint_internal(relid, typid,
4163 78 : stmt->subname,
4164 78 : stmt->newname,
4165 150 : (stmt->relation &&
4166 72 : stmt->relation->inh), /* recursive? */
4167 : false, /* recursing? */
4168 : 0 /* expected inhcount */ );
4169 : }
4170 :
4171 : /*
4172 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4173 : * RENAME
4174 : */
4175 : ObjectAddress
4176 510 : RenameRelation(RenameStmt *stmt)
4177 : {
4178 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4179 : Oid relid;
4180 : ObjectAddress address;
4181 :
4182 : /*
4183 : * Grab an exclusive lock on the target table, index, sequence, view,
4184 : * materialized view, or foreign table, which we will NOT release until
4185 : * end of transaction.
4186 : *
4187 : * Lock level used here should match RenameRelationInternal, to avoid lock
4188 : * escalation. However, because ALTER INDEX can be used with any relation
4189 : * type, we mustn't believe without verification.
4190 : */
4191 : for (;;)
4192 12 : {
4193 : LOCKMODE lockmode;
4194 : char relkind;
4195 : bool obj_is_index;
4196 :
4197 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4198 :
4199 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4200 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4201 : RangeVarCallbackForAlterRelation,
4202 : stmt);
4203 :
4204 472 : if (!OidIsValid(relid))
4205 : {
4206 18 : ereport(NOTICE,
4207 : (errmsg("relation \"%s\" does not exist, skipping",
4208 : stmt->relation->relname)));
4209 18 : return InvalidObjectAddress;
4210 : }
4211 :
4212 : /*
4213 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4214 : * to rename a table), but we might've used the wrong lock level. If
4215 : * that happens, retry with the correct lock level. We don't bother
4216 : * if we already acquired AccessExclusiveLock with an index, however.
4217 : */
4218 454 : relkind = get_rel_relkind(relid);
4219 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4220 : relkind == RELKIND_PARTITIONED_INDEX);
4221 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4222 : break;
4223 :
4224 12 : UnlockRelationOid(relid, lockmode);
4225 12 : is_index_stmt = obj_is_index;
4226 : }
4227 :
4228 : /* Do the work */
4229 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4230 :
4231 430 : ObjectAddressSet(address, RelationRelationId, relid);
4232 :
4233 430 : return address;
4234 : }
4235 :
4236 : /*
4237 : * RenameRelationInternal - change the name of a relation
4238 : */
4239 : void
4240 1662 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4241 : {
4242 : Relation targetrelation;
4243 : Relation relrelation; /* for RELATION relation */
4244 : ItemPointerData otid;
4245 : HeapTuple reltup;
4246 : Form_pg_class relform;
4247 : Oid namespaceId;
4248 :
4249 : /*
4250 : * Grab a lock on the target relation, which we will NOT release until end
4251 : * of transaction. We need at least a self-exclusive lock so that
4252 : * concurrent DDL doesn't overwrite the rename if they start updating
4253 : * while still seeing the old version. The lock also guards against
4254 : * triggering relcache reloads in concurrent sessions, which might not
4255 : * handle this information changing under them. For indexes, we can use a
4256 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4257 : * specially.
4258 : */
4259 1662 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4260 1662 : namespaceId = RelationGetNamespace(targetrelation);
4261 :
4262 : /*
4263 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4264 : */
4265 1662 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4266 :
4267 1662 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4268 1662 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4269 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4270 1662 : otid = reltup->t_self;
4271 1662 : relform = (Form_pg_class) GETSTRUCT(reltup);
4272 :
4273 1662 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4274 12 : ereport(ERROR,
4275 : (errcode(ERRCODE_DUPLICATE_TABLE),
4276 : errmsg("relation \"%s\" already exists",
4277 : newrelname)));
4278 :
4279 : /*
4280 : * RenameRelation is careful not to believe the caller's idea of the
4281 : * relation kind being handled. We don't have to worry about this, but
4282 : * let's not be totally oblivious to it. We can process an index as
4283 : * not-an-index, but not the other way around.
4284 : */
4285 : Assert(!is_index ||
4286 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4287 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4288 :
4289 : /*
4290 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4291 : * because it's a copy...)
4292 : */
4293 1650 : namestrcpy(&(relform->relname), newrelname);
4294 :
4295 1650 : CatalogTupleUpdate(relrelation, &otid, reltup);
4296 1650 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4297 :
4298 1650 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4299 : InvalidOid, is_internal);
4300 :
4301 1650 : heap_freetuple(reltup);
4302 1650 : table_close(relrelation, RowExclusiveLock);
4303 :
4304 : /*
4305 : * Also rename the associated type, if any.
4306 : */
4307 1650 : if (OidIsValid(targetrelation->rd_rel->reltype))
4308 124 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4309 : newrelname, namespaceId);
4310 :
4311 : /*
4312 : * Also rename the associated constraint, if any.
4313 : */
4314 1650 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4315 866 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4316 : {
4317 802 : Oid constraintId = get_index_constraint(myrelid);
4318 :
4319 802 : if (OidIsValid(constraintId))
4320 36 : RenameConstraintById(constraintId, newrelname);
4321 : }
4322 :
4323 : /*
4324 : * Close rel, but keep lock!
4325 : */
4326 1650 : relation_close(targetrelation, NoLock);
4327 1650 : }
4328 :
4329 : /*
4330 : * ResetRelRewrite - reset relrewrite
4331 : */
4332 : void
4333 590 : ResetRelRewrite(Oid myrelid)
4334 : {
4335 : Relation relrelation; /* for RELATION relation */
4336 : HeapTuple reltup;
4337 : Form_pg_class relform;
4338 :
4339 : /*
4340 : * Find relation's pg_class tuple.
4341 : */
4342 590 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4343 :
4344 590 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4345 590 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4346 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4347 590 : relform = (Form_pg_class) GETSTRUCT(reltup);
4348 :
4349 : /*
4350 : * Update pg_class tuple.
4351 : */
4352 590 : relform->relrewrite = InvalidOid;
4353 :
4354 590 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4355 :
4356 590 : heap_freetuple(reltup);
4357 590 : table_close(relrelation, RowExclusiveLock);
4358 590 : }
4359 :
4360 : /*
4361 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4362 : * any open reference to the target table besides the one just acquired by
4363 : * the calling command; this implies there's an open cursor or active plan.
4364 : * We need this check because our lock doesn't protect us against stomping
4365 : * on our own foot, only other people's feet!
4366 : *
4367 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4368 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4369 : * possibly be relaxed to only error out for certain types of alterations.
4370 : * But the use-case for allowing any of these things is not obvious, so we
4371 : * won't work hard at it for now.
4372 : *
4373 : * We also reject these commands if there are any pending AFTER trigger events
4374 : * for the rel. This is certainly necessary for the rewriting variants of
4375 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4376 : * events would try to fetch the wrong tuples. It might be overly cautious
4377 : * in other cases, but again it seems better to err on the side of paranoia.
4378 : *
4379 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4380 : * we are worried about active indexscans on the index. The trigger-event
4381 : * check can be skipped, since we are doing no damage to the parent table.
4382 : *
4383 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4384 : */
4385 : void
4386 163552 : CheckTableNotInUse(Relation rel, const char *stmt)
4387 : {
4388 : int expected_refcnt;
4389 :
4390 163552 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4391 163552 : if (rel->rd_refcnt != expected_refcnt)
4392 42 : ereport(ERROR,
4393 : (errcode(ERRCODE_OBJECT_IN_USE),
4394 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4395 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4396 : stmt, RelationGetRelationName(rel))));
4397 :
4398 163510 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4399 265232 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4400 131578 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4401 18 : ereport(ERROR,
4402 : (errcode(ERRCODE_OBJECT_IN_USE),
4403 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4404 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4405 : stmt, RelationGetRelationName(rel))));
4406 163492 : }
4407 :
4408 : /*
4409 : * CheckAlterTableIsSafe
4410 : * Verify that it's safe to allow ALTER TABLE on this relation.
4411 : *
4412 : * This consists of CheckTableNotInUse() plus a check that the relation
4413 : * isn't another session's temp table. We must split out the temp-table
4414 : * check because there are callers of CheckTableNotInUse() that don't want
4415 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4416 : * an orphaned temp schema.) Compare truncate_check_activity().
4417 : */
4418 : static void
4419 57364 : CheckAlterTableIsSafe(Relation rel)
4420 : {
4421 : /*
4422 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4423 : * manager is not going to cope if we need to change the table's contents.
4424 : * Even if we don't, there may be optimizations that assume temp tables
4425 : * aren't subject to such interference.
4426 : */
4427 57364 : if (RELATION_IS_OTHER_TEMP(rel))
4428 0 : ereport(ERROR,
4429 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4430 : errmsg("cannot alter temporary tables of other sessions")));
4431 :
4432 : /*
4433 : * Also check for active uses of the relation in the current transaction,
4434 : * including open scans and pending AFTER trigger events.
4435 : */
4436 57364 : CheckTableNotInUse(rel, "ALTER TABLE");
4437 57328 : }
4438 :
4439 : /*
4440 : * AlterTableLookupRelation
4441 : * Look up, and lock, the OID for the relation named by an alter table
4442 : * statement.
4443 : */
4444 : Oid
4445 30188 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4446 : {
4447 60288 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4448 30188 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4449 : RangeVarCallbackForAlterRelation,
4450 : stmt);
4451 : }
4452 :
4453 : /*
4454 : * AlterTable
4455 : * Execute ALTER TABLE, which can be a list of subcommands
4456 : *
4457 : * ALTER TABLE is performed in three phases:
4458 : * 1. Examine subcommands and perform pre-transformation checking.
4459 : * 2. Validate and transform subcommands, and update system catalogs.
4460 : * 3. Scan table(s) to check new constraints, and optionally recopy
4461 : * the data into new table(s).
4462 : * Phase 3 is not performed unless one or more of the subcommands requires
4463 : * it. The intention of this design is to allow multiple independent
4464 : * updates of the table schema to be performed with only one pass over the
4465 : * data.
4466 : *
4467 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4468 : * each table to be affected (there may be multiple affected tables if the
4469 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4470 : * validation of the subcommands. Because earlier subcommands may change
4471 : * the catalog state seen by later commands, there are limits to what can
4472 : * be done in this phase. Generally, this phase acquires table locks,
4473 : * checks permissions and relkind, and recurses to find child tables.
4474 : *
4475 : * ATRewriteCatalogs performs phase 2 for each affected table.
4476 : * Certain subcommands need to be performed before others to avoid
4477 : * unnecessary conflicts; for example, DROP COLUMN should come before
4478 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4479 : * lists, one for each logical "pass" of phase 2.
4480 : *
4481 : * ATRewriteTables performs phase 3 for those tables that need it.
4482 : *
4483 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4484 : * since phase 1 already does it. However, for certain subcommand types
4485 : * it is only possible to determine how to recurse at phase 2 time; for
4486 : * those cases, phase 1 sets the cmd->recurse flag.
4487 : *
4488 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4489 : * the whole operation; we don't have to do anything special to clean up.
4490 : *
4491 : * The caller must lock the relation, with an appropriate lock level
4492 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4493 : * or higher. We pass the lock level down
4494 : * so that we can apply it recursively to inherited tables. Note that the
4495 : * lock level we want as we recurse might well be higher than required for
4496 : * that specific subcommand. So we pass down the overall lock requirement,
4497 : * rather than reassess it at lower levels.
4498 : *
4499 : * The caller also provides a "context" which is to be passed back to
4500 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4501 : * Some of the fields therein, such as the relid, are used here as well.
4502 : */
4503 : void
4504 29962 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4505 : AlterTableUtilityContext *context)
4506 : {
4507 : Relation rel;
4508 :
4509 : /* Caller is required to provide an adequate lock. */
4510 29962 : rel = relation_open(context->relid, NoLock);
4511 :
4512 29962 : CheckAlterTableIsSafe(rel);
4513 :
4514 29944 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4515 26404 : }
4516 :
4517 : /*
4518 : * AlterTableInternal
4519 : *
4520 : * ALTER TABLE with target specified by OID
4521 : *
4522 : * We do not reject if the relation is already open, because it's quite
4523 : * likely that one or more layers of caller have it open. That means it
4524 : * is unsafe to use this entry point for alterations that could break
4525 : * existing query plans. On the assumption it's not used for such, we
4526 : * don't have to reject pending AFTER triggers, either.
4527 : *
4528 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4529 : * used for any subcommand types that require parse transformation or
4530 : * could generate subcommands that have to be passed to ProcessUtility.
4531 : */
4532 : void
4533 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4534 : {
4535 : Relation rel;
4536 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4537 :
4538 278 : rel = relation_open(relid, lockmode);
4539 :
4540 278 : EventTriggerAlterTableRelid(relid);
4541 :
4542 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4543 278 : }
4544 :
4545 : /*
4546 : * AlterTableGetLockLevel
4547 : *
4548 : * Sets the overall lock level required for the supplied list of subcommands.
4549 : * Policy for doing this set according to needs of AlterTable(), see
4550 : * comments there for overall explanation.
4551 : *
4552 : * Function is called before and after parsing, so it must give same
4553 : * answer each time it is called. Some subcommands are transformed
4554 : * into other subcommand types, so the transform must never be made to a
4555 : * lower lock level than previously assigned. All transforms are noted below.
4556 : *
4557 : * Since this is called before we lock the table we cannot use table metadata
4558 : * to influence the type of lock we acquire.
4559 : *
4560 : * There should be no lockmodes hardcoded into the subcommand functions. All
4561 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4562 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4563 : * and does not travel through this section of code and cannot be combined with
4564 : * any of the subcommands given here.
4565 : *
4566 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4567 : * so any changes that might affect SELECTs running on standbys need to use
4568 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4569 : * have a solution for that also.
4570 : *
4571 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4572 : * that takes a lock less than AccessExclusiveLock can change object definitions
4573 : * while pg_dump is running. Be careful to check that the appropriate data is
4574 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4575 : * otherwise we might end up with an inconsistent dump that can't restore.
4576 : */
4577 : LOCKMODE
4578 30466 : AlterTableGetLockLevel(List *cmds)
4579 : {
4580 : /*
4581 : * This only works if we read catalog tables using MVCC snapshots.
4582 : */
4583 : ListCell *lcmd;
4584 30466 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4585 :
4586 62092 : foreach(lcmd, cmds)
4587 : {
4588 31626 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4589 31626 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4590 :
4591 31626 : switch (cmd->subtype)
4592 : {
4593 : /*
4594 : * These subcommands rewrite the heap, so require full locks.
4595 : */
4596 3542 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4597 : * to SELECT */
4598 : case AT_SetAccessMethod: /* must rewrite heap */
4599 : case AT_SetTableSpace: /* must rewrite heap */
4600 : case AT_AlterColumnType: /* must rewrite heap */
4601 3542 : cmd_lockmode = AccessExclusiveLock;
4602 3542 : break;
4603 :
4604 : /*
4605 : * These subcommands may require addition of toast tables. If
4606 : * we add a toast table to a table currently being scanned, we
4607 : * might miss data added to the new toast table by concurrent
4608 : * insert transactions.
4609 : */
4610 218 : case AT_SetStorage: /* may add toast tables, see
4611 : * ATRewriteCatalogs() */
4612 218 : cmd_lockmode = AccessExclusiveLock;
4613 218 : break;
4614 :
4615 : /*
4616 : * Removing constraints can affect SELECTs that have been
4617 : * optimized assuming the constraint holds true. See also
4618 : * CloneFkReferenced.
4619 : */
4620 1086 : case AT_DropConstraint: /* as DROP INDEX */
4621 : case AT_DropNotNull: /* may change some SQL plans */
4622 1086 : cmd_lockmode = AccessExclusiveLock;
4623 1086 : break;
4624 :
4625 : /*
4626 : * Subcommands that may be visible to concurrent SELECTs
4627 : */
4628 1750 : case AT_DropColumn: /* change visible to SELECT */
4629 : case AT_AddColumnToView: /* CREATE VIEW */
4630 : case AT_DropOids: /* used to equiv to DropColumn */
4631 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4632 : case AT_EnableReplicaRule: /* may change SELECT rules */
4633 : case AT_EnableRule: /* may change SELECT rules */
4634 : case AT_DisableRule: /* may change SELECT rules */
4635 1750 : cmd_lockmode = AccessExclusiveLock;
4636 1750 : break;
4637 :
4638 : /*
4639 : * Changing owner may remove implicit SELECT privileges
4640 : */
4641 1952 : case AT_ChangeOwner: /* change visible to SELECT */
4642 1952 : cmd_lockmode = AccessExclusiveLock;
4643 1952 : break;
4644 :
4645 : /*
4646 : * Changing foreign table options may affect optimization.
4647 : */
4648 254 : case AT_GenericOptions:
4649 : case AT_AlterColumnGenericOptions:
4650 254 : cmd_lockmode = AccessExclusiveLock;
4651 254 : break;
4652 :
4653 : /*
4654 : * These subcommands affect write operations only.
4655 : */
4656 340 : case AT_EnableTrig:
4657 : case AT_EnableAlwaysTrig:
4658 : case AT_EnableReplicaTrig:
4659 : case AT_EnableTrigAll:
4660 : case AT_EnableTrigUser:
4661 : case AT_DisableTrig:
4662 : case AT_DisableTrigAll:
4663 : case AT_DisableTrigUser:
4664 340 : cmd_lockmode = ShareRowExclusiveLock;
4665 340 : break;
4666 :
4667 : /*
4668 : * These subcommands affect write operations only. XXX
4669 : * Theoretically, these could be ShareRowExclusiveLock.
4670 : */
4671 2706 : case AT_ColumnDefault:
4672 : case AT_CookedColumnDefault:
4673 : case AT_AlterConstraint:
4674 : case AT_AddIndex: /* from ADD CONSTRAINT */
4675 : case AT_AddIndexConstraint:
4676 : case AT_ReplicaIdentity:
4677 : case AT_SetNotNull:
4678 : case AT_EnableRowSecurity:
4679 : case AT_DisableRowSecurity:
4680 : case AT_ForceRowSecurity:
4681 : case AT_NoForceRowSecurity:
4682 : case AT_AddIdentity:
4683 : case AT_DropIdentity:
4684 : case AT_SetIdentity:
4685 : case AT_SetExpression:
4686 : case AT_DropExpression:
4687 : case AT_SetCompression:
4688 2706 : cmd_lockmode = AccessExclusiveLock;
4689 2706 : break;
4690 :
4691 14158 : case AT_AddConstraint:
4692 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4693 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4694 14158 : if (IsA(cmd->def, Constraint))
4695 : {
4696 14158 : Constraint *con = (Constraint *) cmd->def;
4697 :
4698 14158 : switch (con->contype)
4699 : {
4700 10776 : case CONSTR_EXCLUSION:
4701 : case CONSTR_PRIMARY:
4702 : case CONSTR_UNIQUE:
4703 :
4704 : /*
4705 : * Cases essentially the same as CREATE INDEX. We
4706 : * could reduce the lock strength to ShareLock if
4707 : * we can work out how to allow concurrent catalog
4708 : * updates. XXX Might be set down to
4709 : * ShareRowExclusiveLock but requires further
4710 : * analysis.
4711 : */
4712 10776 : cmd_lockmode = AccessExclusiveLock;
4713 10776 : break;
4714 2504 : case CONSTR_FOREIGN:
4715 :
4716 : /*
4717 : * We add triggers to both tables when we add a
4718 : * Foreign Key, so the lock level must be at least
4719 : * as strong as CREATE TRIGGER.
4720 : */
4721 2504 : cmd_lockmode = ShareRowExclusiveLock;
4722 2504 : break;
4723 :
4724 878 : default:
4725 878 : cmd_lockmode = AccessExclusiveLock;
4726 : }
4727 0 : }
4728 14158 : break;
4729 :
4730 : /*
4731 : * These subcommands affect inheritance behaviour. Queries
4732 : * started before us will continue to see the old inheritance
4733 : * behaviour, while queries started after we commit will see
4734 : * new behaviour. No need to prevent reads or writes to the
4735 : * subtable while we hook it up though. Changing the TupDesc
4736 : * may be a problem, so keep highest lock.
4737 : */
4738 496 : case AT_AddInherit:
4739 : case AT_DropInherit:
4740 496 : cmd_lockmode = AccessExclusiveLock;
4741 496 : break;
4742 :
4743 : /*
4744 : * These subcommands affect implicit row type conversion. They
4745 : * have affects similar to CREATE/DROP CAST on queries. don't
4746 : * provide for invalidating parse trees as a result of such
4747 : * changes, so we keep these at AccessExclusiveLock.
4748 : */
4749 72 : case AT_AddOf:
4750 : case AT_DropOf:
4751 72 : cmd_lockmode = AccessExclusiveLock;
4752 72 : break;
4753 :
4754 : /*
4755 : * Only used by CREATE OR REPLACE VIEW which must conflict
4756 : * with an SELECTs currently using the view.
4757 : */
4758 194 : case AT_ReplaceRelOptions:
4759 194 : cmd_lockmode = AccessExclusiveLock;
4760 194 : break;
4761 :
4762 : /*
4763 : * These subcommands affect general strategies for performance
4764 : * and maintenance, though don't change the semantic results
4765 : * from normal data reads and writes. Delaying an ALTER TABLE
4766 : * behind currently active writes only delays the point where
4767 : * the new strategy begins to take effect, so there is no
4768 : * benefit in waiting. In this case the minimum restriction
4769 : * applies: we don't currently allow concurrent catalog
4770 : * updates.
4771 : */
4772 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4773 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4774 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4775 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4776 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4777 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4778 234 : break;
4779 :
4780 112 : case AT_SetLogged:
4781 : case AT_SetUnLogged:
4782 112 : cmd_lockmode = AccessExclusiveLock;
4783 112 : break;
4784 :
4785 412 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4786 412 : cmd_lockmode = ShareUpdateExclusiveLock;
4787 412 : break;
4788 :
4789 : /*
4790 : * Rel options are more complex than first appears. Options
4791 : * are set here for tables, views and indexes; for historical
4792 : * reasons these can all be used with ALTER TABLE, so we can't
4793 : * decide between them using the basic grammar.
4794 : */
4795 764 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4796 : * getTables() */
4797 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4798 : * getTables() */
4799 764 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4800 764 : break;
4801 :
4802 2740 : case AT_AttachPartition:
4803 2740 : cmd_lockmode = ShareUpdateExclusiveLock;
4804 2740 : break;
4805 :
4806 576 : case AT_DetachPartition:
4807 576 : if (((PartitionCmd *) cmd->def)->concurrent)
4808 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4809 : else
4810 412 : cmd_lockmode = AccessExclusiveLock;
4811 576 : break;
4812 :
4813 20 : case AT_DetachPartitionFinalize:
4814 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4815 20 : break;
4816 :
4817 0 : default: /* oops */
4818 0 : elog(ERROR, "unrecognized alter table type: %d",
4819 : (int) cmd->subtype);
4820 : break;
4821 : }
4822 :
4823 : /*
4824 : * Take the greatest lockmode from any subcommand
4825 : */
4826 31626 : if (cmd_lockmode > lockmode)
4827 26320 : lockmode = cmd_lockmode;
4828 : }
4829 :
4830 30466 : return lockmode;
4831 : }
4832 :
4833 : /*
4834 : * ATController provides top level control over the phases.
4835 : *
4836 : * parsetree is passed in to allow it to be passed to event triggers
4837 : * when requested.
4838 : */
4839 : static void
4840 30222 : ATController(AlterTableStmt *parsetree,
4841 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4842 : AlterTableUtilityContext *context)
4843 : {
4844 30222 : List *wqueue = NIL;
4845 : ListCell *lcmd;
4846 :
4847 : /* Phase 1: preliminary examination of commands, create work queue */
4848 61222 : foreach(lcmd, cmds)
4849 : {
4850 31376 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4851 :
4852 31376 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4853 : }
4854 :
4855 : /* Close the relation, but keep lock until commit */
4856 29846 : relation_close(rel, NoLock);
4857 :
4858 : /* Phase 2: update system catalogs */
4859 29846 : ATRewriteCatalogs(&wqueue, lockmode, context);
4860 :
4861 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4862 27084 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4863 26682 : }
4864 :
4865 : /*
4866 : * ATPrepCmd
4867 : *
4868 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4869 : * recursion and permission checks.
4870 : *
4871 : * Caller must have acquired appropriate lock type on relation already.
4872 : * This lock should be held until commit.
4873 : */
4874 : static void
4875 32804 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4876 : bool recurse, bool recursing, LOCKMODE lockmode,
4877 : AlterTableUtilityContext *context)
4878 : {
4879 : AlteredTableInfo *tab;
4880 32804 : AlterTablePass pass = AT_PASS_UNSET;
4881 :
4882 : /* Find or create work queue entry for this table */
4883 32804 : tab = ATGetQueueEntry(wqueue, rel);
4884 :
4885 : /*
4886 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4887 : * partitions that are pending detach.
4888 : */
4889 32804 : if (rel->rd_rel->relispartition &&
4890 2796 : cmd->subtype != AT_DetachPartitionFinalize &&
4891 1398 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4892 2 : ereport(ERROR,
4893 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4894 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4895 : RelationGetRelationName(rel)),
4896 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4897 :
4898 : /*
4899 : * Copy the original subcommand for each table, so we can scribble on it.
4900 : * This avoids conflicts when different child tables need to make
4901 : * different parse transformations (for example, the same column may have
4902 : * different column numbers in different children).
4903 : */
4904 32802 : cmd = copyObject(cmd);
4905 :
4906 : /*
4907 : * Do permissions and relkind checking, recursion to child tables if
4908 : * needed, and any additional phase-1 processing needed. (But beware of
4909 : * adding any processing that looks at table details that another
4910 : * subcommand could change. In some cases we reject multiple subcommands
4911 : * that could try to change the same state in contrary ways.)
4912 : */
4913 32802 : switch (cmd->subtype)
4914 : {
4915 2146 : case AT_AddColumn: /* ADD COLUMN */
4916 2146 : ATSimplePermissions(cmd->subtype, rel,
4917 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4918 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4919 2146 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4920 : lockmode, context);
4921 : /* Recursion occurs during execution phase */
4922 2134 : pass = AT_PASS_ADD_COL;
4923 2134 : break;
4924 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4925 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4926 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4927 : lockmode, context);
4928 : /* Recursion occurs during execution phase */
4929 24 : pass = AT_PASS_ADD_COL;
4930 24 : break;
4931 620 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4932 :
4933 : /*
4934 : * We allow defaults on views so that INSERT into a view can have
4935 : * default-ish behavior. This works because the rewriter
4936 : * substitutes default values into INSERTs before it expands
4937 : * rules.
4938 : */
4939 620 : ATSimplePermissions(cmd->subtype, rel,
4940 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4941 : ATT_FOREIGN_TABLE);
4942 620 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4943 : /* No command-specific prep needed */
4944 620 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4945 620 : break;
4946 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4947 : /* This is currently used only in CREATE TABLE */
4948 : /* (so the permission check really isn't necessary) */
4949 80 : ATSimplePermissions(cmd->subtype, rel,
4950 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4951 : /* This command never recurses */
4952 80 : pass = AT_PASS_ADD_OTHERCONSTR;
4953 80 : break;
4954 166 : case AT_AddIdentity:
4955 166 : ATSimplePermissions(cmd->subtype, rel,
4956 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4957 : ATT_FOREIGN_TABLE);
4958 : /* Set up recursion for phase 2; no other prep needed */
4959 166 : if (recurse)
4960 160 : cmd->recurse = true;
4961 166 : pass = AT_PASS_ADD_OTHERCONSTR;
4962 166 : break;
4963 62 : case AT_SetIdentity:
4964 62 : ATSimplePermissions(cmd->subtype, rel,
4965 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4966 : ATT_FOREIGN_TABLE);
4967 : /* Set up recursion for phase 2; no other prep needed */
4968 62 : if (recurse)
4969 56 : cmd->recurse = true;
4970 : /* This should run after AddIdentity, so do it in MISC pass */
4971 62 : pass = AT_PASS_MISC;
4972 62 : break;
4973 56 : case AT_DropIdentity:
4974 56 : ATSimplePermissions(cmd->subtype, rel,
4975 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4976 : ATT_FOREIGN_TABLE);
4977 : /* Set up recursion for phase 2; no other prep needed */
4978 56 : if (recurse)
4979 50 : cmd->recurse = true;
4980 56 : pass = AT_PASS_DROP;
4981 56 : break;
4982 268 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4983 268 : ATSimplePermissions(cmd->subtype, rel,
4984 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4985 : /* Set up recursion for phase 2; no other prep needed */
4986 262 : if (recurse)
4987 244 : cmd->recurse = true;
4988 262 : pass = AT_PASS_DROP;
4989 262 : break;
4990 390 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4991 390 : ATSimplePermissions(cmd->subtype, rel,
4992 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4993 : /* Set up recursion for phase 2; no other prep needed */
4994 384 : if (recurse)
4995 360 : cmd->recurse = true;
4996 384 : pass = AT_PASS_COL_ATTRS;
4997 384 : break;
4998 180 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4999 180 : ATSimplePermissions(cmd->subtype, rel,
5000 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5001 180 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5002 180 : pass = AT_PASS_SET_EXPRESSION;
5003 180 : break;
5004 86 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5005 86 : ATSimplePermissions(cmd->subtype, rel,
5006 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5007 86 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5008 86 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5009 62 : pass = AT_PASS_DROP;
5010 62 : break;
5011 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5012 164 : ATSimplePermissions(cmd->subtype, rel,
5013 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5014 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5015 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5016 : /* No command-specific prep needed */
5017 164 : pass = AT_PASS_MISC;
5018 164 : break;
5019 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5020 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5021 44 : ATSimplePermissions(cmd->subtype, rel,
5022 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5023 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5024 : /* This command never recurses */
5025 32 : pass = AT_PASS_MISC;
5026 32 : break;
5027 240 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5028 240 : ATSimplePermissions(cmd->subtype, rel,
5029 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5030 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5031 240 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5032 : /* No command-specific prep needed */
5033 240 : pass = AT_PASS_MISC;
5034 240 : break;
5035 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5036 68 : ATSimplePermissions(cmd->subtype, rel,
5037 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5038 : /* This command never recurses */
5039 : /* No command-specific prep needed */
5040 68 : pass = AT_PASS_MISC;
5041 68 : break;
5042 1656 : case AT_DropColumn: /* DROP COLUMN */
5043 1656 : ATSimplePermissions(cmd->subtype, rel,
5044 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5045 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5046 1650 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5047 : lockmode, context);
5048 : /* Recursion occurs during execution phase */
5049 1638 : pass = AT_PASS_DROP;
5050 1638 : break;
5051 0 : case AT_AddIndex: /* ADD INDEX */
5052 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5053 : /* This command never recurses */
5054 : /* No command-specific prep needed */
5055 0 : pass = AT_PASS_ADD_INDEX;
5056 0 : break;
5057 15110 : case AT_AddConstraint: /* ADD CONSTRAINT */
5058 15110 : ATSimplePermissions(cmd->subtype, rel,
5059 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5060 15110 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5061 15104 : if (recurse)
5062 : {
5063 : /* recurses at exec time; lock descendants and set flag */
5064 14724 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5065 14724 : cmd->recurse = true;
5066 : }
5067 15104 : pass = AT_PASS_ADD_CONSTR;
5068 15104 : break;
5069 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5070 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5071 : /* This command never recurses */
5072 : /* No command-specific prep needed */
5073 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5074 0 : break;
5075 780 : case AT_DropConstraint: /* DROP CONSTRAINT */
5076 780 : ATSimplePermissions(cmd->subtype, rel,
5077 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5078 780 : ATCheckPartitionsNotInUse(rel, lockmode);
5079 : /* Other recursion occurs during execution phase */
5080 : /* No command-specific prep needed except saving recurse flag */
5081 774 : if (recurse)
5082 738 : cmd->recurse = true;
5083 774 : pass = AT_PASS_DROP;
5084 774 : break;
5085 1282 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5086 1282 : ATSimplePermissions(cmd->subtype, rel,
5087 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5088 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5089 : /* See comments for ATPrepAlterColumnType */
5090 1282 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5091 : AT_PASS_UNSET, context);
5092 : Assert(cmd != NULL);
5093 : /* Performs own recursion */
5094 1276 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5095 : lockmode, context);
5096 1084 : pass = AT_PASS_ALTER_TYPE;
5097 1084 : break;
5098 172 : case AT_AlterColumnGenericOptions:
5099 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5100 : /* This command never recurses */
5101 : /* No command-specific prep needed */
5102 172 : pass = AT_PASS_MISC;
5103 172 : break;
5104 1928 : case AT_ChangeOwner: /* ALTER OWNER */
5105 : /* This command never recurses */
5106 : /* No command-specific prep needed */
5107 1928 : pass = AT_PASS_MISC;
5108 1928 : break;
5109 64 : case AT_ClusterOn: /* CLUSTER ON */
5110 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5111 64 : ATSimplePermissions(cmd->subtype, rel,
5112 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5113 : /* These commands never recurse */
5114 : /* No command-specific prep needed */
5115 64 : pass = AT_PASS_MISC;
5116 64 : break;
5117 112 : case AT_SetLogged: /* SET LOGGED */
5118 : case AT_SetUnLogged: /* SET UNLOGGED */
5119 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5120 100 : if (tab->chgPersistence)
5121 0 : ereport(ERROR,
5122 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5123 : errmsg("cannot change persistence setting twice")));
5124 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5125 88 : pass = AT_PASS_MISC;
5126 88 : break;
5127 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5128 6 : ATSimplePermissions(cmd->subtype, rel,
5129 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5130 6 : pass = AT_PASS_DROP;
5131 6 : break;
5132 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5133 128 : ATSimplePermissions(cmd->subtype, rel,
5134 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5135 :
5136 : /* check if another access method change was already requested */
5137 128 : if (tab->chgAccessMethod)
5138 18 : ereport(ERROR,
5139 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5140 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5141 :
5142 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5143 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5144 110 : break;
5145 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5146 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5147 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5148 : /* This command never recurses */
5149 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5150 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5151 158 : break;
5152 956 : case AT_SetRelOptions: /* SET (...) */
5153 : case AT_ResetRelOptions: /* RESET (...) */
5154 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5155 956 : ATSimplePermissions(cmd->subtype, rel,
5156 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5157 : ATT_MATVIEW | ATT_INDEX);
5158 : /* This command never recurses */
5159 : /* No command-specific prep needed */
5160 954 : pass = AT_PASS_MISC;
5161 954 : break;
5162 410 : case AT_AddInherit: /* INHERIT */
5163 410 : ATSimplePermissions(cmd->subtype, rel,
5164 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5165 : /* This command never recurses */
5166 410 : ATPrepAddInherit(rel);
5167 392 : pass = AT_PASS_MISC;
5168 392 : break;
5169 86 : case AT_DropInherit: /* NO INHERIT */
5170 86 : ATSimplePermissions(cmd->subtype, rel,
5171 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5172 : /* This command never recurses */
5173 : /* No command-specific prep needed */
5174 86 : pass = AT_PASS_MISC;
5175 86 : break;
5176 192 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5177 192 : ATSimplePermissions(cmd->subtype, rel,
5178 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5179 : /* Recursion occurs during execution phase */
5180 186 : if (recurse)
5181 186 : cmd->recurse = true;
5182 186 : pass = AT_PASS_MISC;
5183 186 : break;
5184 412 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5185 412 : ATSimplePermissions(cmd->subtype, rel,
5186 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5187 : /* Recursion occurs during execution phase */
5188 : /* No command-specific prep needed except saving recurse flag */
5189 412 : if (recurse)
5190 412 : cmd->recurse = true;
5191 412 : pass = AT_PASS_MISC;
5192 412 : break;
5193 490 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5194 490 : ATSimplePermissions(cmd->subtype, rel,
5195 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5196 490 : pass = AT_PASS_MISC;
5197 : /* This command never recurses */
5198 : /* No command-specific prep needed */
5199 490 : break;
5200 340 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5201 : case AT_EnableAlwaysTrig:
5202 : case AT_EnableReplicaTrig:
5203 : case AT_EnableTrigAll:
5204 : case AT_EnableTrigUser:
5205 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5206 : case AT_DisableTrigAll:
5207 : case AT_DisableTrigUser:
5208 340 : ATSimplePermissions(cmd->subtype, rel,
5209 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5210 : /* Set up recursion for phase 2; no other prep needed */
5211 340 : if (recurse)
5212 312 : cmd->recurse = true;
5213 340 : pass = AT_PASS_MISC;
5214 340 : break;
5215 544 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5216 : case AT_EnableAlwaysRule:
5217 : case AT_EnableReplicaRule:
5218 : case AT_DisableRule:
5219 : case AT_AddOf: /* OF */
5220 : case AT_DropOf: /* NOT OF */
5221 : case AT_EnableRowSecurity:
5222 : case AT_DisableRowSecurity:
5223 : case AT_ForceRowSecurity:
5224 : case AT_NoForceRowSecurity:
5225 544 : ATSimplePermissions(cmd->subtype, rel,
5226 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5227 : /* These commands never recurse */
5228 : /* No command-specific prep needed */
5229 544 : pass = AT_PASS_MISC;
5230 544 : break;
5231 58 : case AT_GenericOptions:
5232 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5233 : /* No command-specific prep needed */
5234 58 : pass = AT_PASS_MISC;
5235 58 : break;
5236 2728 : case AT_AttachPartition:
5237 2728 : ATSimplePermissions(cmd->subtype, rel,
5238 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5239 : /* No command-specific prep needed */
5240 2722 : pass = AT_PASS_MISC;
5241 2722 : break;
5242 576 : case AT_DetachPartition:
5243 576 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5244 : /* No command-specific prep needed */
5245 558 : pass = AT_PASS_MISC;
5246 558 : break;
5247 20 : case AT_DetachPartitionFinalize:
5248 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5249 : /* No command-specific prep needed */
5250 14 : pass = AT_PASS_MISC;
5251 14 : break;
5252 0 : default: /* oops */
5253 0 : elog(ERROR, "unrecognized alter table type: %d",
5254 : (int) cmd->subtype);
5255 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5256 : break;
5257 : }
5258 : Assert(pass > AT_PASS_UNSET);
5259 :
5260 : /* Add the subcommand to the appropriate list for phase 2 */
5261 32416 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5262 32416 : }
5263 :
5264 : /*
5265 : * ATRewriteCatalogs
5266 : *
5267 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5268 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5269 : * conflicts).
5270 : */
5271 : static void
5272 29846 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5273 : AlterTableUtilityContext *context)
5274 : {
5275 : ListCell *ltab;
5276 :
5277 : /*
5278 : * We process all the tables "in parallel", one pass at a time. This is
5279 : * needed because we may have to propagate work from one table to another
5280 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5281 : * re-adding of the foreign key constraint to the other table). Work can
5282 : * only be propagated into later passes, however.
5283 : */
5284 375588 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5285 : {
5286 : /* Go through each table that needs to be processed */
5287 709268 : foreach(ltab, *wqueue)
5288 : {
5289 363526 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5290 363526 : List *subcmds = tab->subcmds[pass];
5291 : ListCell *lcmd;
5292 :
5293 363526 : if (subcmds == NIL)
5294 311772 : continue;
5295 :
5296 : /*
5297 : * Open the relation and store it in tab. This allows subroutines
5298 : * close and reopen, if necessary. Appropriate lock was obtained
5299 : * by phase 1, needn't get it again.
5300 : */
5301 51754 : tab->rel = relation_open(tab->relid, NoLock);
5302 :
5303 104782 : foreach(lcmd, subcmds)
5304 55790 : ATExecCmd(wqueue, tab,
5305 55790 : lfirst_node(AlterTableCmd, lcmd),
5306 : lockmode, pass, context);
5307 :
5308 : /*
5309 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5310 : * (this is not done in ATExecAlterColumnType since it should be
5311 : * done only once if multiple columns of a table are altered).
5312 : */
5313 48992 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5314 1114 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5315 :
5316 48992 : if (tab->rel)
5317 : {
5318 48992 : relation_close(tab->rel, NoLock);
5319 48992 : tab->rel = NULL;
5320 : }
5321 : }
5322 : }
5323 :
5324 : /* Check to see if a toast table must be added. */
5325 58118 : foreach(ltab, *wqueue)
5326 : {
5327 31034 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5328 :
5329 : /*
5330 : * If the table is source table of ATTACH PARTITION command, we did
5331 : * not modify anything about it that will change its toasting
5332 : * requirement, so no need to check.
5333 : */
5334 31034 : if (((tab->relkind == RELKIND_RELATION ||
5335 5928 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5336 29126 : tab->partition_constraint == NULL) ||
5337 3898 : tab->relkind == RELKIND_MATVIEW)
5338 27186 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5339 : }
5340 27084 : }
5341 :
5342 : /*
5343 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5344 : */
5345 : static void
5346 55790 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5347 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5348 : AlterTableUtilityContext *context)
5349 : {
5350 55790 : ObjectAddress address = InvalidObjectAddress;
5351 55790 : Relation rel = tab->rel;
5352 :
5353 55790 : switch (cmd->subtype)
5354 : {
5355 2152 : case AT_AddColumn: /* ADD COLUMN */
5356 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5357 2152 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5358 2152 : cmd->recurse, false,
5359 : lockmode, cur_pass, context);
5360 2020 : break;
5361 584 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5362 584 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5363 518 : break;
5364 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5365 80 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5366 80 : break;
5367 166 : case AT_AddIdentity:
5368 166 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5369 : cur_pass, context);
5370 : Assert(cmd != NULL);
5371 154 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5372 106 : break;
5373 62 : case AT_SetIdentity:
5374 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5375 : cur_pass, context);
5376 : Assert(cmd != NULL);
5377 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5378 38 : break;
5379 56 : case AT_DropIdentity:
5380 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5381 38 : break;
5382 262 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5383 262 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5384 160 : break;
5385 384 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5386 384 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5387 384 : cmd->recurse, false, lockmode);
5388 354 : break;
5389 180 : case AT_SetExpression:
5390 180 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5391 150 : break;
5392 56 : case AT_DropExpression:
5393 56 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5394 32 : break;
5395 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5396 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5397 116 : break;
5398 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5399 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5400 26 : break;
5401 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5402 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5403 6 : break;
5404 240 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5405 240 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5406 228 : break;
5407 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5408 68 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5409 : lockmode);
5410 62 : break;
5411 1638 : case AT_DropColumn: /* DROP COLUMN */
5412 1638 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5413 1638 : cmd->behavior, cmd->recurse, false,
5414 1638 : cmd->missing_ok, lockmode,
5415 : NULL);
5416 1458 : break;
5417 1178 : case AT_AddIndex: /* ADD INDEX */
5418 1178 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5419 : lockmode);
5420 1008 : break;
5421 444 : case AT_ReAddIndex: /* ADD INDEX */
5422 444 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5423 : lockmode);
5424 444 : break;
5425 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5426 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5427 : true, lockmode);
5428 14 : break;
5429 26950 : case AT_AddConstraint: /* ADD CONSTRAINT */
5430 : /* Transform the command only during initial examination */
5431 26950 : if (cur_pass == AT_PASS_ADD_CONSTR)
5432 15074 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5433 15104 : cmd->recurse, lockmode,
5434 : cur_pass, context);
5435 : /* Depending on constraint type, might be no more work to do now */
5436 26920 : if (cmd != NULL)
5437 : address =
5438 11846 : ATExecAddConstraint(wqueue, tab, rel,
5439 11846 : (Constraint *) cmd->def,
5440 11846 : cmd->recurse, false, lockmode);
5441 26258 : break;
5442 326 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5443 : address =
5444 326 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5445 : true, true, lockmode);
5446 314 : break;
5447 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5448 : * constraint */
5449 : address =
5450 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5451 14 : ((AlterDomainStmt *) cmd->def)->def,
5452 : NULL);
5453 8 : break;
5454 78 : case AT_ReAddComment: /* Re-add existing comment */
5455 78 : address = CommentObject((CommentStmt *) cmd->def);
5456 78 : break;
5457 9540 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5458 9540 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5459 : lockmode);
5460 9528 : break;
5461 186 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5462 186 : address = ATExecAlterConstraint(wqueue, rel,
5463 186 : castNode(ATAlterConstraint, cmd->def),
5464 186 : cmd->recurse, lockmode);
5465 132 : break;
5466 412 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5467 412 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5468 : false, lockmode);
5469 406 : break;
5470 774 : case AT_DropConstraint: /* DROP CONSTRAINT */
5471 774 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5472 774 : cmd->recurse,
5473 774 : cmd->missing_ok, lockmode);
5474 564 : break;
5475 1048 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5476 : /* parse transformation was done earlier */
5477 1048 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5478 1006 : break;
5479 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5480 : address =
5481 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5482 172 : (List *) cmd->def, lockmode);
5483 166 : break;
5484 1928 : case AT_ChangeOwner: /* ALTER OWNER */
5485 1922 : ATExecChangeOwner(RelationGetRelid(rel),
5486 1928 : get_rolespec_oid(cmd->newowner, false),
5487 : false, lockmode);
5488 1910 : break;
5489 64 : case AT_ClusterOn: /* CLUSTER ON */
5490 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5491 58 : break;
5492 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5493 18 : ATExecDropCluster(rel, lockmode);
5494 12 : break;
5495 88 : case AT_SetLogged: /* SET LOGGED */
5496 : case AT_SetUnLogged: /* SET UNLOGGED */
5497 88 : break;
5498 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5499 : /* nothing to do here, oid columns don't exist anymore */
5500 6 : break;
5501 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5502 :
5503 : /*
5504 : * Only do this for partitioned tables, for which this is just a
5505 : * catalog change. Tables with storage are handled by Phase 3.
5506 : */
5507 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5508 50 : tab->chgAccessMethod)
5509 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5510 92 : break;
5511 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5512 :
5513 : /*
5514 : * Only do this for partitioned tables and indexes, for which this
5515 : * is just a catalog change. Other relation types which have
5516 : * storage are handled by Phase 3.
5517 : */
5518 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5519 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5520 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5521 :
5522 152 : break;
5523 954 : case AT_SetRelOptions: /* SET (...) */
5524 : case AT_ResetRelOptions: /* RESET (...) */
5525 : case AT_ReplaceRelOptions: /* replace entire option list */
5526 954 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5527 902 : break;
5528 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5529 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5530 : TRIGGER_FIRES_ON_ORIGIN, false,
5531 122 : cmd->recurse,
5532 : lockmode);
5533 122 : break;
5534 40 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5535 40 : ATExecEnableDisableTrigger(rel, cmd->name,
5536 : TRIGGER_FIRES_ALWAYS, false,
5537 40 : cmd->recurse,
5538 : lockmode);
5539 40 : break;
5540 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5541 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5542 : TRIGGER_FIRES_ON_REPLICA, false,
5543 16 : cmd->recurse,
5544 : lockmode);
5545 16 : break;
5546 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5547 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5548 : TRIGGER_DISABLED, false,
5549 138 : cmd->recurse,
5550 : lockmode);
5551 138 : break;
5552 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5553 0 : ATExecEnableDisableTrigger(rel, NULL,
5554 : TRIGGER_FIRES_ON_ORIGIN, false,
5555 0 : cmd->recurse,
5556 : lockmode);
5557 0 : break;
5558 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5559 12 : ATExecEnableDisableTrigger(rel, NULL,
5560 : TRIGGER_DISABLED, false,
5561 12 : cmd->recurse,
5562 : lockmode);
5563 12 : break;
5564 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5565 0 : ATExecEnableDisableTrigger(rel, NULL,
5566 : TRIGGER_FIRES_ON_ORIGIN, true,
5567 0 : cmd->recurse,
5568 : lockmode);
5569 0 : break;
5570 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5571 12 : ATExecEnableDisableTrigger(rel, NULL,
5572 : TRIGGER_DISABLED, true,
5573 12 : cmd->recurse,
5574 : lockmode);
5575 12 : break;
5576 :
5577 8 : case AT_EnableRule: /* ENABLE RULE name */
5578 8 : ATExecEnableDisableRule(rel, cmd->name,
5579 : RULE_FIRES_ON_ORIGIN, lockmode);
5580 8 : break;
5581 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5582 0 : ATExecEnableDisableRule(rel, cmd->name,
5583 : RULE_FIRES_ALWAYS, lockmode);
5584 0 : break;
5585 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5586 6 : ATExecEnableDisableRule(rel, cmd->name,
5587 : RULE_FIRES_ON_REPLICA, lockmode);
5588 6 : break;
5589 32 : case AT_DisableRule: /* DISABLE RULE name */
5590 32 : ATExecEnableDisableRule(rel, cmd->name,
5591 : RULE_DISABLED, lockmode);
5592 32 : break;
5593 :
5594 392 : case AT_AddInherit:
5595 392 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5596 284 : break;
5597 86 : case AT_DropInherit:
5598 86 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5599 80 : break;
5600 66 : case AT_AddOf:
5601 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5602 30 : break;
5603 6 : case AT_DropOf:
5604 6 : ATExecDropOf(rel, lockmode);
5605 6 : break;
5606 508 : case AT_ReplicaIdentity:
5607 508 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5608 460 : break;
5609 290 : case AT_EnableRowSecurity:
5610 290 : ATExecSetRowSecurity(rel, true);
5611 290 : break;
5612 10 : case AT_DisableRowSecurity:
5613 10 : ATExecSetRowSecurity(rel, false);
5614 10 : break;
5615 94 : case AT_ForceRowSecurity:
5616 94 : ATExecForceNoForceRowSecurity(rel, true);
5617 94 : break;
5618 32 : case AT_NoForceRowSecurity:
5619 32 : ATExecForceNoForceRowSecurity(rel, false);
5620 32 : break;
5621 58 : case AT_GenericOptions:
5622 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5623 56 : break;
5624 2722 : case AT_AttachPartition:
5625 2722 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5626 : cur_pass, context);
5627 : Assert(cmd != NULL);
5628 2698 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5629 2306 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5630 : context);
5631 : else
5632 392 : address = ATExecAttachPartitionIdx(wqueue, rel,
5633 392 : ((PartitionCmd *) cmd->def)->name);
5634 2320 : break;
5635 558 : case AT_DetachPartition:
5636 558 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5637 : cur_pass, context);
5638 : Assert(cmd != NULL);
5639 : /* ATPrepCmd ensures it must be a table */
5640 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5641 558 : address = ATExecDetachPartition(wqueue, tab, rel,
5642 558 : ((PartitionCmd *) cmd->def)->name,
5643 558 : ((PartitionCmd *) cmd->def)->concurrent);
5644 428 : break;
5645 14 : case AT_DetachPartitionFinalize:
5646 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5647 14 : break;
5648 0 : default: /* oops */
5649 0 : elog(ERROR, "unrecognized alter table type: %d",
5650 : (int) cmd->subtype);
5651 : break;
5652 : }
5653 :
5654 : /*
5655 : * Report the subcommand to interested event triggers.
5656 : */
5657 53028 : if (cmd)
5658 37954 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5659 :
5660 : /*
5661 : * Bump the command counter to ensure the next subcommand in the sequence
5662 : * can see the changes so far
5663 : */
5664 53028 : CommandCounterIncrement();
5665 53028 : }
5666 :
5667 : /*
5668 : * ATParseTransformCmd: perform parse transformation for one subcommand
5669 : *
5670 : * Returns the transformed subcommand tree, if there is one, else NULL.
5671 : *
5672 : * The parser may hand back additional AlterTableCmd(s) and/or other
5673 : * utility statements, either before or after the original subcommand.
5674 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5675 : * AlteredTableInfo (they had better be for later passes than the current one).
5676 : * Utility statements that are supposed to happen before the AlterTableCmd
5677 : * are executed immediately. Those that are supposed to happen afterwards
5678 : * are added to the tab->afterStmts list to be done at the very end.
5679 : */
5680 : static AlterTableCmd *
5681 21926 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5682 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5683 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5684 : {
5685 21926 : AlterTableCmd *newcmd = NULL;
5686 21926 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5687 : List *beforeStmts;
5688 : List *afterStmts;
5689 : ListCell *lc;
5690 :
5691 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5692 21926 : atstmt->relation =
5693 21926 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5694 21926 : pstrdup(RelationGetRelationName(rel)),
5695 : -1);
5696 21926 : atstmt->relation->inh = recurse;
5697 21926 : atstmt->cmds = list_make1(cmd);
5698 21926 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5699 21926 : atstmt->missing_ok = false;
5700 :
5701 : /* Transform the AlterTableStmt */
5702 21926 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5703 : atstmt,
5704 : context->queryString,
5705 : &beforeStmts,
5706 : &afterStmts);
5707 :
5708 : /* Execute any statements that should happen before these subcommand(s) */
5709 22334 : foreach(lc, beforeStmts)
5710 : {
5711 486 : Node *stmt = (Node *) lfirst(lc);
5712 :
5713 486 : ProcessUtilityForAlterTable(stmt, context);
5714 474 : CommandCounterIncrement();
5715 : }
5716 :
5717 : /* Examine the transformed subcommands and schedule them appropriately */
5718 51222 : foreach(lc, atstmt->cmds)
5719 : {
5720 29374 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5721 : AlterTablePass pass;
5722 :
5723 : /*
5724 : * This switch need only cover the subcommand types that can be added
5725 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5726 : * executing the subcommand immediately, as a substitute for the
5727 : * original subcommand. (Note, however, that this does cause
5728 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5729 : * which is important for index and foreign key constraints.)
5730 : *
5731 : * We assume we needn't do any phase-1 checks for added subcommands.
5732 : */
5733 29374 : switch (cmd2->subtype)
5734 : {
5735 1202 : case AT_AddIndex:
5736 1202 : pass = AT_PASS_ADD_INDEX;
5737 1202 : break;
5738 9540 : case AT_AddIndexConstraint:
5739 9540 : pass = AT_PASS_ADD_INDEXCONSTR;
5740 9540 : break;
5741 11858 : case AT_AddConstraint:
5742 : /* Recursion occurs during execution phase */
5743 11858 : if (recurse)
5744 11810 : cmd2->recurse = true;
5745 11858 : switch (castNode(Constraint, cmd2->def)->contype)
5746 : {
5747 8482 : case CONSTR_NOTNULL:
5748 8482 : pass = AT_PASS_COL_ATTRS;
5749 8482 : break;
5750 0 : case CONSTR_PRIMARY:
5751 : case CONSTR_UNIQUE:
5752 : case CONSTR_EXCLUSION:
5753 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5754 0 : break;
5755 3376 : default:
5756 3376 : pass = AT_PASS_ADD_OTHERCONSTR;
5757 3376 : break;
5758 : }
5759 11858 : break;
5760 0 : case AT_AlterColumnGenericOptions:
5761 : /* This command never recurses */
5762 : /* No command-specific prep needed */
5763 0 : pass = AT_PASS_MISC;
5764 0 : break;
5765 6774 : default:
5766 6774 : pass = cur_pass;
5767 6774 : break;
5768 : }
5769 :
5770 29374 : if (pass < cur_pass)
5771 : {
5772 : /* Cannot schedule into a pass we already finished */
5773 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5774 : pass);
5775 : }
5776 29374 : else if (pass > cur_pass)
5777 : {
5778 : /* OK, queue it up for later */
5779 22600 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5780 : }
5781 : else
5782 : {
5783 : /*
5784 : * We should see at most one subcommand for the current pass,
5785 : * which is the transformed version of the original subcommand.
5786 : */
5787 6774 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5788 : {
5789 : /* Found the transformed version of our subcommand */
5790 6774 : newcmd = cmd2;
5791 : }
5792 : else
5793 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5794 : pass);
5795 : }
5796 : }
5797 :
5798 : /* Queue up any after-statements to happen at the end */
5799 21848 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5800 :
5801 21848 : return newcmd;
5802 : }
5803 :
5804 : /*
5805 : * ATRewriteTables: ALTER TABLE phase 3
5806 : */
5807 : static void
5808 27084 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5809 : AlterTableUtilityContext *context)
5810 : {
5811 : ListCell *ltab;
5812 :
5813 : /* Go through each table that needs to be checked or rewritten */
5814 57778 : foreach(ltab, *wqueue)
5815 : {
5816 31010 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5817 :
5818 : /* Relations without storage may be ignored here */
5819 31010 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5820 5620 : continue;
5821 :
5822 : /*
5823 : * If we change column data types, the operation has to be propagated
5824 : * to tables that use this table's rowtype as a column type.
5825 : * tab->newvals will also be non-NULL in the case where we're adding a
5826 : * column with a default. We choose to forbid that case as well,
5827 : * since composite types might eventually support defaults.
5828 : *
5829 : * (Eventually we'll probably need to check for composite type
5830 : * dependencies even when we're just scanning the table without a
5831 : * rewrite, but at the moment a composite type does not enforce any
5832 : * constraints, so it's not necessary/appropriate to enforce them just
5833 : * during ALTER.)
5834 : */
5835 25390 : if (tab->newvals != NIL || tab->rewrite > 0)
5836 : {
5837 : Relation rel;
5838 :
5839 1644 : rel = table_open(tab->relid, NoLock);
5840 1644 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5841 1626 : table_close(rel, NoLock);
5842 : }
5843 :
5844 : /*
5845 : * We only need to rewrite the table if at least one column needs to
5846 : * be recomputed, or we are changing its persistence or access method.
5847 : *
5848 : * There are two reasons for requiring a rewrite when changing
5849 : * persistence: on one hand, we need to ensure that the buffers
5850 : * belonging to each of the two relations are marked with or without
5851 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5852 : * and assigns a new relfilenumber, we automatically create or drop an
5853 : * init fork for the relation as appropriate.
5854 : */
5855 25372 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5856 904 : {
5857 : /* Build a temporary relation and copy data */
5858 : Relation OldHeap;
5859 : Oid OIDNewHeap;
5860 : Oid NewAccessMethod;
5861 : Oid NewTableSpace;
5862 : char persistence;
5863 :
5864 960 : OldHeap = table_open(tab->relid, NoLock);
5865 :
5866 : /*
5867 : * We don't support rewriting of system catalogs; there are too
5868 : * many corner cases and too little benefit. In particular this
5869 : * is certainly not going to work for mapped catalogs.
5870 : */
5871 960 : if (IsSystemRelation(OldHeap))
5872 0 : ereport(ERROR,
5873 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5874 : errmsg("cannot rewrite system relation \"%s\"",
5875 : RelationGetRelationName(OldHeap))));
5876 :
5877 960 : if (RelationIsUsedAsCatalogTable(OldHeap))
5878 2 : ereport(ERROR,
5879 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5880 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5881 : RelationGetRelationName(OldHeap))));
5882 :
5883 : /*
5884 : * Don't allow rewrite on temp tables of other backends ... their
5885 : * local buffer manager is not going to cope. (This is redundant
5886 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5887 : * check here too.)
5888 : */
5889 958 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5890 0 : ereport(ERROR,
5891 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5892 : errmsg("cannot rewrite temporary tables of other sessions")));
5893 :
5894 : /*
5895 : * Select destination tablespace (same as original unless user
5896 : * requested a change)
5897 : */
5898 958 : if (tab->newTableSpace)
5899 0 : NewTableSpace = tab->newTableSpace;
5900 : else
5901 958 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5902 :
5903 : /*
5904 : * Select destination access method (same as original unless user
5905 : * requested a change)
5906 : */
5907 958 : if (tab->chgAccessMethod)
5908 36 : NewAccessMethod = tab->newAccessMethod;
5909 : else
5910 922 : NewAccessMethod = OldHeap->rd_rel->relam;
5911 :
5912 : /*
5913 : * Select persistence of transient table (same as original unless
5914 : * user requested a change)
5915 : */
5916 958 : persistence = tab->chgPersistence ?
5917 906 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5918 :
5919 958 : table_close(OldHeap, NoLock);
5920 :
5921 : /*
5922 : * Fire off an Event Trigger now, before actually rewriting the
5923 : * table.
5924 : *
5925 : * We don't support Event Trigger for nested commands anywhere,
5926 : * here included, and parsetree is given NULL when coming from
5927 : * AlterTableInternal.
5928 : *
5929 : * And fire it only once.
5930 : */
5931 958 : if (parsetree)
5932 958 : EventTriggerTableRewrite((Node *) parsetree,
5933 : tab->relid,
5934 : tab->rewrite);
5935 :
5936 : /*
5937 : * Create transient table that will receive the modified data.
5938 : *
5939 : * Ensure it is marked correctly as logged or unlogged. We have
5940 : * to do this here so that buffers for the new relfilenumber will
5941 : * have the right persistence set, and at the same time ensure
5942 : * that the original filenumbers's buffers will get read in with
5943 : * the correct setting (i.e. the original one). Otherwise a
5944 : * rollback after the rewrite would possibly result with buffers
5945 : * for the original filenumbers having the wrong persistence
5946 : * setting.
5947 : *
5948 : * NB: This relies on swap_relation_files() also swapping the
5949 : * persistence. That wouldn't work for pg_class, but that can't be
5950 : * unlogged anyway.
5951 : */
5952 952 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5953 : persistence, lockmode);
5954 :
5955 : /*
5956 : * Copy the heap data into the new table with the desired
5957 : * modifications, and test the current data within the table
5958 : * against new constraints generated by ALTER TABLE commands.
5959 : */
5960 952 : ATRewriteTable(tab, OIDNewHeap);
5961 :
5962 : /*
5963 : * Swap the physical files of the old and new heaps, then rebuild
5964 : * indexes and discard the old heap. We can use RecentXmin for
5965 : * the table's new relfrozenxid because we rewrote all the tuples
5966 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5967 : * we never try to swap toast tables by content, since we have no
5968 : * interest in letting this code work on system catalogs.
5969 : */
5970 910 : finish_heap_swap(tab->relid, OIDNewHeap,
5971 : false, false, true,
5972 910 : !OidIsValid(tab->newTableSpace),
5973 : RecentXmin,
5974 : ReadNextMultiXactId(),
5975 : persistence);
5976 :
5977 904 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5978 : }
5979 24412 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5980 : {
5981 24 : if (tab->chgPersistence)
5982 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5983 : }
5984 : else
5985 : {
5986 : /*
5987 : * If required, test the current data within the table against new
5988 : * constraints generated by ALTER TABLE commands, but don't
5989 : * rebuild data.
5990 : */
5991 24388 : if (tab->constraints != NIL || tab->verify_new_notnull ||
5992 21712 : tab->partition_constraint != NULL)
5993 4506 : ATRewriteTable(tab, InvalidOid);
5994 :
5995 : /*
5996 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5997 : * just do a block-by-block copy.
5998 : */
5999 24146 : if (tab->newTableSpace)
6000 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6001 : }
6002 :
6003 : /*
6004 : * Also change persistence of owned sequences, so that it matches the
6005 : * table persistence.
6006 : */
6007 25074 : if (tab->chgPersistence)
6008 : {
6009 76 : List *seqlist = getOwnedSequences(tab->relid);
6010 : ListCell *lc;
6011 :
6012 124 : foreach(lc, seqlist)
6013 : {
6014 48 : Oid seq_relid = lfirst_oid(lc);
6015 :
6016 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6017 : }
6018 : }
6019 : }
6020 :
6021 : /*
6022 : * Foreign key constraints are checked in a final pass, since (a) it's
6023 : * generally best to examine each one separately, and (b) it's at least
6024 : * theoretically possible that we have changed both relations of the
6025 : * foreign key, and we'd better have finished both rewrites before we try
6026 : * to read the tables.
6027 : */
6028 57216 : foreach(ltab, *wqueue)
6029 : {
6030 30534 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6031 30534 : Relation rel = NULL;
6032 : ListCell *lcon;
6033 :
6034 : /* Relations without storage may be ignored here too */
6035 30534 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6036 5522 : continue;
6037 :
6038 26738 : foreach(lcon, tab->constraints)
6039 : {
6040 1812 : NewConstraint *con = lfirst(lcon);
6041 :
6042 1812 : if (con->contype == CONSTR_FOREIGN)
6043 : {
6044 1100 : Constraint *fkconstraint = (Constraint *) con->qual;
6045 : Relation refrel;
6046 :
6047 1100 : if (rel == NULL)
6048 : {
6049 : /* Long since locked, no need for another */
6050 1082 : rel = table_open(tab->relid, NoLock);
6051 : }
6052 :
6053 1100 : refrel = table_open(con->refrelid, RowShareLock);
6054 :
6055 1100 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6056 : con->refindid,
6057 : con->conid,
6058 1100 : con->conwithperiod);
6059 :
6060 : /*
6061 : * No need to mark the constraint row as validated, we did
6062 : * that when we inserted the row earlier.
6063 : */
6064 :
6065 1014 : table_close(refrel, NoLock);
6066 : }
6067 : }
6068 :
6069 24926 : if (rel)
6070 996 : table_close(rel, NoLock);
6071 : }
6072 :
6073 : /* Finally, run any afterStmts that were queued up */
6074 57086 : foreach(ltab, *wqueue)
6075 : {
6076 30404 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6077 : ListCell *lc;
6078 :
6079 30490 : foreach(lc, tab->afterStmts)
6080 : {
6081 86 : Node *stmt = (Node *) lfirst(lc);
6082 :
6083 86 : ProcessUtilityForAlterTable(stmt, context);
6084 86 : CommandCounterIncrement();
6085 : }
6086 : }
6087 26682 : }
6088 :
6089 : /*
6090 : * ATRewriteTable: scan or rewrite one table
6091 : *
6092 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6093 : * must already hold AccessExclusiveLock on it.
6094 : */
6095 : static void
6096 5458 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6097 : {
6098 : Relation oldrel;
6099 : Relation newrel;
6100 : TupleDesc oldTupDesc;
6101 : TupleDesc newTupDesc;
6102 5458 : bool needscan = false;
6103 : List *notnull_attrs;
6104 : List *notnull_virtual_attrs;
6105 : int i;
6106 : ListCell *l;
6107 : EState *estate;
6108 : CommandId mycid;
6109 : BulkInsertState bistate;
6110 : int ti_options;
6111 5458 : ExprState *partqualstate = NULL;
6112 :
6113 : /*
6114 : * Open the relation(s). We have surely already locked the existing
6115 : * table.
6116 : */
6117 5458 : oldrel = table_open(tab->relid, NoLock);
6118 5458 : oldTupDesc = tab->oldDesc;
6119 5458 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6120 :
6121 5458 : if (OidIsValid(OIDNewHeap))
6122 : {
6123 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6124 : false));
6125 952 : newrel = table_open(OIDNewHeap, NoLock);
6126 : }
6127 : else
6128 4506 : newrel = NULL;
6129 :
6130 : /*
6131 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6132 : * is empty, so don't bother using it.
6133 : */
6134 5458 : if (newrel)
6135 : {
6136 952 : mycid = GetCurrentCommandId(true);
6137 952 : bistate = GetBulkInsertState();
6138 952 : ti_options = TABLE_INSERT_SKIP_FSM;
6139 : }
6140 : else
6141 : {
6142 : /* keep compiler quiet about using these uninitialized */
6143 4506 : mycid = 0;
6144 4506 : bistate = NULL;
6145 4506 : ti_options = 0;
6146 : }
6147 :
6148 : /*
6149 : * Generate the constraint and default execution states
6150 : */
6151 :
6152 5458 : estate = CreateExecutorState();
6153 :
6154 : /* Build the needed expression execution states */
6155 7390 : foreach(l, tab->constraints)
6156 : {
6157 1932 : NewConstraint *con = lfirst(l);
6158 :
6159 1932 : switch (con->contype)
6160 : {
6161 826 : case CONSTR_CHECK:
6162 826 : needscan = true;
6163 826 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6164 826 : break;
6165 1106 : case CONSTR_FOREIGN:
6166 : /* Nothing to do here */
6167 1106 : break;
6168 0 : default:
6169 0 : elog(ERROR, "unrecognized constraint type: %d",
6170 : (int) con->contype);
6171 : }
6172 : }
6173 :
6174 : /* Build expression execution states for partition check quals */
6175 5458 : if (tab->partition_constraint)
6176 : {
6177 1984 : needscan = true;
6178 1984 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6179 : }
6180 :
6181 6472 : foreach(l, tab->newvals)
6182 : {
6183 1014 : NewColumnValue *ex = lfirst(l);
6184 :
6185 : /* expr already planned */
6186 1014 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6187 : }
6188 :
6189 5458 : notnull_attrs = notnull_virtual_attrs = NIL;
6190 5458 : if (newrel || tab->verify_new_notnull)
6191 : {
6192 : /*
6193 : * If we are rebuilding the tuples OR if we added any new but not
6194 : * verified not-null constraints, check all not-null constraints. This
6195 : * is a bit of overkill but it minimizes risk of bugs.
6196 : *
6197 : * notnull_attrs does *not* collect attribute numbers for not-null
6198 : * constraints over virtual generated columns; instead, they are
6199 : * collected in notnull_virtual_attrs.
6200 : */
6201 7068 : for (i = 0; i < newTupDesc->natts; i++)
6202 : {
6203 5196 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6204 :
6205 5196 : if (attr->attnotnull && !attr->attisdropped)
6206 : {
6207 1988 : if (attr->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6208 1898 : notnull_attrs = lappend_int(notnull_attrs, attr->attnum);
6209 : else
6210 90 : notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6211 90 : attr->attnum);
6212 : }
6213 : }
6214 1872 : if (notnull_attrs || notnull_virtual_attrs)
6215 1426 : needscan = true;
6216 : }
6217 :
6218 5458 : if (newrel || needscan)
6219 : {
6220 : ExprContext *econtext;
6221 : TupleTableSlot *oldslot;
6222 : TupleTableSlot *newslot;
6223 : TableScanDesc scan;
6224 : MemoryContext oldCxt;
6225 4566 : List *dropped_attrs = NIL;
6226 : ListCell *lc;
6227 : Snapshot snapshot;
6228 4566 : ResultRelInfo *rInfo = NULL;
6229 :
6230 : /*
6231 : * When adding or changing a virtual generated column with a not-null
6232 : * constraint, we need to evaluate whether the generation expression
6233 : * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6234 : * prepare a dummy ResultRelInfo.
6235 : */
6236 4566 : if (notnull_virtual_attrs != NIL)
6237 : {
6238 : MemoryContext oldcontext;
6239 :
6240 : Assert(newTupDesc->constr->has_generated_virtual);
6241 : Assert(newTupDesc->constr->has_not_null);
6242 60 : oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6243 60 : rInfo = makeNode(ResultRelInfo);
6244 60 : InitResultRelInfo(rInfo,
6245 : oldrel,
6246 : 0, /* dummy rangetable index */
6247 : NULL,
6248 : estate->es_instrument);
6249 60 : MemoryContextSwitchTo(oldcontext);
6250 : }
6251 :
6252 4566 : if (newrel)
6253 952 : ereport(DEBUG1,
6254 : (errmsg_internal("rewriting table \"%s\"",
6255 : RelationGetRelationName(oldrel))));
6256 : else
6257 3614 : ereport(DEBUG1,
6258 : (errmsg_internal("verifying table \"%s\"",
6259 : RelationGetRelationName(oldrel))));
6260 :
6261 4566 : if (newrel)
6262 : {
6263 : /*
6264 : * All predicate locks on the tuples or pages are about to be made
6265 : * invalid, because we move tuples around. Promote them to
6266 : * relation locks.
6267 : */
6268 952 : TransferPredicateLocksToHeapRelation(oldrel);
6269 : }
6270 :
6271 4566 : econtext = GetPerTupleExprContext(estate);
6272 :
6273 : /*
6274 : * Create necessary tuple slots. When rewriting, two slots are needed,
6275 : * otherwise one suffices. In the case where one slot suffices, we
6276 : * need to use the new tuple descriptor, otherwise some constraints
6277 : * can't be evaluated. Note that even when the tuple layout is the
6278 : * same and no rewrite is required, the tupDescs might not be
6279 : * (consider ADD COLUMN without a default).
6280 : */
6281 4566 : if (tab->rewrite)
6282 : {
6283 : Assert(newrel != NULL);
6284 952 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6285 : table_slot_callbacks(oldrel));
6286 952 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6287 : table_slot_callbacks(newrel));
6288 :
6289 : /*
6290 : * Set all columns in the new slot to NULL initially, to ensure
6291 : * columns added as part of the rewrite are initialized to NULL.
6292 : * That is necessary as tab->newvals will not contain an
6293 : * expression for columns with a NULL default, e.g. when adding a
6294 : * column without a default together with a column with a default
6295 : * requiring an actual rewrite.
6296 : */
6297 952 : ExecStoreAllNullTuple(newslot);
6298 : }
6299 : else
6300 : {
6301 3614 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6302 : table_slot_callbacks(oldrel));
6303 3614 : newslot = NULL;
6304 : }
6305 :
6306 : /*
6307 : * Any attributes that are dropped according to the new tuple
6308 : * descriptor can be set to NULL. We precompute the list of dropped
6309 : * attributes to avoid needing to do so in the per-tuple loop.
6310 : */
6311 16248 : for (i = 0; i < newTupDesc->natts; i++)
6312 : {
6313 11682 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6314 790 : dropped_attrs = lappend_int(dropped_attrs, i);
6315 : }
6316 :
6317 : /*
6318 : * Scan through the rows, generating a new row if needed and then
6319 : * checking all the constraints.
6320 : */
6321 4566 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6322 4566 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6323 :
6324 : /*
6325 : * Switch to per-tuple memory context and reset it for each tuple
6326 : * produced, so we don't leak memory.
6327 : */
6328 4566 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6329 :
6330 769378 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6331 : {
6332 : TupleTableSlot *insertslot;
6333 :
6334 765096 : if (tab->rewrite > 0)
6335 : {
6336 : /* Extract data from old tuple */
6337 99804 : slot_getallattrs(oldslot);
6338 99804 : ExecClearTuple(newslot);
6339 :
6340 : /* copy attributes */
6341 99804 : memcpy(newslot->tts_values, oldslot->tts_values,
6342 99804 : sizeof(Datum) * oldslot->tts_nvalid);
6343 99804 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6344 99804 : sizeof(bool) * oldslot->tts_nvalid);
6345 :
6346 : /* Set dropped attributes to null in new tuple */
6347 99920 : foreach(lc, dropped_attrs)
6348 116 : newslot->tts_isnull[lfirst_int(lc)] = true;
6349 :
6350 : /*
6351 : * Constraints and GENERATED expressions might reference the
6352 : * tableoid column, so fill tts_tableOid with the desired
6353 : * value. (We must do this each time, because it gets
6354 : * overwritten with newrel's OID during storing.)
6355 : */
6356 99804 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6357 :
6358 : /*
6359 : * Process supplied expressions to replace selected columns.
6360 : *
6361 : * First, evaluate expressions whose inputs come from the old
6362 : * tuple.
6363 : */
6364 99804 : econtext->ecxt_scantuple = oldslot;
6365 :
6366 205560 : foreach(l, tab->newvals)
6367 : {
6368 105768 : NewColumnValue *ex = lfirst(l);
6369 :
6370 105768 : if (ex->is_generated)
6371 312 : continue;
6372 :
6373 105456 : newslot->tts_values[ex->attnum - 1]
6374 105444 : = ExecEvalExpr(ex->exprstate,
6375 : econtext,
6376 105456 : &newslot->tts_isnull[ex->attnum - 1]);
6377 : }
6378 :
6379 99792 : ExecStoreVirtualTuple(newslot);
6380 :
6381 : /*
6382 : * Now, evaluate any expressions whose inputs come from the
6383 : * new tuple. We assume these columns won't reference each
6384 : * other, so that there's no ordering dependency.
6385 : */
6386 99792 : econtext->ecxt_scantuple = newslot;
6387 :
6388 205548 : foreach(l, tab->newvals)
6389 : {
6390 105756 : NewColumnValue *ex = lfirst(l);
6391 :
6392 105756 : if (!ex->is_generated)
6393 105444 : continue;
6394 :
6395 312 : newslot->tts_values[ex->attnum - 1]
6396 312 : = ExecEvalExpr(ex->exprstate,
6397 : econtext,
6398 312 : &newslot->tts_isnull[ex->attnum - 1]);
6399 : }
6400 :
6401 99792 : insertslot = newslot;
6402 : }
6403 : else
6404 : {
6405 : /*
6406 : * If there's no rewrite, old and new table are guaranteed to
6407 : * have the same AM, so we can just use the old slot to verify
6408 : * new constraints etc.
6409 : */
6410 665292 : insertslot = oldslot;
6411 : }
6412 :
6413 : /* Now check any constraints on the possibly-changed tuple */
6414 765084 : econtext->ecxt_scantuple = insertslot;
6415 :
6416 4106444 : foreach_int(attn, notnull_attrs)
6417 : {
6418 2576420 : if (slot_attisnull(insertslot, attn))
6419 : {
6420 72 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6421 :
6422 72 : ereport(ERROR,
6423 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6424 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6425 : NameStr(attr->attname),
6426 : RelationGetRelationName(oldrel)),
6427 : errtablecol(oldrel, attn)));
6428 : }
6429 : }
6430 :
6431 765012 : if (notnull_virtual_attrs != NIL)
6432 : {
6433 : AttrNumber attnum;
6434 :
6435 84 : attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6436 : estate,
6437 : notnull_virtual_attrs);
6438 84 : if (attnum != InvalidAttrNumber)
6439 : {
6440 30 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6441 :
6442 30 : ereport(ERROR,
6443 : errcode(ERRCODE_NOT_NULL_VIOLATION),
6444 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6445 : NameStr(attr->attname),
6446 : RelationGetRelationName(oldrel)),
6447 : errtablecol(oldrel, attnum));
6448 : }
6449 : }
6450 :
6451 773136 : foreach(l, tab->constraints)
6452 : {
6453 8250 : NewConstraint *con = lfirst(l);
6454 :
6455 8250 : switch (con->contype)
6456 : {
6457 8144 : case CONSTR_CHECK:
6458 8144 : if (!ExecCheck(con->qualstate, econtext))
6459 96 : ereport(ERROR,
6460 : (errcode(ERRCODE_CHECK_VIOLATION),
6461 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6462 : con->name,
6463 : RelationGetRelationName(oldrel)),
6464 : errtableconstraint(oldrel, con->name)));
6465 8048 : break;
6466 106 : case CONSTR_NOTNULL:
6467 : case CONSTR_FOREIGN:
6468 : /* Nothing to do here */
6469 106 : break;
6470 0 : default:
6471 0 : elog(ERROR, "unrecognized constraint type: %d",
6472 : (int) con->contype);
6473 : }
6474 : }
6475 :
6476 764886 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6477 : {
6478 74 : if (tab->validate_default)
6479 26 : ereport(ERROR,
6480 : (errcode(ERRCODE_CHECK_VIOLATION),
6481 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6482 : RelationGetRelationName(oldrel)),
6483 : errtable(oldrel)));
6484 : else
6485 48 : ereport(ERROR,
6486 : (errcode(ERRCODE_CHECK_VIOLATION),
6487 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6488 : RelationGetRelationName(oldrel)),
6489 : errtable(oldrel)));
6490 : }
6491 :
6492 : /* Write the tuple out to the new relation */
6493 764812 : if (newrel)
6494 99762 : table_tuple_insert(newrel, insertslot, mycid,
6495 : ti_options, bistate);
6496 :
6497 764812 : ResetExprContext(econtext);
6498 :
6499 764812 : CHECK_FOR_INTERRUPTS();
6500 : }
6501 :
6502 4282 : MemoryContextSwitchTo(oldCxt);
6503 4282 : table_endscan(scan);
6504 4282 : UnregisterSnapshot(snapshot);
6505 :
6506 4282 : ExecDropSingleTupleTableSlot(oldslot);
6507 4282 : if (newslot)
6508 910 : ExecDropSingleTupleTableSlot(newslot);
6509 : }
6510 :
6511 5174 : FreeExecutorState(estate);
6512 :
6513 5174 : table_close(oldrel, NoLock);
6514 5174 : if (newrel)
6515 : {
6516 910 : FreeBulkInsertState(bistate);
6517 :
6518 910 : table_finish_bulk_insert(newrel, ti_options);
6519 :
6520 910 : table_close(newrel, NoLock);
6521 : }
6522 5174 : }
6523 :
6524 : /*
6525 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6526 : */
6527 : static AlteredTableInfo *
6528 39532 : ATGetQueueEntry(List **wqueue, Relation rel)
6529 : {
6530 39532 : Oid relid = RelationGetRelid(rel);
6531 : AlteredTableInfo *tab;
6532 : ListCell *ltab;
6533 :
6534 48820 : foreach(ltab, *wqueue)
6535 : {
6536 14452 : tab = (AlteredTableInfo *) lfirst(ltab);
6537 14452 : if (tab->relid == relid)
6538 5164 : return tab;
6539 : }
6540 :
6541 : /*
6542 : * Not there, so add it. Note that we make a copy of the relation's
6543 : * existing descriptor before anything interesting can happen to it.
6544 : */
6545 34368 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6546 34368 : tab->relid = relid;
6547 34368 : tab->rel = NULL; /* set later */
6548 34368 : tab->relkind = rel->rd_rel->relkind;
6549 34368 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6550 34368 : tab->newAccessMethod = InvalidOid;
6551 34368 : tab->chgAccessMethod = false;
6552 34368 : tab->newTableSpace = InvalidOid;
6553 34368 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6554 34368 : tab->chgPersistence = false;
6555 :
6556 34368 : *wqueue = lappend(*wqueue, tab);
6557 :
6558 34368 : return tab;
6559 : }
6560 :
6561 : static const char *
6562 80 : alter_table_type_to_string(AlterTableType cmdtype)
6563 : {
6564 80 : switch (cmdtype)
6565 : {
6566 0 : case AT_AddColumn:
6567 : case AT_AddColumnToView:
6568 0 : return "ADD COLUMN";
6569 0 : case AT_ColumnDefault:
6570 : case AT_CookedColumnDefault:
6571 0 : return "ALTER COLUMN ... SET DEFAULT";
6572 6 : case AT_DropNotNull:
6573 6 : return "ALTER COLUMN ... DROP NOT NULL";
6574 6 : case AT_SetNotNull:
6575 6 : return "ALTER COLUMN ... SET NOT NULL";
6576 0 : case AT_SetExpression:
6577 0 : return "ALTER COLUMN ... SET EXPRESSION";
6578 0 : case AT_DropExpression:
6579 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6580 0 : case AT_SetStatistics:
6581 0 : return "ALTER COLUMN ... SET STATISTICS";
6582 12 : case AT_SetOptions:
6583 12 : return "ALTER COLUMN ... SET";
6584 0 : case AT_ResetOptions:
6585 0 : return "ALTER COLUMN ... RESET";
6586 0 : case AT_SetStorage:
6587 0 : return "ALTER COLUMN ... SET STORAGE";
6588 0 : case AT_SetCompression:
6589 0 : return "ALTER COLUMN ... SET COMPRESSION";
6590 6 : case AT_DropColumn:
6591 6 : return "DROP COLUMN";
6592 0 : case AT_AddIndex:
6593 : case AT_ReAddIndex:
6594 0 : return NULL; /* not real grammar */
6595 0 : case AT_AddConstraint:
6596 : case AT_ReAddConstraint:
6597 : case AT_ReAddDomainConstraint:
6598 : case AT_AddIndexConstraint:
6599 0 : return "ADD CONSTRAINT";
6600 6 : case AT_AlterConstraint:
6601 6 : return "ALTER CONSTRAINT";
6602 0 : case AT_ValidateConstraint:
6603 0 : return "VALIDATE CONSTRAINT";
6604 0 : case AT_DropConstraint:
6605 0 : return "DROP CONSTRAINT";
6606 0 : case AT_ReAddComment:
6607 0 : return NULL; /* not real grammar */
6608 0 : case AT_AlterColumnType:
6609 0 : return "ALTER COLUMN ... SET DATA TYPE";
6610 0 : case AT_AlterColumnGenericOptions:
6611 0 : return "ALTER COLUMN ... OPTIONS";
6612 0 : case AT_ChangeOwner:
6613 0 : return "OWNER TO";
6614 0 : case AT_ClusterOn:
6615 0 : return "CLUSTER ON";
6616 0 : case AT_DropCluster:
6617 0 : return "SET WITHOUT CLUSTER";
6618 0 : case AT_SetAccessMethod:
6619 0 : return "SET ACCESS METHOD";
6620 6 : case AT_SetLogged:
6621 6 : return "SET LOGGED";
6622 6 : case AT_SetUnLogged:
6623 6 : return "SET UNLOGGED";
6624 0 : case AT_DropOids:
6625 0 : return "SET WITHOUT OIDS";
6626 0 : case AT_SetTableSpace:
6627 0 : return "SET TABLESPACE";
6628 2 : case AT_SetRelOptions:
6629 2 : return "SET";
6630 0 : case AT_ResetRelOptions:
6631 0 : return "RESET";
6632 0 : case AT_ReplaceRelOptions:
6633 0 : return NULL; /* not real grammar */
6634 0 : case AT_EnableTrig:
6635 0 : return "ENABLE TRIGGER";
6636 0 : case AT_EnableAlwaysTrig:
6637 0 : return "ENABLE ALWAYS TRIGGER";
6638 0 : case AT_EnableReplicaTrig:
6639 0 : return "ENABLE REPLICA TRIGGER";
6640 0 : case AT_DisableTrig:
6641 0 : return "DISABLE TRIGGER";
6642 0 : case AT_EnableTrigAll:
6643 0 : return "ENABLE TRIGGER ALL";
6644 0 : case AT_DisableTrigAll:
6645 0 : return "DISABLE TRIGGER ALL";
6646 0 : case AT_EnableTrigUser:
6647 0 : return "ENABLE TRIGGER USER";
6648 0 : case AT_DisableTrigUser:
6649 0 : return "DISABLE TRIGGER USER";
6650 0 : case AT_EnableRule:
6651 0 : return "ENABLE RULE";
6652 0 : case AT_EnableAlwaysRule:
6653 0 : return "ENABLE ALWAYS RULE";
6654 0 : case AT_EnableReplicaRule:
6655 0 : return "ENABLE REPLICA RULE";
6656 0 : case AT_DisableRule:
6657 0 : return "DISABLE RULE";
6658 0 : case AT_AddInherit:
6659 0 : return "INHERIT";
6660 0 : case AT_DropInherit:
6661 0 : return "NO INHERIT";
6662 0 : case AT_AddOf:
6663 0 : return "OF";
6664 0 : case AT_DropOf:
6665 0 : return "NOT OF";
6666 0 : case AT_ReplicaIdentity:
6667 0 : return "REPLICA IDENTITY";
6668 0 : case AT_EnableRowSecurity:
6669 0 : return "ENABLE ROW SECURITY";
6670 0 : case AT_DisableRowSecurity:
6671 0 : return "DISABLE ROW SECURITY";
6672 0 : case AT_ForceRowSecurity:
6673 0 : return "FORCE ROW SECURITY";
6674 0 : case AT_NoForceRowSecurity:
6675 0 : return "NO FORCE ROW SECURITY";
6676 0 : case AT_GenericOptions:
6677 0 : return "OPTIONS";
6678 6 : case AT_AttachPartition:
6679 6 : return "ATTACH PARTITION";
6680 18 : case AT_DetachPartition:
6681 18 : return "DETACH PARTITION";
6682 6 : case AT_DetachPartitionFinalize:
6683 6 : return "DETACH PARTITION ... FINALIZE";
6684 0 : case AT_AddIdentity:
6685 0 : return "ALTER COLUMN ... ADD IDENTITY";
6686 0 : case AT_SetIdentity:
6687 0 : return "ALTER COLUMN ... SET";
6688 0 : case AT_DropIdentity:
6689 0 : return "ALTER COLUMN ... DROP IDENTITY";
6690 0 : case AT_ReAddStatistics:
6691 0 : return NULL; /* not real grammar */
6692 : }
6693 :
6694 0 : return NULL;
6695 : }
6696 :
6697 : /*
6698 : * ATSimplePermissions
6699 : *
6700 : * - Ensure that it is a relation (or possibly a view)
6701 : * - Ensure this user is the owner
6702 : * - Ensure that it is not a system table
6703 : */
6704 : static void
6705 36034 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6706 : {
6707 : int actual_target;
6708 :
6709 36034 : switch (rel->rd_rel->relkind)
6710 : {
6711 28290 : case RELKIND_RELATION:
6712 28290 : actual_target = ATT_TABLE;
6713 28290 : break;
6714 5468 : case RELKIND_PARTITIONED_TABLE:
6715 5468 : actual_target = ATT_PARTITIONED_TABLE;
6716 5468 : break;
6717 402 : case RELKIND_VIEW:
6718 402 : actual_target = ATT_VIEW;
6719 402 : break;
6720 46 : case RELKIND_MATVIEW:
6721 46 : actual_target = ATT_MATVIEW;
6722 46 : break;
6723 228 : case RELKIND_INDEX:
6724 228 : actual_target = ATT_INDEX;
6725 228 : break;
6726 434 : case RELKIND_PARTITIONED_INDEX:
6727 434 : actual_target = ATT_PARTITIONED_INDEX;
6728 434 : break;
6729 214 : case RELKIND_COMPOSITE_TYPE:
6730 214 : actual_target = ATT_COMPOSITE_TYPE;
6731 214 : break;
6732 926 : case RELKIND_FOREIGN_TABLE:
6733 926 : actual_target = ATT_FOREIGN_TABLE;
6734 926 : break;
6735 24 : case RELKIND_SEQUENCE:
6736 24 : actual_target = ATT_SEQUENCE;
6737 24 : break;
6738 2 : default:
6739 2 : actual_target = 0;
6740 2 : break;
6741 : }
6742 :
6743 : /* Wrong target type? */
6744 36034 : if ((actual_target & allowed_targets) == 0)
6745 : {
6746 80 : const char *action_str = alter_table_type_to_string(cmdtype);
6747 :
6748 80 : if (action_str)
6749 80 : ereport(ERROR,
6750 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6751 : /* translator: %s is a group of some SQL keywords */
6752 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6753 : action_str, RelationGetRelationName(rel)),
6754 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6755 : else
6756 : /* internal error? */
6757 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6758 : RelationGetRelationName(rel));
6759 : }
6760 :
6761 : /* Permissions checks */
6762 35954 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6763 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6764 12 : RelationGetRelationName(rel));
6765 :
6766 35942 : if (!allowSystemTableMods && IsSystemRelation(rel))
6767 0 : ereport(ERROR,
6768 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6769 : errmsg("permission denied: \"%s\" is a system catalog",
6770 : RelationGetRelationName(rel))));
6771 35942 : }
6772 :
6773 : /*
6774 : * ATSimpleRecursion
6775 : *
6776 : * Simple table recursion sufficient for most ALTER TABLE operations.
6777 : * All direct and indirect children are processed in an unspecified order.
6778 : * Note that if a child inherits from the original table via multiple
6779 : * inheritance paths, it will be visited just once.
6780 : */
6781 : static void
6782 1290 : ATSimpleRecursion(List **wqueue, Relation rel,
6783 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6784 : AlterTableUtilityContext *context)
6785 : {
6786 : /*
6787 : * Propagate to children, if desired and if there are (or might be) any
6788 : * children.
6789 : */
6790 1290 : if (recurse && rel->rd_rel->relhassubclass)
6791 : {
6792 84 : Oid relid = RelationGetRelid(rel);
6793 : ListCell *child;
6794 : List *children;
6795 :
6796 84 : children = find_all_inheritors(relid, lockmode, NULL);
6797 :
6798 : /*
6799 : * find_all_inheritors does the recursive search of the inheritance
6800 : * hierarchy, so all we have to do is process all of the relids in the
6801 : * list that it returns.
6802 : */
6803 366 : foreach(child, children)
6804 : {
6805 282 : Oid childrelid = lfirst_oid(child);
6806 : Relation childrel;
6807 :
6808 282 : if (childrelid == relid)
6809 84 : continue;
6810 : /* find_all_inheritors already got lock */
6811 198 : childrel = relation_open(childrelid, NoLock);
6812 198 : CheckAlterTableIsSafe(childrel);
6813 198 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6814 198 : relation_close(childrel, NoLock);
6815 : }
6816 : }
6817 1290 : }
6818 :
6819 : /*
6820 : * Obtain list of partitions of the given table, locking them all at the given
6821 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6822 : *
6823 : * This function is a no-op if the given relation is not a partitioned table;
6824 : * in particular, nothing is done if it's a legacy inheritance parent.
6825 : */
6826 : static void
6827 780 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6828 : {
6829 780 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6830 : {
6831 : List *inh;
6832 : ListCell *cell;
6833 :
6834 176 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6835 : /* first element is the parent rel; must ignore it */
6836 574 : for_each_from(cell, inh, 1)
6837 : {
6838 : Relation childrel;
6839 :
6840 : /* find_all_inheritors already got lock */
6841 404 : childrel = table_open(lfirst_oid(cell), NoLock);
6842 404 : CheckAlterTableIsSafe(childrel);
6843 398 : table_close(childrel, NoLock);
6844 : }
6845 170 : list_free(inh);
6846 : }
6847 774 : }
6848 :
6849 : /*
6850 : * ATTypedTableRecursion
6851 : *
6852 : * Propagate ALTER TYPE operations to the typed tables of that type.
6853 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6854 : * recursion to inheritance children of the typed tables.
6855 : */
6856 : static void
6857 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6858 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6859 : {
6860 : ListCell *child;
6861 : List *children;
6862 :
6863 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6864 :
6865 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6866 190 : RelationGetRelationName(rel),
6867 : cmd->behavior);
6868 :
6869 202 : foreach(child, children)
6870 : {
6871 30 : Oid childrelid = lfirst_oid(child);
6872 : Relation childrel;
6873 :
6874 30 : childrel = relation_open(childrelid, lockmode);
6875 30 : CheckAlterTableIsSafe(childrel);
6876 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6877 30 : relation_close(childrel, NoLock);
6878 : }
6879 172 : }
6880 :
6881 :
6882 : /*
6883 : * find_composite_type_dependencies
6884 : *
6885 : * Check to see if the type "typeOid" is being used as a column in some table
6886 : * (possibly nested several levels deep in composite types, arrays, etc!).
6887 : * Eventually, we'd like to propagate the check or rewrite operation
6888 : * into such tables, but for now, just error out if we find any.
6889 : *
6890 : * Caller should provide either the associated relation of a rowtype,
6891 : * or a type name (not both) for use in the error message, if any.
6892 : *
6893 : * Note that "typeOid" is not necessarily a composite type; it could also be
6894 : * another container type such as an array or range, or a domain over one of
6895 : * these things. The name of this function is therefore somewhat historical,
6896 : * but it's not worth changing.
6897 : *
6898 : * We assume that functions and views depending on the type are not reasons
6899 : * to reject the ALTER. (How safe is this really?)
6900 : */
6901 : void
6902 4344 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6903 : const char *origTypeName)
6904 : {
6905 : Relation depRel;
6906 : ScanKeyData key[2];
6907 : SysScanDesc depScan;
6908 : HeapTuple depTup;
6909 :
6910 : /* since this function recurses, it could be driven to stack overflow */
6911 4344 : check_stack_depth();
6912 :
6913 : /*
6914 : * We scan pg_depend to find those things that depend on the given type.
6915 : * (We assume we can ignore refobjsubid for a type.)
6916 : */
6917 4344 : depRel = table_open(DependRelationId, AccessShareLock);
6918 :
6919 4344 : ScanKeyInit(&key[0],
6920 : Anum_pg_depend_refclassid,
6921 : BTEqualStrategyNumber, F_OIDEQ,
6922 : ObjectIdGetDatum(TypeRelationId));
6923 4344 : ScanKeyInit(&key[1],
6924 : Anum_pg_depend_refobjid,
6925 : BTEqualStrategyNumber, F_OIDEQ,
6926 : ObjectIdGetDatum(typeOid));
6927 :
6928 4344 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6929 : NULL, 2, key);
6930 :
6931 6684 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6932 : {
6933 2460 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6934 : Relation rel;
6935 : TupleDesc tupleDesc;
6936 : Form_pg_attribute att;
6937 :
6938 : /* Check for directly dependent types */
6939 2460 : if (pg_depend->classid == TypeRelationId)
6940 : {
6941 : /*
6942 : * This must be an array, domain, or range containing the given
6943 : * type, so recursively check for uses of this type. Note that
6944 : * any error message will mention the original type not the
6945 : * container; this is intentional.
6946 : */
6947 2090 : find_composite_type_dependencies(pg_depend->objid,
6948 : origRelation, origTypeName);
6949 2066 : continue;
6950 : }
6951 :
6952 : /* Else, ignore dependees that aren't relations */
6953 370 : if (pg_depend->classid != RelationRelationId)
6954 122 : continue;
6955 :
6956 248 : rel = relation_open(pg_depend->objid, AccessShareLock);
6957 248 : tupleDesc = RelationGetDescr(rel);
6958 :
6959 : /*
6960 : * If objsubid identifies a specific column, refer to that in error
6961 : * messages. Otherwise, search to see if there's a user column of the
6962 : * type. (We assume system columns are never of interesting types.)
6963 : * The search is needed because an index containing an expression
6964 : * column of the target type will just be recorded as a whole-relation
6965 : * dependency. If we do not find a column of the type, the dependency
6966 : * must indicate that the type is transiently referenced in an index
6967 : * expression but not stored on disk, which we assume is OK, just as
6968 : * we do for references in views. (It could also be that the target
6969 : * type is embedded in some container type that is stored in an index
6970 : * column, but the previous recursion should catch such cases.)
6971 : */
6972 248 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6973 90 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6974 : else
6975 : {
6976 158 : att = NULL;
6977 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6978 : {
6979 254 : att = TupleDescAttr(tupleDesc, attno - 1);
6980 254 : if (att->atttypid == typeOid && !att->attisdropped)
6981 6 : break;
6982 248 : att = NULL;
6983 : }
6984 158 : if (att == NULL)
6985 : {
6986 : /* No such column, so assume OK */
6987 152 : relation_close(rel, AccessShareLock);
6988 152 : continue;
6989 : }
6990 : }
6991 :
6992 : /*
6993 : * We definitely should reject if the relation has storage. If it's
6994 : * partitioned, then perhaps we don't have to reject: if there are
6995 : * partitions then we'll fail when we find one, else there is no
6996 : * stored data to worry about. However, it's possible that the type
6997 : * change would affect conclusions about whether the type is sortable
6998 : * or hashable and thus (if it's a partitioning column) break the
6999 : * partitioning rule. For now, reject for partitioned rels too.
7000 : */
7001 96 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7002 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7003 : {
7004 96 : if (origTypeName)
7005 30 : ereport(ERROR,
7006 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7007 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7008 : origTypeName,
7009 : RelationGetRelationName(rel),
7010 : NameStr(att->attname))));
7011 66 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7012 18 : ereport(ERROR,
7013 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7014 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7015 : RelationGetRelationName(origRelation),
7016 : RelationGetRelationName(rel),
7017 : NameStr(att->attname))));
7018 48 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7019 6 : ereport(ERROR,
7020 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7021 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7022 : RelationGetRelationName(origRelation),
7023 : RelationGetRelationName(rel),
7024 : NameStr(att->attname))));
7025 : else
7026 42 : ereport(ERROR,
7027 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7028 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7029 : RelationGetRelationName(origRelation),
7030 : RelationGetRelationName(rel),
7031 : NameStr(att->attname))));
7032 : }
7033 0 : else if (OidIsValid(rel->rd_rel->reltype))
7034 : {
7035 : /*
7036 : * A view or composite type itself isn't a problem, but we must
7037 : * recursively check for indirect dependencies via its rowtype.
7038 : */
7039 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
7040 : origRelation, origTypeName);
7041 : }
7042 :
7043 0 : relation_close(rel, AccessShareLock);
7044 : }
7045 :
7046 4224 : systable_endscan(depScan);
7047 :
7048 4224 : relation_close(depRel, AccessShareLock);
7049 4224 : }
7050 :
7051 :
7052 : /*
7053 : * find_typed_table_dependencies
7054 : *
7055 : * Check to see if a composite type is being used as the type of a
7056 : * typed table. Abort if any are found and behavior is RESTRICT.
7057 : * Else return the list of tables.
7058 : */
7059 : static List *
7060 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7061 : {
7062 : Relation classRel;
7063 : ScanKeyData key[1];
7064 : TableScanDesc scan;
7065 : HeapTuple tuple;
7066 214 : List *result = NIL;
7067 :
7068 214 : classRel = table_open(RelationRelationId, AccessShareLock);
7069 :
7070 214 : ScanKeyInit(&key[0],
7071 : Anum_pg_class_reloftype,
7072 : BTEqualStrategyNumber, F_OIDEQ,
7073 : ObjectIdGetDatum(typeOid));
7074 :
7075 214 : scan = table_beginscan_catalog(classRel, 1, key);
7076 :
7077 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7078 : {
7079 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7080 :
7081 60 : if (behavior == DROP_RESTRICT)
7082 24 : ereport(ERROR,
7083 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7084 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7085 : typeName),
7086 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7087 : else
7088 36 : result = lappend_oid(result, classform->oid);
7089 : }
7090 :
7091 190 : table_endscan(scan);
7092 190 : table_close(classRel, AccessShareLock);
7093 :
7094 190 : return result;
7095 : }
7096 :
7097 :
7098 : /*
7099 : * check_of_type
7100 : *
7101 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7102 : * isn't suitable, throw an error. Currently, we require that the type
7103 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7104 : * would require handling a number of extra corner cases in the DDL commands.
7105 : * (Also, allowing domain-over-composite would open up a can of worms about
7106 : * whether and how the domain's constraints should apply to derived tables.)
7107 : */
7108 : void
7109 182 : check_of_type(HeapTuple typetuple)
7110 : {
7111 182 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7112 182 : bool typeOk = false;
7113 :
7114 182 : if (typ->typtype == TYPTYPE_COMPOSITE)
7115 : {
7116 : Relation typeRelation;
7117 :
7118 : Assert(OidIsValid(typ->typrelid));
7119 176 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7120 176 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7121 :
7122 : /*
7123 : * Close the parent rel, but keep our AccessShareLock on it until xact
7124 : * commit. That will prevent someone else from deleting or ALTERing
7125 : * the type before the typed table creation/conversion commits.
7126 : */
7127 176 : relation_close(typeRelation, NoLock);
7128 :
7129 176 : if (!typeOk)
7130 6 : ereport(ERROR,
7131 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7132 : errmsg("type %s is the row type of another table",
7133 : format_type_be(typ->oid)),
7134 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7135 : }
7136 : else
7137 6 : ereport(ERROR,
7138 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7139 : errmsg("type %s is not a composite type",
7140 : format_type_be(typ->oid))));
7141 170 : }
7142 :
7143 :
7144 : /*
7145 : * ALTER TABLE ADD COLUMN
7146 : *
7147 : * Adds an additional attribute to a relation making the assumption that
7148 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7149 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7150 : * AlterTableCmd's.
7151 : *
7152 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7153 : * have to decide at runtime whether to recurse or not depending on whether we
7154 : * actually add a column or merely merge with an existing column. (We can't
7155 : * check this in a static pre-pass because it won't handle multiple inheritance
7156 : * situations correctly.)
7157 : */
7158 : static void
7159 2170 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7160 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7161 : AlterTableUtilityContext *context)
7162 : {
7163 2170 : if (rel->rd_rel->reloftype && !recursing)
7164 6 : ereport(ERROR,
7165 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7166 : errmsg("cannot add column to typed table")));
7167 :
7168 2164 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7169 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7170 :
7171 2158 : if (recurse && !is_view)
7172 2058 : cmd->recurse = true;
7173 2158 : }
7174 :
7175 : /*
7176 : * Add a column to a table. The return value is the address of the
7177 : * new column in the parent relation.
7178 : *
7179 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7180 : * copy (but that happens only after we check for IF NOT EXISTS).
7181 : */
7182 : static ObjectAddress
7183 2848 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7184 : AlterTableCmd **cmd, bool recurse, bool recursing,
7185 : LOCKMODE lockmode, AlterTablePass cur_pass,
7186 : AlterTableUtilityContext *context)
7187 : {
7188 2848 : Oid myrelid = RelationGetRelid(rel);
7189 2848 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7190 2848 : bool if_not_exists = (*cmd)->missing_ok;
7191 : Relation pgclass,
7192 : attrdesc;
7193 : HeapTuple reltup;
7194 : Form_pg_class relform;
7195 : Form_pg_attribute attribute;
7196 : int newattnum;
7197 : char relkind;
7198 : Expr *defval;
7199 : List *children;
7200 : ListCell *child;
7201 : AlterTableCmd *childcmd;
7202 : ObjectAddress address;
7203 : TupleDesc tupdesc;
7204 :
7205 : /* since this function recurses, it could be driven to stack overflow */
7206 2848 : check_stack_depth();
7207 :
7208 : /* At top level, permission check was done in ATPrepCmd, else do it */
7209 2848 : if (recursing)
7210 696 : ATSimplePermissions((*cmd)->subtype, rel,
7211 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7212 :
7213 2848 : if (rel->rd_rel->relispartition && !recursing)
7214 12 : ereport(ERROR,
7215 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7216 : errmsg("cannot add column to a partition")));
7217 :
7218 2836 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7219 :
7220 : /*
7221 : * Are we adding the column to a recursion child? If so, check whether to
7222 : * merge with an existing definition for the column. If we do merge, we
7223 : * must not recurse. Children will already have the column, and recursing
7224 : * into them would mess up attinhcount.
7225 : */
7226 2836 : if (colDef->inhcount > 0)
7227 : {
7228 : HeapTuple tuple;
7229 :
7230 : /* Does child already have a column by this name? */
7231 696 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7232 696 : if (HeapTupleIsValid(tuple))
7233 : {
7234 48 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7235 : Oid ctypeId;
7236 : int32 ctypmod;
7237 : Oid ccollid;
7238 :
7239 : /* Child column must match on type, typmod, and collation */
7240 48 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7241 48 : if (ctypeId != childatt->atttypid ||
7242 48 : ctypmod != childatt->atttypmod)
7243 0 : ereport(ERROR,
7244 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7245 : errmsg("child table \"%s\" has different type for column \"%s\"",
7246 : RelationGetRelationName(rel), colDef->colname)));
7247 48 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7248 48 : if (ccollid != childatt->attcollation)
7249 0 : ereport(ERROR,
7250 : (errcode(ERRCODE_COLLATION_MISMATCH),
7251 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7252 : RelationGetRelationName(rel), colDef->colname),
7253 : errdetail("\"%s\" versus \"%s\"",
7254 : get_collation_name(ccollid),
7255 : get_collation_name(childatt->attcollation))));
7256 :
7257 : /* Bump the existing child att's inhcount */
7258 48 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7259 : &childatt->attinhcount))
7260 0 : ereport(ERROR,
7261 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7262 : errmsg("too many inheritance parents"));
7263 48 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7264 :
7265 48 : heap_freetuple(tuple);
7266 :
7267 : /* Inform the user about the merge */
7268 48 : ereport(NOTICE,
7269 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7270 : colDef->colname, RelationGetRelationName(rel))));
7271 :
7272 48 : table_close(attrdesc, RowExclusiveLock);
7273 :
7274 : /* Make the child column change visible */
7275 48 : CommandCounterIncrement();
7276 :
7277 48 : return InvalidObjectAddress;
7278 : }
7279 : }
7280 :
7281 : /* skip if the name already exists and if_not_exists is true */
7282 2788 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7283 : {
7284 54 : table_close(attrdesc, RowExclusiveLock);
7285 54 : return InvalidObjectAddress;
7286 : }
7287 :
7288 : /*
7289 : * Okay, we need to add the column, so go ahead and do parse
7290 : * transformation. This can result in queueing up, or even immediately
7291 : * executing, subsidiary operations (such as creation of unique indexes);
7292 : * so we mustn't do it until we have made the if_not_exists check.
7293 : *
7294 : * When recursing, the command was already transformed and we needn't do
7295 : * so again. Also, if context isn't given we can't transform. (That
7296 : * currently happens only for AT_AddColumnToView; we expect that view.c
7297 : * passed us a ColumnDef that doesn't need work.)
7298 : */
7299 2704 : if (context != NULL && !recursing)
7300 : {
7301 2032 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7302 : cur_pass, context);
7303 : Assert(*cmd != NULL);
7304 2026 : colDef = castNode(ColumnDef, (*cmd)->def);
7305 : }
7306 :
7307 : /*
7308 : * Regular inheritance children are independent enough not to inherit the
7309 : * identity column from parent hence cannot recursively add identity
7310 : * column if the table has inheritance children.
7311 : *
7312 : * Partitions, on the other hand, are integral part of a partitioned table
7313 : * and inherit identity column. Hence propagate identity column down the
7314 : * partition hierarchy.
7315 : */
7316 2698 : if (colDef->identity &&
7317 54 : recurse &&
7318 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7319 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7320 6 : ereport(ERROR,
7321 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7322 : errmsg("cannot recursively add identity column to table that has child tables")));
7323 :
7324 2692 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7325 :
7326 2692 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7327 2692 : if (!HeapTupleIsValid(reltup))
7328 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7329 2692 : relform = (Form_pg_class) GETSTRUCT(reltup);
7330 2692 : relkind = relform->relkind;
7331 :
7332 : /* Determine the new attribute's number */
7333 2692 : newattnum = relform->relnatts + 1;
7334 2692 : if (newattnum > MaxHeapAttributeNumber)
7335 0 : ereport(ERROR,
7336 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7337 : errmsg("tables can have at most %d columns",
7338 : MaxHeapAttributeNumber)));
7339 :
7340 : /*
7341 : * Construct new attribute's pg_attribute entry.
7342 : */
7343 2692 : tupdesc = BuildDescForRelation(list_make1(colDef));
7344 :
7345 2680 : attribute = TupleDescAttr(tupdesc, 0);
7346 :
7347 : /* Fix up attribute number */
7348 2680 : attribute->attnum = newattnum;
7349 :
7350 : /* make sure datatype is legal for a column */
7351 2680 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7352 2680 : list_make1_oid(rel->rd_rel->reltype),
7353 : 0);
7354 :
7355 2650 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7356 :
7357 2650 : table_close(attrdesc, RowExclusiveLock);
7358 :
7359 : /*
7360 : * Update pg_class tuple as appropriate
7361 : */
7362 2650 : relform->relnatts = newattnum;
7363 :
7364 2650 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7365 :
7366 2650 : heap_freetuple(reltup);
7367 :
7368 : /* Post creation hook for new attribute */
7369 2650 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7370 :
7371 2650 : table_close(pgclass, RowExclusiveLock);
7372 :
7373 : /* Make the attribute's catalog entry visible */
7374 2650 : CommandCounterIncrement();
7375 :
7376 : /*
7377 : * Store the DEFAULT, if any, in the catalogs
7378 : */
7379 2650 : if (colDef->raw_default)
7380 : {
7381 : RawColumnDefault *rawEnt;
7382 :
7383 882 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7384 882 : rawEnt->attnum = attribute->attnum;
7385 882 : rawEnt->raw_default = copyObject(colDef->raw_default);
7386 882 : rawEnt->generated = colDef->generated;
7387 :
7388 : /*
7389 : * This function is intended for CREATE TABLE, so it processes a
7390 : * _list_ of defaults, but we just do one.
7391 : */
7392 882 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7393 : false, true, false, NULL);
7394 :
7395 : /* Make the additional catalog changes visible */
7396 858 : CommandCounterIncrement();
7397 : }
7398 :
7399 : /*
7400 : * Tell Phase 3 to fill in the default expression, if there is one.
7401 : *
7402 : * If there is no default, Phase 3 doesn't have to do anything, because
7403 : * that effectively means that the default is NULL. The heap tuple access
7404 : * routines always check for attnum > # of attributes in tuple, and return
7405 : * NULL if so, so without any modification of the tuple data we will get
7406 : * the effect of NULL values in the new column.
7407 : *
7408 : * An exception occurs when the new column is of a domain type: the domain
7409 : * might have a not-null constraint, or a check constraint that indirectly
7410 : * rejects nulls. If there are any domain constraints then we construct
7411 : * an explicit NULL default value that will be passed through
7412 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7413 : * rewriting the table which we really wouldn't have to do; but we do it
7414 : * to preserve the historical behavior that such a failure will be raised
7415 : * only if the table currently contains some rows.)
7416 : *
7417 : * Note: we use build_column_default, and not just the cooked default
7418 : * returned by AddRelationNewConstraints, so that the right thing happens
7419 : * when a datatype's default applies.
7420 : *
7421 : * Note: it might seem that this should happen at the end of Phase 2, so
7422 : * that the effects of subsequent subcommands can be taken into account.
7423 : * It's intentional that we do it now, though. The new column should be
7424 : * filled according to what is said in the ADD COLUMN subcommand, so that
7425 : * the effects are the same as if this subcommand had been run by itself
7426 : * and the later subcommands had been issued in new ALTER TABLE commands.
7427 : *
7428 : * We can skip this entirely for relations without storage, since Phase 3
7429 : * is certainly not going to touch them.
7430 : */
7431 2626 : if (RELKIND_HAS_STORAGE(relkind))
7432 : {
7433 : bool has_domain_constraints;
7434 2250 : bool has_missing = false;
7435 :
7436 : /*
7437 : * For an identity column, we can't use build_column_default(),
7438 : * because the sequence ownership isn't set yet. So do it manually.
7439 : */
7440 2250 : if (colDef->identity)
7441 : {
7442 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7443 :
7444 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7445 42 : nve->typeId = attribute->atttypid;
7446 :
7447 42 : defval = (Expr *) nve;
7448 : }
7449 : else
7450 2208 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7451 :
7452 : /* Build CoerceToDomain(NULL) expression if needed */
7453 2250 : has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7454 2250 : if (!defval && has_domain_constraints)
7455 : {
7456 : Oid baseTypeId;
7457 : int32 baseTypeMod;
7458 : Oid baseTypeColl;
7459 :
7460 6 : baseTypeMod = attribute->atttypmod;
7461 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7462 6 : baseTypeColl = get_typcollation(baseTypeId);
7463 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7464 6 : defval = (Expr *) coerce_to_target_type(NULL,
7465 : (Node *) defval,
7466 : baseTypeId,
7467 : attribute->atttypid,
7468 : attribute->atttypmod,
7469 : COERCION_ASSIGNMENT,
7470 : COERCE_IMPLICIT_CAST,
7471 : -1);
7472 6 : if (defval == NULL) /* should not happen */
7473 0 : elog(ERROR, "failed to coerce base type to domain");
7474 : }
7475 :
7476 2250 : if (defval)
7477 : {
7478 : NewColumnValue *newval;
7479 :
7480 : /* Prepare defval for execution, either here or in Phase 3 */
7481 766 : defval = expression_planner(defval);
7482 :
7483 : /* Add the new default to the newvals list */
7484 766 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7485 766 : newval->attnum = attribute->attnum;
7486 766 : newval->expr = defval;
7487 766 : newval->is_generated = (colDef->generated != '\0');
7488 :
7489 766 : tab->newvals = lappend(tab->newvals, newval);
7490 :
7491 : /*
7492 : * Attempt to skip a complete table rewrite by storing the
7493 : * specified DEFAULT value outside of the heap. This is only
7494 : * allowed for plain relations and non-generated columns, and the
7495 : * default expression can't be volatile (stable is OK). Note that
7496 : * contain_volatile_functions deems CoerceToDomain immutable, but
7497 : * here we consider that coercion to a domain with constraints is
7498 : * volatile; else it might fail even when the table is empty.
7499 : */
7500 766 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7501 766 : !colDef->generated &&
7502 640 : !has_domain_constraints &&
7503 628 : !contain_volatile_functions((Node *) defval))
7504 478 : {
7505 : EState *estate;
7506 : ExprState *exprState;
7507 : Datum missingval;
7508 : bool missingIsNull;
7509 :
7510 : /* Evaluate the default expression */
7511 478 : estate = CreateExecutorState();
7512 478 : exprState = ExecPrepareExpr(defval, estate);
7513 478 : missingval = ExecEvalExpr(exprState,
7514 478 : GetPerTupleExprContext(estate),
7515 : &missingIsNull);
7516 : /* If it turns out NULL, nothing to do; else store it */
7517 478 : if (!missingIsNull)
7518 : {
7519 478 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7520 478 : has_missing = true;
7521 : }
7522 478 : FreeExecutorState(estate);
7523 : }
7524 : else
7525 : {
7526 : /*
7527 : * Failed to use missing mode. We have to do a table rewrite
7528 : * to install the value --- unless it's a virtual generated
7529 : * column.
7530 : */
7531 288 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7532 198 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7533 : }
7534 : }
7535 :
7536 2250 : if (!has_missing)
7537 : {
7538 : /*
7539 : * If the new column is NOT NULL, and there is no missing value,
7540 : * tell Phase 3 it needs to check for NULLs.
7541 : */
7542 1772 : tab->verify_new_notnull |= colDef->is_not_null;
7543 : }
7544 : }
7545 :
7546 : /*
7547 : * Add needed dependency entries for the new column.
7548 : */
7549 2626 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7550 2626 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7551 :
7552 : /*
7553 : * Propagate to children as appropriate. Unlike most other ALTER
7554 : * routines, we have to do this one level of recursion at a time; we can't
7555 : * use find_all_inheritors to do it in one pass.
7556 : */
7557 : children =
7558 2626 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7559 :
7560 : /*
7561 : * If we are told not to recurse, there had better not be any child
7562 : * tables; else the addition would put them out of step.
7563 : */
7564 2626 : if (children && !recurse)
7565 12 : ereport(ERROR,
7566 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7567 : errmsg("column must be added to child tables too")));
7568 :
7569 : /* Children should see column as singly inherited */
7570 2614 : if (!recursing)
7571 : {
7572 1966 : childcmd = copyObject(*cmd);
7573 1966 : colDef = castNode(ColumnDef, childcmd->def);
7574 1966 : colDef->inhcount = 1;
7575 1966 : colDef->is_local = false;
7576 : }
7577 : else
7578 648 : childcmd = *cmd; /* no need to copy again */
7579 :
7580 3310 : foreach(child, children)
7581 : {
7582 696 : Oid childrelid = lfirst_oid(child);
7583 : Relation childrel;
7584 : AlteredTableInfo *childtab;
7585 :
7586 : /* find_inheritance_children already got lock */
7587 696 : childrel = table_open(childrelid, NoLock);
7588 696 : CheckAlterTableIsSafe(childrel);
7589 :
7590 : /* Find or create work queue entry for this table */
7591 696 : childtab = ATGetQueueEntry(wqueue, childrel);
7592 :
7593 : /* Recurse to child; return value is ignored */
7594 696 : ATExecAddColumn(wqueue, childtab, childrel,
7595 : &childcmd, recurse, true,
7596 : lockmode, cur_pass, context);
7597 :
7598 696 : table_close(childrel, NoLock);
7599 : }
7600 :
7601 2614 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7602 2614 : return address;
7603 : }
7604 :
7605 : /*
7606 : * If a new or renamed column will collide with the name of an existing
7607 : * column and if_not_exists is false then error out, else do nothing.
7608 : */
7609 : static bool
7610 3238 : check_for_column_name_collision(Relation rel, const char *colname,
7611 : bool if_not_exists)
7612 : {
7613 : HeapTuple attTuple;
7614 : int attnum;
7615 :
7616 : /*
7617 : * this test is deliberately not attisdropped-aware, since if one tries to
7618 : * add a column matching a dropped column name, it's gonna fail anyway.
7619 : */
7620 3238 : attTuple = SearchSysCache2(ATTNAME,
7621 : ObjectIdGetDatum(RelationGetRelid(rel)),
7622 : PointerGetDatum(colname));
7623 3238 : if (!HeapTupleIsValid(attTuple))
7624 3142 : return true;
7625 :
7626 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7627 96 : ReleaseSysCache(attTuple);
7628 :
7629 : /*
7630 : * We throw a different error message for conflicts with system column
7631 : * names, since they are normally not shown and the user might otherwise
7632 : * be confused about the reason for the conflict.
7633 : */
7634 96 : if (attnum <= 0)
7635 12 : ereport(ERROR,
7636 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7637 : errmsg("column name \"%s\" conflicts with a system column name",
7638 : colname)));
7639 : else
7640 : {
7641 84 : if (if_not_exists)
7642 : {
7643 54 : ereport(NOTICE,
7644 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7645 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7646 : colname, RelationGetRelationName(rel))));
7647 54 : return false;
7648 : }
7649 :
7650 30 : ereport(ERROR,
7651 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7652 : errmsg("column \"%s\" of relation \"%s\" already exists",
7653 : colname, RelationGetRelationName(rel))));
7654 : }
7655 :
7656 : return true;
7657 : }
7658 :
7659 : /*
7660 : * Install a column's dependency on its datatype.
7661 : */
7662 : static void
7663 3632 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7664 : {
7665 : ObjectAddress myself,
7666 : referenced;
7667 :
7668 3632 : myself.classId = RelationRelationId;
7669 3632 : myself.objectId = relid;
7670 3632 : myself.objectSubId = attnum;
7671 3632 : referenced.classId = TypeRelationId;
7672 3632 : referenced.objectId = typid;
7673 3632 : referenced.objectSubId = 0;
7674 3632 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7675 3632 : }
7676 :
7677 : /*
7678 : * Install a column's dependency on its collation.
7679 : */
7680 : static void
7681 3632 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7682 : {
7683 : ObjectAddress myself,
7684 : referenced;
7685 :
7686 : /* We know the default collation is pinned, so don't bother recording it */
7687 3632 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7688 : {
7689 18 : myself.classId = RelationRelationId;
7690 18 : myself.objectId = relid;
7691 18 : myself.objectSubId = attnum;
7692 18 : referenced.classId = CollationRelationId;
7693 18 : referenced.objectId = collid;
7694 18 : referenced.objectSubId = 0;
7695 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7696 : }
7697 3632 : }
7698 :
7699 : /*
7700 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7701 : *
7702 : * Return the address of the modified column. If the column was already
7703 : * nullable, InvalidObjectAddress is returned.
7704 : */
7705 : static ObjectAddress
7706 262 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7707 : LOCKMODE lockmode)
7708 : {
7709 : HeapTuple tuple;
7710 : HeapTuple conTup;
7711 : Form_pg_attribute attTup;
7712 : AttrNumber attnum;
7713 : Relation attr_rel;
7714 : ObjectAddress address;
7715 :
7716 : /*
7717 : * lookup the attribute
7718 : */
7719 262 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7720 :
7721 262 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7722 262 : if (!HeapTupleIsValid(tuple))
7723 18 : ereport(ERROR,
7724 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7725 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7726 : colName, RelationGetRelationName(rel))));
7727 244 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7728 244 : attnum = attTup->attnum;
7729 244 : ObjectAddressSubSet(address, RelationRelationId,
7730 : RelationGetRelid(rel), attnum);
7731 :
7732 : /* If the column is already nullable there's nothing to do. */
7733 244 : if (!attTup->attnotnull)
7734 : {
7735 0 : table_close(attr_rel, RowExclusiveLock);
7736 0 : return InvalidObjectAddress;
7737 : }
7738 :
7739 : /* Prevent them from altering a system attribute */
7740 244 : if (attnum <= 0)
7741 0 : ereport(ERROR,
7742 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7743 : errmsg("cannot alter system column \"%s\"",
7744 : colName)));
7745 :
7746 244 : if (attTup->attidentity)
7747 18 : ereport(ERROR,
7748 : (errcode(ERRCODE_SYNTAX_ERROR),
7749 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7750 : colName, RelationGetRelationName(rel))));
7751 :
7752 : /*
7753 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7754 : */
7755 226 : if (rel->rd_rel->relispartition)
7756 : {
7757 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7758 12 : Relation parent = table_open(parentId, AccessShareLock);
7759 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7760 : AttrNumber parent_attnum;
7761 :
7762 12 : parent_attnum = get_attnum(parentId, colName);
7763 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7764 12 : ereport(ERROR,
7765 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7766 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7767 : colName)));
7768 0 : table_close(parent, AccessShareLock);
7769 : }
7770 :
7771 : /*
7772 : * Find the constraint that makes this column NOT NULL, and drop it.
7773 : * dropconstraint_internal() resets attnotnull.
7774 : */
7775 214 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7776 214 : if (conTup == NULL)
7777 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7778 : colName, RelationGetRelationName(rel));
7779 :
7780 : /* The normal case: we have a pg_constraint row, remove it */
7781 214 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7782 : false, lockmode);
7783 160 : heap_freetuple(conTup);
7784 :
7785 160 : InvokeObjectPostAlterHook(RelationRelationId,
7786 : RelationGetRelid(rel), attnum);
7787 :
7788 160 : table_close(attr_rel, RowExclusiveLock);
7789 :
7790 160 : return address;
7791 : }
7792 :
7793 : /*
7794 : * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7795 : * to verify it.
7796 : *
7797 : * When called to alter an existing table, 'wqueue' must be given so that we
7798 : * can queue a check that existing tuples pass the constraint. When called
7799 : * from table creation, 'wqueue' should be passed as NULL.
7800 : */
7801 : static void
7802 23614 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7803 : LOCKMODE lockmode)
7804 : {
7805 : Form_pg_attribute attr;
7806 :
7807 23614 : CheckAlterTableIsSafe(rel);
7808 :
7809 : /*
7810 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7811 : * attribute.
7812 : */
7813 23614 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7814 23614 : if (attr->attisdropped)
7815 0 : return;
7816 :
7817 23614 : if (!attr->attnotnull)
7818 : {
7819 : Relation attr_rel;
7820 : HeapTuple tuple;
7821 :
7822 1268 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7823 :
7824 1268 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7825 1268 : if (!HeapTupleIsValid(tuple))
7826 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7827 : attnum, RelationGetRelid(rel));
7828 :
7829 1268 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7830 : Assert(!attr->attnotnull);
7831 1268 : attr->attnotnull = true;
7832 1268 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7833 :
7834 : /*
7835 : * If the nullness isn't already proven by validated constraints, have
7836 : * ALTER TABLE phase 3 test for it.
7837 : */
7838 1268 : if (wqueue && !NotNullImpliedByRelConstraints(rel, attr))
7839 : {
7840 : AlteredTableInfo *tab;
7841 :
7842 1164 : tab = ATGetQueueEntry(wqueue, rel);
7843 1164 : tab->verify_new_notnull = true;
7844 : }
7845 :
7846 1268 : CommandCounterIncrement();
7847 :
7848 1268 : table_close(attr_rel, RowExclusiveLock);
7849 1268 : heap_freetuple(tuple);
7850 : }
7851 : }
7852 :
7853 : /*
7854 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7855 : *
7856 : * Add a not-null constraint to a single table and its children. Returns
7857 : * the address of the constraint added to the parent relation, if one gets
7858 : * added, or InvalidObjectAddress otherwise.
7859 : *
7860 : * We must recurse to child tables during execution, rather than using
7861 : * ALTER TABLE's normal prep-time recursion.
7862 : */
7863 : static ObjectAddress
7864 676 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7865 : bool recurse, bool recursing, LOCKMODE lockmode)
7866 : {
7867 : HeapTuple tuple;
7868 : AttrNumber attnum;
7869 : ObjectAddress address;
7870 : Constraint *constraint;
7871 : CookedConstraint *ccon;
7872 : List *cooked;
7873 676 : bool is_no_inherit = false;
7874 :
7875 : /* Guard against stack overflow due to overly deep inheritance tree. */
7876 676 : check_stack_depth();
7877 :
7878 : /* At top level, permission check was done in ATPrepCmd, else do it */
7879 676 : if (recursing)
7880 : {
7881 292 : ATSimplePermissions(AT_AddConstraint, rel,
7882 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7883 : Assert(conName != NULL);
7884 : }
7885 :
7886 676 : attnum = get_attnum(RelationGetRelid(rel), colName);
7887 676 : if (attnum == InvalidAttrNumber)
7888 18 : ereport(ERROR,
7889 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7890 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7891 : colName, RelationGetRelationName(rel))));
7892 :
7893 : /* Prevent them from altering a system attribute */
7894 658 : if (attnum <= 0)
7895 0 : ereport(ERROR,
7896 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7897 : errmsg("cannot alter system column \"%s\"",
7898 : colName)));
7899 :
7900 : /* See if there's already a constraint */
7901 658 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7902 658 : if (HeapTupleIsValid(tuple))
7903 : {
7904 128 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7905 128 : bool changed = false;
7906 :
7907 : /*
7908 : * Don't let a NO INHERIT constraint be changed into inherit.
7909 : */
7910 128 : if (conForm->connoinherit && recurse)
7911 12 : ereport(ERROR,
7912 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7913 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7914 : NameStr(conForm->conname),
7915 : RelationGetRelationName(rel)));
7916 :
7917 : /*
7918 : * If we find an appropriate constraint, we're almost done, but just
7919 : * need to change some properties on it: if we're recursing, increment
7920 : * coninhcount; if not, set conislocal if not already set.
7921 : */
7922 116 : if (recursing)
7923 : {
7924 96 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7925 : &conForm->coninhcount))
7926 0 : ereport(ERROR,
7927 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7928 : errmsg("too many inheritance parents"));
7929 96 : changed = true;
7930 : }
7931 20 : else if (!conForm->conislocal)
7932 : {
7933 0 : conForm->conislocal = true;
7934 0 : changed = true;
7935 : }
7936 :
7937 116 : if (changed)
7938 : {
7939 : Relation constr_rel;
7940 :
7941 96 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7942 :
7943 96 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7944 96 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7945 96 : table_close(constr_rel, RowExclusiveLock);
7946 : }
7947 :
7948 116 : if (changed)
7949 96 : return address;
7950 : else
7951 20 : return InvalidObjectAddress;
7952 : }
7953 :
7954 : /*
7955 : * If we're asked not to recurse, and children exist, raise an error for
7956 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
7957 : * specified.
7958 : */
7959 554 : if (!recurse &&
7960 24 : find_inheritance_children(RelationGetRelid(rel),
7961 : NoLock) != NIL)
7962 : {
7963 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7964 6 : ereport(ERROR,
7965 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7966 : errmsg("constraint must be added to child tables too"),
7967 : errhint("Do not specify the ONLY keyword."));
7968 : else
7969 12 : is_no_inherit = true;
7970 : }
7971 :
7972 : /*
7973 : * No constraint exists; we must add one. First determine a name to use,
7974 : * if we haven't already.
7975 : */
7976 524 : if (!recursing)
7977 : {
7978 : Assert(conName == NULL);
7979 334 : conName = ChooseConstraintName(RelationGetRelationName(rel),
7980 : colName, "not_null",
7981 334 : RelationGetNamespace(rel),
7982 : NIL);
7983 : }
7984 :
7985 524 : constraint = makeNotNullConstraint(makeString(colName));
7986 524 : constraint->is_no_inherit = is_no_inherit;
7987 524 : constraint->conname = conName;
7988 :
7989 : /* and do it */
7990 524 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7991 524 : false, !recursing, false, NULL);
7992 524 : ccon = linitial(cooked);
7993 524 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7994 :
7995 524 : InvokeObjectPostAlterHook(RelationRelationId,
7996 : RelationGetRelid(rel), attnum);
7997 :
7998 : /* Mark pg_attribute.attnotnull for the column */
7999 524 : set_attnotnull(wqueue, rel, attnum, lockmode);
8000 :
8001 : /*
8002 : * Recurse to propagate the constraint to children that don't have one.
8003 : */
8004 524 : if (recurse)
8005 : {
8006 : List *children;
8007 :
8008 506 : children = find_inheritance_children(RelationGetRelid(rel),
8009 : lockmode);
8010 :
8011 1244 : foreach_oid(childoid, children)
8012 : {
8013 244 : Relation childrel = table_open(childoid, NoLock);
8014 :
8015 244 : CommandCounterIncrement();
8016 :
8017 244 : ATExecSetNotNull(wqueue, childrel, conName, colName,
8018 : recurse, true, lockmode);
8019 238 : table_close(childrel, NoLock);
8020 : }
8021 : }
8022 :
8023 518 : return address;
8024 : }
8025 :
8026 : /*
8027 : * NotNullImpliedByRelConstraints
8028 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8029 : */
8030 : static bool
8031 1214 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8032 : {
8033 1214 : NullTest *nnulltest = makeNode(NullTest);
8034 :
8035 2428 : nnulltest->arg = (Expr *) makeVar(1,
8036 1214 : attr->attnum,
8037 : attr->atttypid,
8038 : attr->atttypmod,
8039 : attr->attcollation,
8040 : 0);
8041 1214 : nnulltest->nulltesttype = IS_NOT_NULL;
8042 :
8043 : /*
8044 : * argisrow = false is correct even for a composite column, because
8045 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8046 : * case, just IS DISTINCT FROM NULL.
8047 : */
8048 1214 : nnulltest->argisrow = false;
8049 1214 : nnulltest->location = -1;
8050 :
8051 1214 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8052 : {
8053 50 : ereport(DEBUG1,
8054 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8055 : RelationGetRelationName(rel), NameStr(attr->attname))));
8056 50 : return true;
8057 : }
8058 :
8059 1164 : return false;
8060 : }
8061 :
8062 : /*
8063 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8064 : *
8065 : * Return the address of the affected column.
8066 : */
8067 : static ObjectAddress
8068 584 : ATExecColumnDefault(Relation rel, const char *colName,
8069 : Node *newDefault, LOCKMODE lockmode)
8070 : {
8071 584 : TupleDesc tupdesc = RelationGetDescr(rel);
8072 : AttrNumber attnum;
8073 : ObjectAddress address;
8074 :
8075 : /*
8076 : * get the number of the attribute
8077 : */
8078 584 : attnum = get_attnum(RelationGetRelid(rel), colName);
8079 584 : if (attnum == InvalidAttrNumber)
8080 30 : ereport(ERROR,
8081 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8082 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8083 : colName, RelationGetRelationName(rel))));
8084 :
8085 : /* Prevent them from altering a system attribute */
8086 554 : if (attnum <= 0)
8087 0 : ereport(ERROR,
8088 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8089 : errmsg("cannot alter system column \"%s\"",
8090 : colName)));
8091 :
8092 554 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8093 18 : ereport(ERROR,
8094 : (errcode(ERRCODE_SYNTAX_ERROR),
8095 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8096 : colName, RelationGetRelationName(rel)),
8097 : /* translator: %s is an SQL ALTER command */
8098 : newDefault ? 0 : errhint("Use %s instead.",
8099 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8100 :
8101 536 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8102 12 : ereport(ERROR,
8103 : (errcode(ERRCODE_SYNTAX_ERROR),
8104 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8105 : colName, RelationGetRelationName(rel)),
8106 : newDefault ?
8107 : /* translator: %s is an SQL ALTER command */
8108 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8109 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8110 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8111 :
8112 : /*
8113 : * Remove any old default for the column. We use RESTRICT here for
8114 : * safety, but at present we do not expect anything to depend on the
8115 : * default.
8116 : *
8117 : * We treat removing the existing default as an internal operation when it
8118 : * is preparatory to adding a new default, but as a user-initiated
8119 : * operation when the user asked for a drop.
8120 : */
8121 524 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8122 : newDefault != NULL);
8123 :
8124 524 : if (newDefault)
8125 : {
8126 : /* SET DEFAULT */
8127 : RawColumnDefault *rawEnt;
8128 :
8129 350 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8130 350 : rawEnt->attnum = attnum;
8131 350 : rawEnt->raw_default = newDefault;
8132 350 : rawEnt->generated = '\0';
8133 :
8134 : /*
8135 : * This function is intended for CREATE TABLE, so it processes a
8136 : * _list_ of defaults, but we just do one.
8137 : */
8138 350 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8139 : false, true, false, NULL);
8140 : }
8141 :
8142 518 : ObjectAddressSubSet(address, RelationRelationId,
8143 : RelationGetRelid(rel), attnum);
8144 518 : return address;
8145 : }
8146 :
8147 : /*
8148 : * Add a pre-cooked default expression.
8149 : *
8150 : * Return the address of the affected column.
8151 : */
8152 : static ObjectAddress
8153 80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8154 : Node *newDefault)
8155 : {
8156 : ObjectAddress address;
8157 :
8158 : /* We assume no checking is required */
8159 :
8160 : /*
8161 : * Remove any old default for the column. We use RESTRICT here for
8162 : * safety, but at present we do not expect anything to depend on the
8163 : * default. (In ordinary cases, there could not be a default in place
8164 : * anyway, but it's possible when combining LIKE with inheritance.)
8165 : */
8166 80 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8167 : true);
8168 :
8169 80 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8170 :
8171 80 : ObjectAddressSubSet(address, RelationRelationId,
8172 : RelationGetRelid(rel), attnum);
8173 80 : return address;
8174 : }
8175 :
8176 : /*
8177 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8178 : *
8179 : * Return the address of the affected column.
8180 : */
8181 : static ObjectAddress
8182 160 : ATExecAddIdentity(Relation rel, const char *colName,
8183 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8184 : {
8185 : Relation attrelation;
8186 : HeapTuple tuple;
8187 : Form_pg_attribute attTup;
8188 : AttrNumber attnum;
8189 : ObjectAddress address;
8190 160 : ColumnDef *cdef = castNode(ColumnDef, def);
8191 : bool ispartitioned;
8192 :
8193 160 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8194 160 : if (ispartitioned && !recurse)
8195 6 : ereport(ERROR,
8196 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8197 : errmsg("cannot add identity to a column of only the partitioned table"),
8198 : errhint("Do not specify the ONLY keyword.")));
8199 :
8200 154 : if (rel->rd_rel->relispartition && !recursing)
8201 12 : ereport(ERROR,
8202 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8203 : errmsg("cannot add identity to a column of a partition"));
8204 :
8205 142 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8206 :
8207 142 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8208 142 : if (!HeapTupleIsValid(tuple))
8209 0 : ereport(ERROR,
8210 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8211 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8212 : colName, RelationGetRelationName(rel))));
8213 142 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8214 142 : attnum = attTup->attnum;
8215 :
8216 : /* Can't alter a system attribute */
8217 142 : if (attnum <= 0)
8218 0 : ereport(ERROR,
8219 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8220 : errmsg("cannot alter system column \"%s\"",
8221 : colName)));
8222 :
8223 : /*
8224 : * Creating a column as identity implies NOT NULL, so adding the identity
8225 : * to an existing column that is not NOT NULL would create a state that
8226 : * cannot be reproduced without contortions.
8227 : */
8228 142 : if (!attTup->attnotnull)
8229 6 : ereport(ERROR,
8230 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8231 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8232 : colName, RelationGetRelationName(rel))));
8233 :
8234 136 : if (attTup->attidentity)
8235 18 : ereport(ERROR,
8236 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8237 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8238 : colName, RelationGetRelationName(rel))));
8239 :
8240 118 : if (attTup->atthasdef)
8241 6 : ereport(ERROR,
8242 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8243 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8244 : colName, RelationGetRelationName(rel))));
8245 :
8246 112 : attTup->attidentity = cdef->identity;
8247 112 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8248 :
8249 112 : InvokeObjectPostAlterHook(RelationRelationId,
8250 : RelationGetRelid(rel),
8251 : attTup->attnum);
8252 112 : ObjectAddressSubSet(address, RelationRelationId,
8253 : RelationGetRelid(rel), attnum);
8254 112 : heap_freetuple(tuple);
8255 :
8256 112 : table_close(attrelation, RowExclusiveLock);
8257 :
8258 : /*
8259 : * Recurse to propagate the identity column to partitions. Identity is
8260 : * not inherited in regular inheritance children.
8261 : */
8262 112 : if (recurse && ispartitioned)
8263 : {
8264 : List *children;
8265 : ListCell *lc;
8266 :
8267 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8268 :
8269 16 : foreach(lc, children)
8270 : {
8271 : Relation childrel;
8272 :
8273 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8274 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8275 6 : table_close(childrel, NoLock);
8276 : }
8277 : }
8278 :
8279 112 : return address;
8280 : }
8281 :
8282 : /*
8283 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8284 : *
8285 : * Return the address of the affected column.
8286 : */
8287 : static ObjectAddress
8288 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8289 : LOCKMODE lockmode, bool recurse, bool recursing)
8290 : {
8291 : ListCell *option;
8292 74 : DefElem *generatedEl = NULL;
8293 : HeapTuple tuple;
8294 : Form_pg_attribute attTup;
8295 : AttrNumber attnum;
8296 : Relation attrelation;
8297 : ObjectAddress address;
8298 : bool ispartitioned;
8299 :
8300 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8301 74 : if (ispartitioned && !recurse)
8302 6 : ereport(ERROR,
8303 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8304 : errmsg("cannot change identity column of only the partitioned table"),
8305 : errhint("Do not specify the ONLY keyword.")));
8306 :
8307 68 : if (rel->rd_rel->relispartition && !recursing)
8308 12 : ereport(ERROR,
8309 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8310 : errmsg("cannot change identity column of a partition"));
8311 :
8312 100 : foreach(option, castNode(List, def))
8313 : {
8314 44 : DefElem *defel = lfirst_node(DefElem, option);
8315 :
8316 44 : if (strcmp(defel->defname, "generated") == 0)
8317 : {
8318 44 : if (generatedEl)
8319 0 : ereport(ERROR,
8320 : (errcode(ERRCODE_SYNTAX_ERROR),
8321 : errmsg("conflicting or redundant options")));
8322 44 : generatedEl = defel;
8323 : }
8324 : else
8325 0 : elog(ERROR, "option \"%s\" not recognized",
8326 : defel->defname);
8327 : }
8328 :
8329 : /*
8330 : * Even if there is nothing to change here, we run all the checks. There
8331 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8332 : * there.
8333 : */
8334 :
8335 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8336 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8337 56 : if (!HeapTupleIsValid(tuple))
8338 0 : ereport(ERROR,
8339 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8340 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8341 : colName, RelationGetRelationName(rel))));
8342 :
8343 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8344 56 : attnum = attTup->attnum;
8345 :
8346 56 : if (attnum <= 0)
8347 0 : ereport(ERROR,
8348 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8349 : errmsg("cannot alter system column \"%s\"",
8350 : colName)));
8351 :
8352 56 : if (!attTup->attidentity)
8353 6 : ereport(ERROR,
8354 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8355 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8356 : colName, RelationGetRelationName(rel))));
8357 :
8358 50 : if (generatedEl)
8359 : {
8360 44 : attTup->attidentity = defGetInt32(generatedEl);
8361 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8362 :
8363 44 : InvokeObjectPostAlterHook(RelationRelationId,
8364 : RelationGetRelid(rel),
8365 : attTup->attnum);
8366 44 : ObjectAddressSubSet(address, RelationRelationId,
8367 : RelationGetRelid(rel), attnum);
8368 : }
8369 : else
8370 6 : address = InvalidObjectAddress;
8371 :
8372 50 : heap_freetuple(tuple);
8373 50 : table_close(attrelation, RowExclusiveLock);
8374 :
8375 : /*
8376 : * Recurse to propagate the identity change to partitions. Identity is not
8377 : * inherited in regular inheritance children.
8378 : */
8379 50 : if (generatedEl && recurse && ispartitioned)
8380 : {
8381 : List *children;
8382 : ListCell *lc;
8383 :
8384 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8385 :
8386 18 : foreach(lc, children)
8387 : {
8388 : Relation childrel;
8389 :
8390 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8391 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8392 12 : table_close(childrel, NoLock);
8393 : }
8394 : }
8395 :
8396 50 : return address;
8397 : }
8398 :
8399 : /*
8400 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8401 : *
8402 : * Return the address of the affected column.
8403 : */
8404 : static ObjectAddress
8405 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8406 : bool recurse, bool recursing)
8407 : {
8408 : HeapTuple tuple;
8409 : Form_pg_attribute attTup;
8410 : AttrNumber attnum;
8411 : Relation attrelation;
8412 : ObjectAddress address;
8413 : Oid seqid;
8414 : ObjectAddress seqaddress;
8415 : bool ispartitioned;
8416 :
8417 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8418 68 : if (ispartitioned && !recurse)
8419 6 : ereport(ERROR,
8420 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8421 : errmsg("cannot drop identity from a column of only the partitioned table"),
8422 : errhint("Do not specify the ONLY keyword.")));
8423 :
8424 62 : if (rel->rd_rel->relispartition && !recursing)
8425 6 : ereport(ERROR,
8426 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8427 : errmsg("cannot drop identity from a column of a partition"));
8428 :
8429 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8430 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8431 56 : if (!HeapTupleIsValid(tuple))
8432 0 : ereport(ERROR,
8433 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8434 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8435 : colName, RelationGetRelationName(rel))));
8436 :
8437 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8438 56 : attnum = attTup->attnum;
8439 :
8440 56 : if (attnum <= 0)
8441 0 : ereport(ERROR,
8442 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8443 : errmsg("cannot alter system column \"%s\"",
8444 : colName)));
8445 :
8446 56 : if (!attTup->attidentity)
8447 : {
8448 12 : if (!missing_ok)
8449 6 : ereport(ERROR,
8450 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8451 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8452 : colName, RelationGetRelationName(rel))));
8453 : else
8454 : {
8455 6 : ereport(NOTICE,
8456 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8457 : colName, RelationGetRelationName(rel))));
8458 6 : heap_freetuple(tuple);
8459 6 : table_close(attrelation, RowExclusiveLock);
8460 6 : return InvalidObjectAddress;
8461 : }
8462 : }
8463 :
8464 44 : attTup->attidentity = '\0';
8465 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8466 :
8467 44 : InvokeObjectPostAlterHook(RelationRelationId,
8468 : RelationGetRelid(rel),
8469 : attTup->attnum);
8470 44 : ObjectAddressSubSet(address, RelationRelationId,
8471 : RelationGetRelid(rel), attnum);
8472 44 : heap_freetuple(tuple);
8473 :
8474 44 : table_close(attrelation, RowExclusiveLock);
8475 :
8476 : /*
8477 : * Recurse to drop the identity from column in partitions. Identity is
8478 : * not inherited in regular inheritance children so ignore them.
8479 : */
8480 44 : if (recurse && ispartitioned)
8481 : {
8482 : List *children;
8483 : ListCell *lc;
8484 :
8485 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8486 :
8487 12 : foreach(lc, children)
8488 : {
8489 : Relation childrel;
8490 :
8491 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8492 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8493 6 : table_close(childrel, NoLock);
8494 : }
8495 : }
8496 :
8497 44 : if (!recursing)
8498 : {
8499 : /* drop the internal sequence */
8500 32 : seqid = getIdentitySequence(rel, attnum, false);
8501 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8502 : RelationRelationId, DEPENDENCY_INTERNAL);
8503 32 : CommandCounterIncrement();
8504 32 : seqaddress.classId = RelationRelationId;
8505 32 : seqaddress.objectId = seqid;
8506 32 : seqaddress.objectSubId = 0;
8507 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8508 : }
8509 :
8510 44 : return address;
8511 : }
8512 :
8513 : /*
8514 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8515 : *
8516 : * Return the address of the affected column.
8517 : */
8518 : static ObjectAddress
8519 180 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8520 : Node *newExpr, LOCKMODE lockmode)
8521 : {
8522 : HeapTuple tuple;
8523 : Form_pg_attribute attTup;
8524 : AttrNumber attnum;
8525 : char attgenerated;
8526 : bool rewrite;
8527 : Oid attrdefoid;
8528 : ObjectAddress address;
8529 : Expr *defval;
8530 : NewColumnValue *newval;
8531 : RawColumnDefault *rawEnt;
8532 :
8533 180 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8534 180 : if (!HeapTupleIsValid(tuple))
8535 0 : ereport(ERROR,
8536 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8537 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8538 : colName, RelationGetRelationName(rel))));
8539 :
8540 180 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8541 :
8542 180 : attnum = attTup->attnum;
8543 180 : if (attnum <= 0)
8544 0 : ereport(ERROR,
8545 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8546 : errmsg("cannot alter system column \"%s\"",
8547 : colName)));
8548 :
8549 180 : attgenerated = attTup->attgenerated;
8550 180 : if (!attgenerated)
8551 12 : ereport(ERROR,
8552 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8553 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8554 : colName, RelationGetRelationName(rel))));
8555 :
8556 : /*
8557 : * TODO: This could be done, just need to recheck any constraints
8558 : * afterwards.
8559 : */
8560 168 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8561 90 : rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8562 12 : ereport(ERROR,
8563 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8564 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables with check constraints"),
8565 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8566 : colName, RelationGetRelationName(rel))));
8567 :
8568 156 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8569 24 : tab->verify_new_notnull = true;
8570 :
8571 : /*
8572 : * We need to prevent this because a change of expression could affect a
8573 : * row filter and inject expressions that are not permitted in a row
8574 : * filter. XXX We could try to have a more precise check to catch only
8575 : * publications with row filters, or even re-verify the row filter
8576 : * expressions.
8577 : */
8578 234 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8579 78 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8580 6 : ereport(ERROR,
8581 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8582 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables that are part of a publication"),
8583 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8584 : colName, RelationGetRelationName(rel))));
8585 :
8586 150 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8587 :
8588 150 : ReleaseSysCache(tuple);
8589 :
8590 150 : if (rewrite)
8591 : {
8592 : /*
8593 : * Clear all the missing values if we're rewriting the table, since
8594 : * this renders them pointless.
8595 : */
8596 78 : RelationClearMissing(rel);
8597 :
8598 : /* make sure we don't conflict with later attribute modifications */
8599 78 : CommandCounterIncrement();
8600 :
8601 : /*
8602 : * Find everything that depends on the column (constraints, indexes,
8603 : * etc), and record enough information to let us recreate the objects
8604 : * after rewrite.
8605 : */
8606 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8607 : }
8608 :
8609 : /*
8610 : * Drop the dependency records of the GENERATED expression, in particular
8611 : * its INTERNAL dependency on the column, which would otherwise cause
8612 : * dependency.c to refuse to perform the deletion.
8613 : */
8614 150 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8615 150 : if (!OidIsValid(attrdefoid))
8616 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8617 : RelationGetRelid(rel), attnum);
8618 150 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8619 :
8620 : /* Make above changes visible */
8621 150 : CommandCounterIncrement();
8622 :
8623 : /*
8624 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8625 : * safety, but at present we do not expect anything to depend on the
8626 : * expression.
8627 : */
8628 150 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8629 : false, false);
8630 :
8631 : /* Prepare to store the new expression, in the catalogs */
8632 150 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8633 150 : rawEnt->attnum = attnum;
8634 150 : rawEnt->raw_default = newExpr;
8635 150 : rawEnt->generated = attgenerated;
8636 :
8637 : /* Store the generated expression */
8638 150 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8639 : false, true, false, NULL);
8640 :
8641 : /* Make above new expression visible */
8642 150 : CommandCounterIncrement();
8643 :
8644 150 : if (rewrite)
8645 : {
8646 : /* Prepare for table rewrite */
8647 78 : defval = (Expr *) build_column_default(rel, attnum);
8648 :
8649 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8650 78 : newval->attnum = attnum;
8651 78 : newval->expr = expression_planner(defval);
8652 78 : newval->is_generated = true;
8653 :
8654 78 : tab->newvals = lappend(tab->newvals, newval);
8655 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8656 : }
8657 :
8658 : /* Drop any pg_statistic entry for the column */
8659 150 : RemoveStatistics(RelationGetRelid(rel), attnum);
8660 :
8661 150 : InvokeObjectPostAlterHook(RelationRelationId,
8662 : RelationGetRelid(rel), attnum);
8663 :
8664 150 : ObjectAddressSubSet(address, RelationRelationId,
8665 : RelationGetRelid(rel), attnum);
8666 150 : return address;
8667 : }
8668 :
8669 : /*
8670 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8671 : */
8672 : static void
8673 86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8674 : {
8675 : /*
8676 : * Reject ONLY if there are child tables. We could implement this, but it
8677 : * is a bit complicated. GENERATED clauses must be attached to the column
8678 : * definition and cannot be added later like DEFAULT, so if a child table
8679 : * has a generation expression that the parent does not have, the child
8680 : * column will necessarily be an attislocal column. So to implement ONLY
8681 : * here, we'd need extra code to update attislocal of the direct child
8682 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8683 : * resulting state can be properly dumped and restored.
8684 : */
8685 110 : if (!recurse &&
8686 24 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8687 12 : ereport(ERROR,
8688 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8689 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8690 :
8691 : /*
8692 : * Cannot drop generation expression from inherited columns.
8693 : */
8694 74 : if (!recursing)
8695 : {
8696 : HeapTuple tuple;
8697 : Form_pg_attribute attTup;
8698 :
8699 62 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8700 62 : if (!HeapTupleIsValid(tuple))
8701 0 : ereport(ERROR,
8702 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8703 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8704 : cmd->name, RelationGetRelationName(rel))));
8705 :
8706 62 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8707 :
8708 62 : if (attTup->attinhcount > 0)
8709 12 : ereport(ERROR,
8710 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8711 : errmsg("cannot drop generation expression from inherited column")));
8712 : }
8713 62 : }
8714 :
8715 : /*
8716 : * Return the address of the affected column.
8717 : */
8718 : static ObjectAddress
8719 56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8720 : {
8721 : HeapTuple tuple;
8722 : Form_pg_attribute attTup;
8723 : AttrNumber attnum;
8724 : Relation attrelation;
8725 : Oid attrdefoid;
8726 : ObjectAddress address;
8727 :
8728 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8729 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8730 56 : if (!HeapTupleIsValid(tuple))
8731 0 : ereport(ERROR,
8732 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8733 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8734 : colName, RelationGetRelationName(rel))));
8735 :
8736 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8737 56 : attnum = attTup->attnum;
8738 :
8739 56 : if (attnum <= 0)
8740 0 : ereport(ERROR,
8741 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8742 : errmsg("cannot alter system column \"%s\"",
8743 : colName)));
8744 :
8745 : /*
8746 : * TODO: This could be done, but it would need a table rewrite to
8747 : * materialize the generated values. Note that for the time being, we
8748 : * still error with missing_ok, so that we don't silently leave the column
8749 : * as generated.
8750 : */
8751 56 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8752 12 : ereport(ERROR,
8753 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8754 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8755 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8756 : colName, RelationGetRelationName(rel))));
8757 :
8758 44 : if (!attTup->attgenerated)
8759 : {
8760 24 : if (!missing_ok)
8761 12 : ereport(ERROR,
8762 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8763 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8764 : colName, RelationGetRelationName(rel))));
8765 : else
8766 : {
8767 12 : ereport(NOTICE,
8768 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8769 : colName, RelationGetRelationName(rel))));
8770 12 : heap_freetuple(tuple);
8771 12 : table_close(attrelation, RowExclusiveLock);
8772 12 : return InvalidObjectAddress;
8773 : }
8774 : }
8775 :
8776 : /*
8777 : * Mark the column as no longer generated. (The atthasdef flag needs to
8778 : * get cleared too, but RemoveAttrDefault will handle that.)
8779 : */
8780 20 : attTup->attgenerated = '\0';
8781 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8782 :
8783 20 : InvokeObjectPostAlterHook(RelationRelationId,
8784 : RelationGetRelid(rel),
8785 : attnum);
8786 20 : heap_freetuple(tuple);
8787 :
8788 20 : table_close(attrelation, RowExclusiveLock);
8789 :
8790 : /*
8791 : * Drop the dependency records of the GENERATED expression, in particular
8792 : * its INTERNAL dependency on the column, which would otherwise cause
8793 : * dependency.c to refuse to perform the deletion.
8794 : */
8795 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8796 20 : if (!OidIsValid(attrdefoid))
8797 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8798 : RelationGetRelid(rel), attnum);
8799 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8800 :
8801 : /* Make above changes visible */
8802 20 : CommandCounterIncrement();
8803 :
8804 : /*
8805 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8806 : * safety, but at present we do not expect anything to depend on the
8807 : * default.
8808 : */
8809 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8810 : false, false);
8811 :
8812 20 : ObjectAddressSubSet(address, RelationRelationId,
8813 : RelationGetRelid(rel), attnum);
8814 20 : return address;
8815 : }
8816 :
8817 : /*
8818 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8819 : *
8820 : * Return value is the address of the modified column
8821 : */
8822 : static ObjectAddress
8823 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8824 : {
8825 164 : int newtarget = 0;
8826 : bool newtarget_default;
8827 : Relation attrelation;
8828 : HeapTuple tuple,
8829 : newtuple;
8830 : Form_pg_attribute attrtuple;
8831 : AttrNumber attnum;
8832 : ObjectAddress address;
8833 : Datum repl_val[Natts_pg_attribute];
8834 : bool repl_null[Natts_pg_attribute];
8835 : bool repl_repl[Natts_pg_attribute];
8836 :
8837 : /*
8838 : * We allow referencing columns by numbers only for indexes, since table
8839 : * column numbers could contain gaps if columns are later dropped.
8840 : */
8841 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8842 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8843 : !colName)
8844 0 : ereport(ERROR,
8845 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8846 : errmsg("cannot refer to non-index column by number")));
8847 :
8848 : /* -1 was used in previous versions for the default setting */
8849 164 : if (newValue && intVal(newValue) != -1)
8850 : {
8851 120 : newtarget = intVal(newValue);
8852 120 : newtarget_default = false;
8853 : }
8854 : else
8855 44 : newtarget_default = true;
8856 :
8857 164 : if (!newtarget_default)
8858 : {
8859 : /*
8860 : * Limit target to a sane range
8861 : */
8862 120 : if (newtarget < 0)
8863 : {
8864 0 : ereport(ERROR,
8865 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8866 : errmsg("statistics target %d is too low",
8867 : newtarget)));
8868 : }
8869 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8870 : {
8871 0 : newtarget = MAX_STATISTICS_TARGET;
8872 0 : ereport(WARNING,
8873 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8874 : errmsg("lowering statistics target to %d",
8875 : newtarget)));
8876 : }
8877 : }
8878 :
8879 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8880 :
8881 164 : if (colName)
8882 : {
8883 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8884 :
8885 100 : if (!HeapTupleIsValid(tuple))
8886 12 : ereport(ERROR,
8887 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8888 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8889 : colName, RelationGetRelationName(rel))));
8890 : }
8891 : else
8892 : {
8893 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8894 :
8895 64 : if (!HeapTupleIsValid(tuple))
8896 12 : ereport(ERROR,
8897 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8898 : errmsg("column number %d of relation \"%s\" does not exist",
8899 : colNum, RelationGetRelationName(rel))));
8900 : }
8901 :
8902 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8903 :
8904 140 : attnum = attrtuple->attnum;
8905 140 : if (attnum <= 0)
8906 0 : ereport(ERROR,
8907 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8908 : errmsg("cannot alter system column \"%s\"",
8909 : colName)));
8910 :
8911 : /*
8912 : * Prevent this as long as the ANALYZE code skips virtual generated
8913 : * columns.
8914 : */
8915 140 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8916 0 : ereport(ERROR,
8917 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8918 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
8919 : colName)));
8920 :
8921 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8922 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8923 : {
8924 52 : if (attnum > rel->rd_index->indnkeyatts)
8925 6 : ereport(ERROR,
8926 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8927 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8928 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8929 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8930 18 : ereport(ERROR,
8931 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8932 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8933 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8934 : errhint("Alter statistics on table column instead.")));
8935 : }
8936 :
8937 : /* Build new tuple. */
8938 116 : memset(repl_null, false, sizeof(repl_null));
8939 116 : memset(repl_repl, false, sizeof(repl_repl));
8940 116 : if (!newtarget_default)
8941 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8942 : else
8943 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8944 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8945 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8946 : repl_val, repl_null, repl_repl);
8947 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8948 :
8949 116 : InvokeObjectPostAlterHook(RelationRelationId,
8950 : RelationGetRelid(rel),
8951 : attrtuple->attnum);
8952 116 : ObjectAddressSubSet(address, RelationRelationId,
8953 : RelationGetRelid(rel), attnum);
8954 :
8955 116 : heap_freetuple(newtuple);
8956 :
8957 116 : ReleaseSysCache(tuple);
8958 :
8959 116 : table_close(attrelation, RowExclusiveLock);
8960 :
8961 116 : return address;
8962 : }
8963 :
8964 : /*
8965 : * Return value is the address of the modified column
8966 : */
8967 : static ObjectAddress
8968 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8969 : bool isReset, LOCKMODE lockmode)
8970 : {
8971 : Relation attrelation;
8972 : HeapTuple tuple,
8973 : newtuple;
8974 : Form_pg_attribute attrtuple;
8975 : AttrNumber attnum;
8976 : Datum datum,
8977 : newOptions;
8978 : bool isnull;
8979 : ObjectAddress address;
8980 : Datum repl_val[Natts_pg_attribute];
8981 : bool repl_null[Natts_pg_attribute];
8982 : bool repl_repl[Natts_pg_attribute];
8983 :
8984 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8985 :
8986 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8987 :
8988 32 : if (!HeapTupleIsValid(tuple))
8989 0 : ereport(ERROR,
8990 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8991 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8992 : colName, RelationGetRelationName(rel))));
8993 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8994 :
8995 32 : attnum = attrtuple->attnum;
8996 32 : if (attnum <= 0)
8997 0 : ereport(ERROR,
8998 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8999 : errmsg("cannot alter system column \"%s\"",
9000 : colName)));
9001 :
9002 : /* Generate new proposed attoptions (text array) */
9003 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9004 : &isnull);
9005 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9006 : castNode(List, options), NULL, NULL,
9007 : false, isReset);
9008 : /* Validate new options */
9009 32 : (void) attribute_reloptions(newOptions, true);
9010 :
9011 : /* Build new tuple. */
9012 32 : memset(repl_null, false, sizeof(repl_null));
9013 32 : memset(repl_repl, false, sizeof(repl_repl));
9014 32 : if (newOptions != (Datum) 0)
9015 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9016 : else
9017 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
9018 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9019 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9020 : repl_val, repl_null, repl_repl);
9021 :
9022 : /* Update system catalog. */
9023 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9024 :
9025 32 : InvokeObjectPostAlterHook(RelationRelationId,
9026 : RelationGetRelid(rel),
9027 : attrtuple->attnum);
9028 32 : ObjectAddressSubSet(address, RelationRelationId,
9029 : RelationGetRelid(rel), attnum);
9030 :
9031 32 : heap_freetuple(newtuple);
9032 :
9033 32 : ReleaseSysCache(tuple);
9034 :
9035 32 : table_close(attrelation, RowExclusiveLock);
9036 :
9037 32 : return address;
9038 : }
9039 :
9040 : /*
9041 : * Helper function for ATExecSetStorage and ATExecSetCompression
9042 : *
9043 : * Set the attstorage and/or attcompression fields for index columns
9044 : * associated with the specified table column.
9045 : */
9046 : static void
9047 290 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9048 : AttrNumber attnum,
9049 : bool setstorage, char newstorage,
9050 : bool setcompression, char newcompression,
9051 : LOCKMODE lockmode)
9052 : {
9053 : ListCell *lc;
9054 :
9055 374 : foreach(lc, RelationGetIndexList(rel))
9056 : {
9057 84 : Oid indexoid = lfirst_oid(lc);
9058 : Relation indrel;
9059 84 : AttrNumber indattnum = 0;
9060 : HeapTuple tuple;
9061 :
9062 84 : indrel = index_open(indexoid, lockmode);
9063 :
9064 144 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9065 : {
9066 90 : if (indrel->rd_index->indkey.values[i] == attnum)
9067 : {
9068 30 : indattnum = i + 1;
9069 30 : break;
9070 : }
9071 : }
9072 :
9073 84 : if (indattnum == 0)
9074 : {
9075 54 : index_close(indrel, lockmode);
9076 54 : continue;
9077 : }
9078 :
9079 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9080 :
9081 30 : if (HeapTupleIsValid(tuple))
9082 : {
9083 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9084 :
9085 30 : if (setstorage)
9086 24 : attrtuple->attstorage = newstorage;
9087 :
9088 30 : if (setcompression)
9089 6 : attrtuple->attcompression = newcompression;
9090 :
9091 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9092 :
9093 30 : InvokeObjectPostAlterHook(RelationRelationId,
9094 : RelationGetRelid(rel),
9095 : attrtuple->attnum);
9096 :
9097 30 : heap_freetuple(tuple);
9098 : }
9099 :
9100 30 : index_close(indrel, lockmode);
9101 : }
9102 290 : }
9103 :
9104 : /*
9105 : * ALTER TABLE ALTER COLUMN SET STORAGE
9106 : *
9107 : * Return value is the address of the modified column
9108 : */
9109 : static ObjectAddress
9110 240 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9111 : {
9112 : Relation attrelation;
9113 : HeapTuple tuple;
9114 : Form_pg_attribute attrtuple;
9115 : AttrNumber attnum;
9116 : ObjectAddress address;
9117 :
9118 240 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9119 :
9120 240 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9121 :
9122 240 : if (!HeapTupleIsValid(tuple))
9123 12 : ereport(ERROR,
9124 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9125 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9126 : colName, RelationGetRelationName(rel))));
9127 228 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9128 :
9129 228 : attnum = attrtuple->attnum;
9130 228 : if (attnum <= 0)
9131 0 : ereport(ERROR,
9132 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9133 : errmsg("cannot alter system column \"%s\"",
9134 : colName)));
9135 :
9136 228 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9137 :
9138 228 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9139 :
9140 228 : InvokeObjectPostAlterHook(RelationRelationId,
9141 : RelationGetRelid(rel),
9142 : attrtuple->attnum);
9143 :
9144 : /*
9145 : * Apply the change to indexes as well (only for simple index columns,
9146 : * matching behavior of index.c ConstructTupleDescriptor()).
9147 : */
9148 228 : SetIndexStorageProperties(rel, attrelation, attnum,
9149 228 : true, attrtuple->attstorage,
9150 : false, 0,
9151 : lockmode);
9152 :
9153 228 : heap_freetuple(tuple);
9154 :
9155 228 : table_close(attrelation, RowExclusiveLock);
9156 :
9157 228 : ObjectAddressSubSet(address, RelationRelationId,
9158 : RelationGetRelid(rel), attnum);
9159 228 : return address;
9160 : }
9161 :
9162 :
9163 : /*
9164 : * ALTER TABLE DROP COLUMN
9165 : *
9166 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9167 : * because we have to decide at runtime whether to recurse or not depending
9168 : * on whether attinhcount goes to zero or not. (We can't check this in a
9169 : * static pre-pass because it won't handle multiple inheritance situations
9170 : * correctly.)
9171 : */
9172 : static void
9173 1650 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9174 : AlterTableCmd *cmd, LOCKMODE lockmode,
9175 : AlterTableUtilityContext *context)
9176 : {
9177 1650 : if (rel->rd_rel->reloftype && !recursing)
9178 6 : ereport(ERROR,
9179 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9180 : errmsg("cannot drop column from typed table")));
9181 :
9182 1644 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9183 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9184 :
9185 1638 : if (recurse)
9186 1360 : cmd->recurse = true;
9187 1638 : }
9188 :
9189 : /*
9190 : * Drops column 'colName' from relation 'rel' and returns the address of the
9191 : * dropped column. The column is also dropped (or marked as no longer
9192 : * inherited from relation) from the relation's inheritance children, if any.
9193 : *
9194 : * In the recursive invocations for inheritance child relations, instead of
9195 : * dropping the column directly (if to be dropped at all), its object address
9196 : * is added to 'addrs', which must be non-NULL in such invocations. All
9197 : * columns are dropped at the same time after all the children have been
9198 : * checked recursively.
9199 : */
9200 : static ObjectAddress
9201 2194 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9202 : DropBehavior behavior,
9203 : bool recurse, bool recursing,
9204 : bool missing_ok, LOCKMODE lockmode,
9205 : ObjectAddresses *addrs)
9206 : {
9207 : HeapTuple tuple;
9208 : Form_pg_attribute targetatt;
9209 : AttrNumber attnum;
9210 : List *children;
9211 : ObjectAddress object;
9212 : bool is_expr;
9213 :
9214 : /* At top level, permission check was done in ATPrepCmd, else do it */
9215 2194 : if (recursing)
9216 556 : ATSimplePermissions(AT_DropColumn, rel,
9217 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9218 :
9219 : /* Initialize addrs on the first invocation */
9220 : Assert(!recursing || addrs != NULL);
9221 :
9222 : /* since this function recurses, it could be driven to stack overflow */
9223 2194 : check_stack_depth();
9224 :
9225 2194 : if (!recursing)
9226 1638 : addrs = new_object_addresses();
9227 :
9228 : /*
9229 : * get the number of the attribute
9230 : */
9231 2194 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9232 2194 : if (!HeapTupleIsValid(tuple))
9233 : {
9234 54 : if (!missing_ok)
9235 : {
9236 36 : ereport(ERROR,
9237 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9238 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9239 : colName, RelationGetRelationName(rel))));
9240 : }
9241 : else
9242 : {
9243 18 : ereport(NOTICE,
9244 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9245 : colName, RelationGetRelationName(rel))));
9246 18 : return InvalidObjectAddress;
9247 : }
9248 : }
9249 2140 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9250 :
9251 2140 : attnum = targetatt->attnum;
9252 :
9253 : /* Can't drop a system attribute */
9254 2140 : if (attnum <= 0)
9255 6 : ereport(ERROR,
9256 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9257 : errmsg("cannot drop system column \"%s\"",
9258 : colName)));
9259 :
9260 : /*
9261 : * Don't drop inherited columns, unless recursing (presumably from a drop
9262 : * of the parent column)
9263 : */
9264 2134 : if (targetatt->attinhcount > 0 && !recursing)
9265 48 : ereport(ERROR,
9266 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9267 : errmsg("cannot drop inherited column \"%s\"",
9268 : colName)));
9269 :
9270 : /*
9271 : * Don't drop columns used in the partition key, either. (If we let this
9272 : * go through, the key column's dependencies would cause a cascaded drop
9273 : * of the whole table, which is surely not what the user expected.)
9274 : */
9275 2086 : if (has_partition_attrs(rel,
9276 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9277 : &is_expr))
9278 30 : ereport(ERROR,
9279 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9280 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9281 : colName, RelationGetRelationName(rel))));
9282 :
9283 2056 : ReleaseSysCache(tuple);
9284 :
9285 : /*
9286 : * Propagate to children as appropriate. Unlike most other ALTER
9287 : * routines, we have to do this one level of recursion at a time; we can't
9288 : * use find_all_inheritors to do it in one pass.
9289 : */
9290 : children =
9291 2056 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9292 :
9293 2056 : if (children)
9294 : {
9295 : Relation attr_rel;
9296 : ListCell *child;
9297 :
9298 : /*
9299 : * In case of a partitioned table, the column must be dropped from the
9300 : * partitions as well.
9301 : */
9302 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9303 6 : ereport(ERROR,
9304 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9305 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9306 : errhint("Do not specify the ONLY keyword.")));
9307 :
9308 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9309 882 : foreach(child, children)
9310 : {
9311 592 : Oid childrelid = lfirst_oid(child);
9312 : Relation childrel;
9313 : Form_pg_attribute childatt;
9314 :
9315 : /* find_inheritance_children already got lock */
9316 592 : childrel = table_open(childrelid, NoLock);
9317 592 : CheckAlterTableIsSafe(childrel);
9318 :
9319 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9320 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9321 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9322 : colName, childrelid);
9323 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9324 :
9325 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9326 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9327 : childrelid, colName);
9328 :
9329 592 : if (recurse)
9330 : {
9331 : /*
9332 : * If the child column has other definition sources, just
9333 : * decrement its inheritance count; if not, recurse to delete
9334 : * it.
9335 : */
9336 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9337 : {
9338 : /* Time to delete this child column, too */
9339 556 : ATExecDropColumn(wqueue, childrel, colName,
9340 : behavior, true, true,
9341 : false, lockmode, addrs);
9342 : }
9343 : else
9344 : {
9345 : /* Child column must survive my deletion */
9346 12 : childatt->attinhcount--;
9347 :
9348 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9349 :
9350 : /* Make update visible */
9351 12 : CommandCounterIncrement();
9352 : }
9353 : }
9354 : else
9355 : {
9356 : /*
9357 : * If we were told to drop ONLY in this table (no recursion),
9358 : * we need to mark the inheritors' attributes as locally
9359 : * defined rather than inherited.
9360 : */
9361 24 : childatt->attinhcount--;
9362 24 : childatt->attislocal = true;
9363 :
9364 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9365 :
9366 : /* Make update visible */
9367 24 : CommandCounterIncrement();
9368 : }
9369 :
9370 586 : heap_freetuple(tuple);
9371 :
9372 586 : table_close(childrel, NoLock);
9373 : }
9374 290 : table_close(attr_rel, RowExclusiveLock);
9375 : }
9376 :
9377 : /* Add object to delete */
9378 2044 : object.classId = RelationRelationId;
9379 2044 : object.objectId = RelationGetRelid(rel);
9380 2044 : object.objectSubId = attnum;
9381 2044 : add_exact_object_address(&object, addrs);
9382 :
9383 2044 : if (!recursing)
9384 : {
9385 : /* Recursion has ended, drop everything that was collected */
9386 1494 : performMultipleDeletions(addrs, behavior, 0);
9387 1440 : free_object_addresses(addrs);
9388 : }
9389 :
9390 1990 : return object;
9391 : }
9392 :
9393 : /*
9394 : * Prepare to add a primary key on table, by adding not-null constraints
9395 : * on all columns.
9396 : */
9397 : static void
9398 15110 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9399 : bool recurse, LOCKMODE lockmode,
9400 : AlterTableUtilityContext *context)
9401 : {
9402 : ListCell *lc;
9403 : Constraint *pkconstr;
9404 :
9405 15110 : pkconstr = castNode(Constraint, cmd->def);
9406 15110 : if (pkconstr->contype != CONSTR_PRIMARY)
9407 8918 : return;
9408 :
9409 : /*
9410 : * If not recursing, we must ensure that all children have a NOT NULL
9411 : * constraint on the columns, and error out if not.
9412 : */
9413 6192 : if (!recurse)
9414 : {
9415 : List *children;
9416 :
9417 284 : children = find_inheritance_children(RelationGetRelid(rel),
9418 : lockmode);
9419 698 : foreach_oid(childrelid, children)
9420 : {
9421 280 : foreach(lc, pkconstr->keys)
9422 : {
9423 : HeapTuple tup;
9424 : Form_pg_attribute attrForm;
9425 144 : char *attname = strVal(lfirst(lc));
9426 :
9427 144 : tup = SearchSysCacheAttName(childrelid, attname);
9428 144 : if (!tup)
9429 0 : elog(ERROR, "cache lookup failed for attribute %s of relation %u",
9430 : attname, childrelid);
9431 144 : attrForm = (Form_pg_attribute) GETSTRUCT(tup);
9432 144 : if (!attrForm->attnotnull)
9433 6 : ereport(ERROR,
9434 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9435 : attname, get_rel_name(childrelid)));
9436 138 : ReleaseSysCache(tup);
9437 : }
9438 : }
9439 : }
9440 :
9441 : /* Insert not-null constraints in the queue for the PK columns */
9442 7166 : foreach(lc, pkconstr->keys)
9443 : {
9444 : AlterTableCmd *newcmd;
9445 : Constraint *nnconstr;
9446 :
9447 980 : nnconstr = makeNotNullConstraint(lfirst(lc));
9448 :
9449 980 : newcmd = makeNode(AlterTableCmd);
9450 980 : newcmd->subtype = AT_AddConstraint;
9451 980 : newcmd->recurse = true;
9452 980 : newcmd->def = (Node *) nnconstr;
9453 :
9454 980 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9455 : }
9456 : }
9457 :
9458 : /*
9459 : * ALTER TABLE ADD INDEX
9460 : *
9461 : * There is no such command in the grammar, but parse_utilcmd.c converts
9462 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9463 : * us schedule creation of the index at the appropriate time during ALTER.
9464 : *
9465 : * Return value is the address of the new index.
9466 : */
9467 : static ObjectAddress
9468 1622 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9469 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9470 : {
9471 : bool check_rights;
9472 : bool skip_build;
9473 : bool quiet;
9474 : ObjectAddress address;
9475 :
9476 : Assert(IsA(stmt, IndexStmt));
9477 : Assert(!stmt->concurrent);
9478 :
9479 : /* The IndexStmt has already been through transformIndexStmt */
9480 : Assert(stmt->transformed);
9481 :
9482 : /* suppress schema rights check when rebuilding existing index */
9483 1622 : check_rights = !is_rebuild;
9484 : /* skip index build if phase 3 will do it or we're reusing an old one */
9485 1622 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9486 : /* suppress notices when rebuilding existing index */
9487 1622 : quiet = is_rebuild;
9488 :
9489 1622 : address = DefineIndex(RelationGetRelid(rel),
9490 : stmt,
9491 : InvalidOid, /* no predefined OID */
9492 : InvalidOid, /* no parent index */
9493 : InvalidOid, /* no parent constraint */
9494 : -1, /* total_parts unknown */
9495 : true, /* is_alter_table */
9496 : check_rights,
9497 : false, /* check_not_in_use - we did it already */
9498 : skip_build,
9499 : quiet);
9500 :
9501 : /*
9502 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9503 : * new index instead of building from scratch. Restore associated fields.
9504 : * This may store InvalidSubTransactionId in both fields, in which case
9505 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9506 : * this after the CCI that made catalog rows visible to any rebuild. The
9507 : * DROP of the old edition of this index will have scheduled the storage
9508 : * for deletion at commit, so cancel that pending deletion.
9509 : */
9510 1452 : if (RelFileNumberIsValid(stmt->oldNumber))
9511 : {
9512 74 : Relation irel = index_open(address.objectId, NoLock);
9513 :
9514 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9515 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9516 74 : RelationPreserveStorage(irel->rd_locator, true);
9517 74 : index_close(irel, NoLock);
9518 : }
9519 :
9520 1452 : return address;
9521 : }
9522 :
9523 : /*
9524 : * ALTER TABLE ADD STATISTICS
9525 : *
9526 : * This is no such command in the grammar, but we use this internally to add
9527 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9528 : * column type change.
9529 : */
9530 : static ObjectAddress
9531 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9532 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9533 : {
9534 : ObjectAddress address;
9535 :
9536 : Assert(IsA(stmt, CreateStatsStmt));
9537 :
9538 : /* The CreateStatsStmt has already been through transformStatsStmt */
9539 : Assert(stmt->transformed);
9540 :
9541 14 : address = CreateStatistics(stmt);
9542 :
9543 14 : return address;
9544 : }
9545 :
9546 : /*
9547 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9548 : *
9549 : * Returns the address of the new constraint.
9550 : */
9551 : static ObjectAddress
9552 9540 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9553 : IndexStmt *stmt, LOCKMODE lockmode)
9554 : {
9555 9540 : Oid index_oid = stmt->indexOid;
9556 : Relation indexRel;
9557 : char *indexName;
9558 : IndexInfo *indexInfo;
9559 : char *constraintName;
9560 : char constraintType;
9561 : ObjectAddress address;
9562 : bits16 flags;
9563 :
9564 : Assert(IsA(stmt, IndexStmt));
9565 : Assert(OidIsValid(index_oid));
9566 : Assert(stmt->isconstraint);
9567 :
9568 : /*
9569 : * Doing this on partitioned tables is not a simple feature to implement,
9570 : * so let's punt for now.
9571 : */
9572 9540 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9573 6 : ereport(ERROR,
9574 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9575 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9576 :
9577 9534 : indexRel = index_open(index_oid, AccessShareLock);
9578 :
9579 9534 : indexName = pstrdup(RelationGetRelationName(indexRel));
9580 :
9581 9534 : indexInfo = BuildIndexInfo(indexRel);
9582 :
9583 : /* this should have been checked at parse time */
9584 9534 : if (!indexInfo->ii_Unique)
9585 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9586 :
9587 : /*
9588 : * Determine name to assign to constraint. We require a constraint to
9589 : * have the same name as the underlying index; therefore, use the index's
9590 : * existing name as the default constraint name, and if the user
9591 : * explicitly gives some other name for the constraint, rename the index
9592 : * to match.
9593 : */
9594 9534 : constraintName = stmt->idxname;
9595 9534 : if (constraintName == NULL)
9596 9508 : constraintName = indexName;
9597 26 : else if (strcmp(constraintName, indexName) != 0)
9598 : {
9599 20 : ereport(NOTICE,
9600 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9601 : indexName, constraintName)));
9602 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9603 : }
9604 :
9605 : /* Extra checks needed if making primary key */
9606 9534 : if (stmt->primary)
9607 5386 : index_check_primary_key(rel, indexInfo, true, stmt);
9608 :
9609 : /* Note we currently don't support EXCLUSION constraints here */
9610 9528 : if (stmt->primary)
9611 5380 : constraintType = CONSTRAINT_PRIMARY;
9612 : else
9613 4148 : constraintType = CONSTRAINT_UNIQUE;
9614 :
9615 : /* Create the catalog entries for the constraint */
9616 9528 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9617 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9618 19056 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9619 9528 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9620 9528 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9621 :
9622 9528 : address = index_constraint_create(rel,
9623 : index_oid,
9624 : InvalidOid,
9625 : indexInfo,
9626 : constraintName,
9627 : constraintType,
9628 : flags,
9629 : allowSystemTableMods,
9630 : false); /* is_internal */
9631 :
9632 9528 : index_close(indexRel, NoLock);
9633 :
9634 9528 : return address;
9635 : }
9636 :
9637 : /*
9638 : * ALTER TABLE ADD CONSTRAINT
9639 : *
9640 : * Return value is the address of the new constraint; if no constraint was
9641 : * added, InvalidObjectAddress is returned.
9642 : */
9643 : static ObjectAddress
9644 12172 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9645 : Constraint *newConstraint, bool recurse, bool is_readd,
9646 : LOCKMODE lockmode)
9647 : {
9648 12172 : ObjectAddress address = InvalidObjectAddress;
9649 :
9650 : Assert(IsA(newConstraint, Constraint));
9651 :
9652 : /*
9653 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9654 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9655 : * parse_utilcmd.c).
9656 : */
9657 12172 : switch (newConstraint->contype)
9658 : {
9659 9590 : case CONSTR_CHECK:
9660 : case CONSTR_NOTNULL:
9661 : address =
9662 9590 : ATAddCheckNNConstraint(wqueue, tab, rel,
9663 : newConstraint, recurse, false, is_readd,
9664 : lockmode);
9665 9464 : break;
9666 :
9667 2582 : case CONSTR_FOREIGN:
9668 :
9669 : /*
9670 : * Assign or validate constraint name
9671 : */
9672 2582 : if (newConstraint->conname)
9673 : {
9674 1152 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9675 : RelationGetRelid(rel),
9676 1152 : newConstraint->conname))
9677 0 : ereport(ERROR,
9678 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9679 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9680 : newConstraint->conname,
9681 : RelationGetRelationName(rel))));
9682 : }
9683 : else
9684 1430 : newConstraint->conname =
9685 1430 : ChooseConstraintName(RelationGetRelationName(rel),
9686 1430 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9687 : "fkey",
9688 1430 : RelationGetNamespace(rel),
9689 : NIL);
9690 :
9691 2582 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9692 : newConstraint,
9693 : recurse, false,
9694 : lockmode);
9695 2034 : break;
9696 :
9697 0 : default:
9698 0 : elog(ERROR, "unrecognized constraint type: %d",
9699 : (int) newConstraint->contype);
9700 : }
9701 :
9702 11498 : return address;
9703 : }
9704 :
9705 : /*
9706 : * Generate the column-name portion of the constraint name for a new foreign
9707 : * key given the list of column names that reference the referenced
9708 : * table. This will be passed to ChooseConstraintName along with the parent
9709 : * table name and the "fkey" suffix.
9710 : *
9711 : * We know that less than NAMEDATALEN characters will actually be used, so we
9712 : * can truncate the result once we've generated that many.
9713 : *
9714 : * XXX see also ChooseExtendedStatisticNameAddition and
9715 : * ChooseIndexNameAddition.
9716 : */
9717 : static char *
9718 2414 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9719 : {
9720 : char buf[NAMEDATALEN * 2];
9721 2414 : int buflen = 0;
9722 : ListCell *lc;
9723 :
9724 2414 : buf[0] = '\0';
9725 5400 : foreach(lc, colnames)
9726 : {
9727 2986 : const char *name = strVal(lfirst(lc));
9728 :
9729 2986 : if (buflen > 0)
9730 572 : buf[buflen++] = '_'; /* insert _ between names */
9731 :
9732 : /*
9733 : * At this point we have buflen <= NAMEDATALEN. name should be less
9734 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9735 : */
9736 2986 : strlcpy(buf + buflen, name, NAMEDATALEN);
9737 2986 : buflen += strlen(buf + buflen);
9738 2986 : if (buflen >= NAMEDATALEN)
9739 0 : break;
9740 : }
9741 2414 : return pstrdup(buf);
9742 : }
9743 :
9744 : /*
9745 : * Add a check or not-null constraint to a single table and its children.
9746 : * Returns the address of the constraint added to the parent relation,
9747 : * if one gets added, or InvalidObjectAddress otherwise.
9748 : *
9749 : * Subroutine for ATExecAddConstraint.
9750 : *
9751 : * We must recurse to child tables during execution, rather than using
9752 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9753 : * constraints *must* be given the same name, else they won't be seen as
9754 : * related later. If the user didn't explicitly specify a name, then
9755 : * AddRelationNewConstraints would normally assign different names to the
9756 : * child constraints. To fix that, we must capture the name assigned at
9757 : * the parent table and pass that down.
9758 : */
9759 : static ObjectAddress
9760 10438 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9761 : Constraint *constr, bool recurse, bool recursing,
9762 : bool is_readd, LOCKMODE lockmode)
9763 : {
9764 : List *newcons;
9765 : ListCell *lcon;
9766 : List *children;
9767 : ListCell *child;
9768 10438 : ObjectAddress address = InvalidObjectAddress;
9769 :
9770 : /* Guard against stack overflow due to overly deep inheritance tree. */
9771 10438 : check_stack_depth();
9772 :
9773 : /* At top level, permission check was done in ATPrepCmd, else do it */
9774 10438 : if (recursing)
9775 714 : ATSimplePermissions(AT_AddConstraint, rel,
9776 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9777 :
9778 : /*
9779 : * Call AddRelationNewConstraints to do the work, making sure it works on
9780 : * a copy of the Constraint so transformExpr can't modify the original. It
9781 : * returns a list of cooked constraints.
9782 : *
9783 : * If the constraint ends up getting merged with a pre-existing one, it's
9784 : * omitted from the returned list, which is what we want: we do not need
9785 : * to do any validation work. That can only happen at child tables,
9786 : * though, since we disallow merging at the top level.
9787 : */
9788 10438 : newcons = AddRelationNewConstraints(rel, NIL,
9789 10438 : list_make1(copyObject(constr)),
9790 10438 : recursing || is_readd, /* allow_merge */
9791 10438 : !recursing, /* is_local */
9792 : is_readd, /* is_internal */
9793 10438 : NULL); /* queryString not available
9794 : * here */
9795 :
9796 : /* we don't expect more than one constraint here */
9797 : Assert(list_length(newcons) <= 1);
9798 :
9799 : /* Add each to-be-validated constraint to Phase 3's queue */
9800 19994 : foreach(lcon, newcons)
9801 : {
9802 9676 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9803 :
9804 9676 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9805 : {
9806 : NewConstraint *newcon;
9807 :
9808 880 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9809 880 : newcon->name = ccon->name;
9810 880 : newcon->contype = ccon->contype;
9811 880 : newcon->qual = ccon->expr;
9812 :
9813 880 : tab->constraints = lappend(tab->constraints, newcon);
9814 : }
9815 :
9816 : /* Save the actually assigned name if it was defaulted */
9817 9676 : if (constr->conname == NULL)
9818 8182 : constr->conname = ccon->name;
9819 :
9820 : /*
9821 : * If adding a not-null constraint, set the pg_attribute flag and tell
9822 : * phase 3 to verify existing rows, if needed.
9823 : */
9824 9676 : if (constr->contype == CONSTR_NOTNULL)
9825 8306 : set_attnotnull(wqueue, rel, ccon->attnum, lockmode);
9826 :
9827 9676 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9828 : }
9829 :
9830 : /* At this point we must have a locked-down name to use */
9831 : Assert(newcons == NIL || constr->conname != NULL);
9832 :
9833 : /* Advance command counter in case same table is visited multiple times */
9834 10318 : CommandCounterIncrement();
9835 :
9836 : /*
9837 : * If the constraint got merged with an existing constraint, we're done.
9838 : * We mustn't recurse to child tables in this case, because they've
9839 : * already got the constraint, and visiting them again would lead to an
9840 : * incorrect value for coninhcount.
9841 : */
9842 10318 : if (newcons == NIL)
9843 642 : return address;
9844 :
9845 : /*
9846 : * If adding a NO INHERIT constraint, no need to find our children.
9847 : */
9848 9676 : if (constr->is_no_inherit)
9849 72 : return address;
9850 :
9851 : /*
9852 : * Propagate to children as appropriate. Unlike most other ALTER
9853 : * routines, we have to do this one level of recursion at a time; we can't
9854 : * use find_all_inheritors to do it in one pass.
9855 : */
9856 : children =
9857 9604 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9858 :
9859 : /*
9860 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9861 : * constraint creation only if there are no children currently. Error out
9862 : * otherwise.
9863 : */
9864 9604 : if (!recurse && children != NIL)
9865 6 : ereport(ERROR,
9866 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9867 : errmsg("constraint must be added to child tables too")));
9868 :
9869 : /*
9870 : * Recurse to create the constraint on each child.
9871 : */
9872 10282 : foreach(child, children)
9873 : {
9874 714 : Oid childrelid = lfirst_oid(child);
9875 : Relation childrel;
9876 : AlteredTableInfo *childtab;
9877 :
9878 : /* find_inheritance_children already got lock */
9879 714 : childrel = table_open(childrelid, NoLock);
9880 714 : CheckAlterTableIsSafe(childrel);
9881 :
9882 : /* Find or create work queue entry for this table */
9883 714 : childtab = ATGetQueueEntry(wqueue, childrel);
9884 :
9885 : /* Recurse to this child */
9886 714 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
9887 : constr, recurse, true, is_readd, lockmode);
9888 :
9889 684 : table_close(childrel, NoLock);
9890 : }
9891 :
9892 9568 : return address;
9893 : }
9894 :
9895 : /*
9896 : * Add a foreign-key constraint to a single table; return the new constraint's
9897 : * address.
9898 : *
9899 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9900 : * lock on the rel, and have done appropriate validity checks for it.
9901 : * We do permissions checks here, however.
9902 : *
9903 : * When the referenced or referencing tables (or both) are partitioned,
9904 : * multiple pg_constraint rows are required -- one for each partitioned table
9905 : * and each partition on each side (fortunately, not one for every combination
9906 : * thereof). We also need action triggers on each leaf partition on the
9907 : * referenced side, and check triggers on each leaf partition on the
9908 : * referencing side.
9909 : */
9910 : static ObjectAddress
9911 2582 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9912 : Constraint *fkconstraint,
9913 : bool recurse, bool recursing, LOCKMODE lockmode)
9914 : {
9915 : Relation pkrel;
9916 2582 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9917 2582 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9918 2582 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9919 2582 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9920 2582 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
9921 2582 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
9922 2582 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9923 2582 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9924 2582 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9925 2582 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9926 2582 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9927 : bool with_period;
9928 : bool pk_has_without_overlaps;
9929 : int i;
9930 : int numfks,
9931 : numpks,
9932 : numfkdelsetcols;
9933 : Oid indexOid;
9934 : bool old_check_ok;
9935 : ObjectAddress address;
9936 2582 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9937 :
9938 : /*
9939 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9940 : * delete rows out from under us.
9941 : */
9942 2582 : if (OidIsValid(fkconstraint->old_pktable_oid))
9943 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9944 : else
9945 2510 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9946 :
9947 : /*
9948 : * Validity checks (permission checks wait till we have the column
9949 : * numbers)
9950 : */
9951 2576 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9952 6 : ereport(ERROR,
9953 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
9954 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9955 : RelationGetRelationName(rel),
9956 : RelationGetRelationName(pkrel)));
9957 :
9958 2570 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9959 332 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9960 0 : ereport(ERROR,
9961 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9962 : errmsg("referenced relation \"%s\" is not a table",
9963 : RelationGetRelationName(pkrel))));
9964 :
9965 2570 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
9966 2 : ereport(ERROR,
9967 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9968 : errmsg("permission denied: \"%s\" is a system catalog",
9969 : RelationGetRelationName(pkrel))));
9970 :
9971 : /*
9972 : * References from permanent or unlogged tables to temp tables, and from
9973 : * permanent tables to unlogged tables, are disallowed because the
9974 : * referenced data can vanish out from under us. References from temp
9975 : * tables to any other table type are also disallowed, because other
9976 : * backends might need to run the RI triggers on the perm table, but they
9977 : * can't reliably see tuples in the local buffers of other backends.
9978 : */
9979 2568 : switch (rel->rd_rel->relpersistence)
9980 : {
9981 2278 : case RELPERSISTENCE_PERMANENT:
9982 2278 : if (!RelationIsPermanent(pkrel))
9983 0 : ereport(ERROR,
9984 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9985 : errmsg("constraints on permanent tables may reference only permanent tables")));
9986 2278 : break;
9987 12 : case RELPERSISTENCE_UNLOGGED:
9988 12 : if (!RelationIsPermanent(pkrel)
9989 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9990 0 : ereport(ERROR,
9991 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9992 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9993 12 : break;
9994 278 : case RELPERSISTENCE_TEMP:
9995 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9996 0 : ereport(ERROR,
9997 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9998 : errmsg("constraints on temporary tables may reference only temporary tables")));
9999 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10000 0 : ereport(ERROR,
10001 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10002 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10003 278 : break;
10004 : }
10005 :
10006 : /*
10007 : * Look up the referencing attributes to make sure they exist, and record
10008 : * their attnums and type and collation OIDs.
10009 : */
10010 2568 : numfks = transformColumnNameList(RelationGetRelid(rel),
10011 : fkconstraint->fk_attrs,
10012 : fkattnum, fktypoid, fkcolloid);
10013 2538 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10014 2538 : if (with_period && !fkconstraint->fk_with_period)
10015 24 : ereport(ERROR,
10016 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10017 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10018 :
10019 2514 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10020 : fkconstraint->fk_del_set_cols,
10021 : fkdelsetcols, NULL, NULL);
10022 2508 : validateFkOnDeleteSetColumns(numfks, fkattnum,
10023 : numfkdelsetcols, fkdelsetcols,
10024 : fkconstraint->fk_del_set_cols);
10025 :
10026 : /*
10027 : * If the attribute list for the referenced table was omitted, lookup the
10028 : * definition of the primary key and use it. Otherwise, validate the
10029 : * supplied attribute list. In either case, discover the index OID and
10030 : * index opclasses, and the attnums and type and collation OIDs of the
10031 : * attributes.
10032 : */
10033 2502 : if (fkconstraint->pk_attrs == NIL)
10034 : {
10035 1166 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10036 : &fkconstraint->pk_attrs,
10037 : pkattnum, pktypoid, pkcolloid,
10038 : opclasses, &pk_has_without_overlaps);
10039 :
10040 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10041 1166 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10042 24 : ereport(ERROR,
10043 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10044 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10045 : }
10046 : else
10047 : {
10048 1336 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10049 : fkconstraint->pk_attrs,
10050 : pkattnum, pktypoid, pkcolloid);
10051 :
10052 : /* Since we got pk_attrs, one should be a period. */
10053 1306 : if (with_period && !fkconstraint->pk_with_period)
10054 24 : ereport(ERROR,
10055 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10056 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10057 :
10058 : /* Look for an index matching the column list */
10059 1282 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10060 : with_period, opclasses, &pk_has_without_overlaps);
10061 : }
10062 :
10063 : /*
10064 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10065 : * must use PERIOD.
10066 : */
10067 2388 : if (pk_has_without_overlaps && !with_period)
10068 12 : ereport(ERROR,
10069 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10070 : errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10071 :
10072 : /*
10073 : * Now we can check permissions.
10074 : */
10075 2376 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10076 :
10077 : /*
10078 : * Check some things for generated columns.
10079 : */
10080 5550 : for (i = 0; i < numfks; i++)
10081 : {
10082 3204 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10083 :
10084 3204 : if (attgenerated)
10085 : {
10086 : /*
10087 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10088 : */
10089 48 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10090 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10091 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10092 12 : ereport(ERROR,
10093 : (errcode(ERRCODE_SYNTAX_ERROR),
10094 : errmsg("invalid %s action for foreign key constraint containing generated column",
10095 : "ON UPDATE")));
10096 36 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10097 24 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10098 12 : ereport(ERROR,
10099 : (errcode(ERRCODE_SYNTAX_ERROR),
10100 : errmsg("invalid %s action for foreign key constraint containing generated column",
10101 : "ON DELETE")));
10102 : }
10103 :
10104 : /*
10105 : * FKs on virtual columns are not supported. This would require
10106 : * various additional support in ri_triggers.c, including special
10107 : * handling in ri_NullCheck(), ri_KeysEqual(),
10108 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10109 : * as NULL there). Also not really practical as long as you can't
10110 : * index virtual columns.
10111 : */
10112 3180 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10113 6 : ereport(ERROR,
10114 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10115 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10116 : }
10117 :
10118 : /*
10119 : * Some actions are currently unsupported for foreign keys using PERIOD.
10120 : */
10121 2346 : if (fkconstraint->fk_with_period)
10122 : {
10123 242 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10124 230 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10125 212 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10126 194 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10127 66 : ereport(ERROR,
10128 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10129 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10130 : "ON UPDATE"));
10131 :
10132 176 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10133 170 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10134 170 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10135 170 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10136 6 : ereport(ERROR,
10137 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10138 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10139 : "ON DELETE"));
10140 : }
10141 :
10142 : /*
10143 : * Look up the equality operators to use in the constraint.
10144 : *
10145 : * Note that we have to be careful about the difference between the actual
10146 : * PK column type and the opclass' declared input type, which might be
10147 : * only binary-compatible with it. The declared opcintype is the right
10148 : * thing to probe pg_amop with.
10149 : */
10150 2274 : if (numfks != numpks)
10151 0 : ereport(ERROR,
10152 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10153 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10154 :
10155 : /*
10156 : * On the strength of a previous constraint, we might avoid scanning
10157 : * tables to validate this one. See below.
10158 : */
10159 2274 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10160 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10161 :
10162 4920 : for (i = 0; i < numpks; i++)
10163 : {
10164 2886 : Oid pktype = pktypoid[i];
10165 2886 : Oid fktype = fktypoid[i];
10166 : Oid fktyped;
10167 2886 : Oid pkcoll = pkcolloid[i];
10168 2886 : Oid fkcoll = fkcolloid[i];
10169 : HeapTuple cla_ht;
10170 : Form_pg_opclass cla_tup;
10171 : Oid amid;
10172 : Oid opfamily;
10173 : Oid opcintype;
10174 : bool for_overlaps;
10175 : CompareType cmptype;
10176 : Oid pfeqop;
10177 : Oid ppeqop;
10178 : Oid ffeqop;
10179 : int16 eqstrategy;
10180 : Oid pfeqop_right;
10181 :
10182 : /* We need several fields out of the pg_opclass entry */
10183 2886 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10184 2886 : if (!HeapTupleIsValid(cla_ht))
10185 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10186 2886 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10187 2886 : amid = cla_tup->opcmethod;
10188 2886 : opfamily = cla_tup->opcfamily;
10189 2886 : opcintype = cla_tup->opcintype;
10190 2886 : ReleaseSysCache(cla_ht);
10191 :
10192 : /*
10193 : * Get strategy number from index AM.
10194 : *
10195 : * For a normal foreign-key constraint, this should not fail, since we
10196 : * already checked that the index is unique and should therefore have
10197 : * appropriate equal operators. For a period foreign key, this could
10198 : * fail if we selected a non-matching exclusion constraint earlier.
10199 : * (XXX Maybe we should do these lookups earlier so we don't end up
10200 : * doing that.)
10201 : */
10202 2886 : for_overlaps = with_period && i == numpks - 1;
10203 2886 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10204 2886 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10205 2886 : if (eqstrategy == InvalidStrategy)
10206 0 : ereport(ERROR,
10207 : errcode(ERRCODE_UNDEFINED_OBJECT),
10208 : for_overlaps
10209 : ? errmsg("could not identify an overlaps operator for foreign key")
10210 : : errmsg("could not identify an equality operator for foreign key"),
10211 : errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
10212 : cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
10213 :
10214 : /*
10215 : * There had better be a primary equality operator for the index.
10216 : * We'll use it for PK = PK comparisons.
10217 : */
10218 2886 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10219 : eqstrategy);
10220 :
10221 2886 : if (!OidIsValid(ppeqop))
10222 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10223 : eqstrategy, opcintype, opcintype, opfamily);
10224 :
10225 : /*
10226 : * Are there equality operators that take exactly the FK type? Assume
10227 : * we should look through any domain here.
10228 : */
10229 2886 : fktyped = getBaseType(fktype);
10230 :
10231 2886 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10232 : eqstrategy);
10233 2886 : if (OidIsValid(pfeqop))
10234 : {
10235 2262 : pfeqop_right = fktyped;
10236 2262 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10237 : eqstrategy);
10238 : }
10239 : else
10240 : {
10241 : /* keep compiler quiet */
10242 624 : pfeqop_right = InvalidOid;
10243 624 : ffeqop = InvalidOid;
10244 : }
10245 :
10246 2886 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10247 : {
10248 : /*
10249 : * Otherwise, look for an implicit cast from the FK type to the
10250 : * opcintype, and if found, use the primary equality operator.
10251 : * This is a bit tricky because opcintype might be a polymorphic
10252 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10253 : * whether the two actual column types can be concurrently cast to
10254 : * that type. (Otherwise, we'd fail to reject combinations such
10255 : * as int[] and point[].)
10256 : */
10257 : Oid input_typeids[2];
10258 : Oid target_typeids[2];
10259 :
10260 624 : input_typeids[0] = pktype;
10261 624 : input_typeids[1] = fktype;
10262 624 : target_typeids[0] = opcintype;
10263 624 : target_typeids[1] = opcintype;
10264 624 : if (can_coerce_type(2, input_typeids, target_typeids,
10265 : COERCION_IMPLICIT))
10266 : {
10267 396 : pfeqop = ffeqop = ppeqop;
10268 396 : pfeqop_right = opcintype;
10269 : }
10270 : }
10271 :
10272 2886 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10273 228 : ereport(ERROR,
10274 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10275 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10276 : fkconstraint->conname),
10277 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10278 : "are of incompatible types: %s and %s.",
10279 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10280 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10281 : format_type_be(fktype),
10282 : format_type_be(pktype))));
10283 :
10284 : /*
10285 : * This shouldn't be possible, but better check to make sure we have a
10286 : * consistent state for the check below.
10287 : */
10288 2658 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10289 0 : elog(ERROR, "key columns are not both collatable");
10290 :
10291 2658 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10292 : {
10293 : bool pkcolldet;
10294 : bool fkcolldet;
10295 :
10296 104 : pkcolldet = get_collation_isdeterministic(pkcoll);
10297 104 : fkcolldet = get_collation_isdeterministic(fkcoll);
10298 :
10299 : /*
10300 : * SQL requires that both collations are the same. This is
10301 : * because we need a consistent notion of equality on both
10302 : * columns. We relax this by allowing different collations if
10303 : * they are both deterministic. (This is also for backward
10304 : * compatibility, because PostgreSQL has always allowed this.)
10305 : */
10306 104 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10307 12 : ereport(ERROR,
10308 : (errcode(ERRCODE_COLLATION_MISMATCH),
10309 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10310 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10311 : "have incompatible collations: \"%s\" and \"%s\". "
10312 : "If either collation is nondeterministic, then both collations have to be the same.",
10313 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10314 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10315 : get_collation_name(fkcoll),
10316 : get_collation_name(pkcoll))));
10317 : }
10318 :
10319 2646 : if (old_check_ok)
10320 : {
10321 : /*
10322 : * When a pfeqop changes, revalidate the constraint. We could
10323 : * permit intra-opfamily changes, but that adds subtle complexity
10324 : * without any concrete benefit for core types. We need not
10325 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10326 : */
10327 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10328 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10329 : old_pfeqop_item);
10330 : }
10331 2646 : if (old_check_ok)
10332 : {
10333 : Oid old_fktype;
10334 : Oid new_fktype;
10335 : CoercionPathType old_pathtype;
10336 : CoercionPathType new_pathtype;
10337 : Oid old_castfunc;
10338 : Oid new_castfunc;
10339 : Oid old_fkcoll;
10340 : Oid new_fkcoll;
10341 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10342 6 : fkattnum[i] - 1);
10343 :
10344 : /*
10345 : * Identify coercion pathways from each of the old and new FK-side
10346 : * column types to the right (foreign) operand type of the pfeqop.
10347 : * We may assume that pg_constraint.conkey is not changing.
10348 : */
10349 6 : old_fktype = attr->atttypid;
10350 6 : new_fktype = fktype;
10351 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10352 : &old_castfunc);
10353 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10354 : &new_castfunc);
10355 :
10356 6 : old_fkcoll = attr->attcollation;
10357 6 : new_fkcoll = fkcoll;
10358 :
10359 : /*
10360 : * Upon a change to the cast from the FK column to its pfeqop
10361 : * operand, revalidate the constraint. For this evaluation, a
10362 : * binary coercion cast is equivalent to no cast at all. While
10363 : * type implementors should design implicit casts with an eye
10364 : * toward consistency of operations like equality, we cannot
10365 : * assume here that they have done so.
10366 : *
10367 : * A function with a polymorphic argument could change behavior
10368 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10369 : * when the cast destination is polymorphic, we only avoid
10370 : * revalidation if the input type has not changed at all. Given
10371 : * just the core data types and operator classes, this requirement
10372 : * prevents no would-be optimizations.
10373 : *
10374 : * If the cast converts from a base type to a domain thereon, then
10375 : * that domain type must be the opcintype of the unique index.
10376 : * Necessarily, the primary key column must then be of the domain
10377 : * type. Since the constraint was previously valid, all values on
10378 : * the foreign side necessarily exist on the primary side and in
10379 : * turn conform to the domain. Consequently, we need not treat
10380 : * domains specially here.
10381 : *
10382 : * If the collation changes, revalidation is required, unless both
10383 : * collations are deterministic, because those share the same
10384 : * notion of equality (because texteq reduces to bitwise
10385 : * equality).
10386 : *
10387 : * We need not directly consider the PK type. It's necessarily
10388 : * binary coercible to the opcintype of the unique index column,
10389 : * and ri_triggers.c will only deal with PK datums in terms of
10390 : * that opcintype. Changing the opcintype also changes pfeqop.
10391 : */
10392 6 : old_check_ok = (new_pathtype == old_pathtype &&
10393 6 : new_castfunc == old_castfunc &&
10394 6 : (!IsPolymorphicType(pfeqop_right) ||
10395 12 : new_fktype == old_fktype) &&
10396 0 : (new_fkcoll == old_fkcoll ||
10397 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10398 : }
10399 :
10400 2646 : pfeqoperators[i] = pfeqop;
10401 2646 : ppeqoperators[i] = ppeqop;
10402 2646 : ffeqoperators[i] = ffeqop;
10403 : }
10404 :
10405 : /*
10406 : * For FKs with PERIOD we need additional operators to check whether the
10407 : * referencing row's range is contained by the aggregated ranges of the
10408 : * referenced row(s). For rangetypes and multirangetypes this is
10409 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10410 : * support for now. FKs will look these up at "runtime", but we should
10411 : * make sure the lookup works here, even if we don't use the values.
10412 : */
10413 2034 : if (with_period)
10414 : {
10415 : Oid periodoperoid;
10416 : Oid aggedperiodoperoid;
10417 : Oid intersectoperoid;
10418 :
10419 152 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10420 : &intersectoperoid);
10421 : }
10422 :
10423 : /* First, create the constraint catalog entry itself. */
10424 2034 : address = addFkConstraint(addFkBothSides,
10425 : fkconstraint->conname, fkconstraint, rel, pkrel,
10426 : indexOid,
10427 : InvalidOid, /* no parent constraint */
10428 : numfks,
10429 : pkattnum,
10430 : fkattnum,
10431 : pfeqoperators,
10432 : ppeqoperators,
10433 : ffeqoperators,
10434 : numfkdelsetcols,
10435 : fkdelsetcols,
10436 : false,
10437 : with_period);
10438 :
10439 : /* Next process the action triggers at the referenced side and recurse */
10440 2034 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10441 : indexOid,
10442 : address.objectId,
10443 : numfks,
10444 : pkattnum,
10445 : fkattnum,
10446 : pfeqoperators,
10447 : ppeqoperators,
10448 : ffeqoperators,
10449 : numfkdelsetcols,
10450 : fkdelsetcols,
10451 : old_check_ok,
10452 : InvalidOid, InvalidOid,
10453 : with_period);
10454 :
10455 : /* Lastly create the check triggers at the referencing side and recurse */
10456 2034 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10457 : indexOid,
10458 : address.objectId,
10459 : numfks,
10460 : pkattnum,
10461 : fkattnum,
10462 : pfeqoperators,
10463 : ppeqoperators,
10464 : ffeqoperators,
10465 : numfkdelsetcols,
10466 : fkdelsetcols,
10467 : old_check_ok,
10468 : lockmode,
10469 : InvalidOid, InvalidOid,
10470 : with_period);
10471 :
10472 : /*
10473 : * Done. Close pk table, but keep lock until we've committed.
10474 : */
10475 2034 : table_close(pkrel, NoLock);
10476 :
10477 2034 : return address;
10478 : }
10479 :
10480 : /*
10481 : * validateFkOnDeleteSetColumns
10482 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10483 : * column lists are valid.
10484 : */
10485 : void
10486 2508 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10487 : int numfksetcols, const int16 *fksetcolsattnums,
10488 : List *fksetcols)
10489 : {
10490 2532 : for (int i = 0; i < numfksetcols; i++)
10491 : {
10492 30 : int16 setcol_attnum = fksetcolsattnums[i];
10493 30 : bool seen = false;
10494 :
10495 54 : for (int j = 0; j < numfks; j++)
10496 : {
10497 48 : if (fkattnums[j] == setcol_attnum)
10498 : {
10499 24 : seen = true;
10500 24 : break;
10501 : }
10502 : }
10503 :
10504 30 : if (!seen)
10505 : {
10506 6 : char *col = strVal(list_nth(fksetcols, i));
10507 :
10508 6 : ereport(ERROR,
10509 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10510 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10511 : }
10512 : }
10513 2502 : }
10514 :
10515 : /*
10516 : * addFkConstraint
10517 : * Install pg_constraint entries to implement a foreign key constraint.
10518 : * Caller must separately invoke addFkRecurseReferenced and
10519 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10520 : * and (for partitioned tables) recurse to partitions.
10521 : *
10522 : * fkside: the side of the FK (or both) to create. Caller should
10523 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10524 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10525 : * addFkBothSides.
10526 : * constraintname: the base name for the constraint being added,
10527 : * copied to fkconstraint->conname if the latter is not set
10528 : * fkconstraint: the constraint being added
10529 : * rel: the root referencing relation
10530 : * pkrel: the referenced relation; might be a partition, if recursing
10531 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10532 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10533 : * top-level constraint
10534 : * numfks: the number of columns in the foreign key
10535 : * pkattnum: the attnum array of referenced attributes
10536 : * fkattnum: the attnum array of referencing attributes
10537 : * pf/pp/ffeqoperators: OID array of operators between columns
10538 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10539 : * (...) clause
10540 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10541 : * NULL/DEFAULT clause
10542 : * with_period: true if this is a temporal FK
10543 : */
10544 : static ObjectAddress
10545 3788 : addFkConstraint(addFkConstraintSides fkside,
10546 : char *constraintname, Constraint *fkconstraint,
10547 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10548 : int numfks, int16 *pkattnum,
10549 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10550 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10551 : bool is_internal, bool with_period)
10552 : {
10553 : ObjectAddress address;
10554 : Oid constrOid;
10555 : char *conname;
10556 : bool conislocal;
10557 : int16 coninhcount;
10558 : bool connoinherit;
10559 :
10560 : /*
10561 : * Verify relkind for each referenced partition. At the top level, this
10562 : * is redundant with a previous check, but we need it when recursing.
10563 : */
10564 3788 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10565 790 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10566 0 : ereport(ERROR,
10567 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10568 : errmsg("referenced relation \"%s\" is not a table",
10569 : RelationGetRelationName(pkrel))));
10570 :
10571 : /*
10572 : * Caller supplies us with a constraint name; however, it may be used in
10573 : * this partition, so come up with a different one in that case.
10574 : */
10575 3788 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10576 : RelationGetRelid(rel),
10577 : constraintname))
10578 984 : conname = ChooseConstraintName(RelationGetRelationName(rel),
10579 984 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10580 : "fkey",
10581 984 : RelationGetNamespace(rel), NIL);
10582 : else
10583 2804 : conname = constraintname;
10584 :
10585 3788 : if (fkconstraint->conname == NULL)
10586 418 : fkconstraint->conname = pstrdup(conname);
10587 :
10588 3788 : if (OidIsValid(parentConstr))
10589 : {
10590 1754 : conislocal = false;
10591 1754 : coninhcount = 1;
10592 1754 : connoinherit = false;
10593 : }
10594 : else
10595 : {
10596 2034 : conislocal = true;
10597 2034 : coninhcount = 0;
10598 :
10599 : /*
10600 : * always inherit for partitioned tables, never for legacy inheritance
10601 : */
10602 2034 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10603 : }
10604 :
10605 : /*
10606 : * Record the FK constraint in pg_constraint.
10607 : */
10608 3788 : constrOid = CreateConstraintEntry(conname,
10609 3788 : RelationGetNamespace(rel),
10610 : CONSTRAINT_FOREIGN,
10611 3788 : fkconstraint->deferrable,
10612 3788 : fkconstraint->initdeferred,
10613 : true, /* Is Enforced */
10614 3788 : fkconstraint->initially_valid,
10615 : parentConstr,
10616 : RelationGetRelid(rel),
10617 : fkattnum,
10618 : numfks,
10619 : numfks,
10620 : InvalidOid, /* not a domain constraint */
10621 : indexOid,
10622 : RelationGetRelid(pkrel),
10623 : pkattnum,
10624 : pfeqoperators,
10625 : ppeqoperators,
10626 : ffeqoperators,
10627 : numfks,
10628 3788 : fkconstraint->fk_upd_action,
10629 3788 : fkconstraint->fk_del_action,
10630 : fkdelsetcols,
10631 : numfkdelsetcols,
10632 3788 : fkconstraint->fk_matchtype,
10633 : NULL, /* no exclusion constraint */
10634 : NULL, /* no check constraint */
10635 : NULL,
10636 : conislocal, /* islocal */
10637 : coninhcount, /* inhcount */
10638 : connoinherit, /* conNoInherit */
10639 : with_period, /* conPeriod */
10640 : is_internal); /* is_internal */
10641 :
10642 3788 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10643 :
10644 : /*
10645 : * In partitioning cases, create the dependency entries for this
10646 : * constraint. (For non-partitioned cases, relevant entries were created
10647 : * by CreateConstraintEntry.)
10648 : *
10649 : * On the referenced side, we need the constraint to have an internal
10650 : * dependency on its parent constraint; this means that this constraint
10651 : * cannot be dropped on its own -- only through the parent constraint. It
10652 : * also means the containing partition cannot be dropped on its own, but
10653 : * it can be detached, at which point this dependency is removed (after
10654 : * verifying that no rows are referenced via this FK.)
10655 : *
10656 : * When processing the referencing side, we link the constraint via the
10657 : * special partitioning dependencies: the parent constraint is the primary
10658 : * dependent, and the partition on which the foreign key exists is the
10659 : * secondary dependency. That way, this constraint is dropped if either
10660 : * of these objects is.
10661 : *
10662 : * Note that this is only necessary for the subsidiary pg_constraint rows
10663 : * in partitions; the topmost row doesn't need any of this.
10664 : */
10665 3788 : if (OidIsValid(parentConstr))
10666 : {
10667 : ObjectAddress referenced;
10668 :
10669 1754 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10670 :
10671 : Assert(fkside != addFkBothSides);
10672 1754 : if (fkside == addFkReferencedSide)
10673 978 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10674 : else
10675 : {
10676 776 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10677 776 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10678 776 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10679 : }
10680 : }
10681 :
10682 : /* make new constraint visible, in case we add more */
10683 3788 : CommandCounterIncrement();
10684 :
10685 3788 : return address;
10686 : }
10687 :
10688 : /*
10689 : * addFkRecurseReferenced
10690 : * Recursive helper for the referenced side of foreign key creation,
10691 : * which creates the action triggers and recurses
10692 : *
10693 : * If the referenced relation is a plain relation, create the necessary action
10694 : * triggers that implement the constraint. If the referenced relation is a
10695 : * partitioned table, then we create a pg_constraint row referencing the parent
10696 : * of the referencing side for it and recurse on this routine for each
10697 : * partition.
10698 : *
10699 : * fkconstraint: the constraint being added
10700 : * rel: the root referencing relation
10701 : * pkrel: the referenced relation; might be a partition, if recursing
10702 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10703 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10704 : * top-level constraint
10705 : * numfks: the number of columns in the foreign key
10706 : * pkattnum: the attnum array of referenced attributes
10707 : * fkattnum: the attnum array of referencing attributes
10708 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10709 : * NULL/DEFAULT (...) clause
10710 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10711 : * NULL/DEFAULT clause
10712 : * pf/pp/ffeqoperators: OID array of operators between columns
10713 : * old_check_ok: true if this constraint replaces an existing one that
10714 : * was already validated (thus this one doesn't need validation)
10715 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10716 : * partition, the OIDs of the parent action triggers for DELETE and
10717 : * UPDATE respectively.
10718 : * with_period: true if this is a temporal FK
10719 : */
10720 : static void
10721 3102 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10722 : Relation pkrel, Oid indexOid, Oid parentConstr,
10723 : int numfks,
10724 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10725 : Oid *ppeqoperators, Oid *ffeqoperators,
10726 : int numfkdelsetcols, int16 *fkdelsetcols,
10727 : bool old_check_ok,
10728 : Oid parentDelTrigger, Oid parentUpdTrigger,
10729 : bool with_period)
10730 : {
10731 : Oid deleteTriggerOid,
10732 : updateTriggerOid;
10733 :
10734 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10735 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10736 :
10737 : /*
10738 : * Create the action triggers that enforce the constraint.
10739 : */
10740 3102 : createForeignKeyActionTriggers(RelationGetRelid(rel),
10741 : RelationGetRelid(pkrel),
10742 : fkconstraint,
10743 : parentConstr, indexOid,
10744 : parentDelTrigger, parentUpdTrigger,
10745 : &deleteTriggerOid, &updateTriggerOid);
10746 :
10747 : /*
10748 : * If the referenced table is partitioned, recurse on ourselves to handle
10749 : * each partition. We need one pg_constraint row created for each
10750 : * partition in addition to the pg_constraint row for the parent table.
10751 : */
10752 3102 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10753 : {
10754 492 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10755 :
10756 1344 : for (int i = 0; i < pd->nparts; i++)
10757 : {
10758 : Relation partRel;
10759 : AttrMap *map;
10760 : AttrNumber *mapped_pkattnum;
10761 : Oid partIndexId;
10762 : ObjectAddress address;
10763 :
10764 : /* XXX would it be better to acquire these locks beforehand? */
10765 852 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10766 :
10767 : /*
10768 : * Map the attribute numbers in the referenced side of the FK
10769 : * definition to match the partition's column layout.
10770 : */
10771 852 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10772 : RelationGetDescr(pkrel),
10773 : false);
10774 852 : if (map)
10775 : {
10776 130 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10777 272 : for (int j = 0; j < numfks; j++)
10778 142 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10779 : }
10780 : else
10781 722 : mapped_pkattnum = pkattnum;
10782 :
10783 : /* Determine the index to use at this level */
10784 852 : partIndexId = index_get_partition(partRel, indexOid);
10785 852 : if (!OidIsValid(partIndexId))
10786 0 : elog(ERROR, "index for %u not found in partition %s",
10787 : indexOid, RelationGetRelationName(partRel));
10788 :
10789 : /* Create entry at this level ... */
10790 852 : address = addFkConstraint(addFkReferencedSide,
10791 : fkconstraint->conname, fkconstraint, rel,
10792 : partRel, partIndexId, parentConstr,
10793 : numfks, mapped_pkattnum,
10794 : fkattnum, pfeqoperators, ppeqoperators,
10795 : ffeqoperators, numfkdelsetcols,
10796 : fkdelsetcols, true, with_period);
10797 : /* ... and recurse to our children */
10798 852 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10799 : partIndexId, address.objectId, numfks,
10800 : mapped_pkattnum, fkattnum,
10801 : pfeqoperators, ppeqoperators, ffeqoperators,
10802 : numfkdelsetcols, fkdelsetcols,
10803 : old_check_ok,
10804 : deleteTriggerOid, updateTriggerOid,
10805 : with_period);
10806 :
10807 : /* Done -- clean up (but keep the lock) */
10808 852 : table_close(partRel, NoLock);
10809 852 : if (map)
10810 : {
10811 130 : pfree(mapped_pkattnum);
10812 130 : free_attrmap(map);
10813 : }
10814 : }
10815 : }
10816 3102 : }
10817 :
10818 : /*
10819 : * addFkRecurseReferencing
10820 : * Recursive helper for the referencing side of foreign key creation,
10821 : * which creates the check triggers and recurses
10822 : *
10823 : * If the referencing relation is a plain relation, create the necessary check
10824 : * triggers that implement the constraint, and set up for Phase 3 constraint
10825 : * verification. If the referencing relation is a partitioned table, then
10826 : * we create a pg_constraint row for it and recurse on this routine for each
10827 : * partition.
10828 : *
10829 : * We assume that the referenced relation is locked against concurrent
10830 : * deletions. If it's a partitioned relation, every partition must be so
10831 : * locked.
10832 : *
10833 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
10834 : * of an ALTER TABLE sequence.
10835 : * fkconstraint: the constraint being added
10836 : * rel: the referencing relation; might be a partition, if recursing
10837 : * pkrel: the root referenced relation
10838 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10839 : * parentConstr: the OID of the parent constraint (there is always one)
10840 : * numfks: the number of columns in the foreign key
10841 : * pkattnum: the attnum array of referenced attributes
10842 : * fkattnum: the attnum array of referencing attributes
10843 : * pf/pp/ffeqoperators: OID array of operators between columns
10844 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10845 : * (...) clause
10846 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10847 : * NULL/DEFAULT clause
10848 : * old_check_ok: true if this constraint replaces an existing one that
10849 : * was already validated (thus this one doesn't need validation)
10850 : * lockmode: the lockmode to acquire on partitions when recursing
10851 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
10852 : * a partition, the OIDs of the parent check triggers for INSERT and
10853 : * UPDATE respectively.
10854 : * with_period: true if this is a temporal FK
10855 : */
10856 : static void
10857 2810 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10858 : Relation pkrel, Oid indexOid, Oid parentConstr,
10859 : int numfks, int16 *pkattnum, int16 *fkattnum,
10860 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10861 : int numfkdelsetcols, int16 *fkdelsetcols,
10862 : bool old_check_ok, LOCKMODE lockmode,
10863 : Oid parentInsTrigger, Oid parentUpdTrigger,
10864 : bool with_period)
10865 : {
10866 : Oid insertTriggerOid,
10867 : updateTriggerOid;
10868 :
10869 : Assert(OidIsValid(parentConstr));
10870 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10871 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10872 :
10873 2810 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10874 0 : ereport(ERROR,
10875 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10876 : errmsg("foreign key constraints are not supported on foreign tables")));
10877 :
10878 : /*
10879 : * Add the check triggers to it and, if necessary, schedule it to be
10880 : * checked in Phase 3.
10881 : *
10882 : * If the relation is partitioned, drill down to do it to its partitions.
10883 : */
10884 2810 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10885 : RelationGetRelid(pkrel),
10886 : fkconstraint,
10887 : parentConstr,
10888 : indexOid,
10889 : parentInsTrigger, parentUpdTrigger,
10890 : &insertTriggerOid, &updateTriggerOid);
10891 :
10892 2810 : if (rel->rd_rel->relkind == RELKIND_RELATION)
10893 : {
10894 : /*
10895 : * Tell Phase 3 to check that the constraint is satisfied by existing
10896 : * rows. We can skip this during table creation, when requested
10897 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10898 : * and when we're recreating a constraint following a SET DATA TYPE
10899 : * operation that did not impugn its validity.
10900 : */
10901 2344 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10902 : {
10903 : NewConstraint *newcon;
10904 : AlteredTableInfo *tab;
10905 :
10906 784 : tab = ATGetQueueEntry(wqueue, rel);
10907 :
10908 784 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10909 784 : newcon->name = get_constraint_name(parentConstr);
10910 784 : newcon->contype = CONSTR_FOREIGN;
10911 784 : newcon->refrelid = RelationGetRelid(pkrel);
10912 784 : newcon->refindid = indexOid;
10913 784 : newcon->conid = parentConstr;
10914 784 : newcon->conwithperiod = fkconstraint->fk_with_period;
10915 784 : newcon->qual = (Node *) fkconstraint;
10916 :
10917 784 : tab->constraints = lappend(tab->constraints, newcon);
10918 : }
10919 : }
10920 466 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10921 : {
10922 466 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10923 : Relation trigrel;
10924 :
10925 : /*
10926 : * Triggers of the foreign keys will be manipulated a bunch of times
10927 : * in the loop below. To avoid repeatedly opening/closing the trigger
10928 : * catalog relation, we open it here and pass it to the subroutines
10929 : * called below.
10930 : */
10931 466 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10932 :
10933 : /*
10934 : * Recurse to take appropriate action on each partition; either we
10935 : * find an existing constraint to reparent to ours, or we create a new
10936 : * one.
10937 : */
10938 836 : for (int i = 0; i < pd->nparts; i++)
10939 : {
10940 376 : Relation partition = table_open(pd->oids[i], lockmode);
10941 : List *partFKs;
10942 : AttrMap *attmap;
10943 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10944 : bool attached;
10945 : ObjectAddress address;
10946 :
10947 376 : CheckAlterTableIsSafe(partition);
10948 :
10949 370 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10950 : RelationGetDescr(rel),
10951 : false);
10952 950 : for (int j = 0; j < numfks; j++)
10953 580 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10954 :
10955 : /* Check whether an existing constraint can be repurposed */
10956 370 : partFKs = copyObject(RelationGetFKeyList(partition));
10957 370 : attached = false;
10958 758 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
10959 : {
10960 30 : if (tryAttachPartitionForeignKey(wqueue,
10961 : fk,
10962 : partition,
10963 : parentConstr,
10964 : numfks,
10965 : mapped_fkattnum,
10966 : pkattnum,
10967 : pfeqoperators,
10968 : insertTriggerOid,
10969 : updateTriggerOid,
10970 : trigrel))
10971 : {
10972 12 : attached = true;
10973 12 : break;
10974 : }
10975 : }
10976 370 : if (attached)
10977 : {
10978 12 : table_close(partition, NoLock);
10979 12 : continue;
10980 : }
10981 :
10982 : /*
10983 : * No luck finding a good constraint to reuse; create our own.
10984 : */
10985 358 : address = addFkConstraint(addFkReferencingSide,
10986 : fkconstraint->conname, fkconstraint,
10987 : partition, pkrel, indexOid, parentConstr,
10988 : numfks, pkattnum,
10989 : mapped_fkattnum, pfeqoperators,
10990 : ppeqoperators, ffeqoperators,
10991 : numfkdelsetcols, fkdelsetcols, true,
10992 : with_period);
10993 :
10994 : /* call ourselves to finalize the creation and we're done */
10995 358 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10996 : indexOid,
10997 : address.objectId,
10998 : numfks,
10999 : pkattnum,
11000 : mapped_fkattnum,
11001 : pfeqoperators,
11002 : ppeqoperators,
11003 : ffeqoperators,
11004 : numfkdelsetcols,
11005 : fkdelsetcols,
11006 : old_check_ok,
11007 : lockmode,
11008 : insertTriggerOid,
11009 : updateTriggerOid,
11010 : with_period);
11011 :
11012 358 : table_close(partition, NoLock);
11013 : }
11014 :
11015 460 : table_close(trigrel, RowExclusiveLock);
11016 : }
11017 2804 : }
11018 :
11019 : /*
11020 : * CloneForeignKeyConstraints
11021 : * Clone foreign keys from a partitioned table to a newly acquired
11022 : * partition.
11023 : *
11024 : * partitionRel is a partition of parentRel, so we can be certain that it has
11025 : * the same columns with the same datatypes. The columns may be in different
11026 : * order, though.
11027 : *
11028 : * wqueue must be passed to set up phase 3 constraint checking, unless the
11029 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
11030 : * PARTITION OF).
11031 : */
11032 : static void
11033 9452 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
11034 : Relation partitionRel)
11035 : {
11036 : /* This only works for declarative partitioning */
11037 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11038 :
11039 : /*
11040 : * Clone constraints for which the parent is on the referenced side.
11041 : */
11042 9452 : CloneFkReferenced(parentRel, partitionRel);
11043 :
11044 : /*
11045 : * Now clone constraints where the parent is on the referencing side.
11046 : */
11047 9452 : CloneFkReferencing(wqueue, parentRel, partitionRel);
11048 9440 : }
11049 :
11050 : /*
11051 : * CloneFkReferenced
11052 : * Subroutine for CloneForeignKeyConstraints
11053 : *
11054 : * Find all the FKs that have the parent relation on the referenced side;
11055 : * clone those constraints to the given partition. This is to be called
11056 : * when the partition is being created or attached.
11057 : *
11058 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
11059 : *
11060 : * This recurses to partitions, if the relation being attached is partitioned.
11061 : * Recursion is done by calling addFkRecurseReferenced.
11062 : */
11063 : static void
11064 9452 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11065 : {
11066 : Relation pg_constraint;
11067 : AttrMap *attmap;
11068 : ListCell *cell;
11069 : SysScanDesc scan;
11070 : ScanKeyData key[2];
11071 : HeapTuple tuple;
11072 9452 : List *clone = NIL;
11073 : Relation trigrel;
11074 :
11075 : /*
11076 : * Search for any constraints where this partition's parent is in the
11077 : * referenced side. However, we must not clone any constraint whose
11078 : * parent constraint is also going to be cloned, to avoid duplicates. So
11079 : * do it in two steps: first construct the list of constraints to clone,
11080 : * then go over that list cloning those whose parents are not in the list.
11081 : * (We must not rely on the parent being seen first, since the catalog
11082 : * scan could return children first.)
11083 : */
11084 9452 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11085 9452 : ScanKeyInit(&key[0],
11086 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11087 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11088 9452 : ScanKeyInit(&key[1],
11089 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11090 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11091 : /* This is a seqscan, as we don't have a usable index ... */
11092 9452 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11093 : NULL, 2, key);
11094 9758 : while ((tuple = systable_getnext(scan)) != NULL)
11095 : {
11096 306 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11097 :
11098 306 : clone = lappend_oid(clone, constrForm->oid);
11099 : }
11100 9452 : systable_endscan(scan);
11101 9452 : table_close(pg_constraint, RowShareLock);
11102 :
11103 : /*
11104 : * Triggers of the foreign keys will be manipulated a bunch of times in
11105 : * the loop below. To avoid repeatedly opening/closing the trigger
11106 : * catalog relation, we open it here and pass it to the subroutines called
11107 : * below.
11108 : */
11109 9452 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11110 :
11111 9452 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11112 : RelationGetDescr(parentRel),
11113 : false);
11114 9758 : foreach(cell, clone)
11115 : {
11116 306 : Oid constrOid = lfirst_oid(cell);
11117 : Form_pg_constraint constrForm;
11118 : Relation fkRel;
11119 : Oid indexOid;
11120 : Oid partIndexId;
11121 : int numfks;
11122 : AttrNumber conkey[INDEX_MAX_KEYS];
11123 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11124 : AttrNumber confkey[INDEX_MAX_KEYS];
11125 : Oid conpfeqop[INDEX_MAX_KEYS];
11126 : Oid conppeqop[INDEX_MAX_KEYS];
11127 : Oid conffeqop[INDEX_MAX_KEYS];
11128 : int numfkdelsetcols;
11129 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11130 : Constraint *fkconstraint;
11131 : ObjectAddress address;
11132 : Oid deleteTriggerOid,
11133 : updateTriggerOid;
11134 :
11135 306 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11136 306 : if (!HeapTupleIsValid(tuple))
11137 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11138 306 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11139 :
11140 : /*
11141 : * As explained above: don't try to clone a constraint for which we're
11142 : * going to clone the parent.
11143 : */
11144 306 : if (list_member_oid(clone, constrForm->conparentid))
11145 : {
11146 126 : ReleaseSysCache(tuple);
11147 180 : continue;
11148 : }
11149 :
11150 : /*
11151 : * Don't clone self-referencing foreign keys, which can be in the
11152 : * partitioned table or in the partition-to-be.
11153 : */
11154 180 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11155 138 : constrForm->conrelid == RelationGetRelid(partitionRel))
11156 : {
11157 54 : ReleaseSysCache(tuple);
11158 54 : continue;
11159 : }
11160 :
11161 : /* We need the same lock level that CreateTrigger will acquire */
11162 126 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11163 :
11164 126 : indexOid = constrForm->conindid;
11165 126 : DeconstructFkConstraintRow(tuple,
11166 : &numfks,
11167 : conkey,
11168 : confkey,
11169 : conpfeqop,
11170 : conppeqop,
11171 : conffeqop,
11172 : &numfkdelsetcols,
11173 : confdelsetcols);
11174 :
11175 258 : for (int i = 0; i < numfks; i++)
11176 132 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11177 :
11178 126 : fkconstraint = makeNode(Constraint);
11179 126 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11180 126 : fkconstraint->conname = NameStr(constrForm->conname);
11181 126 : fkconstraint->deferrable = constrForm->condeferrable;
11182 126 : fkconstraint->initdeferred = constrForm->condeferred;
11183 126 : fkconstraint->location = -1;
11184 126 : fkconstraint->pktable = NULL;
11185 : /* ->fk_attrs determined below */
11186 126 : fkconstraint->pk_attrs = NIL;
11187 126 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11188 126 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11189 126 : fkconstraint->fk_del_action = constrForm->confdeltype;
11190 126 : fkconstraint->fk_del_set_cols = NIL;
11191 126 : fkconstraint->old_conpfeqop = NIL;
11192 126 : fkconstraint->old_pktable_oid = InvalidOid;
11193 126 : fkconstraint->skip_validation = false;
11194 126 : fkconstraint->initially_valid = true;
11195 :
11196 : /* set up colnames that are used to generate the constraint name */
11197 258 : for (int i = 0; i < numfks; i++)
11198 : {
11199 : Form_pg_attribute att;
11200 :
11201 132 : att = TupleDescAttr(RelationGetDescr(fkRel),
11202 132 : conkey[i] - 1);
11203 132 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11204 132 : makeString(NameStr(att->attname)));
11205 : }
11206 :
11207 : /*
11208 : * Add the new foreign key constraint pointing to the new partition.
11209 : * Because this new partition appears in the referenced side of the
11210 : * constraint, we don't need to set up for Phase 3 check.
11211 : */
11212 126 : partIndexId = index_get_partition(partitionRel, indexOid);
11213 126 : if (!OidIsValid(partIndexId))
11214 0 : elog(ERROR, "index for %u not found in partition %s",
11215 : indexOid, RelationGetRelationName(partitionRel));
11216 :
11217 : /*
11218 : * Get the "action" triggers belonging to the constraint to pass as
11219 : * parent OIDs for similar triggers that will be created on the
11220 : * partition in addFkRecurseReferenced().
11221 : */
11222 126 : GetForeignKeyActionTriggers(trigrel, constrOid,
11223 : constrForm->confrelid, constrForm->conrelid,
11224 : &deleteTriggerOid, &updateTriggerOid);
11225 :
11226 : /* Add this constraint ... */
11227 126 : address = addFkConstraint(addFkReferencedSide,
11228 : fkconstraint->conname, fkconstraint, fkRel,
11229 : partitionRel, partIndexId, constrOid,
11230 : numfks, mapped_confkey,
11231 : conkey, conpfeqop, conppeqop, conffeqop,
11232 : numfkdelsetcols, confdelsetcols, false,
11233 126 : constrForm->conperiod);
11234 : /* ... and recurse */
11235 126 : addFkRecurseReferenced(fkconstraint,
11236 : fkRel,
11237 : partitionRel,
11238 : partIndexId,
11239 : address.objectId,
11240 : numfks,
11241 : mapped_confkey,
11242 : conkey,
11243 : conpfeqop,
11244 : conppeqop,
11245 : conffeqop,
11246 : numfkdelsetcols,
11247 : confdelsetcols,
11248 : true,
11249 : deleteTriggerOid,
11250 : updateTriggerOid,
11251 126 : constrForm->conperiod);
11252 :
11253 126 : table_close(fkRel, NoLock);
11254 126 : ReleaseSysCache(tuple);
11255 : }
11256 :
11257 9452 : table_close(trigrel, RowExclusiveLock);
11258 9452 : }
11259 :
11260 : /*
11261 : * CloneFkReferencing
11262 : * Subroutine for CloneForeignKeyConstraints
11263 : *
11264 : * For each FK constraint of the parent relation in the given list, find an
11265 : * equivalent constraint in its partition relation that can be reparented;
11266 : * if one cannot be found, create a new constraint in the partition as its
11267 : * child.
11268 : *
11269 : * If wqueue is given, it is used to set up phase-3 verification for each
11270 : * cloned constraint; omit it if such verification is not needed
11271 : * (example: the partition is being created anew).
11272 : */
11273 : static void
11274 9452 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11275 : {
11276 : AttrMap *attmap;
11277 : List *partFKs;
11278 9452 : List *clone = NIL;
11279 : ListCell *cell;
11280 : Relation trigrel;
11281 :
11282 : /* obtain a list of constraints that we need to clone */
11283 10624 : foreach(cell, RelationGetFKeyList(parentRel))
11284 : {
11285 1178 : ForeignKeyCacheInfo *fk = lfirst(cell);
11286 :
11287 : /*
11288 : * Refuse to attach a table as partition that this partitioned table
11289 : * already has a foreign key to. This isn't useful schema, which is
11290 : * proven by the fact that there have been no user complaints that
11291 : * it's already impossible to achieve this in the opposite direction,
11292 : * i.e., creating a foreign key that references a partition. This
11293 : * restriction allows us to dodge some complexities around
11294 : * pg_constraint and pg_trigger row creations that would be needed
11295 : * during ATTACH/DETACH for this kind of relationship.
11296 : */
11297 1178 : if (fk->confrelid == RelationGetRelid(partRel))
11298 6 : ereport(ERROR,
11299 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11300 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11301 : RelationGetRelationName(partRel),
11302 : get_constraint_name(fk->conoid))));
11303 :
11304 1172 : clone = lappend_oid(clone, fk->conoid);
11305 : }
11306 :
11307 : /*
11308 : * Silently do nothing if there's nothing to do. In particular, this
11309 : * avoids throwing a spurious error for foreign tables.
11310 : */
11311 9446 : if (clone == NIL)
11312 8944 : return;
11313 :
11314 502 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11315 0 : ereport(ERROR,
11316 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11317 : errmsg("foreign key constraints are not supported on foreign tables")));
11318 :
11319 : /*
11320 : * Triggers of the foreign keys will be manipulated a bunch of times in
11321 : * the loop below. To avoid repeatedly opening/closing the trigger
11322 : * catalog relation, we open it here and pass it to the subroutines called
11323 : * below.
11324 : */
11325 502 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11326 :
11327 : /*
11328 : * The constraint key may differ, if the columns in the partition are
11329 : * different. This map is used to convert them.
11330 : */
11331 502 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11332 : RelationGetDescr(parentRel),
11333 : false);
11334 :
11335 502 : partFKs = copyObject(RelationGetFKeyList(partRel));
11336 :
11337 1668 : foreach(cell, clone)
11338 : {
11339 1172 : Oid parentConstrOid = lfirst_oid(cell);
11340 : Form_pg_constraint constrForm;
11341 : Relation pkrel;
11342 : HeapTuple tuple;
11343 : int numfks;
11344 : AttrNumber conkey[INDEX_MAX_KEYS];
11345 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11346 : AttrNumber confkey[INDEX_MAX_KEYS];
11347 : Oid conpfeqop[INDEX_MAX_KEYS];
11348 : Oid conppeqop[INDEX_MAX_KEYS];
11349 : Oid conffeqop[INDEX_MAX_KEYS];
11350 : int numfkdelsetcols;
11351 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11352 : Constraint *fkconstraint;
11353 : bool attached;
11354 : Oid indexOid;
11355 : ObjectAddress address;
11356 : ListCell *lc;
11357 : Oid insertTriggerOid,
11358 : updateTriggerOid;
11359 : bool with_period;
11360 :
11361 1172 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11362 1172 : if (!HeapTupleIsValid(tuple))
11363 0 : elog(ERROR, "cache lookup failed for constraint %u",
11364 : parentConstrOid);
11365 1172 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11366 :
11367 : /* Don't clone constraints whose parents are being cloned */
11368 1172 : if (list_member_oid(clone, constrForm->conparentid))
11369 : {
11370 634 : ReleaseSysCache(tuple);
11371 754 : continue;
11372 : }
11373 :
11374 : /*
11375 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11376 : * relation, that means to lock all partitions.
11377 : */
11378 538 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11379 538 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11380 226 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11381 : ShareRowExclusiveLock, NULL);
11382 :
11383 538 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11384 : conpfeqop, conppeqop, conffeqop,
11385 : &numfkdelsetcols, confdelsetcols);
11386 1280 : for (int i = 0; i < numfks; i++)
11387 742 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11388 :
11389 : /*
11390 : * Get the "check" triggers belonging to the constraint to pass as
11391 : * parent OIDs for similar triggers that will be created on the
11392 : * partition in addFkRecurseReferencing(). They are also passed to
11393 : * tryAttachPartitionForeignKey() below to simply assign as parents to
11394 : * the partition's existing "check" triggers, that is, if the
11395 : * corresponding constraints is deemed attachable to the parent
11396 : * constraint.
11397 : */
11398 538 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11399 : constrForm->confrelid, constrForm->conrelid,
11400 : &insertTriggerOid, &updateTriggerOid);
11401 :
11402 : /*
11403 : * Before creating a new constraint, see whether any existing FKs are
11404 : * fit for the purpose. If one is, attach the parent constraint to
11405 : * it, and don't clone anything. This way we avoid the expensive
11406 : * verification step and don't end up with a duplicate FK, and we
11407 : * don't need to recurse to partitions for this constraint.
11408 : */
11409 538 : attached = false;
11410 748 : foreach(lc, partFKs)
11411 : {
11412 330 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11413 :
11414 330 : if (tryAttachPartitionForeignKey(wqueue,
11415 : fk,
11416 : partRel,
11417 : parentConstrOid,
11418 : numfks,
11419 : mapped_conkey,
11420 : confkey,
11421 : conpfeqop,
11422 : insertTriggerOid,
11423 : updateTriggerOid,
11424 : trigrel))
11425 : {
11426 120 : attached = true;
11427 120 : table_close(pkrel, NoLock);
11428 120 : break;
11429 : }
11430 : }
11431 538 : if (attached)
11432 : {
11433 120 : ReleaseSysCache(tuple);
11434 120 : continue;
11435 : }
11436 :
11437 : /* No dice. Set up to create our own constraint */
11438 418 : fkconstraint = makeNode(Constraint);
11439 418 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11440 : /* ->conname determined below */
11441 418 : fkconstraint->deferrable = constrForm->condeferrable;
11442 418 : fkconstraint->initdeferred = constrForm->condeferred;
11443 418 : fkconstraint->location = -1;
11444 418 : fkconstraint->pktable = NULL;
11445 : /* ->fk_attrs determined below */
11446 418 : fkconstraint->pk_attrs = NIL;
11447 418 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11448 418 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11449 418 : fkconstraint->fk_del_action = constrForm->confdeltype;
11450 418 : fkconstraint->fk_del_set_cols = NIL;
11451 418 : fkconstraint->old_conpfeqop = NIL;
11452 418 : fkconstraint->old_pktable_oid = InvalidOid;
11453 418 : fkconstraint->skip_validation = false;
11454 418 : fkconstraint->initially_valid = constrForm->convalidated;
11455 950 : for (int i = 0; i < numfks; i++)
11456 : {
11457 : Form_pg_attribute att;
11458 :
11459 532 : att = TupleDescAttr(RelationGetDescr(partRel),
11460 532 : mapped_conkey[i] - 1);
11461 532 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11462 532 : makeString(NameStr(att->attname)));
11463 : }
11464 :
11465 418 : indexOid = constrForm->conindid;
11466 418 : with_period = constrForm->conperiod;
11467 :
11468 : /* Create the pg_constraint entry at this level */
11469 418 : address = addFkConstraint(addFkReferencingSide,
11470 418 : NameStr(constrForm->conname), fkconstraint,
11471 : partRel, pkrel, indexOid, parentConstrOid,
11472 : numfks, confkey,
11473 : mapped_conkey, conpfeqop,
11474 : conppeqop, conffeqop,
11475 : numfkdelsetcols, confdelsetcols,
11476 : false, with_period);
11477 :
11478 : /* Done with the cloned constraint's tuple */
11479 418 : ReleaseSysCache(tuple);
11480 :
11481 : /* Create the check triggers, and recurse to partitions, if any */
11482 418 : addFkRecurseReferencing(wqueue,
11483 : fkconstraint,
11484 : partRel,
11485 : pkrel,
11486 : indexOid,
11487 : address.objectId,
11488 : numfks,
11489 : confkey,
11490 : mapped_conkey,
11491 : conpfeqop,
11492 : conppeqop,
11493 : conffeqop,
11494 : numfkdelsetcols,
11495 : confdelsetcols,
11496 : false, /* no old check exists */
11497 : AccessExclusiveLock,
11498 : insertTriggerOid,
11499 : updateTriggerOid,
11500 : with_period);
11501 412 : table_close(pkrel, NoLock);
11502 : }
11503 :
11504 496 : table_close(trigrel, RowExclusiveLock);
11505 : }
11506 :
11507 : /*
11508 : * When the parent of a partition receives [the referencing side of] a foreign
11509 : * key, we must propagate that foreign key to the partition. However, the
11510 : * partition might already have an equivalent foreign key; this routine
11511 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11512 : * by the other parameters. If they are equivalent, create the link between
11513 : * the two constraints and return true.
11514 : *
11515 : * If the given FK does not match the one defined by rest of the params,
11516 : * return false.
11517 : */
11518 : static bool
11519 360 : tryAttachPartitionForeignKey(List **wqueue,
11520 : ForeignKeyCacheInfo *fk,
11521 : Relation partition,
11522 : Oid parentConstrOid,
11523 : int numfks,
11524 : AttrNumber *mapped_conkey,
11525 : AttrNumber *confkey,
11526 : Oid *conpfeqop,
11527 : Oid parentInsTrigger,
11528 : Oid parentUpdTrigger,
11529 : Relation trigrel)
11530 : {
11531 : HeapTuple parentConstrTup;
11532 : Form_pg_constraint parentConstr;
11533 : HeapTuple partcontup;
11534 : Form_pg_constraint partConstr;
11535 :
11536 360 : parentConstrTup = SearchSysCache1(CONSTROID,
11537 : ObjectIdGetDatum(parentConstrOid));
11538 360 : if (!HeapTupleIsValid(parentConstrTup))
11539 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11540 360 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11541 :
11542 : /*
11543 : * Do some quick & easy initial checks. If any of these fail, we cannot
11544 : * use this constraint.
11545 : */
11546 360 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11547 : {
11548 120 : ReleaseSysCache(parentConstrTup);
11549 120 : return false;
11550 : }
11551 672 : for (int i = 0; i < numfks; i++)
11552 : {
11553 432 : if (fk->conkey[i] != mapped_conkey[i] ||
11554 432 : fk->confkey[i] != confkey[i] ||
11555 432 : fk->conpfeqop[i] != conpfeqop[i])
11556 : {
11557 0 : ReleaseSysCache(parentConstrTup);
11558 0 : return false;
11559 : }
11560 : }
11561 :
11562 : /* Looks good so far; perform more extensive checks. */
11563 240 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11564 240 : if (!HeapTupleIsValid(partcontup))
11565 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11566 240 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11567 240 : if (OidIsValid(partConstr->conparentid) ||
11568 210 : partConstr->condeferrable != parentConstr->condeferrable ||
11569 182 : partConstr->condeferred != parentConstr->condeferred ||
11570 182 : partConstr->confupdtype != parentConstr->confupdtype ||
11571 146 : partConstr->confdeltype != parentConstr->confdeltype ||
11572 146 : partConstr->confmatchtype != parentConstr->confmatchtype)
11573 : {
11574 108 : ReleaseSysCache(parentConstrTup);
11575 108 : ReleaseSysCache(partcontup);
11576 108 : return false;
11577 : }
11578 :
11579 132 : ReleaseSysCache(parentConstrTup);
11580 132 : ReleaseSysCache(partcontup);
11581 :
11582 : /* Looks good! Attach this constraint. */
11583 132 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11584 : parentConstrOid, parentInsTrigger,
11585 : parentUpdTrigger, trigrel);
11586 :
11587 132 : return true;
11588 : }
11589 :
11590 : /*
11591 : * AttachPartitionForeignKey
11592 : *
11593 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11594 : * attaching the constraint, removing redundant triggers and entries from
11595 : * pg_constraint, and setting the constraint's parent.
11596 : */
11597 : static void
11598 132 : AttachPartitionForeignKey(List **wqueue,
11599 : Relation partition,
11600 : Oid partConstrOid,
11601 : Oid parentConstrOid,
11602 : Oid parentInsTrigger,
11603 : Oid parentUpdTrigger,
11604 : Relation trigrel)
11605 : {
11606 : HeapTuple parentConstrTup;
11607 : Form_pg_constraint parentConstr;
11608 : HeapTuple partcontup;
11609 : Form_pg_constraint partConstr;
11610 : bool queueValidation;
11611 : Oid partConstrFrelid;
11612 : Oid partConstrRelid;
11613 : Oid insertTriggerOid,
11614 : updateTriggerOid;
11615 :
11616 : /* Fetch the parent constraint tuple */
11617 132 : parentConstrTup = SearchSysCache1(CONSTROID,
11618 : ObjectIdGetDatum(parentConstrOid));
11619 132 : if (!HeapTupleIsValid(parentConstrTup))
11620 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11621 132 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11622 :
11623 : /* Fetch the child constraint tuple */
11624 132 : partcontup = SearchSysCache1(CONSTROID,
11625 : ObjectIdGetDatum(partConstrOid));
11626 132 : if (!HeapTupleIsValid(partcontup))
11627 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11628 132 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11629 132 : partConstrFrelid = partConstr->confrelid;
11630 132 : partConstrRelid = partConstr->conrelid;
11631 :
11632 : /*
11633 : * If the referenced table is partitioned, then the partition we're
11634 : * attaching now has extra pg_constraint rows and action triggers that are
11635 : * no longer needed. Remove those.
11636 : */
11637 132 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11638 : {
11639 24 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11640 :
11641 24 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11642 : partConstrRelid);
11643 :
11644 24 : table_close(pg_constraint, RowShareLock);
11645 : }
11646 :
11647 : /*
11648 : * Will we need to validate this constraint? A valid parent constraint
11649 : * implies that all child constraints have been validated, so if this one
11650 : * isn't, we must trigger phase 3 validation.
11651 : */
11652 132 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11653 :
11654 132 : ReleaseSysCache(partcontup);
11655 132 : ReleaseSysCache(parentConstrTup);
11656 :
11657 : /*
11658 : * The action triggers in the new partition become redundant -- the parent
11659 : * table already has equivalent ones, and those will be able to reach the
11660 : * partition. Remove the ones in the partition. We identify them because
11661 : * they have our constraint OID, as well as being on the referenced rel.
11662 : */
11663 132 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11664 : partConstrRelid);
11665 :
11666 132 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11667 : RelationGetRelid(partition));
11668 :
11669 : /*
11670 : * Like the constraint, attach partition's "check" triggers to the
11671 : * corresponding parent triggers.
11672 : */
11673 132 : GetForeignKeyCheckTriggers(trigrel,
11674 : partConstrOid, partConstrFrelid, partConstrRelid,
11675 : &insertTriggerOid, &updateTriggerOid);
11676 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11677 132 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11678 : RelationGetRelid(partition));
11679 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11680 132 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11681 : RelationGetRelid(partition));
11682 :
11683 : /*
11684 : * We updated this pg_constraint row above to set its parent; validating
11685 : * it will cause its convalidated flag to change, so we need CCI here. In
11686 : * addition, we need it unconditionally for the rare case where the parent
11687 : * table has *two* identical constraints; when reaching this function for
11688 : * the second one, we must have made our changes visible, otherwise we
11689 : * would try to attach both to this one.
11690 : */
11691 132 : CommandCounterIncrement();
11692 :
11693 : /* If validation is needed, put it in the queue now. */
11694 132 : if (queueValidation)
11695 : {
11696 : Relation conrel;
11697 :
11698 18 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11699 :
11700 18 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11701 18 : if (!HeapTupleIsValid(partcontup))
11702 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11703 :
11704 : /* Use the same lock as for AT_ValidateConstraint */
11705 18 : QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11706 : ShareUpdateExclusiveLock);
11707 18 : ReleaseSysCache(partcontup);
11708 18 : table_close(conrel, RowExclusiveLock);
11709 : }
11710 132 : }
11711 :
11712 : /*
11713 : * RemoveInheritedConstraint
11714 : *
11715 : * Removes the constraint and its associated trigger from the specified
11716 : * relation, which inherited the given constraint.
11717 : */
11718 : static void
11719 24 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11720 : Oid conrelid)
11721 : {
11722 : ObjectAddresses *objs;
11723 : HeapTuple consttup;
11724 : ScanKeyData key;
11725 : SysScanDesc scan;
11726 : HeapTuple trigtup;
11727 :
11728 24 : ScanKeyInit(&key,
11729 : Anum_pg_constraint_conrelid,
11730 : BTEqualStrategyNumber, F_OIDEQ,
11731 : ObjectIdGetDatum(conrelid));
11732 :
11733 24 : scan = systable_beginscan(conrel,
11734 : ConstraintRelidTypidNameIndexId,
11735 : true, NULL, 1, &key);
11736 24 : objs = new_object_addresses();
11737 240 : while ((consttup = systable_getnext(scan)) != NULL)
11738 : {
11739 216 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11740 :
11741 216 : if (conform->conparentid != conoid)
11742 168 : continue;
11743 : else
11744 : {
11745 : ObjectAddress addr;
11746 : SysScanDesc scan2;
11747 : ScanKeyData key2;
11748 : int n PG_USED_FOR_ASSERTS_ONLY;
11749 :
11750 48 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11751 48 : add_exact_object_address(&addr, objs);
11752 :
11753 : /*
11754 : * First we must delete the dependency record that binds the
11755 : * constraint records together.
11756 : */
11757 48 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11758 : conform->oid,
11759 : DEPENDENCY_INTERNAL,
11760 : ConstraintRelationId,
11761 : conoid);
11762 : Assert(n == 1); /* actually only one is expected */
11763 :
11764 : /*
11765 : * Now search for the triggers for this constraint and set them up
11766 : * for deletion too
11767 : */
11768 48 : ScanKeyInit(&key2,
11769 : Anum_pg_trigger_tgconstraint,
11770 : BTEqualStrategyNumber, F_OIDEQ,
11771 : ObjectIdGetDatum(conform->oid));
11772 48 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11773 : true, NULL, 1, &key2);
11774 144 : while ((trigtup = systable_getnext(scan2)) != NULL)
11775 : {
11776 96 : ObjectAddressSet(addr, TriggerRelationId,
11777 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11778 96 : add_exact_object_address(&addr, objs);
11779 : }
11780 48 : systable_endscan(scan2);
11781 : }
11782 : }
11783 : /* make the dependency deletions visible */
11784 24 : CommandCounterIncrement();
11785 24 : performMultipleDeletions(objs, DROP_RESTRICT,
11786 : PERFORM_DELETION_INTERNAL);
11787 24 : systable_endscan(scan);
11788 24 : }
11789 :
11790 : /*
11791 : * DropForeignKeyConstraintTriggers
11792 : *
11793 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11794 : * action triggers for the foreign key constraint.
11795 : */
11796 : static void
11797 132 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
11798 : Oid conrelid)
11799 : {
11800 : ScanKeyData key;
11801 : SysScanDesc scan;
11802 : HeapTuple trigtup;
11803 :
11804 132 : ScanKeyInit(&key,
11805 : Anum_pg_trigger_tgconstraint,
11806 : BTEqualStrategyNumber, F_OIDEQ,
11807 : ObjectIdGetDatum(conoid));
11808 132 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11809 : NULL, 1, &key);
11810 660 : while ((trigtup = systable_getnext(scan)) != NULL)
11811 : {
11812 528 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11813 : ObjectAddress trigger;
11814 :
11815 528 : if (trgform->tgconstrrelid != conrelid)
11816 264 : continue;
11817 264 : if (trgform->tgrelid != confrelid)
11818 0 : continue;
11819 :
11820 : /*
11821 : * The constraint is originally set up to contain this trigger as an
11822 : * implementation object, so there's a dependency record that links
11823 : * the two; however, since the trigger is no longer needed, we remove
11824 : * the dependency link in order to be able to drop the trigger while
11825 : * keeping the constraint intact.
11826 : */
11827 264 : deleteDependencyRecordsFor(TriggerRelationId,
11828 : trgform->oid,
11829 : false);
11830 : /* make dependency deletion visible to performDeletion */
11831 264 : CommandCounterIncrement();
11832 264 : ObjectAddressSet(trigger, TriggerRelationId,
11833 : trgform->oid);
11834 264 : performDeletion(&trigger, DROP_RESTRICT, 0);
11835 : /* make trigger drop visible, in case the loop iterates */
11836 264 : CommandCounterIncrement();
11837 : }
11838 :
11839 132 : systable_endscan(scan);
11840 132 : }
11841 :
11842 : /*
11843 : * GetForeignKeyActionTriggers
11844 : * Returns delete and update "action" triggers of the given relation
11845 : * belonging to the given constraint
11846 : */
11847 : static void
11848 126 : GetForeignKeyActionTriggers(Relation trigrel,
11849 : Oid conoid, Oid confrelid, Oid conrelid,
11850 : Oid *deleteTriggerOid,
11851 : Oid *updateTriggerOid)
11852 : {
11853 : ScanKeyData key;
11854 : SysScanDesc scan;
11855 : HeapTuple trigtup;
11856 :
11857 126 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11858 126 : ScanKeyInit(&key,
11859 : Anum_pg_trigger_tgconstraint,
11860 : BTEqualStrategyNumber, F_OIDEQ,
11861 : ObjectIdGetDatum(conoid));
11862 :
11863 126 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11864 : NULL, 1, &key);
11865 270 : while ((trigtup = systable_getnext(scan)) != NULL)
11866 : {
11867 270 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11868 :
11869 270 : if (trgform->tgconstrrelid != conrelid)
11870 18 : continue;
11871 252 : if (trgform->tgrelid != confrelid)
11872 0 : continue;
11873 : /* Only ever look at "action" triggers on the PK side. */
11874 252 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11875 0 : continue;
11876 252 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11877 : {
11878 : Assert(*deleteTriggerOid == InvalidOid);
11879 126 : *deleteTriggerOid = trgform->oid;
11880 : }
11881 126 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11882 : {
11883 : Assert(*updateTriggerOid == InvalidOid);
11884 126 : *updateTriggerOid = trgform->oid;
11885 : }
11886 : #ifndef USE_ASSERT_CHECKING
11887 : /* In an assert-enabled build, continue looking to find duplicates */
11888 252 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11889 126 : break;
11890 : #endif
11891 : }
11892 :
11893 126 : if (!OidIsValid(*deleteTriggerOid))
11894 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11895 : conoid);
11896 126 : if (!OidIsValid(*updateTriggerOid))
11897 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11898 : conoid);
11899 :
11900 126 : systable_endscan(scan);
11901 126 : }
11902 :
11903 : /*
11904 : * GetForeignKeyCheckTriggers
11905 : * Returns insert and update "check" triggers of the given relation
11906 : * belonging to the given constraint
11907 : */
11908 : static void
11909 760 : GetForeignKeyCheckTriggers(Relation trigrel,
11910 : Oid conoid, Oid confrelid, Oid conrelid,
11911 : Oid *insertTriggerOid,
11912 : Oid *updateTriggerOid)
11913 : {
11914 : ScanKeyData key;
11915 : SysScanDesc scan;
11916 : HeapTuple trigtup;
11917 :
11918 760 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11919 760 : ScanKeyInit(&key,
11920 : Anum_pg_trigger_tgconstraint,
11921 : BTEqualStrategyNumber, F_OIDEQ,
11922 : ObjectIdGetDatum(conoid));
11923 :
11924 760 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11925 : NULL, 1, &key);
11926 2466 : while ((trigtup = systable_getnext(scan)) != NULL)
11927 : {
11928 2466 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11929 :
11930 2466 : if (trgform->tgconstrrelid != confrelid)
11931 864 : continue;
11932 1602 : if (trgform->tgrelid != conrelid)
11933 0 : continue;
11934 : /* Only ever look at "check" triggers on the FK side. */
11935 1602 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11936 82 : continue;
11937 1520 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
11938 : {
11939 : Assert(*insertTriggerOid == InvalidOid);
11940 760 : *insertTriggerOid = trgform->oid;
11941 : }
11942 760 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11943 : {
11944 : Assert(*updateTriggerOid == InvalidOid);
11945 760 : *updateTriggerOid = trgform->oid;
11946 : }
11947 : #ifndef USE_ASSERT_CHECKING
11948 : /* In an assert-enabled build, continue looking to find duplicates. */
11949 1520 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11950 760 : break;
11951 : #endif
11952 : }
11953 :
11954 760 : if (!OidIsValid(*insertTriggerOid))
11955 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11956 : conoid);
11957 760 : if (!OidIsValid(*updateTriggerOid))
11958 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11959 : conoid);
11960 :
11961 760 : systable_endscan(scan);
11962 760 : }
11963 :
11964 : /*
11965 : * ALTER TABLE ALTER CONSTRAINT
11966 : *
11967 : * Update the attributes of a constraint.
11968 : *
11969 : * Currently only works for Foreign Key and not null constraints.
11970 : *
11971 : * If the constraint is modified, returns its address; otherwise, return
11972 : * InvalidObjectAddress.
11973 : */
11974 : static ObjectAddress
11975 186 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
11976 : bool recurse, LOCKMODE lockmode)
11977 : {
11978 : Relation conrel;
11979 : Relation tgrel;
11980 : SysScanDesc scan;
11981 : ScanKeyData skey[3];
11982 : HeapTuple contuple;
11983 : Form_pg_constraint currcon;
11984 : ObjectAddress address;
11985 :
11986 : /*
11987 : * Disallow altering ONLY a partitioned table, as it would make no sense.
11988 : * This is okay for legacy inheritance.
11989 : */
11990 186 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
11991 0 : ereport(ERROR,
11992 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11993 : errmsg("constraint must be altered in child tables too"),
11994 : errhint("Do not specify the ONLY keyword."));
11995 :
11996 :
11997 186 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11998 186 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11999 :
12000 : /*
12001 : * Find and check the target constraint
12002 : */
12003 186 : ScanKeyInit(&skey[0],
12004 : Anum_pg_constraint_conrelid,
12005 : BTEqualStrategyNumber, F_OIDEQ,
12006 : ObjectIdGetDatum(RelationGetRelid(rel)));
12007 186 : ScanKeyInit(&skey[1],
12008 : Anum_pg_constraint_contypid,
12009 : BTEqualStrategyNumber, F_OIDEQ,
12010 : ObjectIdGetDatum(InvalidOid));
12011 186 : ScanKeyInit(&skey[2],
12012 : Anum_pg_constraint_conname,
12013 : BTEqualStrategyNumber, F_NAMEEQ,
12014 186 : CStringGetDatum(cmdcon->conname));
12015 186 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12016 : true, NULL, 3, skey);
12017 :
12018 : /* There can be at most one matching row */
12019 186 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12020 6 : ereport(ERROR,
12021 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12022 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12023 : cmdcon->conname, RelationGetRelationName(rel))));
12024 :
12025 180 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12026 180 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12027 0 : ereport(ERROR,
12028 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12029 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12030 : cmdcon->conname, RelationGetRelationName(rel))));
12031 180 : if (cmdcon->alterInheritability &&
12032 78 : currcon->contype != CONSTRAINT_NOTNULL)
12033 24 : ereport(ERROR,
12034 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12035 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12036 : cmdcon->conname, RelationGetRelationName(rel)));
12037 :
12038 : /* Refuse to modify inheritability of inherited constraints */
12039 156 : if (cmdcon->alterInheritability &&
12040 54 : cmdcon->noinherit && currcon->coninhcount > 0)
12041 6 : ereport(ERROR,
12042 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12043 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12044 : NameStr(currcon->conname),
12045 : RelationGetRelationName(rel)));
12046 :
12047 : /*
12048 : * If it's not the topmost constraint, raise an error.
12049 : *
12050 : * Altering a non-topmost constraint leaves some triggers untouched, since
12051 : * they are not directly connected to this constraint; also, pg_dump would
12052 : * ignore the deferrability status of the individual constraint, since it
12053 : * only dumps topmost constraints. Avoid these problems by refusing this
12054 : * operation and telling the user to alter the parent constraint instead.
12055 : */
12056 150 : if (OidIsValid(currcon->conparentid))
12057 : {
12058 : HeapTuple tp;
12059 12 : Oid parent = currcon->conparentid;
12060 12 : char *ancestorname = NULL;
12061 12 : char *ancestortable = NULL;
12062 :
12063 : /* Loop to find the topmost constraint */
12064 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12065 : {
12066 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12067 :
12068 : /* If no parent, this is the constraint we want */
12069 24 : if (!OidIsValid(contup->conparentid))
12070 : {
12071 12 : ancestorname = pstrdup(NameStr(contup->conname));
12072 12 : ancestortable = get_rel_name(contup->conrelid);
12073 12 : ReleaseSysCache(tp);
12074 12 : break;
12075 : }
12076 :
12077 12 : parent = contup->conparentid;
12078 12 : ReleaseSysCache(tp);
12079 : }
12080 :
12081 12 : ereport(ERROR,
12082 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12083 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12084 : cmdcon->conname, RelationGetRelationName(rel)),
12085 : ancestorname && ancestortable ?
12086 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12087 : cmdcon->conname, ancestorname, ancestortable) : 0,
12088 : errhint("You may alter the constraint it derives from instead.")));
12089 : }
12090 :
12091 138 : address = InvalidObjectAddress;
12092 :
12093 : /*
12094 : * Do the actual catalog work, and recurse if necessary.
12095 : */
12096 138 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12097 : contuple, recurse, lockmode))
12098 132 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12099 :
12100 132 : systable_endscan(scan);
12101 :
12102 132 : table_close(tgrel, RowExclusiveLock);
12103 132 : table_close(conrel, RowExclusiveLock);
12104 :
12105 132 : return address;
12106 : }
12107 :
12108 : /*
12109 : * A subroutine of ATExecAlterConstraint that calls the respective routines for
12110 : * altering constraint attributes.
12111 : */
12112 : static bool
12113 138 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12114 : Relation conrel, Relation tgrel, Relation rel,
12115 : HeapTuple contuple, bool recurse,
12116 : LOCKMODE lockmode)
12117 : {
12118 138 : bool changed = false;
12119 138 : List *otherrelids = NIL;
12120 :
12121 : /*
12122 : * Do the catalog work for the deferrability change, recurse if necessary.
12123 : */
12124 228 : if (cmdcon->alterDeferrability &&
12125 90 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12126 : contuple, recurse, &otherrelids,
12127 : lockmode))
12128 : {
12129 : /*
12130 : * AlterConstrUpdateConstraintEntry already invalidated relcache for
12131 : * the relations having the constraint itself; here we also invalidate
12132 : * for relations that have any triggers that are part of the
12133 : * constraint.
12134 : */
12135 300 : foreach_oid(relid, otherrelids)
12136 120 : CacheInvalidateRelcacheByRelid(relid);
12137 :
12138 90 : changed = true;
12139 : }
12140 :
12141 : /*
12142 : * Do the catalog work for the inheritability change.
12143 : */
12144 180 : if (cmdcon->alterInheritability &&
12145 48 : ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12146 : lockmode))
12147 42 : changed = true;
12148 :
12149 132 : return changed;
12150 : }
12151 :
12152 : /*
12153 : * Returns true if the constraint's deferrability is altered.
12154 : *
12155 : * *otherrelids is appended OIDs of relations containing affected triggers.
12156 : *
12157 : * Note that we must recurse even when the values are correct, in case
12158 : * indirect descendants have had their constraints altered locally.
12159 : * (This could be avoided if we forbade altering constraints in partitions
12160 : * but existing releases don't do that.)
12161 : */
12162 : static bool
12163 156 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
12164 : Relation conrel, Relation tgrel, Relation rel,
12165 : HeapTuple contuple, bool recurse,
12166 : List **otherrelids, LOCKMODE lockmode)
12167 : {
12168 : Form_pg_constraint currcon;
12169 : Oid refrelid;
12170 156 : bool changed = false;
12171 :
12172 : /* since this function recurses, it could be driven to stack overflow */
12173 156 : check_stack_depth();
12174 :
12175 : Assert(cmdcon->alterDeferrability);
12176 :
12177 156 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12178 156 : refrelid = currcon->confrelid;
12179 :
12180 : /* Should be foreign key constraint */
12181 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12182 :
12183 : /*
12184 : * If called to modify a constraint that's already in the desired state,
12185 : * silently do nothing.
12186 : */
12187 156 : if (currcon->condeferrable != cmdcon->deferrable ||
12188 6 : currcon->condeferred != cmdcon->initdeferred)
12189 : {
12190 156 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12191 156 : changed = true;
12192 :
12193 : /*
12194 : * Now we need to update the multiple entries in pg_trigger that
12195 : * implement the constraint.
12196 : */
12197 156 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12198 156 : cmdcon->deferrable,
12199 156 : cmdcon->initdeferred, otherrelids);
12200 : }
12201 :
12202 : /*
12203 : * If the table at either end of the constraint is partitioned, we need to
12204 : * handle every constraint that is a child of this one.
12205 : */
12206 156 : if (recurse && changed &&
12207 288 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12208 132 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12209 42 : AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12210 : contuple, recurse, otherrelids,
12211 : lockmode);
12212 :
12213 156 : return changed;
12214 : }
12215 :
12216 : /*
12217 : * Returns true if the constraint's inheritability is altered.
12218 : */
12219 : static bool
12220 48 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
12221 : Relation conrel, Relation rel,
12222 : HeapTuple contuple, LOCKMODE lockmode)
12223 : {
12224 : Form_pg_constraint currcon;
12225 : AttrNumber colNum;
12226 : char *colName;
12227 : List *children;
12228 :
12229 : Assert(cmdcon->alterInheritability);
12230 :
12231 48 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12232 :
12233 : /* The current implementation only works for NOT NULL constraints */
12234 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12235 :
12236 : /*
12237 : * If called to modify a constraint that's already in the desired state,
12238 : * silently do nothing.
12239 : */
12240 48 : if (cmdcon->noinherit == currcon->connoinherit)
12241 0 : return false;
12242 :
12243 48 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12244 48 : CommandCounterIncrement();
12245 :
12246 : /* Fetch the column number and name */
12247 48 : colNum = extractNotNullColumn(contuple);
12248 48 : colName = get_attname(currcon->conrelid, colNum, false);
12249 :
12250 : /*
12251 : * Propagate the change to children. For this subcommand type we don't
12252 : * recursively affect children, just the immediate level.
12253 : */
12254 48 : children = find_inheritance_children(RelationGetRelid(rel),
12255 : lockmode);
12256 156 : foreach_oid(childoid, children)
12257 : {
12258 : ObjectAddress addr;
12259 :
12260 72 : if (cmdcon->noinherit)
12261 : {
12262 : HeapTuple childtup;
12263 : Form_pg_constraint childcon;
12264 :
12265 24 : childtup = findNotNullConstraint(childoid, colName);
12266 24 : if (!childtup)
12267 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12268 : colName, childoid);
12269 24 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12270 : Assert(childcon->coninhcount > 0);
12271 24 : childcon->coninhcount--;
12272 24 : childcon->conislocal = true;
12273 24 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12274 24 : heap_freetuple(childtup);
12275 : }
12276 : else
12277 : {
12278 48 : Relation childrel = table_open(childoid, NoLock);
12279 :
12280 48 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12281 : colName, true, true, lockmode);
12282 42 : if (OidIsValid(addr.objectId))
12283 42 : CommandCounterIncrement();
12284 42 : table_close(childrel, NoLock);
12285 : }
12286 : }
12287 :
12288 42 : return true;
12289 : }
12290 :
12291 : /*
12292 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12293 : * trigger's deferrability.
12294 : *
12295 : * The arguments to this function have the same meaning as the arguments to
12296 : * ATExecAlterConstrDeferrability.
12297 : */
12298 : static void
12299 156 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12300 : bool deferrable, bool initdeferred,
12301 : List **otherrelids)
12302 : {
12303 : HeapTuple tgtuple;
12304 : ScanKeyData tgkey;
12305 : SysScanDesc tgscan;
12306 :
12307 156 : ScanKeyInit(&tgkey,
12308 : Anum_pg_trigger_tgconstraint,
12309 : BTEqualStrategyNumber, F_OIDEQ,
12310 : ObjectIdGetDatum(conoid));
12311 156 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12312 : NULL, 1, &tgkey);
12313 648 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12314 : {
12315 492 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12316 : Form_pg_trigger copy_tg;
12317 : HeapTuple tgCopyTuple;
12318 :
12319 : /*
12320 : * Remember OIDs of other relation(s) involved in FK constraint.
12321 : * (Note: it's likely that we could skip forcing a relcache inval for
12322 : * other rels that don't have a trigger whose properties change, but
12323 : * let's be conservative.)
12324 : */
12325 492 : if (tgform->tgrelid != RelationGetRelid(rel))
12326 240 : *otherrelids = list_append_unique_oid(*otherrelids,
12327 : tgform->tgrelid);
12328 :
12329 : /*
12330 : * Update enable status and deferrability of RI_FKey_noaction_del,
12331 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12332 : * triggers, but not others; see createForeignKeyActionTriggers and
12333 : * CreateFKCheckTrigger.
12334 : */
12335 492 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12336 390 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12337 270 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12338 144 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12339 18 : continue;
12340 :
12341 474 : tgCopyTuple = heap_copytuple(tgtuple);
12342 474 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12343 :
12344 474 : copy_tg->tgdeferrable = deferrable;
12345 474 : copy_tg->tginitdeferred = initdeferred;
12346 474 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12347 :
12348 474 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12349 :
12350 474 : heap_freetuple(tgCopyTuple);
12351 : }
12352 :
12353 156 : systable_endscan(tgscan);
12354 156 : }
12355 :
12356 : /*
12357 : * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12358 : * the specified constraint.
12359 : *
12360 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12361 : * list of child relations and recursing; instead it uses the conparentid
12362 : * relationships. This may need to be reconsidered.
12363 : *
12364 : * The arguments to this function have the same meaning as the arguments to
12365 : * ATExecAlterConstrDeferrability.
12366 : */
12367 : static void
12368 42 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12369 : Relation conrel, Relation tgrel, Relation rel,
12370 : HeapTuple contuple, bool recurse,
12371 : List **otherrelids, LOCKMODE lockmode)
12372 : {
12373 : Form_pg_constraint currcon;
12374 : Oid conoid;
12375 : ScanKeyData pkey;
12376 : SysScanDesc pscan;
12377 : HeapTuple childtup;
12378 :
12379 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12380 42 : conoid = currcon->oid;
12381 :
12382 42 : ScanKeyInit(&pkey,
12383 : Anum_pg_constraint_conparentid,
12384 : BTEqualStrategyNumber, F_OIDEQ,
12385 : ObjectIdGetDatum(conoid));
12386 :
12387 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12388 : true, NULL, 1, &pkey);
12389 :
12390 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12391 : {
12392 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12393 : Relation childrel;
12394 :
12395 66 : childrel = table_open(childcon->conrelid, lockmode);
12396 :
12397 66 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12398 : childtup, recurse, otherrelids, lockmode);
12399 66 : table_close(childrel, NoLock);
12400 : }
12401 :
12402 42 : systable_endscan(pscan);
12403 42 : }
12404 :
12405 : /*
12406 : * Update the constraint entry for the given ATAlterConstraint command, and
12407 : * invoke the appropriate hooks.
12408 : */
12409 : static void
12410 204 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
12411 : HeapTuple contuple)
12412 : {
12413 : HeapTuple copyTuple;
12414 : Form_pg_constraint copy_con;
12415 :
12416 : Assert(cmdcon->alterDeferrability || cmdcon->alterInheritability);
12417 :
12418 204 : copyTuple = heap_copytuple(contuple);
12419 204 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12420 :
12421 204 : if (cmdcon->alterDeferrability)
12422 : {
12423 156 : copy_con->condeferrable = cmdcon->deferrable;
12424 156 : copy_con->condeferred = cmdcon->initdeferred;
12425 : }
12426 204 : if (cmdcon->alterInheritability)
12427 48 : copy_con->connoinherit = cmdcon->noinherit;
12428 :
12429 204 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12430 204 : InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12431 :
12432 : /* Make new constraint flags visible to others */
12433 204 : CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12434 :
12435 204 : heap_freetuple(copyTuple);
12436 204 : }
12437 :
12438 : /*
12439 : * ALTER TABLE VALIDATE CONSTRAINT
12440 : *
12441 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12442 : * there's no good way to skip recursing when handling foreign keys: there is
12443 : * no need to lock children in that case, yet we wouldn't be able to avoid
12444 : * doing so at that level.
12445 : *
12446 : * Return value is the address of the validated constraint. If the constraint
12447 : * was already validated, InvalidObjectAddress is returned.
12448 : */
12449 : static ObjectAddress
12450 460 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12451 : bool recurse, bool recursing, LOCKMODE lockmode)
12452 : {
12453 : Relation conrel;
12454 : SysScanDesc scan;
12455 : ScanKeyData skey[3];
12456 : HeapTuple tuple;
12457 : Form_pg_constraint con;
12458 : ObjectAddress address;
12459 :
12460 460 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12461 :
12462 : /*
12463 : * Find and check the target constraint
12464 : */
12465 460 : ScanKeyInit(&skey[0],
12466 : Anum_pg_constraint_conrelid,
12467 : BTEqualStrategyNumber, F_OIDEQ,
12468 : ObjectIdGetDatum(RelationGetRelid(rel)));
12469 460 : ScanKeyInit(&skey[1],
12470 : Anum_pg_constraint_contypid,
12471 : BTEqualStrategyNumber, F_OIDEQ,
12472 : ObjectIdGetDatum(InvalidOid));
12473 460 : ScanKeyInit(&skey[2],
12474 : Anum_pg_constraint_conname,
12475 : BTEqualStrategyNumber, F_NAMEEQ,
12476 : CStringGetDatum(constrName));
12477 460 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12478 : true, NULL, 3, skey);
12479 :
12480 : /* There can be at most one matching row */
12481 460 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12482 0 : ereport(ERROR,
12483 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12484 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12485 : constrName, RelationGetRelationName(rel))));
12486 :
12487 460 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12488 460 : if (con->contype != CONSTRAINT_FOREIGN &&
12489 144 : con->contype != CONSTRAINT_CHECK)
12490 0 : ereport(ERROR,
12491 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12492 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12493 : constrName, RelationGetRelationName(rel))));
12494 :
12495 460 : if (!con->conenforced)
12496 6 : ereport(ERROR,
12497 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12498 : errmsg("cannot validate NOT ENFORCED constraint")));
12499 :
12500 454 : if (!con->convalidated)
12501 : {
12502 436 : if (con->contype == CONSTRAINT_FOREIGN)
12503 : {
12504 310 : QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12505 : }
12506 126 : else if (con->contype == CONSTRAINT_CHECK)
12507 : {
12508 126 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12509 : tuple, recurse, recursing, lockmode);
12510 : }
12511 :
12512 436 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12513 : }
12514 : else
12515 18 : address = InvalidObjectAddress; /* already validated */
12516 :
12517 454 : systable_endscan(scan);
12518 :
12519 454 : table_close(conrel, RowExclusiveLock);
12520 :
12521 454 : return address;
12522 : }
12523 :
12524 : /*
12525 : * QueueFKConstraintValidation
12526 : *
12527 : * Add an entry to the wqueue to validate the given foreign key constraint in
12528 : * Phase 3 and update the convalidated field in the pg_constraint catalog
12529 : * for the specified relation and all its children.
12530 : */
12531 : static void
12532 334 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12533 : HeapTuple contuple, LOCKMODE lockmode)
12534 : {
12535 : Form_pg_constraint con;
12536 : AlteredTableInfo *tab;
12537 : HeapTuple copyTuple;
12538 : Form_pg_constraint copy_con;
12539 :
12540 334 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12541 : Assert(con->contype == CONSTRAINT_FOREIGN);
12542 : Assert(!con->convalidated);
12543 :
12544 334 : if (rel->rd_rel->relkind == RELKIND_RELATION)
12545 : {
12546 : NewConstraint *newcon;
12547 : Constraint *fkconstraint;
12548 :
12549 : /* Queue validation for phase 3 */
12550 322 : fkconstraint = makeNode(Constraint);
12551 : /* for now this is all we need */
12552 322 : fkconstraint->conname = pstrdup(NameStr(con->conname));
12553 :
12554 322 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12555 322 : newcon->name = fkconstraint->conname;
12556 322 : newcon->contype = CONSTR_FOREIGN;
12557 322 : newcon->refrelid = con->confrelid;
12558 322 : newcon->refindid = con->conindid;
12559 322 : newcon->conid = con->oid;
12560 322 : newcon->qual = (Node *) fkconstraint;
12561 :
12562 : /* Find or create work queue entry for this table */
12563 322 : tab = ATGetQueueEntry(wqueue, rel);
12564 322 : tab->constraints = lappend(tab->constraints, newcon);
12565 : }
12566 :
12567 : /*
12568 : * If the table at either end of the constraint is partitioned, we need to
12569 : * recurse and handle every constraint that is a child of this constraint.
12570 : */
12571 656 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12572 322 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
12573 : {
12574 : ScanKeyData pkey;
12575 : SysScanDesc pscan;
12576 : HeapTuple childtup;
12577 :
12578 18 : ScanKeyInit(&pkey,
12579 : Anum_pg_constraint_conparentid,
12580 : BTEqualStrategyNumber, F_OIDEQ,
12581 : ObjectIdGetDatum(con->oid));
12582 :
12583 18 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12584 : true, NULL, 1, &pkey);
12585 :
12586 36 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12587 : {
12588 : Form_pg_constraint childcon;
12589 : Relation childrel;
12590 :
12591 18 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12592 :
12593 : /*
12594 : * If the child constraint has already been validated, no further
12595 : * action is required for it or its descendants, as they are all
12596 : * valid.
12597 : */
12598 18 : if (childcon->convalidated)
12599 12 : continue;
12600 :
12601 6 : childrel = table_open(childcon->conrelid, lockmode);
12602 :
12603 6 : QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
12604 : lockmode);
12605 6 : table_close(childrel, NoLock);
12606 : }
12607 :
12608 18 : systable_endscan(pscan);
12609 : }
12610 :
12611 : /*
12612 : * Now update the catalog, while we have the door open.
12613 : */
12614 334 : copyTuple = heap_copytuple(contuple);
12615 334 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12616 334 : copy_con->convalidated = true;
12617 334 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12618 :
12619 334 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12620 :
12621 334 : heap_freetuple(copyTuple);
12622 334 : }
12623 :
12624 : /*
12625 : * QueueCheckConstraintValidation
12626 : *
12627 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
12628 : * and update the convalidated field in the pg_constraint catalog for the
12629 : * specified relation and all its inheriting children.
12630 : */
12631 : static void
12632 126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12633 : char *constrName, HeapTuple contuple,
12634 : bool recurse, bool recursing, LOCKMODE lockmode)
12635 : {
12636 : Form_pg_constraint con;
12637 : AlteredTableInfo *tab;
12638 : HeapTuple copyTuple;
12639 : Form_pg_constraint copy_con;
12640 :
12641 126 : List *children = NIL;
12642 : ListCell *child;
12643 : NewConstraint *newcon;
12644 : Datum val;
12645 : char *conbin;
12646 :
12647 126 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12648 : Assert(con->contype == CONSTRAINT_CHECK);
12649 :
12650 : /*
12651 : * If we're recursing, the parent has already done this, so skip it. Also,
12652 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
12653 : * for it in the children.
12654 : */
12655 126 : if (!recursing && !con->connoinherit)
12656 72 : children = find_all_inheritors(RelationGetRelid(rel),
12657 : lockmode, NULL);
12658 :
12659 : /*
12660 : * For CHECK constraints, we must ensure that we only mark the constraint
12661 : * as validated on the parent if it's already validated on the children.
12662 : *
12663 : * We recurse before validating on the parent, to reduce risk of
12664 : * deadlocks.
12665 : */
12666 246 : foreach(child, children)
12667 : {
12668 120 : Oid childoid = lfirst_oid(child);
12669 : Relation childrel;
12670 :
12671 120 : if (childoid == RelationGetRelid(rel))
12672 72 : continue;
12673 :
12674 : /*
12675 : * If we are told not to recurse, there had better not be any child
12676 : * tables, because we can't mark the constraint on the parent valid
12677 : * unless it is valid for all child tables.
12678 : */
12679 48 : if (!recurse)
12680 0 : ereport(ERROR,
12681 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12682 : errmsg("constraint must be validated on child tables too")));
12683 :
12684 : /* find_all_inheritors already got lock */
12685 48 : childrel = table_open(childoid, NoLock);
12686 :
12687 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
12688 : true, lockmode);
12689 48 : table_close(childrel, NoLock);
12690 : }
12691 :
12692 : /* Queue validation for phase 3 */
12693 126 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12694 126 : newcon->name = constrName;
12695 126 : newcon->contype = CONSTR_CHECK;
12696 126 : newcon->refrelid = InvalidOid;
12697 126 : newcon->refindid = InvalidOid;
12698 126 : newcon->conid = con->oid;
12699 :
12700 126 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
12701 : Anum_pg_constraint_conbin);
12702 126 : conbin = TextDatumGetCString(val);
12703 126 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
12704 :
12705 : /* Find or create work queue entry for this table */
12706 126 : tab = ATGetQueueEntry(wqueue, rel);
12707 126 : tab->constraints = lappend(tab->constraints, newcon);
12708 :
12709 : /*
12710 : * Invalidate relcache so that others see the new validated constraint.
12711 : */
12712 126 : CacheInvalidateRelcache(rel);
12713 :
12714 : /*
12715 : * Now update the catalog, while we have the door open.
12716 : */
12717 126 : copyTuple = heap_copytuple(contuple);
12718 126 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12719 126 : copy_con->convalidated = true;
12720 126 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12721 :
12722 126 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12723 :
12724 126 : heap_freetuple(copyTuple);
12725 126 : }
12726 :
12727 : /*
12728 : * transformColumnNameList - transform list of column names
12729 : *
12730 : * Lookup each name and return its attnum and, optionally, type and collation
12731 : * OIDs
12732 : *
12733 : * Note: the name of this function suggests that it's general-purpose,
12734 : * but actually it's only used to look up names appearing in foreign-key
12735 : * clauses. The error messages would need work to use it in other cases,
12736 : * and perhaps the validity checks as well.
12737 : */
12738 : static int
12739 6418 : transformColumnNameList(Oid relId, List *colList,
12740 : int16 *attnums, Oid *atttypids, Oid *attcollids)
12741 : {
12742 : ListCell *l;
12743 : int attnum;
12744 :
12745 6418 : attnum = 0;
12746 11704 : foreach(l, colList)
12747 : {
12748 5352 : char *attname = strVal(lfirst(l));
12749 : HeapTuple atttuple;
12750 : Form_pg_attribute attform;
12751 :
12752 5352 : atttuple = SearchSysCacheAttName(relId, attname);
12753 5352 : if (!HeapTupleIsValid(atttuple))
12754 54 : ereport(ERROR,
12755 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12756 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12757 : attname)));
12758 5298 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12759 5298 : if (attform->attnum < 0)
12760 12 : ereport(ERROR,
12761 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12762 : errmsg("system columns cannot be used in foreign keys")));
12763 5286 : if (attnum >= INDEX_MAX_KEYS)
12764 0 : ereport(ERROR,
12765 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
12766 : errmsg("cannot have more than %d keys in a foreign key",
12767 : INDEX_MAX_KEYS)));
12768 5286 : attnums[attnum] = attform->attnum;
12769 5286 : if (atttypids != NULL)
12770 5256 : atttypids[attnum] = attform->atttypid;
12771 5286 : if (attcollids != NULL)
12772 5256 : attcollids[attnum] = attform->attcollation;
12773 5286 : ReleaseSysCache(atttuple);
12774 5286 : attnum++;
12775 : }
12776 :
12777 6352 : return attnum;
12778 : }
12779 :
12780 : /*
12781 : * transformFkeyGetPrimaryKey -
12782 : *
12783 : * Look up the names, attnums, types, and collations of the primary key attributes
12784 : * for the pkrel. Also return the index OID and index opclasses of the
12785 : * index supporting the primary key. Also return whether the index has
12786 : * WITHOUT OVERLAPS.
12787 : *
12788 : * All parameters except pkrel are output parameters. Also, the function
12789 : * return value is the number of attributes in the primary key.
12790 : *
12791 : * Used when the column list in the REFERENCES specification is omitted.
12792 : */
12793 : static int
12794 1166 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12795 : List **attnamelist,
12796 : int16 *attnums, Oid *atttypids, Oid *attcollids,
12797 : Oid *opclasses, bool *pk_has_without_overlaps)
12798 : {
12799 : List *indexoidlist;
12800 : ListCell *indexoidscan;
12801 1166 : HeapTuple indexTuple = NULL;
12802 1166 : Form_pg_index indexStruct = NULL;
12803 : Datum indclassDatum;
12804 : oidvector *indclass;
12805 : int i;
12806 :
12807 : /*
12808 : * Get the list of index OIDs for the table from the relcache, and look up
12809 : * each one in the pg_index syscache until we find one marked primary key
12810 : * (hopefully there isn't more than one such). Insist it's valid, too.
12811 : */
12812 1166 : *indexOid = InvalidOid;
12813 :
12814 1166 : indexoidlist = RelationGetIndexList(pkrel);
12815 :
12816 1172 : foreach(indexoidscan, indexoidlist)
12817 : {
12818 1172 : Oid indexoid = lfirst_oid(indexoidscan);
12819 :
12820 1172 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12821 1172 : if (!HeapTupleIsValid(indexTuple))
12822 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12823 1172 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12824 1172 : if (indexStruct->indisprimary && indexStruct->indisvalid)
12825 : {
12826 : /*
12827 : * Refuse to use a deferrable primary key. This is per SQL spec,
12828 : * and there would be a lot of interesting semantic problems if we
12829 : * tried to allow it.
12830 : */
12831 1166 : if (!indexStruct->indimmediate)
12832 0 : ereport(ERROR,
12833 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12834 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12835 : RelationGetRelationName(pkrel))));
12836 :
12837 1166 : *indexOid = indexoid;
12838 1166 : break;
12839 : }
12840 6 : ReleaseSysCache(indexTuple);
12841 : }
12842 :
12843 1166 : list_free(indexoidlist);
12844 :
12845 : /*
12846 : * Check that we found it
12847 : */
12848 1166 : if (!OidIsValid(*indexOid))
12849 0 : ereport(ERROR,
12850 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12851 : errmsg("there is no primary key for referenced table \"%s\"",
12852 : RelationGetRelationName(pkrel))));
12853 :
12854 : /* Must get indclass the hard way */
12855 1166 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12856 : Anum_pg_index_indclass);
12857 1166 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12858 :
12859 : /*
12860 : * Now build the list of PK attributes from the indkey definition (we
12861 : * assume a primary key cannot have expressional elements)
12862 : */
12863 1166 : *attnamelist = NIL;
12864 2744 : for (i = 0; i < indexStruct->indnkeyatts; i++)
12865 : {
12866 1578 : int pkattno = indexStruct->indkey.values[i];
12867 :
12868 1578 : attnums[i] = pkattno;
12869 1578 : atttypids[i] = attnumTypeId(pkrel, pkattno);
12870 1578 : attcollids[i] = attnumCollationId(pkrel, pkattno);
12871 1578 : opclasses[i] = indclass->values[i];
12872 1578 : *attnamelist = lappend(*attnamelist,
12873 1578 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12874 : }
12875 :
12876 1166 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12877 :
12878 1166 : ReleaseSysCache(indexTuple);
12879 :
12880 1166 : return i;
12881 : }
12882 :
12883 : /*
12884 : * transformFkeyCheckAttrs -
12885 : *
12886 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12887 : * reference as part of a foreign key constraint.
12888 : *
12889 : * Returns the OID of the unique index supporting the constraint and
12890 : * populates the caller-provided 'opclasses' array with the opclasses
12891 : * associated with the index columns. Also sets whether the index
12892 : * uses WITHOUT OVERLAPS.
12893 : *
12894 : * Raises an ERROR on validation failure.
12895 : */
12896 : static Oid
12897 1282 : transformFkeyCheckAttrs(Relation pkrel,
12898 : int numattrs, int16 *attnums,
12899 : bool with_period, Oid *opclasses,
12900 : bool *pk_has_without_overlaps)
12901 : {
12902 1282 : Oid indexoid = InvalidOid;
12903 1282 : bool found = false;
12904 1282 : bool found_deferrable = false;
12905 : List *indexoidlist;
12906 : ListCell *indexoidscan;
12907 : int i,
12908 : j;
12909 :
12910 : /*
12911 : * Reject duplicate appearances of columns in the referenced-columns list.
12912 : * Such a case is forbidden by the SQL standard, and even if we thought it
12913 : * useful to allow it, there would be ambiguity about how to match the
12914 : * list to unique indexes (in particular, it'd be unclear which index
12915 : * opclass goes with which FK column).
12916 : */
12917 2992 : for (i = 0; i < numattrs; i++)
12918 : {
12919 2256 : for (j = i + 1; j < numattrs; j++)
12920 : {
12921 546 : if (attnums[i] == attnums[j])
12922 24 : ereport(ERROR,
12923 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12924 : errmsg("foreign key referenced-columns list must not contain duplicates")));
12925 : }
12926 : }
12927 :
12928 : /*
12929 : * Get the list of index OIDs for the table from the relcache, and look up
12930 : * each one in the pg_index syscache, and match unique indexes to the list
12931 : * of attnums we are given.
12932 : */
12933 1258 : indexoidlist = RelationGetIndexList(pkrel);
12934 :
12935 1438 : foreach(indexoidscan, indexoidlist)
12936 : {
12937 : HeapTuple indexTuple;
12938 : Form_pg_index indexStruct;
12939 :
12940 1426 : indexoid = lfirst_oid(indexoidscan);
12941 1426 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12942 1426 : if (!HeapTupleIsValid(indexTuple))
12943 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12944 1426 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12945 :
12946 : /*
12947 : * Must have the right number of columns; must be unique (or if
12948 : * temporal then exclusion instead) and not a partial index; forget it
12949 : * if there are any expressions, too. Invalid indexes are out as well.
12950 : */
12951 2744 : if (indexStruct->indnkeyatts == numattrs &&
12952 1318 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12953 2608 : indexStruct->indisvalid &&
12954 2608 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12955 1304 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12956 : {
12957 : Datum indclassDatum;
12958 : oidvector *indclass;
12959 :
12960 : /* Must get indclass the hard way */
12961 1304 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12962 : Anum_pg_index_indclass);
12963 1304 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12964 :
12965 : /*
12966 : * The given attnum list may match the index columns in any order.
12967 : * Check for a match, and extract the appropriate opclasses while
12968 : * we're at it.
12969 : *
12970 : * We know that attnums[] is duplicate-free per the test at the
12971 : * start of this function, and we checked above that the number of
12972 : * index columns agrees, so if we find a match for each attnums[]
12973 : * entry then we must have a one-to-one match in some order.
12974 : */
12975 3002 : for (i = 0; i < numattrs; i++)
12976 : {
12977 1756 : found = false;
12978 2336 : for (j = 0; j < numattrs; j++)
12979 : {
12980 2278 : if (attnums[i] == indexStruct->indkey.values[j])
12981 : {
12982 1698 : opclasses[i] = indclass->values[j];
12983 1698 : found = true;
12984 1698 : break;
12985 : }
12986 : }
12987 1756 : if (!found)
12988 58 : break;
12989 : }
12990 : /* The last attribute in the index must be the PERIOD FK part */
12991 1304 : if (found && with_period)
12992 : {
12993 122 : int16 periodattnum = attnums[numattrs - 1];
12994 :
12995 122 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12996 : }
12997 :
12998 : /*
12999 : * Refuse to use a deferrable unique/primary key. This is per SQL
13000 : * spec, and there would be a lot of interesting semantic problems
13001 : * if we tried to allow it.
13002 : */
13003 1304 : if (found && !indexStruct->indimmediate)
13004 : {
13005 : /*
13006 : * Remember that we found an otherwise matching index, so that
13007 : * we can generate a more appropriate error message.
13008 : */
13009 0 : found_deferrable = true;
13010 0 : found = false;
13011 : }
13012 :
13013 : /* We need to know whether the index has WITHOUT OVERLAPS */
13014 1304 : if (found)
13015 1246 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13016 : }
13017 1426 : ReleaseSysCache(indexTuple);
13018 1426 : if (found)
13019 1246 : break;
13020 : }
13021 :
13022 1258 : if (!found)
13023 : {
13024 12 : if (found_deferrable)
13025 0 : ereport(ERROR,
13026 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13027 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13028 : RelationGetRelationName(pkrel))));
13029 : else
13030 12 : ereport(ERROR,
13031 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13032 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13033 : RelationGetRelationName(pkrel))));
13034 : }
13035 :
13036 1246 : list_free(indexoidlist);
13037 :
13038 1246 : return indexoid;
13039 : }
13040 :
13041 : /*
13042 : * findFkeyCast -
13043 : *
13044 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13045 : * Caller has equal regard for binary coercibility and for an exact match.
13046 : */
13047 : static CoercionPathType
13048 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13049 : {
13050 : CoercionPathType ret;
13051 :
13052 12 : if (targetTypeId == sourceTypeId)
13053 : {
13054 12 : ret = COERCION_PATH_RELABELTYPE;
13055 12 : *funcid = InvalidOid;
13056 : }
13057 : else
13058 : {
13059 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13060 : COERCION_IMPLICIT, funcid);
13061 0 : if (ret == COERCION_PATH_NONE)
13062 : /* A previously-relied-upon cast is now gone. */
13063 0 : elog(ERROR, "could not find cast from %u to %u",
13064 : sourceTypeId, targetTypeId);
13065 : }
13066 :
13067 12 : return ret;
13068 : }
13069 :
13070 : /*
13071 : * Permissions checks on the referenced table for ADD FOREIGN KEY
13072 : *
13073 : * Note: we have already checked that the user owns the referencing table,
13074 : * else we'd have failed much earlier; no additional checks are needed for it.
13075 : */
13076 : static void
13077 2376 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13078 : {
13079 2376 : Oid roleid = GetUserId();
13080 : AclResult aclresult;
13081 : int i;
13082 :
13083 : /* Okay if we have relation-level REFERENCES permission */
13084 2376 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13085 : ACL_REFERENCES);
13086 2376 : if (aclresult == ACLCHECK_OK)
13087 2376 : return;
13088 : /* Else we must have REFERENCES on each column */
13089 0 : for (i = 0; i < natts; i++)
13090 : {
13091 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13092 : roleid, ACL_REFERENCES);
13093 0 : if (aclresult != ACLCHECK_OK)
13094 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13095 0 : RelationGetRelationName(rel));
13096 : }
13097 : }
13098 :
13099 : /*
13100 : * Scan the existing rows in a table to verify they meet a proposed FK
13101 : * constraint.
13102 : *
13103 : * Caller must have opened and locked both relations appropriately.
13104 : */
13105 : static void
13106 1100 : validateForeignKeyConstraint(char *conname,
13107 : Relation rel,
13108 : Relation pkrel,
13109 : Oid pkindOid,
13110 : Oid constraintOid,
13111 : bool hasperiod)
13112 : {
13113 : TupleTableSlot *slot;
13114 : TableScanDesc scan;
13115 1100 : Trigger trig = {0};
13116 : Snapshot snapshot;
13117 : MemoryContext oldcxt;
13118 : MemoryContext perTupCxt;
13119 :
13120 1100 : ereport(DEBUG1,
13121 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13122 :
13123 : /*
13124 : * Build a trigger call structure; we'll need it either way.
13125 : */
13126 1100 : trig.tgoid = InvalidOid;
13127 1100 : trig.tgname = conname;
13128 1100 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13129 1100 : trig.tgisinternal = true;
13130 1100 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13131 1100 : trig.tgconstrindid = pkindOid;
13132 1100 : trig.tgconstraint = constraintOid;
13133 1100 : trig.tgdeferrable = false;
13134 1100 : trig.tginitdeferred = false;
13135 : /* we needn't fill in remaining fields */
13136 :
13137 : /*
13138 : * See if we can do it with a single LEFT JOIN query. A false result
13139 : * indicates we must proceed with the fire-the-trigger method. We can't do
13140 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13141 : * left joins.
13142 : */
13143 1100 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13144 924 : return;
13145 :
13146 : /*
13147 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13148 : * if that tuple had just been inserted. If any of those fail, it should
13149 : * ereport(ERROR) and that's that.
13150 : */
13151 108 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13152 108 : slot = table_slot_create(rel, NULL);
13153 108 : scan = table_beginscan(rel, snapshot, 0, NULL);
13154 :
13155 108 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13156 : "validateForeignKeyConstraint",
13157 : ALLOCSET_SMALL_SIZES);
13158 108 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13159 :
13160 192 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13161 : {
13162 102 : LOCAL_FCINFO(fcinfo, 0);
13163 102 : TriggerData trigdata = {0};
13164 :
13165 102 : CHECK_FOR_INTERRUPTS();
13166 :
13167 : /*
13168 : * Make a call to the trigger function
13169 : *
13170 : * No parameters are passed, but we do set a context
13171 : */
13172 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13173 :
13174 : /*
13175 : * We assume RI_FKey_check_ins won't look at flinfo...
13176 : */
13177 102 : trigdata.type = T_TriggerData;
13178 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
13179 102 : trigdata.tg_relation = rel;
13180 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13181 102 : trigdata.tg_trigslot = slot;
13182 102 : trigdata.tg_trigger = &trig;
13183 :
13184 102 : fcinfo->context = (Node *) &trigdata;
13185 :
13186 102 : RI_FKey_check_ins(fcinfo);
13187 :
13188 84 : MemoryContextReset(perTupCxt);
13189 : }
13190 :
13191 90 : MemoryContextSwitchTo(oldcxt);
13192 90 : MemoryContextDelete(perTupCxt);
13193 90 : table_endscan(scan);
13194 90 : UnregisterSnapshot(snapshot);
13195 90 : ExecDropSingleTupleTableSlot(slot);
13196 : }
13197 :
13198 : /*
13199 : * CreateFKCheckTrigger
13200 : * Creates the insert (on_insert=true) or update "check" trigger that
13201 : * implements a given foreign key
13202 : *
13203 : * Returns the OID of the so created trigger.
13204 : */
13205 : static Oid
13206 5620 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13207 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13208 : bool on_insert)
13209 : {
13210 : ObjectAddress trigAddress;
13211 : CreateTrigStmt *fk_trigger;
13212 :
13213 : /*
13214 : * Note: for a self-referential FK (referencing and referenced tables are
13215 : * the same), it is important that the ON UPDATE action fires before the
13216 : * CHECK action, since both triggers will fire on the same row during an
13217 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13218 : * state of the row. Triggers fire in name order, so we ensure this by
13219 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13220 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13221 : */
13222 5620 : fk_trigger = makeNode(CreateTrigStmt);
13223 5620 : fk_trigger->replace = false;
13224 5620 : fk_trigger->isconstraint = true;
13225 5620 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
13226 5620 : fk_trigger->relation = NULL;
13227 :
13228 : /* Either ON INSERT or ON UPDATE */
13229 5620 : if (on_insert)
13230 : {
13231 2810 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13232 2810 : fk_trigger->events = TRIGGER_TYPE_INSERT;
13233 : }
13234 : else
13235 : {
13236 2810 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13237 2810 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13238 : }
13239 :
13240 5620 : fk_trigger->args = NIL;
13241 5620 : fk_trigger->row = true;
13242 5620 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13243 5620 : fk_trigger->columns = NIL;
13244 5620 : fk_trigger->whenClause = NULL;
13245 5620 : fk_trigger->transitionRels = NIL;
13246 5620 : fk_trigger->deferrable = fkconstraint->deferrable;
13247 5620 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13248 5620 : fk_trigger->constrrel = NULL;
13249 :
13250 5620 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13251 : constraintOid, indexOid, InvalidOid,
13252 : parentTrigOid, NULL, true, false);
13253 :
13254 : /* Make changes-so-far visible */
13255 5620 : CommandCounterIncrement();
13256 :
13257 5620 : return trigAddress.objectId;
13258 : }
13259 :
13260 : /*
13261 : * createForeignKeyActionTriggers
13262 : * Create the referenced-side "action" triggers that implement a foreign
13263 : * key.
13264 : *
13265 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
13266 : * *updateTrigOid.
13267 : */
13268 : static void
13269 3102 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13270 : Oid constraintOid, Oid indexOid,
13271 : Oid parentDelTrigger, Oid parentUpdTrigger,
13272 : Oid *deleteTrigOid, Oid *updateTrigOid)
13273 : {
13274 : CreateTrigStmt *fk_trigger;
13275 : ObjectAddress trigAddress;
13276 :
13277 : /*
13278 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13279 : * DELETE action on the referenced table.
13280 : */
13281 3102 : fk_trigger = makeNode(CreateTrigStmt);
13282 3102 : fk_trigger->replace = false;
13283 3102 : fk_trigger->isconstraint = true;
13284 3102 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13285 3102 : fk_trigger->relation = NULL;
13286 3102 : fk_trigger->args = NIL;
13287 3102 : fk_trigger->row = true;
13288 3102 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13289 3102 : fk_trigger->events = TRIGGER_TYPE_DELETE;
13290 3102 : fk_trigger->columns = NIL;
13291 3102 : fk_trigger->whenClause = NULL;
13292 3102 : fk_trigger->transitionRels = NIL;
13293 3102 : fk_trigger->constrrel = NULL;
13294 :
13295 3102 : switch (fkconstraint->fk_del_action)
13296 : {
13297 2486 : case FKCONSTR_ACTION_NOACTION:
13298 2486 : fk_trigger->deferrable = fkconstraint->deferrable;
13299 2486 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13300 2486 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13301 2486 : break;
13302 30 : case FKCONSTR_ACTION_RESTRICT:
13303 30 : fk_trigger->deferrable = false;
13304 30 : fk_trigger->initdeferred = false;
13305 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13306 30 : break;
13307 428 : case FKCONSTR_ACTION_CASCADE:
13308 428 : fk_trigger->deferrable = false;
13309 428 : fk_trigger->initdeferred = false;
13310 428 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13311 428 : break;
13312 98 : case FKCONSTR_ACTION_SETNULL:
13313 98 : fk_trigger->deferrable = false;
13314 98 : fk_trigger->initdeferred = false;
13315 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13316 98 : break;
13317 60 : case FKCONSTR_ACTION_SETDEFAULT:
13318 60 : fk_trigger->deferrable = false;
13319 60 : fk_trigger->initdeferred = false;
13320 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13321 60 : break;
13322 0 : default:
13323 0 : elog(ERROR, "unrecognized FK action type: %d",
13324 : (int) fkconstraint->fk_del_action);
13325 : break;
13326 : }
13327 :
13328 3102 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13329 : constraintOid, indexOid, InvalidOid,
13330 : parentDelTrigger, NULL, true, false);
13331 3102 : if (deleteTrigOid)
13332 3102 : *deleteTrigOid = trigAddress.objectId;
13333 :
13334 : /* Make changes-so-far visible */
13335 3102 : CommandCounterIncrement();
13336 :
13337 : /*
13338 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13339 : * UPDATE action on the referenced table.
13340 : */
13341 3102 : fk_trigger = makeNode(CreateTrigStmt);
13342 3102 : fk_trigger->replace = false;
13343 3102 : fk_trigger->isconstraint = true;
13344 3102 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13345 3102 : fk_trigger->relation = NULL;
13346 3102 : fk_trigger->args = NIL;
13347 3102 : fk_trigger->row = true;
13348 3102 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13349 3102 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13350 3102 : fk_trigger->columns = NIL;
13351 3102 : fk_trigger->whenClause = NULL;
13352 3102 : fk_trigger->transitionRels = NIL;
13353 3102 : fk_trigger->constrrel = NULL;
13354 :
13355 3102 : switch (fkconstraint->fk_upd_action)
13356 : {
13357 2680 : case FKCONSTR_ACTION_NOACTION:
13358 2680 : fk_trigger->deferrable = fkconstraint->deferrable;
13359 2680 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13360 2680 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13361 2680 : break;
13362 36 : case FKCONSTR_ACTION_RESTRICT:
13363 36 : fk_trigger->deferrable = false;
13364 36 : fk_trigger->initdeferred = false;
13365 36 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13366 36 : break;
13367 282 : case FKCONSTR_ACTION_CASCADE:
13368 282 : fk_trigger->deferrable = false;
13369 282 : fk_trigger->initdeferred = false;
13370 282 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13371 282 : break;
13372 62 : case FKCONSTR_ACTION_SETNULL:
13373 62 : fk_trigger->deferrable = false;
13374 62 : fk_trigger->initdeferred = false;
13375 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13376 62 : break;
13377 42 : case FKCONSTR_ACTION_SETDEFAULT:
13378 42 : fk_trigger->deferrable = false;
13379 42 : fk_trigger->initdeferred = false;
13380 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13381 42 : break;
13382 0 : default:
13383 0 : elog(ERROR, "unrecognized FK action type: %d",
13384 : (int) fkconstraint->fk_upd_action);
13385 : break;
13386 : }
13387 :
13388 3102 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13389 : constraintOid, indexOid, InvalidOid,
13390 : parentUpdTrigger, NULL, true, false);
13391 3102 : if (updateTrigOid)
13392 3102 : *updateTrigOid = trigAddress.objectId;
13393 3102 : }
13394 :
13395 : /*
13396 : * createForeignKeyCheckTriggers
13397 : * Create the referencing-side "check" triggers that implement a foreign
13398 : * key.
13399 : *
13400 : * Returns the OIDs of the so created triggers in *insertTrigOid and
13401 : * *updateTrigOid.
13402 : */
13403 : static void
13404 2810 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
13405 : Constraint *fkconstraint, Oid constraintOid,
13406 : Oid indexOid,
13407 : Oid parentInsTrigger, Oid parentUpdTrigger,
13408 : Oid *insertTrigOid, Oid *updateTrigOid)
13409 : {
13410 2810 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13411 : constraintOid, indexOid,
13412 : parentInsTrigger, true);
13413 2810 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13414 : constraintOid, indexOid,
13415 : parentUpdTrigger, false);
13416 2810 : }
13417 :
13418 : /*
13419 : * ALTER TABLE DROP CONSTRAINT
13420 : *
13421 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13422 : */
13423 : static void
13424 774 : ATExecDropConstraint(Relation rel, const char *constrName,
13425 : DropBehavior behavior, bool recurse,
13426 : bool missing_ok, LOCKMODE lockmode)
13427 : {
13428 : Relation conrel;
13429 : SysScanDesc scan;
13430 : ScanKeyData skey[3];
13431 : HeapTuple tuple;
13432 774 : bool found = false;
13433 :
13434 774 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13435 :
13436 : /*
13437 : * Find and drop the target constraint
13438 : */
13439 774 : ScanKeyInit(&skey[0],
13440 : Anum_pg_constraint_conrelid,
13441 : BTEqualStrategyNumber, F_OIDEQ,
13442 : ObjectIdGetDatum(RelationGetRelid(rel)));
13443 774 : ScanKeyInit(&skey[1],
13444 : Anum_pg_constraint_contypid,
13445 : BTEqualStrategyNumber, F_OIDEQ,
13446 : ObjectIdGetDatum(InvalidOid));
13447 774 : ScanKeyInit(&skey[2],
13448 : Anum_pg_constraint_conname,
13449 : BTEqualStrategyNumber, F_NAMEEQ,
13450 : CStringGetDatum(constrName));
13451 774 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13452 : true, NULL, 3, skey);
13453 :
13454 : /* There can be at most one matching row */
13455 774 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13456 : {
13457 738 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
13458 : missing_ok, lockmode);
13459 552 : found = true;
13460 : }
13461 :
13462 588 : systable_endscan(scan);
13463 :
13464 588 : if (!found)
13465 : {
13466 36 : if (!missing_ok)
13467 24 : ereport(ERROR,
13468 : errcode(ERRCODE_UNDEFINED_OBJECT),
13469 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13470 : constrName, RelationGetRelationName(rel)));
13471 : else
13472 12 : ereport(NOTICE,
13473 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
13474 : constrName, RelationGetRelationName(rel)));
13475 : }
13476 :
13477 564 : table_close(conrel, RowExclusiveLock);
13478 564 : }
13479 :
13480 : /*
13481 : * Remove a constraint, using its pg_constraint tuple
13482 : *
13483 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
13484 : * DROP NOT NULL.
13485 : *
13486 : * Returns the address of the constraint being removed.
13487 : */
13488 : static ObjectAddress
13489 1162 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
13490 : bool recurse, bool recursing, bool missing_ok,
13491 : LOCKMODE lockmode)
13492 : {
13493 : Relation conrel;
13494 : Form_pg_constraint con;
13495 : ObjectAddress conobj;
13496 : List *children;
13497 1162 : bool is_no_inherit_constraint = false;
13498 : char *constrName;
13499 1162 : char *colname = NULL;
13500 :
13501 : /* Guard against stack overflow due to overly deep inheritance tree. */
13502 1162 : check_stack_depth();
13503 :
13504 : /* At top level, permission check was done in ATPrepCmd, else do it */
13505 1162 : if (recursing)
13506 210 : ATSimplePermissions(AT_DropConstraint, rel,
13507 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
13508 :
13509 1156 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13510 :
13511 1156 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
13512 1156 : constrName = NameStr(con->conname);
13513 :
13514 : /* Don't allow drop of inherited constraints */
13515 1156 : if (con->coninhcount > 0 && !recursing)
13516 156 : ereport(ERROR,
13517 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13518 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13519 : constrName, RelationGetRelationName(rel))));
13520 :
13521 : /*
13522 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
13523 : *
13524 : * While doing that, we're in a good position to disallow dropping a not-
13525 : * null constraint underneath a primary key, a replica identity index, or
13526 : * a generated identity column.
13527 : */
13528 1000 : if (con->contype == CONSTRAINT_NOTNULL)
13529 : {
13530 290 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
13531 290 : AttrNumber attnum = extractNotNullColumn(constraintTup);
13532 : Bitmapset *pkattrs;
13533 : Bitmapset *irattrs;
13534 : HeapTuple atttup;
13535 : Form_pg_attribute attForm;
13536 :
13537 : /* save column name for recursion step */
13538 290 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13539 :
13540 : /*
13541 : * Disallow if it's in the primary key. For partitioned tables we
13542 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
13543 : * return NULL if the primary key is invalid; but we still need to
13544 : * protect not-null constraints under such a constraint, so check the
13545 : * slow way.
13546 : */
13547 290 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
13548 :
13549 290 : if (pkattrs == NULL &&
13550 254 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13551 : {
13552 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
13553 :
13554 18 : if (OidIsValid(pkindex))
13555 : {
13556 0 : Relation pk = relation_open(pkindex, AccessShareLock);
13557 :
13558 0 : pkattrs = NULL;
13559 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
13560 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
13561 :
13562 0 : relation_close(pk, AccessShareLock);
13563 : }
13564 : }
13565 :
13566 326 : if (pkattrs &&
13567 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
13568 24 : ereport(ERROR,
13569 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13570 : errmsg("column \"%s\" is in a primary key",
13571 : get_attname(RelationGetRelid(rel), attnum, false)));
13572 :
13573 : /* Disallow if it's in the replica identity */
13574 266 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
13575 266 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
13576 12 : ereport(ERROR,
13577 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13578 : errmsg("column \"%s\" is in index used as replica identity",
13579 : get_attname(RelationGetRelid(rel), attnum, false)));
13580 :
13581 : /* Disallow if it's a GENERATED AS IDENTITY column */
13582 254 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
13583 254 : if (!HeapTupleIsValid(atttup))
13584 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13585 : attnum, RelationGetRelid(rel));
13586 254 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13587 254 : if (attForm->attidentity != '\0')
13588 0 : ereport(ERROR,
13589 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13590 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
13591 : get_attname(RelationGetRelid(rel), attnum,
13592 : false),
13593 : RelationGetRelationName(rel)));
13594 :
13595 : /* All good -- reset attnotnull if needed */
13596 254 : if (attForm->attnotnull)
13597 : {
13598 254 : attForm->attnotnull = false;
13599 254 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
13600 : }
13601 :
13602 254 : table_close(attrel, RowExclusiveLock);
13603 : }
13604 :
13605 964 : is_no_inherit_constraint = con->connoinherit;
13606 :
13607 : /*
13608 : * If it's a foreign-key constraint, we'd better lock the referenced table
13609 : * and check that that's not in use, just as we've already done for the
13610 : * constrained table (else we might, eg, be dropping a trigger that has
13611 : * unfired events). But we can/must skip that in the self-referential
13612 : * case.
13613 : */
13614 964 : if (con->contype == CONSTRAINT_FOREIGN &&
13615 168 : con->confrelid != RelationGetRelid(rel))
13616 : {
13617 : Relation frel;
13618 :
13619 : /* Must match lock taken by RemoveTriggerById: */
13620 168 : frel = table_open(con->confrelid, AccessExclusiveLock);
13621 168 : CheckAlterTableIsSafe(frel);
13622 162 : table_close(frel, NoLock);
13623 : }
13624 :
13625 : /*
13626 : * Perform the actual constraint deletion
13627 : */
13628 958 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13629 958 : performDeletion(&conobj, behavior, 0);
13630 :
13631 : /*
13632 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13633 : * are dropped via the dependency mechanism, so we're done here.
13634 : */
13635 922 : if (con->contype != CONSTRAINT_CHECK &&
13636 604 : con->contype != CONSTRAINT_NOTNULL &&
13637 350 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13638 : {
13639 78 : table_close(conrel, RowExclusiveLock);
13640 78 : return conobj;
13641 : }
13642 :
13643 : /*
13644 : * Propagate to children as appropriate. Unlike most other ALTER
13645 : * routines, we have to do this one level of recursion at a time; we can't
13646 : * use find_all_inheritors to do it in one pass.
13647 : */
13648 844 : if (!is_no_inherit_constraint)
13649 560 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13650 : else
13651 284 : children = NIL;
13652 :
13653 2054 : foreach_oid(childrelid, children)
13654 : {
13655 : Relation childrel;
13656 : HeapTuple tuple;
13657 : Form_pg_constraint childcon;
13658 :
13659 : /* find_inheritance_children already got lock */
13660 378 : childrel = table_open(childrelid, NoLock);
13661 378 : CheckAlterTableIsSafe(childrel);
13662 :
13663 : /*
13664 : * We search for not-null constraints by column name, and others by
13665 : * constraint name.
13666 : */
13667 378 : if (con->contype == CONSTRAINT_NOTNULL)
13668 : {
13669 142 : tuple = findNotNullConstraint(childrelid, colname);
13670 142 : if (!HeapTupleIsValid(tuple))
13671 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13672 : colname, RelationGetRelid(childrel));
13673 : }
13674 : else
13675 : {
13676 : SysScanDesc scan;
13677 : ScanKeyData skey[3];
13678 :
13679 236 : ScanKeyInit(&skey[0],
13680 : Anum_pg_constraint_conrelid,
13681 : BTEqualStrategyNumber, F_OIDEQ,
13682 : ObjectIdGetDatum(childrelid));
13683 236 : ScanKeyInit(&skey[1],
13684 : Anum_pg_constraint_contypid,
13685 : BTEqualStrategyNumber, F_OIDEQ,
13686 : ObjectIdGetDatum(InvalidOid));
13687 236 : ScanKeyInit(&skey[2],
13688 : Anum_pg_constraint_conname,
13689 : BTEqualStrategyNumber, F_NAMEEQ,
13690 : CStringGetDatum(constrName));
13691 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13692 : true, NULL, 3, skey);
13693 : /* There can only be one, so no need to loop */
13694 236 : tuple = systable_getnext(scan);
13695 236 : if (!HeapTupleIsValid(tuple))
13696 0 : ereport(ERROR,
13697 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13698 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13699 : constrName,
13700 : RelationGetRelationName(childrel))));
13701 236 : tuple = heap_copytuple(tuple);
13702 236 : systable_endscan(scan);
13703 : }
13704 :
13705 378 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13706 :
13707 : /* Right now only CHECK and not-null constraints can be inherited */
13708 378 : if (childcon->contype != CONSTRAINT_CHECK &&
13709 142 : childcon->contype != CONSTRAINT_NOTNULL)
13710 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13711 :
13712 378 : if (childcon->coninhcount <= 0) /* shouldn't happen */
13713 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13714 : childrelid, NameStr(childcon->conname));
13715 :
13716 378 : if (recurse)
13717 : {
13718 : /*
13719 : * If the child constraint has other definition sources, just
13720 : * decrement its inheritance count; if not, recurse to delete it.
13721 : */
13722 276 : if (childcon->coninhcount == 1 && !childcon->conislocal)
13723 : {
13724 : /* Time to delete this child constraint, too */
13725 210 : dropconstraint_internal(childrel, tuple, behavior,
13726 : recurse, true, missing_ok,
13727 : lockmode);
13728 : }
13729 : else
13730 : {
13731 : /* Child constraint must survive my deletion */
13732 66 : childcon->coninhcount--;
13733 66 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13734 :
13735 : /* Make update visible */
13736 66 : CommandCounterIncrement();
13737 : }
13738 : }
13739 : else
13740 : {
13741 : /*
13742 : * If we were told to drop ONLY in this table (no recursion) and
13743 : * there are no further parents for this constraint, we need to
13744 : * mark the inheritors' constraints as locally defined rather than
13745 : * inherited.
13746 : */
13747 102 : childcon->coninhcount--;
13748 102 : if (childcon->coninhcount == 0)
13749 102 : childcon->conislocal = true;
13750 :
13751 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13752 :
13753 : /* Make update visible */
13754 102 : CommandCounterIncrement();
13755 : }
13756 :
13757 372 : heap_freetuple(tuple);
13758 :
13759 372 : table_close(childrel, NoLock);
13760 : }
13761 :
13762 838 : table_close(conrel, RowExclusiveLock);
13763 :
13764 838 : return conobj;
13765 : }
13766 :
13767 : /*
13768 : * ALTER COLUMN TYPE
13769 : *
13770 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13771 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13772 : * transformed (and must be, because we rely on some transformed fields).
13773 : *
13774 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13775 : * table will be done "in parallel" during phase 3, so all the USING
13776 : * expressions should be parsed assuming the original column types. Also,
13777 : * this allows a USING expression to refer to a field that will be dropped.
13778 : *
13779 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13780 : * the first two execution steps in phase 2; they must not see the effects
13781 : * of any other subcommand types, since the USING expressions are parsed
13782 : * against the unmodified table's state.
13783 : */
13784 : static void
13785 1276 : ATPrepAlterColumnType(List **wqueue,
13786 : AlteredTableInfo *tab, Relation rel,
13787 : bool recurse, bool recursing,
13788 : AlterTableCmd *cmd, LOCKMODE lockmode,
13789 : AlterTableUtilityContext *context)
13790 : {
13791 1276 : char *colName = cmd->name;
13792 1276 : ColumnDef *def = (ColumnDef *) cmd->def;
13793 1276 : TypeName *typeName = def->typeName;
13794 1276 : Node *transform = def->cooked_default;
13795 : HeapTuple tuple;
13796 : Form_pg_attribute attTup;
13797 : AttrNumber attnum;
13798 : Oid targettype;
13799 : int32 targettypmod;
13800 : Oid targetcollid;
13801 : NewColumnValue *newval;
13802 1276 : ParseState *pstate = make_parsestate(NULL);
13803 : AclResult aclresult;
13804 : bool is_expr;
13805 :
13806 1276 : pstate->p_sourcetext = context->queryString;
13807 :
13808 1276 : if (rel->rd_rel->reloftype && !recursing)
13809 6 : ereport(ERROR,
13810 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13811 : errmsg("cannot alter column type of typed table"),
13812 : parser_errposition(pstate, def->location)));
13813 :
13814 : /* lookup the attribute so we can check inheritance status */
13815 1270 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13816 1270 : if (!HeapTupleIsValid(tuple))
13817 0 : ereport(ERROR,
13818 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13819 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13820 : colName, RelationGetRelationName(rel)),
13821 : parser_errposition(pstate, def->location)));
13822 1270 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13823 1270 : attnum = attTup->attnum;
13824 :
13825 : /* Can't alter a system attribute */
13826 1270 : if (attnum <= 0)
13827 6 : ereport(ERROR,
13828 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13829 : errmsg("cannot alter system column \"%s\"", colName),
13830 : parser_errposition(pstate, def->location)));
13831 :
13832 : /*
13833 : * Cannot specify USING when altering type of a generated column, because
13834 : * that would violate the generation expression.
13835 : */
13836 1264 : if (attTup->attgenerated && def->cooked_default)
13837 12 : ereport(ERROR,
13838 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
13839 : errmsg("cannot specify USING when altering type of generated column"),
13840 : errdetail("Column \"%s\" is a generated column.", colName),
13841 : parser_errposition(pstate, def->location)));
13842 :
13843 : /*
13844 : * Don't alter inherited columns. At outer level, there had better not be
13845 : * any inherited definition; when recursing, we assume this was checked at
13846 : * the parent level (see below).
13847 : */
13848 1252 : if (attTup->attinhcount > 0 && !recursing)
13849 6 : ereport(ERROR,
13850 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13851 : errmsg("cannot alter inherited column \"%s\"", colName),
13852 : parser_errposition(pstate, def->location)));
13853 :
13854 : /* Don't alter columns used in the partition key */
13855 1246 : if (has_partition_attrs(rel,
13856 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13857 : &is_expr))
13858 18 : ereport(ERROR,
13859 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13860 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13861 : colName, RelationGetRelationName(rel)),
13862 : parser_errposition(pstate, def->location)));
13863 :
13864 : /* Look up the target type */
13865 1228 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
13866 :
13867 1222 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13868 1222 : if (aclresult != ACLCHECK_OK)
13869 12 : aclcheck_error_type(aclresult, targettype);
13870 :
13871 : /* And the collation */
13872 1210 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
13873 :
13874 : /* make sure datatype is legal for a column */
13875 1204 : CheckAttributeType(colName, targettype, targetcollid,
13876 1204 : list_make1_oid(rel->rd_rel->reltype),
13877 : 0);
13878 :
13879 1198 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
13880 : {
13881 : /* do nothing */
13882 : }
13883 1162 : else if (tab->relkind == RELKIND_RELATION ||
13884 202 : tab->relkind == RELKIND_PARTITIONED_TABLE)
13885 : {
13886 : /*
13887 : * Set up an expression to transform the old data value to the new
13888 : * type. If a USING option was given, use the expression as
13889 : * transformed by transformAlterTableStmt, else just take the old
13890 : * value and try to coerce it. We do this first so that type
13891 : * incompatibility can be detected before we waste effort, and because
13892 : * we need the expression to be parsed against the original table row
13893 : * type.
13894 : */
13895 1026 : if (!transform)
13896 : {
13897 804 : transform = (Node *) makeVar(1, attnum,
13898 : attTup->atttypid, attTup->atttypmod,
13899 : attTup->attcollation,
13900 : 0);
13901 : }
13902 :
13903 1026 : transform = coerce_to_target_type(pstate,
13904 : transform, exprType(transform),
13905 : targettype, targettypmod,
13906 : COERCION_ASSIGNMENT,
13907 : COERCE_IMPLICIT_CAST,
13908 : -1);
13909 1026 : if (transform == NULL)
13910 : {
13911 : /* error text depends on whether USING was specified or not */
13912 24 : if (def->cooked_default != NULL)
13913 6 : ereport(ERROR,
13914 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13915 : errmsg("result of USING clause for column \"%s\""
13916 : " cannot be cast automatically to type %s",
13917 : colName, format_type_be(targettype)),
13918 : errhint("You might need to add an explicit cast.")));
13919 : else
13920 18 : ereport(ERROR,
13921 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13922 : errmsg("column \"%s\" cannot be cast automatically to type %s",
13923 : colName, format_type_be(targettype)),
13924 : !attTup->attgenerated ?
13925 : /* translator: USING is SQL, don't translate it */
13926 : errhint("You might need to specify \"USING %s::%s\".",
13927 : quote_identifier(colName),
13928 : format_type_with_typemod(targettype,
13929 : targettypmod)) : 0));
13930 : }
13931 :
13932 : /* Fix collations after all else */
13933 1002 : assign_expr_collations(pstate, transform);
13934 :
13935 : /* Plan the expr now so we can accurately assess the need to rewrite. */
13936 1002 : transform = (Node *) expression_planner((Expr *) transform);
13937 :
13938 : /*
13939 : * Add a work queue item to make ATRewriteTable update the column
13940 : * contents.
13941 : */
13942 1002 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13943 1002 : newval->attnum = attnum;
13944 1002 : newval->expr = (Expr *) transform;
13945 1002 : newval->is_generated = false;
13946 :
13947 1002 : tab->newvals = lappend(tab->newvals, newval);
13948 1002 : if (ATColumnChangeRequiresRewrite(transform, attnum))
13949 806 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13950 : }
13951 136 : else if (transform)
13952 12 : ereport(ERROR,
13953 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13954 : errmsg("\"%s\" is not a table",
13955 : RelationGetRelationName(rel))));
13956 :
13957 1162 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
13958 : {
13959 : /*
13960 : * For relations or columns without storage, do this check now.
13961 : * Regular tables will check it later when the table is being
13962 : * rewritten.
13963 : */
13964 226 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13965 : }
13966 :
13967 1114 : ReleaseSysCache(tuple);
13968 :
13969 : /*
13970 : * Recurse manually by queueing a new command for each child, if
13971 : * necessary. We cannot apply ATSimpleRecursion here because we need to
13972 : * remap attribute numbers in the USING expression, if any.
13973 : *
13974 : * If we are told not to recurse, there had better not be any child
13975 : * tables; else the alter would put them out of step.
13976 : */
13977 1114 : if (recurse)
13978 : {
13979 856 : Oid relid = RelationGetRelid(rel);
13980 : List *child_oids,
13981 : *child_numparents;
13982 : ListCell *lo,
13983 : *li;
13984 :
13985 856 : child_oids = find_all_inheritors(relid, lockmode,
13986 : &child_numparents);
13987 :
13988 : /*
13989 : * find_all_inheritors does the recursive search of the inheritance
13990 : * hierarchy, so all we have to do is process all of the relids in the
13991 : * list that it returns.
13992 : */
13993 1920 : forboth(lo, child_oids, li, child_numparents)
13994 : {
13995 1088 : Oid childrelid = lfirst_oid(lo);
13996 1088 : int numparents = lfirst_int(li);
13997 : Relation childrel;
13998 : HeapTuple childtuple;
13999 : Form_pg_attribute childattTup;
14000 :
14001 1088 : if (childrelid == relid)
14002 856 : continue;
14003 :
14004 : /* find_all_inheritors already got lock */
14005 232 : childrel = relation_open(childrelid, NoLock);
14006 232 : CheckAlterTableIsSafe(childrel);
14007 :
14008 : /*
14009 : * Verify that the child doesn't have any inherited definitions of
14010 : * this column that came from outside this inheritance hierarchy.
14011 : * (renameatt makes a similar test, though in a different way
14012 : * because of its different recursion mechanism.)
14013 : */
14014 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14015 : colName);
14016 232 : if (!HeapTupleIsValid(childtuple))
14017 0 : ereport(ERROR,
14018 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14019 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14020 : colName, RelationGetRelationName(childrel))));
14021 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14022 :
14023 232 : if (childattTup->attinhcount > numparents)
14024 6 : ereport(ERROR,
14025 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14026 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14027 : colName, RelationGetRelationName(childrel))));
14028 :
14029 226 : ReleaseSysCache(childtuple);
14030 :
14031 : /*
14032 : * Remap the attribute numbers. If no USING expression was
14033 : * specified, there is no need for this step.
14034 : */
14035 226 : if (def->cooked_default)
14036 : {
14037 : AttrMap *attmap;
14038 : bool found_whole_row;
14039 :
14040 : /* create a copy to scribble on */
14041 78 : cmd = copyObject(cmd);
14042 :
14043 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14044 : RelationGetDescr(rel),
14045 : false);
14046 156 : ((ColumnDef *) cmd->def)->cooked_default =
14047 78 : map_variable_attnos(def->cooked_default,
14048 : 1, 0,
14049 : attmap,
14050 : InvalidOid, &found_whole_row);
14051 78 : if (found_whole_row)
14052 6 : ereport(ERROR,
14053 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14054 : errmsg("cannot convert whole-row table reference"),
14055 : errdetail("USING expression contains a whole-row table reference.")));
14056 72 : pfree(attmap);
14057 : }
14058 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14059 208 : relation_close(childrel, NoLock);
14060 : }
14061 : }
14062 308 : else if (!recursing &&
14063 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
14064 0 : ereport(ERROR,
14065 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14066 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
14067 : colName)));
14068 :
14069 1090 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14070 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14071 1084 : }
14072 :
14073 : /*
14074 : * When the data type of a column is changed, a rewrite might not be required
14075 : * if the new type is sufficiently identical to the old one, and the USING
14076 : * clause isn't trying to insert some other value. It's safe to skip the
14077 : * rewrite in these cases:
14078 : *
14079 : * - the old type is binary coercible to the new type
14080 : * - the new type is an unconstrained domain over the old type
14081 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14082 : *
14083 : * In the case of a constrained domain, we could get by with scanning the
14084 : * table and checking the constraint rather than actually rewriting it, but we
14085 : * don't currently try to do that.
14086 : */
14087 : static bool
14088 1120 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
14089 : {
14090 : Assert(expr != NULL);
14091 :
14092 : for (;;)
14093 : {
14094 : /* only one varno, so no need to check that */
14095 1120 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14096 196 : return false;
14097 924 : else if (IsA(expr, RelabelType))
14098 106 : expr = (Node *) ((RelabelType *) expr)->arg;
14099 818 : else if (IsA(expr, CoerceToDomain))
14100 : {
14101 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
14102 :
14103 0 : if (DomainHasConstraints(d->resulttype))
14104 0 : return true;
14105 0 : expr = (Node *) d->arg;
14106 : }
14107 818 : else if (IsA(expr, FuncExpr))
14108 : {
14109 612 : FuncExpr *f = (FuncExpr *) expr;
14110 :
14111 612 : switch (f->funcid)
14112 : {
14113 18 : case F_TIMESTAMPTZ_TIMESTAMP:
14114 : case F_TIMESTAMP_TIMESTAMPTZ:
14115 18 : if (TimestampTimestampTzRequiresRewrite())
14116 6 : return true;
14117 : else
14118 12 : expr = linitial(f->args);
14119 12 : break;
14120 594 : default:
14121 594 : return true;
14122 : }
14123 : }
14124 : else
14125 206 : return true;
14126 : }
14127 : }
14128 :
14129 : /*
14130 : * ALTER COLUMN .. SET DATA TYPE
14131 : *
14132 : * Return the address of the modified column.
14133 : */
14134 : static ObjectAddress
14135 1048 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14136 : AlterTableCmd *cmd, LOCKMODE lockmode)
14137 : {
14138 1048 : char *colName = cmd->name;
14139 1048 : ColumnDef *def = (ColumnDef *) cmd->def;
14140 1048 : TypeName *typeName = def->typeName;
14141 : HeapTuple heapTup;
14142 : Form_pg_attribute attTup,
14143 : attOldTup;
14144 : AttrNumber attnum;
14145 : HeapTuple typeTuple;
14146 : Form_pg_type tform;
14147 : Oid targettype;
14148 : int32 targettypmod;
14149 : Oid targetcollid;
14150 : Node *defaultexpr;
14151 : Relation attrelation;
14152 : Relation depRel;
14153 : ScanKeyData key[3];
14154 : SysScanDesc scan;
14155 : HeapTuple depTup;
14156 : ObjectAddress address;
14157 :
14158 : /*
14159 : * Clear all the missing values if we're rewriting the table, since this
14160 : * renders them pointless.
14161 : */
14162 1048 : if (tab->rewrite)
14163 : {
14164 : Relation newrel;
14165 :
14166 746 : newrel = table_open(RelationGetRelid(rel), NoLock);
14167 746 : RelationClearMissing(newrel);
14168 746 : relation_close(newrel, NoLock);
14169 : /* make sure we don't conflict with later attribute modifications */
14170 746 : CommandCounterIncrement();
14171 : }
14172 :
14173 1048 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14174 :
14175 : /* Look up the target column */
14176 1048 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14177 1048 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14178 0 : ereport(ERROR,
14179 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14180 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14181 : colName, RelationGetRelationName(rel))));
14182 1048 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14183 1048 : attnum = attTup->attnum;
14184 1048 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14185 :
14186 : /* Check for multiple ALTER TYPE on same column --- can't cope */
14187 1048 : if (attTup->atttypid != attOldTup->atttypid ||
14188 1048 : attTup->atttypmod != attOldTup->atttypmod)
14189 0 : ereport(ERROR,
14190 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14191 : errmsg("cannot alter type of column \"%s\" twice",
14192 : colName)));
14193 :
14194 : /* Look up the target type (should not fail, since prep found it) */
14195 1048 : typeTuple = typenameType(NULL, typeName, &targettypmod);
14196 1048 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
14197 1048 : targettype = tform->oid;
14198 : /* And the collation */
14199 1048 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
14200 :
14201 : /*
14202 : * If there is a default expression for the column, get it and ensure we
14203 : * can coerce it to the new datatype. (We must do this before changing
14204 : * the column type, because build_column_default itself will try to
14205 : * coerce, and will not issue the error message we want if it fails.)
14206 : *
14207 : * We remove any implicit coercion steps at the top level of the old
14208 : * default expression; this has been agreed to satisfy the principle of
14209 : * least surprise. (The conversion to the new column type should act like
14210 : * it started from what the user sees as the stored expression, and the
14211 : * implicit coercions aren't going to be shown.)
14212 : */
14213 1048 : if (attTup->atthasdef)
14214 : {
14215 92 : defaultexpr = build_column_default(rel, attnum);
14216 : Assert(defaultexpr);
14217 92 : defaultexpr = strip_implicit_coercions(defaultexpr);
14218 92 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14219 : defaultexpr, exprType(defaultexpr),
14220 : targettype, targettypmod,
14221 : COERCION_ASSIGNMENT,
14222 : COERCE_IMPLICIT_CAST,
14223 : -1);
14224 92 : if (defaultexpr == NULL)
14225 : {
14226 6 : if (attTup->attgenerated)
14227 0 : ereport(ERROR,
14228 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14229 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14230 : colName, format_type_be(targettype))));
14231 : else
14232 6 : ereport(ERROR,
14233 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14234 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14235 : colName, format_type_be(targettype))));
14236 : }
14237 : }
14238 : else
14239 956 : defaultexpr = NULL;
14240 :
14241 : /*
14242 : * Find everything that depends on the column (constraints, indexes, etc),
14243 : * and record enough information to let us recreate the objects.
14244 : *
14245 : * The actual recreation does not happen here, but only after we have
14246 : * performed all the individual ALTER TYPE operations. We have to save
14247 : * the info before executing ALTER TYPE, though, else the deparser will
14248 : * get confused.
14249 : */
14250 1042 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
14251 :
14252 : /*
14253 : * Now scan for dependencies of this column on other things. The only
14254 : * things we should find are the dependency on the column datatype and
14255 : * possibly a collation dependency. Those can be removed.
14256 : */
14257 1006 : depRel = table_open(DependRelationId, RowExclusiveLock);
14258 :
14259 1006 : ScanKeyInit(&key[0],
14260 : Anum_pg_depend_classid,
14261 : BTEqualStrategyNumber, F_OIDEQ,
14262 : ObjectIdGetDatum(RelationRelationId));
14263 1006 : ScanKeyInit(&key[1],
14264 : Anum_pg_depend_objid,
14265 : BTEqualStrategyNumber, F_OIDEQ,
14266 : ObjectIdGetDatum(RelationGetRelid(rel)));
14267 1006 : ScanKeyInit(&key[2],
14268 : Anum_pg_depend_objsubid,
14269 : BTEqualStrategyNumber, F_INT4EQ,
14270 : Int32GetDatum((int32) attnum));
14271 :
14272 1006 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
14273 : NULL, 3, key);
14274 :
14275 1010 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14276 : {
14277 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14278 : ObjectAddress foundObject;
14279 :
14280 4 : foundObject.classId = foundDep->refclassid;
14281 4 : foundObject.objectId = foundDep->refobjid;
14282 4 : foundObject.objectSubId = foundDep->refobjsubid;
14283 :
14284 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
14285 0 : elog(ERROR, "found unexpected dependency type '%c'",
14286 : foundDep->deptype);
14287 4 : if (!(foundDep->refclassid == TypeRelationId &&
14288 4 : foundDep->refobjid == attTup->atttypid) &&
14289 0 : !(foundDep->refclassid == CollationRelationId &&
14290 0 : foundDep->refobjid == attTup->attcollation))
14291 0 : elog(ERROR, "found unexpected dependency for column: %s",
14292 : getObjectDescription(&foundObject, false));
14293 :
14294 4 : CatalogTupleDelete(depRel, &depTup->t_self);
14295 : }
14296 :
14297 1006 : systable_endscan(scan);
14298 :
14299 1006 : table_close(depRel, RowExclusiveLock);
14300 :
14301 : /*
14302 : * Here we go --- change the recorded column type and collation. (Note
14303 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14304 : * fix up the missing value if any.
14305 : */
14306 1006 : if (attTup->atthasmissing)
14307 : {
14308 : Datum missingval;
14309 : bool missingNull;
14310 :
14311 : /* if rewrite is true the missing value should already be cleared */
14312 : Assert(tab->rewrite == 0);
14313 :
14314 : /* Get the missing value datum */
14315 6 : missingval = heap_getattr(heapTup,
14316 : Anum_pg_attribute_attmissingval,
14317 : attrelation->rd_att,
14318 : &missingNull);
14319 :
14320 : /* if it's a null array there is nothing to do */
14321 :
14322 6 : if (!missingNull)
14323 : {
14324 : /*
14325 : * Get the datum out of the array and repack it in a new array
14326 : * built with the new type data. We assume that since the table
14327 : * doesn't need rewriting, the actual Datum doesn't need to be
14328 : * changed, only the array metadata.
14329 : */
14330 :
14331 6 : int one = 1;
14332 : bool isNull;
14333 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
14334 6 : bool nullsAtt[Natts_pg_attribute] = {0};
14335 6 : bool replacesAtt[Natts_pg_attribute] = {0};
14336 : HeapTuple newTup;
14337 :
14338 12 : missingval = array_get_element(missingval,
14339 : 1,
14340 : &one,
14341 : 0,
14342 6 : attTup->attlen,
14343 6 : attTup->attbyval,
14344 6 : attTup->attalign,
14345 : &isNull);
14346 6 : missingval = PointerGetDatum(construct_array(&missingval,
14347 : 1,
14348 : targettype,
14349 6 : tform->typlen,
14350 6 : tform->typbyval,
14351 6 : tform->typalign));
14352 :
14353 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14354 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14355 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14356 :
14357 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14358 : valuesAtt, nullsAtt, replacesAtt);
14359 6 : heap_freetuple(heapTup);
14360 6 : heapTup = newTup;
14361 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14362 : }
14363 : }
14364 :
14365 1006 : attTup->atttypid = targettype;
14366 1006 : attTup->atttypmod = targettypmod;
14367 1006 : attTup->attcollation = targetcollid;
14368 1006 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14369 0 : ereport(ERROR,
14370 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14371 : errmsg("too many array dimensions"));
14372 1006 : attTup->attndims = list_length(typeName->arrayBounds);
14373 1006 : attTup->attlen = tform->typlen;
14374 1006 : attTup->attbyval = tform->typbyval;
14375 1006 : attTup->attalign = tform->typalign;
14376 1006 : attTup->attstorage = tform->typstorage;
14377 1006 : attTup->attcompression = InvalidCompressionMethod;
14378 :
14379 1006 : ReleaseSysCache(typeTuple);
14380 :
14381 1006 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14382 :
14383 1006 : table_close(attrelation, RowExclusiveLock);
14384 :
14385 : /* Install dependencies on new datatype and collation */
14386 1006 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
14387 1006 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
14388 :
14389 : /*
14390 : * Drop any pg_statistic entry for the column, since it's now wrong type
14391 : */
14392 1006 : RemoveStatistics(RelationGetRelid(rel), attnum);
14393 :
14394 1006 : InvokeObjectPostAlterHook(RelationRelationId,
14395 : RelationGetRelid(rel), attnum);
14396 :
14397 : /*
14398 : * Update the default, if present, by brute force --- remove and re-add
14399 : * the default. Probably unsafe to take shortcuts, since the new version
14400 : * may well have additional dependencies. (It's okay to do this now,
14401 : * rather than after other ALTER TYPE commands, since the default won't
14402 : * depend on other column types.)
14403 : */
14404 1006 : if (defaultexpr)
14405 : {
14406 : /*
14407 : * If it's a GENERATED default, drop its dependency records, in
14408 : * particular its INTERNAL dependency on the column, which would
14409 : * otherwise cause dependency.c to refuse to perform the deletion.
14410 : */
14411 86 : if (attTup->attgenerated)
14412 : {
14413 36 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14414 :
14415 36 : if (!OidIsValid(attrdefoid))
14416 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14417 : RelationGetRelid(rel), attnum);
14418 36 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14419 : }
14420 :
14421 : /*
14422 : * Make updates-so-far visible, particularly the new pg_attribute row
14423 : * which will be updated again.
14424 : */
14425 86 : CommandCounterIncrement();
14426 :
14427 : /*
14428 : * We use RESTRICT here for safety, but at present we do not expect
14429 : * anything to depend on the default.
14430 : */
14431 86 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14432 : true);
14433 :
14434 86 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14435 : }
14436 :
14437 1006 : ObjectAddressSubSet(address, RelationRelationId,
14438 : RelationGetRelid(rel), attnum);
14439 :
14440 : /* Cleanup */
14441 1006 : heap_freetuple(heapTup);
14442 :
14443 1006 : return address;
14444 : }
14445 :
14446 : /*
14447 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14448 : * that depends on the column (constraints, indexes, etc), and record enough
14449 : * information to let us recreate the objects.
14450 : */
14451 : static void
14452 1120 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
14453 : Relation rel, AttrNumber attnum, const char *colName)
14454 : {
14455 : Relation depRel;
14456 : ScanKeyData key[3];
14457 : SysScanDesc scan;
14458 : HeapTuple depTup;
14459 :
14460 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14461 :
14462 1120 : depRel = table_open(DependRelationId, RowExclusiveLock);
14463 :
14464 1120 : ScanKeyInit(&key[0],
14465 : Anum_pg_depend_refclassid,
14466 : BTEqualStrategyNumber, F_OIDEQ,
14467 : ObjectIdGetDatum(RelationRelationId));
14468 1120 : ScanKeyInit(&key[1],
14469 : Anum_pg_depend_refobjid,
14470 : BTEqualStrategyNumber, F_OIDEQ,
14471 : ObjectIdGetDatum(RelationGetRelid(rel)));
14472 1120 : ScanKeyInit(&key[2],
14473 : Anum_pg_depend_refobjsubid,
14474 : BTEqualStrategyNumber, F_INT4EQ,
14475 : Int32GetDatum((int32) attnum));
14476 :
14477 1120 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14478 : NULL, 3, key);
14479 :
14480 2246 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14481 : {
14482 1162 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14483 : ObjectAddress foundObject;
14484 :
14485 1162 : foundObject.classId = foundDep->classid;
14486 1162 : foundObject.objectId = foundDep->objid;
14487 1162 : foundObject.objectSubId = foundDep->objsubid;
14488 :
14489 1162 : switch (foundObject.classId)
14490 : {
14491 274 : case RelationRelationId:
14492 : {
14493 274 : char relKind = get_rel_relkind(foundObject.objectId);
14494 :
14495 274 : if (relKind == RELKIND_INDEX ||
14496 : relKind == RELKIND_PARTITIONED_INDEX)
14497 : {
14498 : Assert(foundObject.objectSubId == 0);
14499 236 : RememberIndexForRebuilding(foundObject.objectId, tab);
14500 : }
14501 38 : else if (relKind == RELKIND_SEQUENCE)
14502 : {
14503 : /*
14504 : * This must be a SERIAL column's sequence. We need
14505 : * not do anything to it.
14506 : */
14507 : Assert(foundObject.objectSubId == 0);
14508 : }
14509 : else
14510 : {
14511 : /* Not expecting any other direct dependencies... */
14512 0 : elog(ERROR, "unexpected object depending on column: %s",
14513 : getObjectDescription(&foundObject, false));
14514 : }
14515 274 : break;
14516 : }
14517 :
14518 674 : case ConstraintRelationId:
14519 : Assert(foundObject.objectSubId == 0);
14520 674 : RememberConstraintForRebuilding(foundObject.objectId, tab);
14521 674 : break;
14522 :
14523 0 : case ProcedureRelationId:
14524 :
14525 : /*
14526 : * A new-style SQL function can depend on a column, if that
14527 : * column is referenced in the parsed function body. Ideally
14528 : * we'd automatically update the function by deparsing and
14529 : * reparsing it, but that's risky and might well fail anyhow.
14530 : * FIXME someday.
14531 : *
14532 : * This is only a problem for AT_AlterColumnType, not
14533 : * AT_SetExpression.
14534 : */
14535 0 : if (subtype == AT_AlterColumnType)
14536 0 : ereport(ERROR,
14537 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14538 : errmsg("cannot alter type of a column used by a function or procedure"),
14539 : errdetail("%s depends on column \"%s\"",
14540 : getObjectDescription(&foundObject, false),
14541 : colName)));
14542 0 : break;
14543 :
14544 12 : case RewriteRelationId:
14545 :
14546 : /*
14547 : * View/rule bodies have pretty much the same issues as
14548 : * function bodies. FIXME someday.
14549 : */
14550 12 : if (subtype == AT_AlterColumnType)
14551 12 : ereport(ERROR,
14552 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14553 : errmsg("cannot alter type of a column used by a view or rule"),
14554 : errdetail("%s depends on column \"%s\"",
14555 : getObjectDescription(&foundObject, false),
14556 : colName)));
14557 0 : break;
14558 :
14559 0 : case TriggerRelationId:
14560 :
14561 : /*
14562 : * A trigger can depend on a column because the column is
14563 : * specified as an update target, or because the column is
14564 : * used in the trigger's WHEN condition. The first case would
14565 : * not require any extra work, but the second case would
14566 : * require updating the WHEN expression, which has the same
14567 : * issues as above. Since we can't easily tell which case
14568 : * applies, we punt for both. FIXME someday.
14569 : */
14570 0 : if (subtype == AT_AlterColumnType)
14571 0 : ereport(ERROR,
14572 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14573 : errmsg("cannot alter type of a column used in a trigger definition"),
14574 : errdetail("%s depends on column \"%s\"",
14575 : getObjectDescription(&foundObject, false),
14576 : colName)));
14577 0 : break;
14578 :
14579 0 : case PolicyRelationId:
14580 :
14581 : /*
14582 : * A policy can depend on a column because the column is
14583 : * specified in the policy's USING or WITH CHECK qual
14584 : * expressions. It might be possible to rewrite and recheck
14585 : * the policy expression, but punt for now. It's certainly
14586 : * easy enough to remove and recreate the policy; still, FIXME
14587 : * someday.
14588 : */
14589 0 : if (subtype == AT_AlterColumnType)
14590 0 : ereport(ERROR,
14591 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14592 : errmsg("cannot alter type of a column used in a policy definition"),
14593 : errdetail("%s depends on column \"%s\"",
14594 : getObjectDescription(&foundObject, false),
14595 : colName)));
14596 0 : break;
14597 :
14598 188 : case AttrDefaultRelationId:
14599 : {
14600 188 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14601 :
14602 188 : if (col.objectId == RelationGetRelid(rel) &&
14603 188 : col.objectSubId == attnum)
14604 : {
14605 : /*
14606 : * Ignore the column's own default expression. The
14607 : * caller deals with it.
14608 : */
14609 : }
14610 : else
14611 : {
14612 : /*
14613 : * This must be a reference from the expression of a
14614 : * generated column elsewhere in the same table.
14615 : * Changing the type/generated expression of a column
14616 : * that is used by a generated column is not allowed
14617 : * by SQL standard, so just punt for now. It might be
14618 : * doable with some thinking and effort.
14619 : */
14620 24 : if (subtype == AT_AlterColumnType)
14621 24 : ereport(ERROR,
14622 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14623 : errmsg("cannot alter type of a column used by a generated column"),
14624 : errdetail("Column \"%s\" is used by generated column \"%s\".",
14625 : colName,
14626 : get_attname(col.objectId,
14627 : col.objectSubId,
14628 : false))));
14629 : }
14630 164 : break;
14631 : }
14632 :
14633 14 : case StatisticExtRelationId:
14634 :
14635 : /*
14636 : * Give the extended-stats machinery a chance to fix anything
14637 : * that this column type change would break.
14638 : */
14639 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
14640 14 : break;
14641 :
14642 0 : case PublicationRelRelationId:
14643 :
14644 : /*
14645 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14646 : * clause. Same issues as above. FIXME someday.
14647 : */
14648 0 : if (subtype == AT_AlterColumnType)
14649 0 : ereport(ERROR,
14650 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14651 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
14652 : errdetail("%s depends on column \"%s\"",
14653 : getObjectDescription(&foundObject, false),
14654 : colName)));
14655 0 : break;
14656 :
14657 0 : default:
14658 :
14659 : /*
14660 : * We don't expect any other sorts of objects to depend on a
14661 : * column.
14662 : */
14663 0 : elog(ERROR, "unexpected object depending on column: %s",
14664 : getObjectDescription(&foundObject, false));
14665 : break;
14666 : }
14667 : }
14668 :
14669 1084 : systable_endscan(scan);
14670 1084 : table_close(depRel, NoLock);
14671 1084 : }
14672 :
14673 : /*
14674 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
14675 : * needs to be reset.
14676 : */
14677 : static void
14678 444 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14679 : {
14680 444 : if (!get_index_isreplident(indoid))
14681 426 : return;
14682 :
14683 18 : if (tab->replicaIdentityIndex)
14684 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14685 :
14686 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
14687 : }
14688 :
14689 : /*
14690 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
14691 : */
14692 : static void
14693 444 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14694 : {
14695 444 : if (!get_index_isclustered(indoid))
14696 426 : return;
14697 :
14698 18 : if (tab->clusterOnIndex)
14699 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14700 :
14701 18 : tab->clusterOnIndex = get_rel_name(indoid);
14702 : }
14703 :
14704 : /*
14705 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14706 : * to be rebuilt (which we might already know).
14707 : */
14708 : static void
14709 686 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14710 : {
14711 : /*
14712 : * This de-duplication check is critical for two independent reasons: we
14713 : * mustn't try to recreate the same constraint twice, and if a constraint
14714 : * depends on more than one column whose type is to be altered, we must
14715 : * capture its definition string before applying any of the column type
14716 : * changes. ruleutils.c will get confused if we ask again later.
14717 : */
14718 686 : if (!list_member_oid(tab->changedConstraintOids, conoid))
14719 : {
14720 : /* OK, capture the constraint's existing definition string */
14721 596 : char *defstring = pg_get_constraintdef_command(conoid);
14722 : Oid indoid;
14723 :
14724 : /*
14725 : * It is critical to create not-null constraints ahead of primary key
14726 : * indexes; otherwise, the not-null constraint would be created by the
14727 : * primary key, and the constraint name would be wrong.
14728 : */
14729 596 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
14730 : {
14731 198 : tab->changedConstraintOids = lcons_oid(conoid,
14732 : tab->changedConstraintOids);
14733 198 : tab->changedConstraintDefs = lcons(defstring,
14734 : tab->changedConstraintDefs);
14735 : }
14736 : else
14737 : {
14738 :
14739 398 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14740 : conoid);
14741 398 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14742 : defstring);
14743 : }
14744 :
14745 : /*
14746 : * For the index of a constraint, if any, remember if it is used for
14747 : * the table's replica identity or if it is a clustered index, so that
14748 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14749 : * those properties.
14750 : */
14751 596 : indoid = get_constraint_index(conoid);
14752 596 : if (OidIsValid(indoid))
14753 : {
14754 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
14755 228 : RememberClusterOnForRebuilding(indoid, tab);
14756 : }
14757 : }
14758 686 : }
14759 :
14760 : /*
14761 : * Subroutine for ATExecAlterColumnType: remember that an index needs
14762 : * to be rebuilt (which we might already know).
14763 : */
14764 : static void
14765 236 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14766 : {
14767 : /*
14768 : * This de-duplication check is critical for two independent reasons: we
14769 : * mustn't try to recreate the same index twice, and if an index depends
14770 : * on more than one column whose type is to be altered, we must capture
14771 : * its definition string before applying any of the column type changes.
14772 : * ruleutils.c will get confused if we ask again later.
14773 : */
14774 236 : if (!list_member_oid(tab->changedIndexOids, indoid))
14775 : {
14776 : /*
14777 : * Before adding it as an index-to-rebuild, we'd better see if it
14778 : * belongs to a constraint, and if so rebuild the constraint instead.
14779 : * Typically this check fails, because constraint indexes normally
14780 : * have only dependencies on their constraint. But it's possible for
14781 : * such an index to also have direct dependencies on table columns,
14782 : * for example with a partial exclusion constraint.
14783 : */
14784 228 : Oid conoid = get_index_constraint(indoid);
14785 :
14786 228 : if (OidIsValid(conoid))
14787 : {
14788 12 : RememberConstraintForRebuilding(conoid, tab);
14789 : }
14790 : else
14791 : {
14792 : /* OK, capture the index's existing definition string */
14793 216 : char *defstring = pg_get_indexdef_string(indoid);
14794 :
14795 216 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14796 : indoid);
14797 216 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14798 : defstring);
14799 :
14800 : /*
14801 : * Remember if this index is used for the table's replica identity
14802 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14803 : * can queue up commands necessary to restore those properties.
14804 : */
14805 216 : RememberReplicaIdentityForRebuilding(indoid, tab);
14806 216 : RememberClusterOnForRebuilding(indoid, tab);
14807 : }
14808 : }
14809 236 : }
14810 :
14811 : /*
14812 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
14813 : * needs to be rebuilt (which we might already know).
14814 : */
14815 : static void
14816 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14817 : {
14818 : /*
14819 : * This de-duplication check is critical for two independent reasons: we
14820 : * mustn't try to recreate the same statistics object twice, and if the
14821 : * statistics object depends on more than one column whose type is to be
14822 : * altered, we must capture its definition string before applying any of
14823 : * the type changes. ruleutils.c will get confused if we ask again later.
14824 : */
14825 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14826 : {
14827 : /* OK, capture the statistics object's existing definition string */
14828 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
14829 :
14830 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14831 : stxoid);
14832 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14833 : defstring);
14834 : }
14835 14 : }
14836 :
14837 : /*
14838 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14839 : * operations for a particular relation. We have to drop and recreate all the
14840 : * indexes and constraints that depend on the altered columns. We do the
14841 : * actual dropping here, but re-creation is managed by adding work queue
14842 : * entries to do those steps later.
14843 : */
14844 : static void
14845 1114 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14846 : {
14847 : ObjectAddress obj;
14848 : ObjectAddresses *objects;
14849 : ListCell *def_item;
14850 : ListCell *oid_item;
14851 :
14852 : /*
14853 : * Collect all the constraints and indexes to drop so we can process them
14854 : * in a single call. That way we don't have to worry about dependencies
14855 : * among them.
14856 : */
14857 1114 : objects = new_object_addresses();
14858 :
14859 : /*
14860 : * Re-parse the index and constraint definitions, and attach them to the
14861 : * appropriate work queue entries. We do this before dropping because in
14862 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14863 : * lock on the table the constraint is attached to, and we need to get
14864 : * that before reparsing/dropping.
14865 : *
14866 : * We can't rely on the output of deparsing to tell us which relation to
14867 : * operate on, because concurrent activity might have made the name
14868 : * resolve differently. Instead, we've got to use the OID of the
14869 : * constraint or index we're processing to figure out which relation to
14870 : * operate on.
14871 : */
14872 1710 : forboth(oid_item, tab->changedConstraintOids,
14873 : def_item, tab->changedConstraintDefs)
14874 : {
14875 596 : Oid oldId = lfirst_oid(oid_item);
14876 : HeapTuple tup;
14877 : Form_pg_constraint con;
14878 : Oid relid;
14879 : Oid confrelid;
14880 : char contype;
14881 : bool conislocal;
14882 :
14883 596 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14884 596 : if (!HeapTupleIsValid(tup)) /* should not happen */
14885 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14886 596 : con = (Form_pg_constraint) GETSTRUCT(tup);
14887 596 : if (OidIsValid(con->conrelid))
14888 582 : relid = con->conrelid;
14889 : else
14890 : {
14891 : /* must be a domain constraint */
14892 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
14893 14 : if (!OidIsValid(relid))
14894 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14895 : }
14896 596 : confrelid = con->confrelid;
14897 596 : contype = con->contype;
14898 596 : conislocal = con->conislocal;
14899 596 : ReleaseSysCache(tup);
14900 :
14901 596 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
14902 596 : add_exact_object_address(&obj, objects);
14903 :
14904 : /*
14905 : * If the constraint is inherited (only), we don't want to inject a
14906 : * new definition here; it'll get recreated when
14907 : * ATAddCheckNNConstraint recurses from adding the parent table's
14908 : * constraint. But we had to carry the info this far so that we can
14909 : * drop the constraint below.
14910 : */
14911 596 : if (!conislocal)
14912 28 : continue;
14913 :
14914 : /*
14915 : * When rebuilding an FK constraint that references the table we're
14916 : * modifying, we might not yet have any lock on the FK's table, so get
14917 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14918 : * step, so there's no value in asking for anything weaker.
14919 : */
14920 568 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14921 36 : LockRelationOid(relid, AccessExclusiveLock);
14922 :
14923 568 : ATPostAlterTypeParse(oldId, relid, confrelid,
14924 568 : (char *) lfirst(def_item),
14925 568 : wqueue, lockmode, tab->rewrite);
14926 : }
14927 1330 : forboth(oid_item, tab->changedIndexOids,
14928 : def_item, tab->changedIndexDefs)
14929 : {
14930 216 : Oid oldId = lfirst_oid(oid_item);
14931 : Oid relid;
14932 :
14933 216 : relid = IndexGetRelation(oldId, false);
14934 216 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14935 216 : (char *) lfirst(def_item),
14936 216 : wqueue, lockmode, tab->rewrite);
14937 :
14938 216 : ObjectAddressSet(obj, RelationRelationId, oldId);
14939 216 : add_exact_object_address(&obj, objects);
14940 : }
14941 :
14942 : /* add dependencies for new statistics */
14943 1128 : forboth(oid_item, tab->changedStatisticsOids,
14944 : def_item, tab->changedStatisticsDefs)
14945 : {
14946 14 : Oid oldId = lfirst_oid(oid_item);
14947 : Oid relid;
14948 :
14949 14 : relid = StatisticsGetRelation(oldId, false);
14950 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14951 14 : (char *) lfirst(def_item),
14952 14 : wqueue, lockmode, tab->rewrite);
14953 :
14954 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14955 14 : add_exact_object_address(&obj, objects);
14956 : }
14957 :
14958 : /*
14959 : * Queue up command to restore replica identity index marking
14960 : */
14961 1114 : if (tab->replicaIdentityIndex)
14962 : {
14963 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14964 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14965 :
14966 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14967 18 : subcmd->name = tab->replicaIdentityIndex;
14968 18 : cmd->subtype = AT_ReplicaIdentity;
14969 18 : cmd->def = (Node *) subcmd;
14970 :
14971 : /* do it after indexes and constraints */
14972 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14973 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14974 : }
14975 :
14976 : /*
14977 : * Queue up command to restore marking of index used for cluster.
14978 : */
14979 1114 : if (tab->clusterOnIndex)
14980 : {
14981 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14982 :
14983 18 : cmd->subtype = AT_ClusterOn;
14984 18 : cmd->name = tab->clusterOnIndex;
14985 :
14986 : /* do it after indexes and constraints */
14987 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14988 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14989 : }
14990 :
14991 : /*
14992 : * It should be okay to use DROP_RESTRICT here, since nothing else should
14993 : * be depending on these objects.
14994 : */
14995 1114 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14996 :
14997 1114 : free_object_addresses(objects);
14998 :
14999 : /*
15000 : * The objects will get recreated during subsequent passes over the work
15001 : * queue.
15002 : */
15003 1114 : }
15004 :
15005 : /*
15006 : * Parse the previously-saved definition string for a constraint, index or
15007 : * statistics object against the newly-established column data type(s), and
15008 : * queue up the resulting command parsetrees for execution.
15009 : *
15010 : * This might fail if, for example, you have a WHERE clause that uses an
15011 : * operator that's not available for the new column type.
15012 : */
15013 : static void
15014 798 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15015 : List **wqueue, LOCKMODE lockmode, bool rewrite)
15016 : {
15017 : List *raw_parsetree_list;
15018 : List *querytree_list;
15019 : ListCell *list_item;
15020 : Relation rel;
15021 :
15022 : /*
15023 : * We expect that we will get only ALTER TABLE and CREATE INDEX
15024 : * statements. Hence, there is no need to pass them through
15025 : * parse_analyze_*() or the rewriter, but instead we need to pass them
15026 : * through parse_utilcmd.c to make them ready for execution.
15027 : */
15028 798 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15029 798 : querytree_list = NIL;
15030 1596 : foreach(list_item, raw_parsetree_list)
15031 : {
15032 798 : RawStmt *rs = lfirst_node(RawStmt, list_item);
15033 798 : Node *stmt = rs->stmt;
15034 :
15035 798 : if (IsA(stmt, IndexStmt))
15036 216 : querytree_list = lappend(querytree_list,
15037 216 : transformIndexStmt(oldRelId,
15038 : (IndexStmt *) stmt,
15039 : cmd));
15040 582 : else if (IsA(stmt, AlterTableStmt))
15041 : {
15042 : List *beforeStmts;
15043 : List *afterStmts;
15044 :
15045 554 : stmt = (Node *) transformAlterTableStmt(oldRelId,
15046 : (AlterTableStmt *) stmt,
15047 : cmd,
15048 : &beforeStmts,
15049 : &afterStmts);
15050 554 : querytree_list = list_concat(querytree_list, beforeStmts);
15051 554 : querytree_list = lappend(querytree_list, stmt);
15052 554 : querytree_list = list_concat(querytree_list, afterStmts);
15053 : }
15054 28 : else if (IsA(stmt, CreateStatsStmt))
15055 14 : querytree_list = lappend(querytree_list,
15056 14 : transformStatsStmt(oldRelId,
15057 : (CreateStatsStmt *) stmt,
15058 : cmd));
15059 : else
15060 14 : querytree_list = lappend(querytree_list, stmt);
15061 : }
15062 :
15063 : /* Caller should already have acquired whatever lock we need. */
15064 798 : rel = relation_open(oldRelId, NoLock);
15065 :
15066 : /*
15067 : * Attach each generated command to the proper place in the work queue.
15068 : * Note this could result in creation of entirely new work-queue entries.
15069 : *
15070 : * Also note that we have to tweak the command subtypes, because it turns
15071 : * out that re-creation of indexes and constraints has to act a bit
15072 : * differently from initial creation.
15073 : */
15074 1596 : foreach(list_item, querytree_list)
15075 : {
15076 798 : Node *stm = (Node *) lfirst(list_item);
15077 : AlteredTableInfo *tab;
15078 :
15079 798 : tab = ATGetQueueEntry(wqueue, rel);
15080 :
15081 798 : if (IsA(stm, IndexStmt))
15082 : {
15083 216 : IndexStmt *stmt = (IndexStmt *) stm;
15084 : AlterTableCmd *newcmd;
15085 :
15086 216 : if (!rewrite)
15087 56 : TryReuseIndex(oldId, stmt);
15088 216 : stmt->reset_default_tblspc = true;
15089 : /* keep the index's comment */
15090 216 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15091 :
15092 216 : newcmd = makeNode(AlterTableCmd);
15093 216 : newcmd->subtype = AT_ReAddIndex;
15094 216 : newcmd->def = (Node *) stmt;
15095 216 : tab->subcmds[AT_PASS_OLD_INDEX] =
15096 216 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15097 : }
15098 582 : else if (IsA(stm, AlterTableStmt))
15099 : {
15100 554 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
15101 : ListCell *lcmd;
15102 :
15103 1108 : foreach(lcmd, stmt->cmds)
15104 : {
15105 554 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
15106 :
15107 554 : if (cmd->subtype == AT_AddIndex)
15108 : {
15109 : IndexStmt *indstmt;
15110 : Oid indoid;
15111 :
15112 228 : indstmt = castNode(IndexStmt, cmd->def);
15113 228 : indoid = get_constraint_index(oldId);
15114 :
15115 228 : if (!rewrite)
15116 48 : TryReuseIndex(indoid, indstmt);
15117 : /* keep any comment on the index */
15118 228 : indstmt->idxcomment = GetComment(indoid,
15119 : RelationRelationId, 0);
15120 228 : indstmt->reset_default_tblspc = true;
15121 :
15122 228 : cmd->subtype = AT_ReAddIndex;
15123 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15124 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15125 :
15126 : /* recreate any comment on the constraint */
15127 228 : RebuildConstraintComment(tab,
15128 : AT_PASS_OLD_INDEX,
15129 : oldId,
15130 : rel,
15131 : NIL,
15132 228 : indstmt->idxname);
15133 : }
15134 326 : else if (cmd->subtype == AT_AddConstraint)
15135 : {
15136 326 : Constraint *con = castNode(Constraint, cmd->def);
15137 :
15138 326 : con->old_pktable_oid = refRelId;
15139 : /* rewriting neither side of a FK */
15140 326 : if (con->contype == CONSTR_FOREIGN &&
15141 72 : !rewrite && tab->rewrite == 0)
15142 6 : TryReuseForeignKey(oldId, con);
15143 326 : con->reset_default_tblspc = true;
15144 326 : cmd->subtype = AT_ReAddConstraint;
15145 326 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15146 326 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15147 :
15148 : /*
15149 : * Recreate any comment on the constraint. If we have
15150 : * recreated a primary key, then transformTableConstraint
15151 : * has added an unnamed not-null constraint here; skip
15152 : * this in that case.
15153 : */
15154 326 : if (con->conname)
15155 326 : RebuildConstraintComment(tab,
15156 : AT_PASS_OLD_CONSTR,
15157 : oldId,
15158 : rel,
15159 : NIL,
15160 326 : con->conname);
15161 : else
15162 : Assert(con->contype == CONSTR_NOTNULL);
15163 : }
15164 : else
15165 0 : elog(ERROR, "unexpected statement subtype: %d",
15166 : (int) cmd->subtype);
15167 : }
15168 : }
15169 28 : else if (IsA(stm, AlterDomainStmt))
15170 : {
15171 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
15172 :
15173 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15174 : {
15175 14 : Constraint *con = castNode(Constraint, stmt->def);
15176 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15177 :
15178 14 : cmd->subtype = AT_ReAddDomainConstraint;
15179 14 : cmd->def = (Node *) stmt;
15180 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15181 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15182 :
15183 : /* recreate any comment on the constraint */
15184 14 : RebuildConstraintComment(tab,
15185 : AT_PASS_OLD_CONSTR,
15186 : oldId,
15187 : NULL,
15188 : stmt->typeName,
15189 14 : con->conname);
15190 : }
15191 : else
15192 0 : elog(ERROR, "unexpected statement subtype: %d",
15193 : (int) stmt->subtype);
15194 : }
15195 14 : else if (IsA(stm, CreateStatsStmt))
15196 : {
15197 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
15198 : AlterTableCmd *newcmd;
15199 :
15200 : /* keep the statistics object's comment */
15201 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15202 :
15203 14 : newcmd = makeNode(AlterTableCmd);
15204 14 : newcmd->subtype = AT_ReAddStatistics;
15205 14 : newcmd->def = (Node *) stmt;
15206 14 : tab->subcmds[AT_PASS_MISC] =
15207 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15208 : }
15209 : else
15210 0 : elog(ERROR, "unexpected statement type: %d",
15211 : (int) nodeTag(stm));
15212 : }
15213 :
15214 798 : relation_close(rel, NoLock);
15215 798 : }
15216 :
15217 : /*
15218 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15219 : * for a table or domain constraint that is being rebuilt.
15220 : *
15221 : * objid is the OID of the constraint.
15222 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15223 : * as a string list) for a domain constraint.
15224 : * (We could dig that info, as well as the conname, out of the pg_constraint
15225 : * entry; but callers already have them so might as well pass them.)
15226 : */
15227 : static void
15228 568 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
15229 : Relation rel, List *domname,
15230 : const char *conname)
15231 : {
15232 : CommentStmt *cmd;
15233 : char *comment_str;
15234 : AlterTableCmd *newcmd;
15235 :
15236 : /* Look for comment for object wanted, and leave if none */
15237 568 : comment_str = GetComment(objid, ConstraintRelationId, 0);
15238 568 : if (comment_str == NULL)
15239 478 : return;
15240 :
15241 : /* Build CommentStmt node, copying all input data for safety */
15242 90 : cmd = makeNode(CommentStmt);
15243 90 : if (rel)
15244 : {
15245 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
15246 78 : cmd->object = (Node *)
15247 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
15248 : makeString(pstrdup(RelationGetRelationName(rel))),
15249 : makeString(pstrdup(conname)));
15250 : }
15251 : else
15252 : {
15253 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
15254 12 : cmd->object = (Node *)
15255 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
15256 : makeString(pstrdup(conname)));
15257 : }
15258 90 : cmd->comment = comment_str;
15259 :
15260 : /* Append it to list of commands */
15261 90 : newcmd = makeNode(AlterTableCmd);
15262 90 : newcmd->subtype = AT_ReAddComment;
15263 90 : newcmd->def = (Node *) cmd;
15264 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15265 : }
15266 :
15267 : /*
15268 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15269 : * for the real analysis, then mutates the IndexStmt based on that verdict.
15270 : */
15271 : static void
15272 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
15273 : {
15274 104 : if (CheckIndexCompatible(oldId,
15275 104 : stmt->accessMethod,
15276 104 : stmt->indexParams,
15277 104 : stmt->excludeOpNames,
15278 104 : stmt->iswithoutoverlaps))
15279 : {
15280 104 : Relation irel = index_open(oldId, NoLock);
15281 :
15282 : /* If it's a partitioned index, there is no storage to share. */
15283 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15284 : {
15285 74 : stmt->oldNumber = irel->rd_locator.relNumber;
15286 74 : stmt->oldCreateSubid = irel->rd_createSubid;
15287 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15288 : }
15289 104 : index_close(irel, NoLock);
15290 : }
15291 104 : }
15292 :
15293 : /*
15294 : * Subroutine for ATPostAlterTypeParse().
15295 : *
15296 : * Stash the old P-F equality operator into the Constraint node, for possible
15297 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15298 : * this constraint can be skipped.
15299 : */
15300 : static void
15301 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
15302 : {
15303 : HeapTuple tup;
15304 : Datum adatum;
15305 : ArrayType *arr;
15306 : Oid *rawarr;
15307 : int numkeys;
15308 : int i;
15309 :
15310 : Assert(con->contype == CONSTR_FOREIGN);
15311 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15312 :
15313 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15314 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
15315 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15316 :
15317 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15318 : Anum_pg_constraint_conpfeqop);
15319 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15320 6 : numkeys = ARR_DIMS(arr)[0];
15321 : /* test follows the one in ri_FetchConstraintInfo() */
15322 6 : if (ARR_NDIM(arr) != 1 ||
15323 6 : ARR_HASNULL(arr) ||
15324 6 : ARR_ELEMTYPE(arr) != OIDOID)
15325 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
15326 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
15327 :
15328 : /* stash a List of the operator Oids in our Constraint node */
15329 12 : for (i = 0; i < numkeys; i++)
15330 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15331 :
15332 6 : ReleaseSysCache(tup);
15333 6 : }
15334 :
15335 : /*
15336 : * ALTER COLUMN .. OPTIONS ( ... )
15337 : *
15338 : * Returns the address of the modified column
15339 : */
15340 : static ObjectAddress
15341 172 : ATExecAlterColumnGenericOptions(Relation rel,
15342 : const char *colName,
15343 : List *options,
15344 : LOCKMODE lockmode)
15345 : {
15346 : Relation ftrel;
15347 : Relation attrel;
15348 : ForeignServer *server;
15349 : ForeignDataWrapper *fdw;
15350 : HeapTuple tuple;
15351 : HeapTuple newtuple;
15352 : bool isnull;
15353 : Datum repl_val[Natts_pg_attribute];
15354 : bool repl_null[Natts_pg_attribute];
15355 : bool repl_repl[Natts_pg_attribute];
15356 : Datum datum;
15357 : Form_pg_foreign_table fttableform;
15358 : Form_pg_attribute atttableform;
15359 : AttrNumber attnum;
15360 : ObjectAddress address;
15361 :
15362 172 : if (options == NIL)
15363 0 : return InvalidObjectAddress;
15364 :
15365 : /* First, determine FDW validator associated to the foreign table. */
15366 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15367 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15368 172 : if (!HeapTupleIsValid(tuple))
15369 0 : ereport(ERROR,
15370 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15371 : errmsg("foreign table \"%s\" does not exist",
15372 : RelationGetRelationName(rel))));
15373 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15374 172 : server = GetForeignServer(fttableform->ftserver);
15375 172 : fdw = GetForeignDataWrapper(server->fdwid);
15376 :
15377 172 : table_close(ftrel, AccessShareLock);
15378 172 : ReleaseSysCache(tuple);
15379 :
15380 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
15381 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15382 172 : if (!HeapTupleIsValid(tuple))
15383 0 : ereport(ERROR,
15384 : (errcode(ERRCODE_UNDEFINED_COLUMN),
15385 : errmsg("column \"%s\" of relation \"%s\" does not exist",
15386 : colName, RelationGetRelationName(rel))));
15387 :
15388 : /* Prevent them from altering a system attribute */
15389 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15390 172 : attnum = atttableform->attnum;
15391 172 : if (attnum <= 0)
15392 6 : ereport(ERROR,
15393 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15394 : errmsg("cannot alter system column \"%s\"", colName)));
15395 :
15396 :
15397 : /* Initialize buffers for new tuple values */
15398 166 : memset(repl_val, 0, sizeof(repl_val));
15399 166 : memset(repl_null, false, sizeof(repl_null));
15400 166 : memset(repl_repl, false, sizeof(repl_repl));
15401 :
15402 : /* Extract the current options */
15403 166 : datum = SysCacheGetAttr(ATTNAME,
15404 : tuple,
15405 : Anum_pg_attribute_attfdwoptions,
15406 : &isnull);
15407 166 : if (isnull)
15408 156 : datum = PointerGetDatum(NULL);
15409 :
15410 : /* Transform the options */
15411 166 : datum = transformGenericOptions(AttributeRelationId,
15412 : datum,
15413 : options,
15414 : fdw->fdwvalidator);
15415 :
15416 166 : if (PointerIsValid(DatumGetPointer(datum)))
15417 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15418 : else
15419 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15420 :
15421 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15422 :
15423 : /* Everything looks good - update the tuple */
15424 :
15425 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15426 : repl_val, repl_null, repl_repl);
15427 :
15428 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15429 :
15430 166 : InvokeObjectPostAlterHook(RelationRelationId,
15431 : RelationGetRelid(rel),
15432 : atttableform->attnum);
15433 166 : ObjectAddressSubSet(address, RelationRelationId,
15434 : RelationGetRelid(rel), attnum);
15435 :
15436 166 : ReleaseSysCache(tuple);
15437 :
15438 166 : table_close(attrel, RowExclusiveLock);
15439 :
15440 166 : heap_freetuple(newtuple);
15441 :
15442 166 : return address;
15443 : }
15444 :
15445 : /*
15446 : * ALTER TABLE OWNER
15447 : *
15448 : * recursing is true if we are recursing from a table to its indexes,
15449 : * sequences, or toast table. We don't allow the ownership of those things to
15450 : * be changed separately from the parent table. Also, we can skip permission
15451 : * checks (this is necessary not just an optimization, else we'd fail to
15452 : * handle toast tables properly).
15453 : *
15454 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15455 : * free-standing composite type.
15456 : */
15457 : void
15458 2154 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
15459 : {
15460 : Relation target_rel;
15461 : Relation class_rel;
15462 : HeapTuple tuple;
15463 : Form_pg_class tuple_class;
15464 :
15465 : /*
15466 : * Get exclusive lock till end of transaction on the target table. Use
15467 : * relation_open so that we can work on indexes and sequences.
15468 : */
15469 2154 : target_rel = relation_open(relationOid, lockmode);
15470 :
15471 : /* Get its pg_class tuple, too */
15472 2154 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
15473 :
15474 2154 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
15475 2154 : if (!HeapTupleIsValid(tuple))
15476 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
15477 2154 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15478 :
15479 : /* Can we change the ownership of this tuple? */
15480 2154 : switch (tuple_class->relkind)
15481 : {
15482 1870 : case RELKIND_RELATION:
15483 : case RELKIND_VIEW:
15484 : case RELKIND_MATVIEW:
15485 : case RELKIND_FOREIGN_TABLE:
15486 : case RELKIND_PARTITIONED_TABLE:
15487 : /* ok to change owner */
15488 1870 : break;
15489 96 : case RELKIND_INDEX:
15490 96 : if (!recursing)
15491 : {
15492 : /*
15493 : * Because ALTER INDEX OWNER used to be allowed, and in fact
15494 : * is generated by old versions of pg_dump, we give a warning
15495 : * and do nothing rather than erroring out. Also, to avoid
15496 : * unnecessary chatter while restoring those old dumps, say
15497 : * nothing at all if the command would be a no-op anyway.
15498 : */
15499 0 : if (tuple_class->relowner != newOwnerId)
15500 0 : ereport(WARNING,
15501 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15502 : errmsg("cannot change owner of index \"%s\"",
15503 : NameStr(tuple_class->relname)),
15504 : errhint("Change the ownership of the index's table instead.")));
15505 : /* quick hack to exit via the no-op path */
15506 0 : newOwnerId = tuple_class->relowner;
15507 : }
15508 96 : break;
15509 20 : case RELKIND_PARTITIONED_INDEX:
15510 20 : if (recursing)
15511 20 : break;
15512 0 : ereport(ERROR,
15513 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15514 : errmsg("cannot change owner of index \"%s\"",
15515 : NameStr(tuple_class->relname)),
15516 : errhint("Change the ownership of the index's table instead.")));
15517 : break;
15518 118 : case RELKIND_SEQUENCE:
15519 118 : if (!recursing &&
15520 70 : tuple_class->relowner != newOwnerId)
15521 : {
15522 : /* if it's an owned sequence, disallow changing it by itself */
15523 : Oid tableId;
15524 : int32 colId;
15525 :
15526 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15527 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15528 0 : ereport(ERROR,
15529 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15530 : errmsg("cannot change owner of sequence \"%s\"",
15531 : NameStr(tuple_class->relname)),
15532 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
15533 : NameStr(tuple_class->relname),
15534 : get_rel_name(tableId))));
15535 : }
15536 118 : break;
15537 8 : case RELKIND_COMPOSITE_TYPE:
15538 8 : if (recursing)
15539 8 : break;
15540 0 : ereport(ERROR,
15541 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15542 : errmsg("\"%s\" is a composite type",
15543 : NameStr(tuple_class->relname)),
15544 : /* translator: %s is an SQL ALTER command */
15545 : errhint("Use %s instead.",
15546 : "ALTER TYPE")));
15547 : break;
15548 42 : case RELKIND_TOASTVALUE:
15549 42 : if (recursing)
15550 42 : break;
15551 : /* FALL THRU */
15552 : default:
15553 0 : ereport(ERROR,
15554 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15555 : errmsg("cannot change owner of relation \"%s\"",
15556 : NameStr(tuple_class->relname)),
15557 : errdetail_relkind_not_supported(tuple_class->relkind)));
15558 : }
15559 :
15560 : /*
15561 : * If the new owner is the same as the existing owner, consider the
15562 : * command to have succeeded. This is for dump restoration purposes.
15563 : */
15564 2154 : if (tuple_class->relowner != newOwnerId)
15565 : {
15566 : Datum repl_val[Natts_pg_class];
15567 : bool repl_null[Natts_pg_class];
15568 : bool repl_repl[Natts_pg_class];
15569 : Acl *newAcl;
15570 : Datum aclDatum;
15571 : bool isNull;
15572 : HeapTuple newtuple;
15573 :
15574 : /* skip permission checks when recursing to index or toast table */
15575 498 : if (!recursing)
15576 : {
15577 : /* Superusers can always do it */
15578 280 : if (!superuser())
15579 : {
15580 42 : Oid namespaceOid = tuple_class->relnamespace;
15581 : AclResult aclresult;
15582 :
15583 : /* Otherwise, must be owner of the existing object */
15584 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15585 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15586 0 : RelationGetRelationName(target_rel));
15587 :
15588 : /* Must be able to become new owner */
15589 42 : check_can_set_role(GetUserId(), newOwnerId);
15590 :
15591 : /* New owner must have CREATE privilege on namespace */
15592 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15593 : ACL_CREATE);
15594 30 : if (aclresult != ACLCHECK_OK)
15595 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
15596 0 : get_namespace_name(namespaceOid));
15597 : }
15598 : }
15599 :
15600 486 : memset(repl_null, false, sizeof(repl_null));
15601 486 : memset(repl_repl, false, sizeof(repl_repl));
15602 :
15603 486 : repl_repl[Anum_pg_class_relowner - 1] = true;
15604 486 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15605 :
15606 : /*
15607 : * Determine the modified ACL for the new owner. This is only
15608 : * necessary when the ACL is non-null.
15609 : */
15610 486 : aclDatum = SysCacheGetAttr(RELOID, tuple,
15611 : Anum_pg_class_relacl,
15612 : &isNull);
15613 486 : if (!isNull)
15614 : {
15615 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15616 : tuple_class->relowner, newOwnerId);
15617 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
15618 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15619 : }
15620 :
15621 486 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15622 :
15623 486 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15624 :
15625 486 : heap_freetuple(newtuple);
15626 :
15627 : /*
15628 : * We must similarly update any per-column ACLs to reflect the new
15629 : * owner; for neatness reasons that's split out as a subroutine.
15630 : */
15631 486 : change_owner_fix_column_acls(relationOid,
15632 : tuple_class->relowner,
15633 : newOwnerId);
15634 :
15635 : /*
15636 : * Update owner dependency reference, if any. A composite type has
15637 : * none, because it's tracked for the pg_type entry instead of here;
15638 : * indexes and TOAST tables don't have their own entries either.
15639 : */
15640 486 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15641 478 : tuple_class->relkind != RELKIND_INDEX &&
15642 382 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15643 362 : tuple_class->relkind != RELKIND_TOASTVALUE)
15644 320 : changeDependencyOnOwner(RelationRelationId, relationOid,
15645 : newOwnerId);
15646 :
15647 : /*
15648 : * Also change the ownership of the table's row type, if it has one
15649 : */
15650 486 : if (OidIsValid(tuple_class->reltype))
15651 294 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15652 :
15653 : /*
15654 : * If we are operating on a table or materialized view, also change
15655 : * the ownership of any indexes and sequences that belong to the
15656 : * relation, as well as its toast table (if it has one).
15657 : */
15658 486 : if (tuple_class->relkind == RELKIND_RELATION ||
15659 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15660 224 : tuple_class->relkind == RELKIND_MATVIEW ||
15661 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
15662 : {
15663 : List *index_oid_list;
15664 : ListCell *i;
15665 :
15666 : /* Find all the indexes belonging to this relation */
15667 304 : index_oid_list = RelationGetIndexList(target_rel);
15668 :
15669 : /* For each index, recursively change its ownership */
15670 420 : foreach(i, index_oid_list)
15671 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15672 :
15673 304 : list_free(index_oid_list);
15674 : }
15675 :
15676 : /* If it has a toast table, recurse to change its ownership */
15677 486 : if (tuple_class->reltoastrelid != InvalidOid)
15678 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15679 : true, lockmode);
15680 :
15681 : /* If it has dependent sequences, recurse to change them too */
15682 486 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15683 : }
15684 :
15685 2142 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15686 :
15687 2142 : ReleaseSysCache(tuple);
15688 2142 : table_close(class_rel, RowExclusiveLock);
15689 2142 : relation_close(target_rel, NoLock);
15690 2142 : }
15691 :
15692 : /*
15693 : * change_owner_fix_column_acls
15694 : *
15695 : * Helper function for ATExecChangeOwner. Scan the columns of the table
15696 : * and fix any non-null column ACLs to reflect the new owner.
15697 : */
15698 : static void
15699 486 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15700 : {
15701 : Relation attRelation;
15702 : SysScanDesc scan;
15703 : ScanKeyData key[1];
15704 : HeapTuple attributeTuple;
15705 :
15706 486 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15707 486 : ScanKeyInit(&key[0],
15708 : Anum_pg_attribute_attrelid,
15709 : BTEqualStrategyNumber, F_OIDEQ,
15710 : ObjectIdGetDatum(relationOid));
15711 486 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15712 : true, NULL, 1, key);
15713 3372 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15714 : {
15715 2886 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15716 : Datum repl_val[Natts_pg_attribute];
15717 : bool repl_null[Natts_pg_attribute];
15718 : bool repl_repl[Natts_pg_attribute];
15719 : Acl *newAcl;
15720 : Datum aclDatum;
15721 : bool isNull;
15722 : HeapTuple newtuple;
15723 :
15724 : /* Ignore dropped columns */
15725 2886 : if (att->attisdropped)
15726 2884 : continue;
15727 :
15728 2886 : aclDatum = heap_getattr(attributeTuple,
15729 : Anum_pg_attribute_attacl,
15730 : RelationGetDescr(attRelation),
15731 : &isNull);
15732 : /* Null ACLs do not require changes */
15733 2886 : if (isNull)
15734 2884 : continue;
15735 :
15736 2 : memset(repl_null, false, sizeof(repl_null));
15737 2 : memset(repl_repl, false, sizeof(repl_repl));
15738 :
15739 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15740 : oldOwnerId, newOwnerId);
15741 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
15742 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15743 :
15744 2 : newtuple = heap_modify_tuple(attributeTuple,
15745 : RelationGetDescr(attRelation),
15746 : repl_val, repl_null, repl_repl);
15747 :
15748 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15749 :
15750 2 : heap_freetuple(newtuple);
15751 : }
15752 486 : systable_endscan(scan);
15753 486 : table_close(attRelation, RowExclusiveLock);
15754 486 : }
15755 :
15756 : /*
15757 : * change_owner_recurse_to_sequences
15758 : *
15759 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
15760 : * for sequences that are dependent on serial columns, and changes their
15761 : * ownership.
15762 : */
15763 : static void
15764 486 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15765 : {
15766 : Relation depRel;
15767 : SysScanDesc scan;
15768 : ScanKeyData key[2];
15769 : HeapTuple tup;
15770 :
15771 : /*
15772 : * SERIAL sequences are those having an auto dependency on one of the
15773 : * table's columns (we don't care *which* column, exactly).
15774 : */
15775 486 : depRel = table_open(DependRelationId, AccessShareLock);
15776 :
15777 486 : ScanKeyInit(&key[0],
15778 : Anum_pg_depend_refclassid,
15779 : BTEqualStrategyNumber, F_OIDEQ,
15780 : ObjectIdGetDatum(RelationRelationId));
15781 486 : ScanKeyInit(&key[1],
15782 : Anum_pg_depend_refobjid,
15783 : BTEqualStrategyNumber, F_OIDEQ,
15784 : ObjectIdGetDatum(relationOid));
15785 : /* we leave refobjsubid unspecified */
15786 :
15787 486 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15788 : NULL, 2, key);
15789 :
15790 1374 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
15791 : {
15792 888 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15793 : Relation seqRel;
15794 :
15795 : /* skip dependencies other than auto dependencies on columns */
15796 888 : if (depForm->refobjsubid == 0 ||
15797 352 : depForm->classid != RelationRelationId ||
15798 142 : depForm->objsubid != 0 ||
15799 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15800 746 : continue;
15801 :
15802 : /* Use relation_open just in case it's an index */
15803 142 : seqRel = relation_open(depForm->objid, lockmode);
15804 :
15805 : /* skip non-sequence relations */
15806 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15807 : {
15808 : /* No need to keep the lock */
15809 116 : relation_close(seqRel, lockmode);
15810 116 : continue;
15811 : }
15812 :
15813 : /* We don't need to close the sequence while we alter it. */
15814 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15815 :
15816 : /* Now we can close it. Keep the lock till end of transaction. */
15817 26 : relation_close(seqRel, NoLock);
15818 : }
15819 :
15820 486 : systable_endscan(scan);
15821 :
15822 486 : relation_close(depRel, AccessShareLock);
15823 486 : }
15824 :
15825 : /*
15826 : * ALTER TABLE CLUSTER ON
15827 : *
15828 : * The only thing we have to do is to change the indisclustered bits.
15829 : *
15830 : * Return the address of the new clustering index.
15831 : */
15832 : static ObjectAddress
15833 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15834 : {
15835 : Oid indexOid;
15836 : ObjectAddress address;
15837 :
15838 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15839 :
15840 64 : if (!OidIsValid(indexOid))
15841 0 : ereport(ERROR,
15842 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15843 : errmsg("index \"%s\" for table \"%s\" does not exist",
15844 : indexName, RelationGetRelationName(rel))));
15845 :
15846 : /* Check index is valid to cluster on */
15847 64 : check_index_is_clusterable(rel, indexOid, lockmode);
15848 :
15849 : /* And do the work */
15850 64 : mark_index_clustered(rel, indexOid, false);
15851 :
15852 58 : ObjectAddressSet(address,
15853 : RelationRelationId, indexOid);
15854 :
15855 58 : return address;
15856 : }
15857 :
15858 : /*
15859 : * ALTER TABLE SET WITHOUT CLUSTER
15860 : *
15861 : * We have to find any indexes on the table that have indisclustered bit
15862 : * set and turn it off.
15863 : */
15864 : static void
15865 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15866 : {
15867 18 : mark_index_clustered(rel, InvalidOid, false);
15868 12 : }
15869 :
15870 : /*
15871 : * Preparation phase for SET ACCESS METHOD
15872 : *
15873 : * Check that the access method exists and determine whether a change is
15874 : * actually needed.
15875 : */
15876 : static void
15877 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15878 : {
15879 : Oid amoid;
15880 :
15881 : /*
15882 : * Look up the access method name and check that it differs from the
15883 : * table's current AM. If DEFAULT was specified for a partitioned table
15884 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15885 : */
15886 110 : if (amname != NULL)
15887 74 : amoid = get_table_am_oid(amname, false);
15888 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15889 18 : amoid = InvalidOid;
15890 : else
15891 18 : amoid = get_table_am_oid(default_table_access_method, false);
15892 :
15893 : /* if it's a match, phase 3 doesn't need to do anything */
15894 110 : if (rel->rd_rel->relam == amoid)
15895 12 : return;
15896 :
15897 : /* Save info for Phase 3 to do the real work */
15898 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15899 98 : tab->newAccessMethod = amoid;
15900 98 : tab->chgAccessMethod = true;
15901 : }
15902 :
15903 : /*
15904 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15905 : * storage that have an interest in preserving AM.
15906 : *
15907 : * Since these have no storage, setting the access method is a catalog only
15908 : * operation.
15909 : */
15910 : static void
15911 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15912 : {
15913 : Relation pg_class;
15914 : Oid oldAccessMethodId;
15915 : HeapTuple tuple;
15916 : Form_pg_class rd_rel;
15917 44 : Oid reloid = RelationGetRelid(rel);
15918 :
15919 : /*
15920 : * Shouldn't be called on relations having storage; these are processed in
15921 : * phase 3.
15922 : */
15923 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15924 :
15925 : /* Get a modifiable copy of the relation's pg_class row. */
15926 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
15927 :
15928 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15929 44 : if (!HeapTupleIsValid(tuple))
15930 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
15931 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15932 :
15933 : /* Update the pg_class row. */
15934 44 : oldAccessMethodId = rd_rel->relam;
15935 44 : rd_rel->relam = newAccessMethodId;
15936 :
15937 : /* Leave if no update required */
15938 44 : if (rd_rel->relam == oldAccessMethodId)
15939 : {
15940 0 : heap_freetuple(tuple);
15941 0 : table_close(pg_class, RowExclusiveLock);
15942 0 : return;
15943 : }
15944 :
15945 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15946 :
15947 : /*
15948 : * Update the dependency on the new access method. No dependency is added
15949 : * if the new access method is InvalidOid (default case). Be very careful
15950 : * that this has to compare the previous value stored in pg_class with the
15951 : * new one.
15952 : */
15953 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15954 20 : {
15955 : ObjectAddress relobj,
15956 : referenced;
15957 :
15958 : /*
15959 : * New access method is defined and there was no dependency
15960 : * previously, so record a new one.
15961 : */
15962 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
15963 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15964 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15965 : }
15966 24 : else if (OidIsValid(oldAccessMethodId) &&
15967 24 : !OidIsValid(rd_rel->relam))
15968 : {
15969 : /*
15970 : * There was an access method defined, and no new one, so just remove
15971 : * the existing dependency.
15972 : */
15973 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
15974 : AccessMethodRelationId,
15975 : DEPENDENCY_NORMAL);
15976 : }
15977 : else
15978 : {
15979 : Assert(OidIsValid(oldAccessMethodId) &&
15980 : OidIsValid(rd_rel->relam));
15981 :
15982 : /* Both are valid, so update the dependency */
15983 12 : changeDependencyFor(RelationRelationId, reloid,
15984 : AccessMethodRelationId,
15985 : oldAccessMethodId, rd_rel->relam);
15986 : }
15987 :
15988 : /* make the relam and dependency changes visible */
15989 44 : CommandCounterIncrement();
15990 :
15991 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15992 :
15993 44 : heap_freetuple(tuple);
15994 44 : table_close(pg_class, RowExclusiveLock);
15995 : }
15996 :
15997 : /*
15998 : * ALTER TABLE SET TABLESPACE
15999 : */
16000 : static void
16001 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16002 : {
16003 : Oid tablespaceId;
16004 :
16005 : /* Check that the tablespace exists */
16006 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
16007 :
16008 : /* Check permissions except when moving to database's default */
16009 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16010 : {
16011 : AclResult aclresult;
16012 :
16013 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16014 66 : if (aclresult != ACLCHECK_OK)
16015 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16016 : }
16017 :
16018 : /* Save info for Phase 3 to do the real work */
16019 158 : if (OidIsValid(tab->newTableSpace))
16020 0 : ereport(ERROR,
16021 : (errcode(ERRCODE_SYNTAX_ERROR),
16022 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
16023 :
16024 158 : tab->newTableSpace = tablespaceId;
16025 158 : }
16026 :
16027 : /*
16028 : * Set, reset, or replace reloptions.
16029 : */
16030 : static void
16031 954 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
16032 : LOCKMODE lockmode)
16033 : {
16034 : Oid relid;
16035 : Relation pgclass;
16036 : HeapTuple tuple;
16037 : HeapTuple newtuple;
16038 : Datum datum;
16039 : Datum newOptions;
16040 : Datum repl_val[Natts_pg_class];
16041 : bool repl_null[Natts_pg_class];
16042 : bool repl_repl[Natts_pg_class];
16043 954 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16044 :
16045 954 : if (defList == NIL && operation != AT_ReplaceRelOptions)
16046 0 : return; /* nothing to do */
16047 :
16048 954 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
16049 :
16050 : /* Fetch heap tuple */
16051 954 : relid = RelationGetRelid(rel);
16052 954 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16053 954 : if (!HeapTupleIsValid(tuple))
16054 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16055 :
16056 954 : if (operation == AT_ReplaceRelOptions)
16057 : {
16058 : /*
16059 : * If we're supposed to replace the reloptions list, we just pretend
16060 : * there were none before.
16061 : */
16062 194 : datum = (Datum) 0;
16063 : }
16064 : else
16065 : {
16066 : bool isnull;
16067 :
16068 : /* Get the old reloptions */
16069 760 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16070 : &isnull);
16071 760 : if (isnull)
16072 472 : datum = (Datum) 0;
16073 : }
16074 :
16075 : /* Generate new proposed reloptions (text array) */
16076 954 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16077 : operation == AT_ResetRelOptions);
16078 :
16079 : /* Validate */
16080 948 : switch (rel->rd_rel->relkind)
16081 : {
16082 530 : case RELKIND_RELATION:
16083 : case RELKIND_MATVIEW:
16084 530 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16085 530 : break;
16086 6 : case RELKIND_PARTITIONED_TABLE:
16087 6 : (void) partitioned_table_reloptions(newOptions, true);
16088 0 : break;
16089 296 : case RELKIND_VIEW:
16090 296 : (void) view_reloptions(newOptions, true);
16091 278 : break;
16092 116 : case RELKIND_INDEX:
16093 : case RELKIND_PARTITIONED_INDEX:
16094 116 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16095 94 : break;
16096 0 : case RELKIND_TOASTVALUE:
16097 : /* fall through to error -- shouldn't ever get here */
16098 : default:
16099 0 : ereport(ERROR,
16100 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16101 : errmsg("cannot set options for relation \"%s\"",
16102 : RelationGetRelationName(rel)),
16103 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16104 : break;
16105 : }
16106 :
16107 : /* Special-case validation of view options */
16108 902 : if (rel->rd_rel->relkind == RELKIND_VIEW)
16109 : {
16110 278 : Query *view_query = get_view_query(rel);
16111 278 : List *view_options = untransformRelOptions(newOptions);
16112 : ListCell *cell;
16113 278 : bool check_option = false;
16114 :
16115 380 : foreach(cell, view_options)
16116 : {
16117 102 : DefElem *defel = (DefElem *) lfirst(cell);
16118 :
16119 102 : if (strcmp(defel->defname, "check_option") == 0)
16120 24 : check_option = true;
16121 : }
16122 :
16123 : /*
16124 : * If the check option is specified, look to see if the view is
16125 : * actually auto-updatable or not.
16126 : */
16127 278 : if (check_option)
16128 : {
16129 : const char *view_updatable_error =
16130 24 : view_query_is_auto_updatable(view_query, true);
16131 :
16132 24 : if (view_updatable_error)
16133 0 : ereport(ERROR,
16134 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16135 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16136 : errhint("%s", _(view_updatable_error))));
16137 : }
16138 : }
16139 :
16140 : /*
16141 : * All we need do here is update the pg_class row; the new options will be
16142 : * propagated into relcaches during post-commit cache inval.
16143 : */
16144 902 : memset(repl_val, 0, sizeof(repl_val));
16145 902 : memset(repl_null, false, sizeof(repl_null));
16146 902 : memset(repl_repl, false, sizeof(repl_repl));
16147 :
16148 902 : if (newOptions != (Datum) 0)
16149 608 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16150 : else
16151 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
16152 :
16153 902 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16154 :
16155 902 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16156 : repl_val, repl_null, repl_repl);
16157 :
16158 902 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16159 902 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16160 :
16161 902 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16162 :
16163 902 : heap_freetuple(newtuple);
16164 :
16165 902 : ReleaseSysCache(tuple);
16166 :
16167 : /* repeat the whole exercise for the toast table, if there's one */
16168 902 : if (OidIsValid(rel->rd_rel->reltoastrelid))
16169 : {
16170 : Relation toastrel;
16171 262 : Oid toastid = rel->rd_rel->reltoastrelid;
16172 :
16173 262 : toastrel = table_open(toastid, lockmode);
16174 :
16175 : /* Fetch heap tuple */
16176 262 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16177 262 : if (!HeapTupleIsValid(tuple))
16178 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
16179 :
16180 262 : if (operation == AT_ReplaceRelOptions)
16181 : {
16182 : /*
16183 : * If we're supposed to replace the reloptions list, we just
16184 : * pretend there were none before.
16185 : */
16186 0 : datum = (Datum) 0;
16187 : }
16188 : else
16189 : {
16190 : bool isnull;
16191 :
16192 : /* Get the old reloptions */
16193 262 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16194 : &isnull);
16195 262 : if (isnull)
16196 226 : datum = (Datum) 0;
16197 : }
16198 :
16199 262 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16200 : false, operation == AT_ResetRelOptions);
16201 :
16202 262 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16203 :
16204 262 : memset(repl_val, 0, sizeof(repl_val));
16205 262 : memset(repl_null, false, sizeof(repl_null));
16206 262 : memset(repl_repl, false, sizeof(repl_repl));
16207 :
16208 262 : if (newOptions != (Datum) 0)
16209 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16210 : else
16211 220 : repl_null[Anum_pg_class_reloptions - 1] = true;
16212 :
16213 262 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16214 :
16215 262 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16216 : repl_val, repl_null, repl_repl);
16217 :
16218 262 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16219 :
16220 262 : InvokeObjectPostAlterHookArg(RelationRelationId,
16221 : RelationGetRelid(toastrel), 0,
16222 : InvalidOid, true);
16223 :
16224 262 : heap_freetuple(newtuple);
16225 :
16226 262 : ReleaseSysCache(tuple);
16227 :
16228 262 : table_close(toastrel, NoLock);
16229 : }
16230 :
16231 902 : table_close(pgclass, RowExclusiveLock);
16232 : }
16233 :
16234 : /*
16235 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16236 : * rewriting to be done, so we just want to copy the data as fast as possible.
16237 : */
16238 : static void
16239 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16240 : {
16241 : Relation rel;
16242 : Oid reltoastrelid;
16243 : RelFileNumber newrelfilenumber;
16244 : RelFileLocator newrlocator;
16245 162 : List *reltoastidxids = NIL;
16246 : ListCell *lc;
16247 :
16248 : /*
16249 : * Need lock here in case we are recursing to toast table or index
16250 : */
16251 162 : rel = relation_open(tableOid, lockmode);
16252 :
16253 : /* Check first if relation can be moved to new tablespace */
16254 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16255 : {
16256 2 : InvokeObjectPostAlterHook(RelationRelationId,
16257 : RelationGetRelid(rel), 0);
16258 2 : relation_close(rel, NoLock);
16259 2 : return;
16260 : }
16261 :
16262 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
16263 : /* Fetch the list of indexes on toast relation if necessary */
16264 160 : if (OidIsValid(reltoastrelid))
16265 : {
16266 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
16267 :
16268 20 : reltoastidxids = RelationGetIndexList(toastRel);
16269 20 : relation_close(toastRel, lockmode);
16270 : }
16271 :
16272 : /*
16273 : * Relfilenumbers are not unique in databases across tablespaces, so we
16274 : * need to allocate a new one in the new tablespace.
16275 : */
16276 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16277 160 : rel->rd_rel->relpersistence);
16278 :
16279 : /* Open old and new relation */
16280 160 : newrlocator = rel->rd_locator;
16281 160 : newrlocator.relNumber = newrelfilenumber;
16282 160 : newrlocator.spcOid = newTableSpace;
16283 :
16284 : /* hand off to AM to actually create new rel storage and copy the data */
16285 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
16286 : {
16287 62 : index_copy_data(rel, newrlocator);
16288 : }
16289 : else
16290 : {
16291 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16292 98 : table_relation_copy_data(rel, &newrlocator);
16293 : }
16294 :
16295 : /*
16296 : * Update the pg_class row.
16297 : *
16298 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16299 : * executed on pg_class or its indexes (the above copy wouldn't contain
16300 : * the updated pg_class entry), but that's forbidden with
16301 : * CheckRelationTableSpaceMove().
16302 : */
16303 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16304 :
16305 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16306 :
16307 160 : RelationAssumeNewRelfilelocator(rel);
16308 :
16309 160 : relation_close(rel, NoLock);
16310 :
16311 : /* Make sure the reltablespace change is visible */
16312 160 : CommandCounterIncrement();
16313 :
16314 : /* Move associated toast relation and/or indexes, too */
16315 160 : if (OidIsValid(reltoastrelid))
16316 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16317 180 : foreach(lc, reltoastidxids)
16318 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16319 :
16320 : /* Clean up */
16321 160 : list_free(reltoastidxids);
16322 : }
16323 :
16324 : /*
16325 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16326 : * storage that have an interest in preserving tablespace.
16327 : *
16328 : * Since these have no storage the tablespace can be updated with a simple
16329 : * metadata only operation to update the tablespace.
16330 : */
16331 : static void
16332 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
16333 : {
16334 : /*
16335 : * Shouldn't be called on relations having storage; these are processed in
16336 : * phase 3.
16337 : */
16338 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16339 :
16340 : /* check if relation can be moved to its new tablespace */
16341 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16342 : {
16343 0 : InvokeObjectPostAlterHook(RelationRelationId,
16344 : RelationGetRelid(rel),
16345 : 0);
16346 0 : return;
16347 : }
16348 :
16349 : /* Update can be done, so change reltablespace */
16350 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16351 :
16352 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16353 :
16354 : /* Make sure the reltablespace change is visible */
16355 30 : CommandCounterIncrement();
16356 : }
16357 :
16358 : /*
16359 : * Alter Table ALL ... SET TABLESPACE
16360 : *
16361 : * Allows a user to move all objects of some type in a given tablespace in the
16362 : * current database to another tablespace. Objects can be chosen based on the
16363 : * owner of the object also, to allow users to move only their objects.
16364 : * The user must have CREATE rights on the new tablespace, as usual. The main
16365 : * permissions handling is done by the lower-level table move function.
16366 : *
16367 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
16368 : * lock can't be acquired then we ereport(ERROR).
16369 : */
16370 : Oid
16371 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
16372 : {
16373 30 : List *relations = NIL;
16374 : ListCell *l;
16375 : ScanKeyData key[1];
16376 : Relation rel;
16377 : TableScanDesc scan;
16378 : HeapTuple tuple;
16379 : Oid orig_tablespaceoid;
16380 : Oid new_tablespaceoid;
16381 30 : List *role_oids = roleSpecsToIds(stmt->roles);
16382 :
16383 : /* Ensure we were not asked to move something we can't */
16384 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16385 12 : stmt->objtype != OBJECT_MATVIEW)
16386 0 : ereport(ERROR,
16387 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16388 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16389 :
16390 : /* Get the orig and new tablespace OIDs */
16391 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16392 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16393 :
16394 : /* Can't move shared relations in to or out of pg_global */
16395 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16396 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16397 : new_tablespaceoid == GLOBALTABLESPACE_OID)
16398 0 : ereport(ERROR,
16399 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16400 : errmsg("cannot move relations in to or out of pg_global tablespace")));
16401 :
16402 : /*
16403 : * Must have CREATE rights on the new tablespace, unless it is the
16404 : * database default tablespace (which all users implicitly have CREATE
16405 : * rights on).
16406 : */
16407 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16408 : {
16409 : AclResult aclresult;
16410 :
16411 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16412 : ACL_CREATE);
16413 0 : if (aclresult != ACLCHECK_OK)
16414 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
16415 0 : get_tablespace_name(new_tablespaceoid));
16416 : }
16417 :
16418 : /*
16419 : * Now that the checks are done, check if we should set either to
16420 : * InvalidOid because it is our database's default tablespace.
16421 : */
16422 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
16423 0 : orig_tablespaceoid = InvalidOid;
16424 :
16425 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
16426 30 : new_tablespaceoid = InvalidOid;
16427 :
16428 : /* no-op */
16429 30 : if (orig_tablespaceoid == new_tablespaceoid)
16430 0 : return new_tablespaceoid;
16431 :
16432 : /*
16433 : * Walk the list of objects in the tablespace and move them. This will
16434 : * only find objects in our database, of course.
16435 : */
16436 30 : ScanKeyInit(&key[0],
16437 : Anum_pg_class_reltablespace,
16438 : BTEqualStrategyNumber, F_OIDEQ,
16439 : ObjectIdGetDatum(orig_tablespaceoid));
16440 :
16441 30 : rel = table_open(RelationRelationId, AccessShareLock);
16442 30 : scan = table_beginscan_catalog(rel, 1, key);
16443 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16444 : {
16445 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16446 102 : Oid relOid = relForm->oid;
16447 :
16448 : /*
16449 : * Do not move objects in pg_catalog as part of this, if an admin
16450 : * really wishes to do so, they can issue the individual ALTER
16451 : * commands directly.
16452 : *
16453 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
16454 : * (TOAST will be moved with the main table).
16455 : */
16456 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
16457 204 : relForm->relisshared ||
16458 204 : isAnyTempNamespace(relForm->relnamespace) ||
16459 102 : IsToastNamespace(relForm->relnamespace))
16460 0 : continue;
16461 :
16462 : /* Only move the object type requested */
16463 102 : if ((stmt->objtype == OBJECT_TABLE &&
16464 60 : relForm->relkind != RELKIND_RELATION &&
16465 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16466 66 : (stmt->objtype == OBJECT_INDEX &&
16467 36 : relForm->relkind != RELKIND_INDEX &&
16468 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16469 60 : (stmt->objtype == OBJECT_MATVIEW &&
16470 6 : relForm->relkind != RELKIND_MATVIEW))
16471 42 : continue;
16472 :
16473 : /* Check if we are only moving objects owned by certain roles */
16474 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16475 0 : continue;
16476 :
16477 : /*
16478 : * Handle permissions-checking here since we are locking the tables
16479 : * and also to avoid doing a bunch of work only to fail part-way. Note
16480 : * that permissions will also be checked by AlterTableInternal().
16481 : *
16482 : * Caller must be considered an owner on the table to move it.
16483 : */
16484 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16485 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
16486 0 : NameStr(relForm->relname));
16487 :
16488 60 : if (stmt->nowait &&
16489 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
16490 0 : ereport(ERROR,
16491 : (errcode(ERRCODE_OBJECT_IN_USE),
16492 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
16493 : get_namespace_name(relForm->relnamespace),
16494 : NameStr(relForm->relname))));
16495 : else
16496 60 : LockRelationOid(relOid, AccessExclusiveLock);
16497 :
16498 : /* Add to our list of objects to move */
16499 60 : relations = lappend_oid(relations, relOid);
16500 : }
16501 :
16502 30 : table_endscan(scan);
16503 30 : table_close(rel, AccessShareLock);
16504 :
16505 30 : if (relations == NIL)
16506 12 : ereport(NOTICE,
16507 : (errcode(ERRCODE_NO_DATA_FOUND),
16508 : errmsg("no matching relations in tablespace \"%s\" found",
16509 : orig_tablespaceoid == InvalidOid ? "(database default)" :
16510 : get_tablespace_name(orig_tablespaceoid))));
16511 :
16512 : /* Everything is locked, loop through and move all of the relations. */
16513 90 : foreach(l, relations)
16514 : {
16515 60 : List *cmds = NIL;
16516 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
16517 :
16518 60 : cmd->subtype = AT_SetTableSpace;
16519 60 : cmd->name = stmt->new_tablespacename;
16520 :
16521 60 : cmds = lappend(cmds, cmd);
16522 :
16523 60 : EventTriggerAlterTableStart((Node *) stmt);
16524 : /* OID is set by AlterTableInternal */
16525 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
16526 60 : EventTriggerAlterTableEnd();
16527 : }
16528 :
16529 30 : return new_tablespaceoid;
16530 : }
16531 :
16532 : static void
16533 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
16534 : {
16535 : SMgrRelation dstrel;
16536 :
16537 : /*
16538 : * Since we copy the file directly without looking at the shared buffers,
16539 : * we'd better first flush out any pages of the source relation that are
16540 : * in shared buffers. We assume no new changes will be made while we are
16541 : * holding exclusive lock on the rel.
16542 : */
16543 62 : FlushRelationBuffers(rel);
16544 :
16545 : /*
16546 : * Create and copy all forks of the relation, and schedule unlinking of
16547 : * old physical files.
16548 : *
16549 : * NOTE: any conflict in relfilenumber value will be caught in
16550 : * RelationCreateStorage().
16551 : */
16552 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16553 :
16554 : /* copy main fork */
16555 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
16556 62 : rel->rd_rel->relpersistence);
16557 :
16558 : /* copy those extra forks that exist */
16559 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16560 186 : forkNum <= MAX_FORKNUM; forkNum++)
16561 : {
16562 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
16563 : {
16564 0 : smgrcreate(dstrel, forkNum, false);
16565 :
16566 : /*
16567 : * WAL log creation if the relation is persistent, or this is the
16568 : * init fork of an unlogged relation.
16569 : */
16570 0 : if (RelationIsPermanent(rel) ||
16571 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16572 : forkNum == INIT_FORKNUM))
16573 0 : log_smgrcreate(&newrlocator, forkNum);
16574 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16575 0 : rel->rd_rel->relpersistence);
16576 : }
16577 : }
16578 :
16579 : /* drop old relation, and close new one */
16580 62 : RelationDropStorage(rel);
16581 62 : smgrclose(dstrel);
16582 62 : }
16583 :
16584 : /*
16585 : * ALTER TABLE ENABLE/DISABLE TRIGGER
16586 : *
16587 : * We just pass this off to trigger.c.
16588 : */
16589 : static void
16590 340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16591 : char fires_when, bool skip_system, bool recurse,
16592 : LOCKMODE lockmode)
16593 : {
16594 340 : EnableDisableTrigger(rel, trigname, InvalidOid,
16595 : fires_when, skip_system, recurse,
16596 : lockmode);
16597 :
16598 340 : InvokeObjectPostAlterHook(RelationRelationId,
16599 : RelationGetRelid(rel), 0);
16600 340 : }
16601 :
16602 : /*
16603 : * ALTER TABLE ENABLE/DISABLE RULE
16604 : *
16605 : * We just pass this off to rewriteDefine.c.
16606 : */
16607 : static void
16608 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
16609 : char fires_when, LOCKMODE lockmode)
16610 : {
16611 46 : EnableDisableRule(rel, rulename, fires_when);
16612 :
16613 46 : InvokeObjectPostAlterHook(RelationRelationId,
16614 : RelationGetRelid(rel), 0);
16615 46 : }
16616 :
16617 : /*
16618 : * ALTER TABLE INHERIT
16619 : *
16620 : * Add a parent to the child's parents. This verifies that all the columns and
16621 : * check constraints of the parent appear in the child and that they have the
16622 : * same data types and expressions.
16623 : */
16624 : static void
16625 410 : ATPrepAddInherit(Relation child_rel)
16626 : {
16627 410 : if (child_rel->rd_rel->reloftype)
16628 6 : ereport(ERROR,
16629 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16630 : errmsg("cannot change inheritance of typed table")));
16631 :
16632 404 : if (child_rel->rd_rel->relispartition)
16633 6 : ereport(ERROR,
16634 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16635 : errmsg("cannot change inheritance of a partition")));
16636 :
16637 398 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16638 6 : ereport(ERROR,
16639 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16640 : errmsg("cannot change inheritance of partitioned table")));
16641 392 : }
16642 :
16643 : /*
16644 : * Return the address of the new parent relation.
16645 : */
16646 : static ObjectAddress
16647 392 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16648 : {
16649 : Relation parent_rel;
16650 : List *children;
16651 : ObjectAddress address;
16652 : const char *trigger_name;
16653 :
16654 : /*
16655 : * A self-exclusive lock is needed here. See the similar case in
16656 : * MergeAttributes() for a full explanation.
16657 : */
16658 392 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16659 :
16660 : /*
16661 : * Must be owner of both parent and child -- child was checked by
16662 : * ATSimplePermissions call in ATPrepCmd
16663 : */
16664 392 : ATSimplePermissions(AT_AddInherit, parent_rel,
16665 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
16666 :
16667 : /* Permanent rels cannot inherit from temporary ones */
16668 392 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16669 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16670 0 : ereport(ERROR,
16671 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16672 : errmsg("cannot inherit from temporary relation \"%s\"",
16673 : RelationGetRelationName(parent_rel))));
16674 :
16675 : /* If parent rel is temp, it must belong to this session */
16676 392 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16677 6 : !parent_rel->rd_islocaltemp)
16678 0 : ereport(ERROR,
16679 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16680 : errmsg("cannot inherit from temporary relation of another session")));
16681 :
16682 : /* Ditto for the child */
16683 392 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16684 6 : !child_rel->rd_islocaltemp)
16685 0 : ereport(ERROR,
16686 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16687 : errmsg("cannot inherit to temporary relation of another session")));
16688 :
16689 : /* Prevent partitioned tables from becoming inheritance parents */
16690 392 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16691 6 : ereport(ERROR,
16692 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16693 : errmsg("cannot inherit from partitioned table \"%s\"",
16694 : parent->relname)));
16695 :
16696 : /* Likewise for partitions */
16697 386 : if (parent_rel->rd_rel->relispartition)
16698 6 : ereport(ERROR,
16699 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16700 : errmsg("cannot inherit from a partition")));
16701 :
16702 : /*
16703 : * Prevent circularity by seeing if proposed parent inherits from child.
16704 : * (In particular, this disallows making a rel inherit from itself.)
16705 : *
16706 : * This is not completely bulletproof because of race conditions: in
16707 : * multi-level inheritance trees, someone else could concurrently be
16708 : * making another inheritance link that closes the loop but does not join
16709 : * either of the rels we have locked. Preventing that seems to require
16710 : * exclusive locks on the entire inheritance tree, which is a cure worse
16711 : * than the disease. find_all_inheritors() will cope with circularity
16712 : * anyway, so don't sweat it too much.
16713 : *
16714 : * We use weakest lock we can on child's children, namely AccessShareLock.
16715 : */
16716 380 : children = find_all_inheritors(RelationGetRelid(child_rel),
16717 : AccessShareLock, NULL);
16718 :
16719 380 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
16720 12 : ereport(ERROR,
16721 : (errcode(ERRCODE_DUPLICATE_TABLE),
16722 : errmsg("circular inheritance not allowed"),
16723 : errdetail("\"%s\" is already a child of \"%s\".",
16724 : parent->relname,
16725 : RelationGetRelationName(child_rel))));
16726 :
16727 : /*
16728 : * If child_rel has row-level triggers with transition tables, we
16729 : * currently don't allow it to become an inheritance child. See also
16730 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
16731 : */
16732 368 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16733 368 : if (trigger_name != NULL)
16734 6 : ereport(ERROR,
16735 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16736 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16737 : trigger_name, RelationGetRelationName(child_rel)),
16738 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16739 :
16740 : /* OK to create inheritance */
16741 362 : CreateInheritance(child_rel, parent_rel, false);
16742 :
16743 284 : ObjectAddressSet(address, RelationRelationId,
16744 : RelationGetRelid(parent_rel));
16745 :
16746 : /* keep our lock on the parent relation until commit */
16747 284 : table_close(parent_rel, NoLock);
16748 :
16749 284 : return address;
16750 : }
16751 :
16752 : /*
16753 : * CreateInheritance
16754 : * Catalog manipulation portion of creating inheritance between a child
16755 : * table and a parent table.
16756 : *
16757 : * Common to ATExecAddInherit() and ATExecAttachPartition().
16758 : */
16759 : static void
16760 2512 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16761 : {
16762 : Relation catalogRelation;
16763 : SysScanDesc scan;
16764 : ScanKeyData key;
16765 : HeapTuple inheritsTuple;
16766 : int32 inhseqno;
16767 :
16768 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16769 2512 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16770 :
16771 : /*
16772 : * Check for duplicates in the list of parents, and determine the highest
16773 : * inhseqno already present; we'll use the next one for the new parent.
16774 : * Also, if proposed child is a partition, it cannot already be
16775 : * inheriting.
16776 : *
16777 : * Note: we do not reject the case where the child already inherits from
16778 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16779 : */
16780 2512 : ScanKeyInit(&key,
16781 : Anum_pg_inherits_inhrelid,
16782 : BTEqualStrategyNumber, F_OIDEQ,
16783 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16784 2512 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16785 : true, NULL, 1, &key);
16786 :
16787 : /* inhseqno sequences start at 1 */
16788 2512 : inhseqno = 0;
16789 2578 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16790 : {
16791 72 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16792 :
16793 72 : if (inh->inhparent == RelationGetRelid(parent_rel))
16794 6 : ereport(ERROR,
16795 : (errcode(ERRCODE_DUPLICATE_TABLE),
16796 : errmsg("relation \"%s\" would be inherited from more than once",
16797 : RelationGetRelationName(parent_rel))));
16798 :
16799 66 : if (inh->inhseqno > inhseqno)
16800 66 : inhseqno = inh->inhseqno;
16801 : }
16802 2506 : systable_endscan(scan);
16803 :
16804 : /* Match up the columns and bump attinhcount as needed */
16805 2506 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16806 :
16807 : /* Match up the constraints and bump coninhcount as needed */
16808 2380 : MergeConstraintsIntoExisting(child_rel, parent_rel);
16809 :
16810 : /*
16811 : * OK, it looks valid. Make the catalog entries that show inheritance.
16812 : */
16813 2332 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
16814 : RelationGetRelid(parent_rel),
16815 : inhseqno + 1,
16816 : catalogRelation,
16817 2332 : parent_rel->rd_rel->relkind ==
16818 : RELKIND_PARTITIONED_TABLE);
16819 :
16820 : /* Now we're done with pg_inherits */
16821 2332 : table_close(catalogRelation, RowExclusiveLock);
16822 2332 : }
16823 :
16824 : /*
16825 : * Obtain the source-text form of the constraint expression for a check
16826 : * constraint, given its pg_constraint tuple
16827 : */
16828 : static char *
16829 184 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16830 : {
16831 : Form_pg_constraint con;
16832 : bool isnull;
16833 : Datum attr;
16834 : Datum expr;
16835 :
16836 184 : con = (Form_pg_constraint) GETSTRUCT(contup);
16837 184 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16838 184 : if (isnull)
16839 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
16840 :
16841 184 : expr = DirectFunctionCall2(pg_get_expr, attr,
16842 : ObjectIdGetDatum(con->conrelid));
16843 184 : return TextDatumGetCString(expr);
16844 : }
16845 :
16846 : /*
16847 : * Determine whether two check constraints are functionally equivalent
16848 : *
16849 : * The test we apply is to see whether they reverse-compile to the same
16850 : * source string. This insulates us from issues like whether attributes
16851 : * have the same physical column numbers in parent and child relations.
16852 : *
16853 : * Note that we ignore enforceability as there are cases where constraints
16854 : * with differing enforceability are allowed.
16855 : */
16856 : static bool
16857 92 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16858 : {
16859 92 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16860 92 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16861 :
16862 92 : if (acon->condeferrable != bcon->condeferrable ||
16863 92 : acon->condeferred != bcon->condeferred ||
16864 92 : strcmp(decompile_conbin(a, tupleDesc),
16865 92 : decompile_conbin(b, tupleDesc)) != 0)
16866 6 : return false;
16867 : else
16868 86 : return true;
16869 : }
16870 :
16871 : /*
16872 : * Check columns in child table match up with columns in parent, and increment
16873 : * their attinhcount.
16874 : *
16875 : * Called by CreateInheritance
16876 : *
16877 : * Currently all parent columns must be found in child. Missing columns are an
16878 : * error. One day we might consider creating new columns like CREATE TABLE
16879 : * does. However, that is widely unpopular --- in the common use case of
16880 : * partitioned tables it's a foot-gun.
16881 : *
16882 : * The data type must match exactly. If the parent column is NOT NULL then
16883 : * the child must be as well. Defaults are not compared, however.
16884 : */
16885 : static void
16886 2506 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16887 : {
16888 : Relation attrrel;
16889 : TupleDesc parent_desc;
16890 :
16891 2506 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16892 2506 : parent_desc = RelationGetDescr(parent_rel);
16893 :
16894 8064 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16895 : {
16896 5684 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16897 5684 : char *parent_attname = NameStr(parent_att->attname);
16898 : HeapTuple tuple;
16899 :
16900 : /* Ignore dropped columns in the parent. */
16901 5684 : if (parent_att->attisdropped)
16902 296 : continue;
16903 :
16904 : /* Find same column in child (matching on column name). */
16905 5388 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16906 5388 : if (HeapTupleIsValid(tuple))
16907 : {
16908 5376 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16909 :
16910 5376 : if (parent_att->atttypid != child_att->atttypid ||
16911 5370 : parent_att->atttypmod != child_att->atttypmod)
16912 12 : ereport(ERROR,
16913 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16914 : errmsg("child table \"%s\" has different type for column \"%s\"",
16915 : RelationGetRelationName(child_rel), parent_attname)));
16916 :
16917 5364 : if (parent_att->attcollation != child_att->attcollation)
16918 6 : ereport(ERROR,
16919 : (errcode(ERRCODE_COLLATION_MISMATCH),
16920 : errmsg("child table \"%s\" has different collation for column \"%s\"",
16921 : RelationGetRelationName(child_rel), parent_attname)));
16922 :
16923 : /*
16924 : * If the parent has a not-null constraint that's not NO INHERIT,
16925 : * make sure the child has one too.
16926 : *
16927 : * Other constraints are checked elsewhere.
16928 : */
16929 5358 : if (parent_att->attnotnull && !child_att->attnotnull)
16930 : {
16931 : HeapTuple contup;
16932 :
16933 42 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16934 42 : parent_att->attnum);
16935 42 : if (HeapTupleIsValid(contup) &&
16936 42 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16937 24 : ereport(ERROR,
16938 : errcode(ERRCODE_DATATYPE_MISMATCH),
16939 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16940 : parent_attname, RelationGetRelationName(child_rel)));
16941 : }
16942 :
16943 : /*
16944 : * Child column must be generated if and only if parent column is.
16945 : */
16946 5334 : if (parent_att->attgenerated && !child_att->attgenerated)
16947 36 : ereport(ERROR,
16948 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16949 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16950 5298 : if (child_att->attgenerated && !parent_att->attgenerated)
16951 24 : ereport(ERROR,
16952 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16953 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16954 :
16955 5274 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
16956 12 : ereport(ERROR,
16957 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16958 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
16959 : errdetail("Parent column is %s, child column is %s.",
16960 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
16961 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
16962 :
16963 : /*
16964 : * Regular inheritance children are independent enough not to
16965 : * inherit identity columns. But partitions are integral part of
16966 : * a partitioned table and inherit identity column.
16967 : */
16968 5262 : if (ispartition)
16969 4602 : child_att->attidentity = parent_att->attidentity;
16970 :
16971 : /*
16972 : * OK, bump the child column's inheritance count. (If we fail
16973 : * later on, this change will just roll back.)
16974 : */
16975 5262 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
16976 : &child_att->attinhcount))
16977 0 : ereport(ERROR,
16978 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16979 : errmsg("too many inheritance parents"));
16980 :
16981 : /*
16982 : * In case of partitions, we must enforce that value of attislocal
16983 : * is same in all partitions. (Note: there are only inherited
16984 : * attributes in partitions)
16985 : */
16986 5262 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16987 : {
16988 : Assert(child_att->attinhcount == 1);
16989 4602 : child_att->attislocal = false;
16990 : }
16991 :
16992 5262 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16993 5262 : heap_freetuple(tuple);
16994 : }
16995 : else
16996 : {
16997 12 : ereport(ERROR,
16998 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16999 : errmsg("child table is missing column \"%s\"", parent_attname)));
17000 : }
17001 : }
17002 :
17003 2380 : table_close(attrrel, RowExclusiveLock);
17004 2380 : }
17005 :
17006 : /*
17007 : * Check constraints in child table match up with constraints in parent,
17008 : * and increment their coninhcount.
17009 : *
17010 : * Constraints that are marked ONLY in the parent are ignored.
17011 : *
17012 : * Called by CreateInheritance
17013 : *
17014 : * Currently all constraints in parent must be present in the child. One day we
17015 : * may consider adding new constraints like CREATE TABLE does.
17016 : *
17017 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
17018 : * constraints. As long as tables have more like 10 constraints it shouldn't be
17019 : * a problem though. Even 100 constraints ought not be the end of the world.
17020 : *
17021 : * XXX See MergeWithExistingConstraint too if you change this code.
17022 : */
17023 : static void
17024 2380 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
17025 : {
17026 : Relation constraintrel;
17027 : SysScanDesc parent_scan;
17028 : ScanKeyData parent_key;
17029 : HeapTuple parent_tuple;
17030 2380 : Oid parent_relid = RelationGetRelid(parent_rel);
17031 : AttrMap *attmap;
17032 :
17033 2380 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17034 :
17035 : /* Outer loop scans through the parent's constraint definitions */
17036 2380 : ScanKeyInit(&parent_key,
17037 : Anum_pg_constraint_conrelid,
17038 : BTEqualStrategyNumber, F_OIDEQ,
17039 : ObjectIdGetDatum(parent_relid));
17040 2380 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17041 : true, NULL, 1, &parent_key);
17042 :
17043 2380 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17044 : RelationGetDescr(child_rel),
17045 : true);
17046 :
17047 4154 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17048 : {
17049 1822 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17050 : SysScanDesc child_scan;
17051 : ScanKeyData child_key;
17052 : HeapTuple child_tuple;
17053 : AttrNumber parent_attno;
17054 1822 : bool found = false;
17055 :
17056 1822 : if (parent_con->contype != CONSTRAINT_CHECK &&
17057 1686 : parent_con->contype != CONSTRAINT_NOTNULL)
17058 880 : continue;
17059 :
17060 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17061 986 : if (parent_con->connoinherit)
17062 44 : continue;
17063 :
17064 942 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17065 826 : parent_attno = extractNotNullColumn(parent_tuple);
17066 : else
17067 116 : parent_attno = InvalidAttrNumber;
17068 :
17069 : /* Search for a child constraint matching this one */
17070 942 : ScanKeyInit(&child_key,
17071 : Anum_pg_constraint_conrelid,
17072 : BTEqualStrategyNumber, F_OIDEQ,
17073 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17074 942 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17075 : true, NULL, 1, &child_key);
17076 :
17077 1592 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17078 : {
17079 1568 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17080 : HeapTuple child_copy;
17081 :
17082 1568 : if (child_con->contype != parent_con->contype)
17083 352 : continue;
17084 :
17085 : /*
17086 : * CHECK constraint are matched by constraint name, NOT NULL ones
17087 : * by attribute number.
17088 : */
17089 1216 : if (child_con->contype == CONSTRAINT_CHECK)
17090 : {
17091 122 : if (strcmp(NameStr(parent_con->conname),
17092 122 : NameStr(child_con->conname)) != 0)
17093 30 : continue;
17094 : }
17095 1094 : else if (child_con->contype == CONSTRAINT_NOTNULL)
17096 : {
17097 : Form_pg_attribute parent_attr;
17098 : Form_pg_attribute child_attr;
17099 : AttrNumber child_attno;
17100 :
17101 1094 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17102 1094 : child_attno = extractNotNullColumn(child_tuple);
17103 1094 : if (parent_attno != attmap->attnums[child_attno - 1])
17104 268 : continue;
17105 :
17106 826 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17107 : /* there shouldn't be constraints on dropped columns */
17108 826 : if (parent_attr->attisdropped || child_attr->attisdropped)
17109 0 : elog(ERROR, "found not-null constraint on dropped columns");
17110 : }
17111 :
17112 918 : if (child_con->contype == CONSTRAINT_CHECK &&
17113 92 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17114 6 : ereport(ERROR,
17115 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17116 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17117 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17118 :
17119 : /*
17120 : * If the child constraint is "no inherit" then cannot merge
17121 : */
17122 912 : if (child_con->connoinherit)
17123 12 : ereport(ERROR,
17124 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17125 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17126 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17127 :
17128 : /*
17129 : * If the child constraint is "not valid" then cannot merge with a
17130 : * valid parent constraint
17131 : */
17132 900 : if (parent_con->convalidated && child_con->conenforced &&
17133 888 : !child_con->convalidated)
17134 0 : ereport(ERROR,
17135 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17136 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17137 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17138 :
17139 : /*
17140 : * A non-enforced child constraint cannot be merged with an
17141 : * enforced parent constraint. However, the reverse is allowed,
17142 : * where the child constraint is enforced.
17143 : */
17144 900 : if (parent_con->conenforced && !child_con->conenforced)
17145 6 : ereport(ERROR,
17146 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17147 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17148 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17149 :
17150 : /*
17151 : * OK, bump the child constraint's inheritance count. (If we fail
17152 : * later on, this change will just roll back.)
17153 : */
17154 894 : child_copy = heap_copytuple(child_tuple);
17155 894 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17156 :
17157 894 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
17158 : &child_con->coninhcount))
17159 0 : ereport(ERROR,
17160 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17161 : errmsg("too many inheritance parents"));
17162 :
17163 : /*
17164 : * In case of partitions, an inherited constraint must be
17165 : * inherited only once since it cannot have multiple parents and
17166 : * it is never considered local.
17167 : */
17168 894 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17169 : {
17170 : Assert(child_con->coninhcount == 1);
17171 760 : child_con->conislocal = false;
17172 : }
17173 :
17174 894 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17175 894 : heap_freetuple(child_copy);
17176 :
17177 894 : found = true;
17178 894 : break;
17179 : }
17180 :
17181 918 : systable_endscan(child_scan);
17182 :
17183 918 : if (!found)
17184 : {
17185 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17186 0 : ereport(ERROR,
17187 : errcode(ERRCODE_DATATYPE_MISMATCH),
17188 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17189 : get_attname(parent_relid,
17190 : extractNotNullColumn(parent_tuple),
17191 : false),
17192 : RelationGetRelationName(child_rel)));
17193 :
17194 24 : ereport(ERROR,
17195 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17196 : errmsg("child table is missing constraint \"%s\"",
17197 : NameStr(parent_con->conname))));
17198 : }
17199 : }
17200 :
17201 2332 : systable_endscan(parent_scan);
17202 2332 : table_close(constraintrel, RowExclusiveLock);
17203 2332 : }
17204 :
17205 : /*
17206 : * ALTER TABLE NO INHERIT
17207 : *
17208 : * Return value is the address of the relation that is no longer parent.
17209 : */
17210 : static ObjectAddress
17211 86 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
17212 : {
17213 : ObjectAddress address;
17214 : Relation parent_rel;
17215 :
17216 86 : if (rel->rd_rel->relispartition)
17217 0 : ereport(ERROR,
17218 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17219 : errmsg("cannot change inheritance of a partition")));
17220 :
17221 : /*
17222 : * AccessShareLock on the parent is probably enough, seeing that DROP
17223 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
17224 : * be inspecting the parent's schema.
17225 : */
17226 86 : parent_rel = table_openrv(parent, AccessShareLock);
17227 :
17228 : /*
17229 : * We don't bother to check ownership of the parent table --- ownership of
17230 : * the child is presumed enough rights.
17231 : */
17232 :
17233 : /* Off to RemoveInheritance() where most of the work happens */
17234 86 : RemoveInheritance(rel, parent_rel, false);
17235 :
17236 80 : ObjectAddressSet(address, RelationRelationId,
17237 : RelationGetRelid(parent_rel));
17238 :
17239 : /* keep our lock on the parent relation until commit */
17240 80 : table_close(parent_rel, NoLock);
17241 :
17242 80 : return address;
17243 : }
17244 :
17245 : /*
17246 : * MarkInheritDetached
17247 : *
17248 : * Set inhdetachpending for a partition, for ATExecDetachPartition
17249 : * in concurrent mode. While at it, verify that no other partition is
17250 : * already pending detach.
17251 : */
17252 : static void
17253 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
17254 : {
17255 : Relation catalogRelation;
17256 : SysScanDesc scan;
17257 : ScanKeyData key;
17258 : HeapTuple inheritsTuple;
17259 146 : bool found = false;
17260 :
17261 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17262 :
17263 : /*
17264 : * Find pg_inherits entries by inhparent. (We need to scan them all in
17265 : * order to verify that no other partition is pending detach.)
17266 : */
17267 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17268 146 : ScanKeyInit(&key,
17269 : Anum_pg_inherits_inhparent,
17270 : BTEqualStrategyNumber, F_OIDEQ,
17271 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17272 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17273 : true, NULL, 1, &key);
17274 :
17275 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17276 : {
17277 : Form_pg_inherits inhForm;
17278 :
17279 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17280 286 : if (inhForm->inhdetachpending)
17281 2 : ereport(ERROR,
17282 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17283 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17284 : get_rel_name(inhForm->inhrelid),
17285 : get_namespace_name(parent_rel->rd_rel->relnamespace),
17286 : RelationGetRelationName(parent_rel)),
17287 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17288 :
17289 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
17290 : {
17291 : HeapTuple newtup;
17292 :
17293 144 : newtup = heap_copytuple(inheritsTuple);
17294 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17295 :
17296 144 : CatalogTupleUpdate(catalogRelation,
17297 : &inheritsTuple->t_self,
17298 : newtup);
17299 144 : found = true;
17300 144 : heap_freetuple(newtup);
17301 : /* keep looking, to ensure we catch others pending detach */
17302 : }
17303 : }
17304 :
17305 : /* Done */
17306 144 : systable_endscan(scan);
17307 144 : table_close(catalogRelation, RowExclusiveLock);
17308 :
17309 144 : if (!found)
17310 0 : ereport(ERROR,
17311 : (errcode(ERRCODE_UNDEFINED_TABLE),
17312 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17313 : RelationGetRelationName(child_rel),
17314 : RelationGetRelationName(parent_rel))));
17315 144 : }
17316 :
17317 : /*
17318 : * RemoveInheritance
17319 : *
17320 : * Drop a parent from the child's parents. This just adjusts the attinhcount
17321 : * and attislocal of the columns and removes the pg_inherit and pg_depend
17322 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17323 : *
17324 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17325 : * up attislocal stays true, which means if a child is ever removed from a
17326 : * parent then its columns will never be automatically dropped which may
17327 : * surprise. But at least we'll never surprise by dropping columns someone
17328 : * isn't expecting to be dropped which would actually mean data loss.
17329 : *
17330 : * coninhcount and conislocal for inherited constraints are adjusted in
17331 : * exactly the same way.
17332 : *
17333 : * Common to ATExecDropInherit() and ATExecDetachPartition().
17334 : */
17335 : static void
17336 578 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17337 : {
17338 : Relation catalogRelation;
17339 : SysScanDesc scan;
17340 : ScanKeyData key[3];
17341 : HeapTuple attributeTuple,
17342 : constraintTuple;
17343 : AttrMap *attmap;
17344 : List *connames;
17345 : List *nncolumns;
17346 : bool found;
17347 : bool is_partitioning;
17348 :
17349 578 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17350 :
17351 578 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17352 : RelationGetRelid(parent_rel),
17353 : expect_detached,
17354 578 : RelationGetRelationName(child_rel));
17355 578 : if (!found)
17356 : {
17357 24 : if (is_partitioning)
17358 18 : ereport(ERROR,
17359 : (errcode(ERRCODE_UNDEFINED_TABLE),
17360 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17361 : RelationGetRelationName(child_rel),
17362 : RelationGetRelationName(parent_rel))));
17363 : else
17364 6 : ereport(ERROR,
17365 : (errcode(ERRCODE_UNDEFINED_TABLE),
17366 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17367 : RelationGetRelationName(parent_rel),
17368 : RelationGetRelationName(child_rel))));
17369 : }
17370 :
17371 : /*
17372 : * Search through child columns looking for ones matching parent rel
17373 : */
17374 554 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17375 554 : ScanKeyInit(&key[0],
17376 : Anum_pg_attribute_attrelid,
17377 : BTEqualStrategyNumber, F_OIDEQ,
17378 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17379 554 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17380 : true, NULL, 1, key);
17381 4946 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17382 : {
17383 4392 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17384 :
17385 : /* Ignore if dropped or not inherited */
17386 4392 : if (att->attisdropped)
17387 6 : continue;
17388 4386 : if (att->attinhcount <= 0)
17389 3354 : continue;
17390 :
17391 1032 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
17392 1032 : NameStr(att->attname)))
17393 : {
17394 : /* Decrement inhcount and possibly set islocal to true */
17395 978 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
17396 978 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17397 :
17398 978 : copy_att->attinhcount--;
17399 978 : if (copy_att->attinhcount == 0)
17400 948 : copy_att->attislocal = true;
17401 :
17402 978 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17403 978 : heap_freetuple(copyTuple);
17404 : }
17405 : }
17406 554 : systable_endscan(scan);
17407 554 : table_close(catalogRelation, RowExclusiveLock);
17408 :
17409 : /*
17410 : * Likewise, find inherited check and not-null constraints and disinherit
17411 : * them. To do this, we first need a list of the names of the parent's
17412 : * check constraints. (We cheat a bit by only checking for name matches,
17413 : * assuming that the expressions will match.)
17414 : *
17415 : * For NOT NULL columns, we store column numbers to match, mapping them in
17416 : * to the child rel's attribute numbers.
17417 : */
17418 554 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17419 : RelationGetDescr(parent_rel),
17420 : false);
17421 :
17422 554 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17423 554 : ScanKeyInit(&key[0],
17424 : Anum_pg_constraint_conrelid,
17425 : BTEqualStrategyNumber, F_OIDEQ,
17426 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17427 554 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17428 : true, NULL, 1, key);
17429 :
17430 554 : connames = NIL;
17431 554 : nncolumns = NIL;
17432 :
17433 1082 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17434 : {
17435 528 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17436 :
17437 528 : if (con->connoinherit)
17438 98 : continue;
17439 :
17440 430 : if (con->contype == CONSTRAINT_CHECK)
17441 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
17442 430 : if (con->contype == CONSTRAINT_NOTNULL)
17443 : {
17444 196 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17445 :
17446 196 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
17447 : }
17448 : }
17449 :
17450 554 : systable_endscan(scan);
17451 :
17452 : /* Now scan the child's constraints to find matches */
17453 554 : ScanKeyInit(&key[0],
17454 : Anum_pg_constraint_conrelid,
17455 : BTEqualStrategyNumber, F_OIDEQ,
17456 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17457 554 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17458 : true, NULL, 1, key);
17459 :
17460 1272 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17461 : {
17462 718 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17463 718 : bool match = false;
17464 :
17465 : /*
17466 : * Match CHECK constraints by name, not-null constraints by column
17467 : * number, and ignore all others.
17468 : */
17469 718 : if (con->contype == CONSTRAINT_CHECK)
17470 : {
17471 350 : foreach_ptr(char, chkname, connames)
17472 : {
17473 18 : if (con->contype == CONSTRAINT_CHECK &&
17474 18 : strcmp(NameStr(con->conname), chkname) == 0)
17475 : {
17476 12 : match = true;
17477 12 : connames = foreach_delete_current(connames, chkname);
17478 12 : break;
17479 : }
17480 : }
17481 : }
17482 546 : else if (con->contype == CONSTRAINT_NOTNULL)
17483 : {
17484 256 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
17485 :
17486 518 : foreach_int(prevattno, nncolumns)
17487 : {
17488 202 : if (prevattno == child_attno)
17489 : {
17490 196 : match = true;
17491 196 : nncolumns = foreach_delete_current(nncolumns, prevattno);
17492 196 : break;
17493 : }
17494 : }
17495 : }
17496 : else
17497 290 : continue;
17498 :
17499 428 : if (match)
17500 : {
17501 : /* Decrement inhcount and possibly set islocal to true */
17502 208 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
17503 208 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17504 :
17505 208 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
17506 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17507 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
17508 :
17509 208 : copy_con->coninhcount--;
17510 208 : if (copy_con->coninhcount == 0)
17511 190 : copy_con->conislocal = true;
17512 :
17513 208 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17514 208 : heap_freetuple(copyTuple);
17515 : }
17516 : }
17517 :
17518 : /* We should have matched all constraints */
17519 554 : if (connames != NIL || nncolumns != NIL)
17520 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
17521 : list_length(connames) + list_length(nncolumns),
17522 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
17523 :
17524 554 : systable_endscan(scan);
17525 554 : table_close(catalogRelation, RowExclusiveLock);
17526 :
17527 554 : drop_parent_dependency(RelationGetRelid(child_rel),
17528 : RelationRelationId,
17529 : RelationGetRelid(parent_rel),
17530 : child_dependency_type(is_partitioning));
17531 :
17532 : /*
17533 : * Post alter hook of this inherits. Since object_access_hook doesn't take
17534 : * multiple object identifiers, we relay oid of parent relation using
17535 : * auxiliary_id argument.
17536 : */
17537 554 : InvokeObjectPostAlterHookArg(InheritsRelationId,
17538 : RelationGetRelid(child_rel), 0,
17539 : RelationGetRelid(parent_rel), false);
17540 554 : }
17541 :
17542 : /*
17543 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17544 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17545 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17546 : * be TypeRelationId). There's no convenient way to do this, so go trawling
17547 : * through pg_depend.
17548 : */
17549 : static void
17550 566 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
17551 : DependencyType deptype)
17552 : {
17553 : Relation catalogRelation;
17554 : SysScanDesc scan;
17555 : ScanKeyData key[3];
17556 : HeapTuple depTuple;
17557 :
17558 566 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17559 :
17560 566 : ScanKeyInit(&key[0],
17561 : Anum_pg_depend_classid,
17562 : BTEqualStrategyNumber, F_OIDEQ,
17563 : ObjectIdGetDatum(RelationRelationId));
17564 566 : ScanKeyInit(&key[1],
17565 : Anum_pg_depend_objid,
17566 : BTEqualStrategyNumber, F_OIDEQ,
17567 : ObjectIdGetDatum(relid));
17568 566 : ScanKeyInit(&key[2],
17569 : Anum_pg_depend_objsubid,
17570 : BTEqualStrategyNumber, F_INT4EQ,
17571 : Int32GetDatum(0));
17572 :
17573 566 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17574 : NULL, 3, key);
17575 :
17576 1758 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17577 : {
17578 1192 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17579 :
17580 1192 : if (dep->refclassid == refclassid &&
17581 608 : dep->refobjid == refobjid &&
17582 566 : dep->refobjsubid == 0 &&
17583 566 : dep->deptype == deptype)
17584 566 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17585 : }
17586 :
17587 566 : systable_endscan(scan);
17588 566 : table_close(catalogRelation, RowExclusiveLock);
17589 566 : }
17590 :
17591 : /*
17592 : * ALTER TABLE OF
17593 : *
17594 : * Attach a table to a composite type, as though it had been created with CREATE
17595 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17596 : * subject table must not have inheritance parents. These restrictions ensure
17597 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17598 : *
17599 : * The address of the type is returned.
17600 : */
17601 : static ObjectAddress
17602 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17603 : {
17604 66 : Oid relid = RelationGetRelid(rel);
17605 : Type typetuple;
17606 : Form_pg_type typeform;
17607 : Oid typeid;
17608 : Relation inheritsRelation,
17609 : relationRelation;
17610 : SysScanDesc scan;
17611 : ScanKeyData key;
17612 : AttrNumber table_attno,
17613 : type_attno;
17614 : TupleDesc typeTupleDesc,
17615 : tableTupleDesc;
17616 : ObjectAddress tableobj,
17617 : typeobj;
17618 : HeapTuple classtuple;
17619 :
17620 : /* Validate the type. */
17621 66 : typetuple = typenameType(NULL, ofTypename, NULL);
17622 66 : check_of_type(typetuple);
17623 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
17624 66 : typeid = typeform->oid;
17625 :
17626 : /* Fail if the table has any inheritance parents. */
17627 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17628 66 : ScanKeyInit(&key,
17629 : Anum_pg_inherits_inhrelid,
17630 : BTEqualStrategyNumber, F_OIDEQ,
17631 : ObjectIdGetDatum(relid));
17632 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17633 : true, NULL, 1, &key);
17634 66 : if (HeapTupleIsValid(systable_getnext(scan)))
17635 6 : ereport(ERROR,
17636 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17637 : errmsg("typed tables cannot inherit")));
17638 60 : systable_endscan(scan);
17639 60 : table_close(inheritsRelation, AccessShareLock);
17640 :
17641 : /*
17642 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
17643 : * require that the order also match. However, attnotnull need not match.
17644 : */
17645 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17646 60 : tableTupleDesc = RelationGetDescr(rel);
17647 60 : table_attno = 1;
17648 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17649 : {
17650 : Form_pg_attribute type_attr,
17651 : table_attr;
17652 : const char *type_attname,
17653 : *table_attname;
17654 :
17655 : /* Get the next non-dropped type attribute. */
17656 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17657 154 : if (type_attr->attisdropped)
17658 44 : continue;
17659 110 : type_attname = NameStr(type_attr->attname);
17660 :
17661 : /* Get the next non-dropped table attribute. */
17662 : do
17663 : {
17664 122 : if (table_attno > tableTupleDesc->natts)
17665 6 : ereport(ERROR,
17666 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17667 : errmsg("table is missing column \"%s\"",
17668 : type_attname)));
17669 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17670 116 : table_attno++;
17671 116 : } while (table_attr->attisdropped);
17672 104 : table_attname = NameStr(table_attr->attname);
17673 :
17674 : /* Compare name. */
17675 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17676 6 : ereport(ERROR,
17677 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17678 : errmsg("table has column \"%s\" where type requires \"%s\"",
17679 : table_attname, type_attname)));
17680 :
17681 : /* Compare type. */
17682 98 : if (table_attr->atttypid != type_attr->atttypid ||
17683 92 : table_attr->atttypmod != type_attr->atttypmod ||
17684 86 : table_attr->attcollation != type_attr->attcollation)
17685 12 : ereport(ERROR,
17686 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17687 : errmsg("table \"%s\" has different type for column \"%s\"",
17688 : RelationGetRelationName(rel), type_attname)));
17689 : }
17690 36 : ReleaseTupleDesc(typeTupleDesc);
17691 :
17692 : /* Any remaining columns at the end of the table had better be dropped. */
17693 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
17694 : {
17695 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17696 : table_attno - 1);
17697 :
17698 6 : if (!table_attr->attisdropped)
17699 6 : ereport(ERROR,
17700 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17701 : errmsg("table has extra column \"%s\"",
17702 : NameStr(table_attr->attname))));
17703 : }
17704 :
17705 : /* If the table was already typed, drop the existing dependency. */
17706 30 : if (rel->rd_rel->reloftype)
17707 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17708 : DEPENDENCY_NORMAL);
17709 :
17710 : /* Record a dependency on the new type. */
17711 30 : tableobj.classId = RelationRelationId;
17712 30 : tableobj.objectId = relid;
17713 30 : tableobj.objectSubId = 0;
17714 30 : typeobj.classId = TypeRelationId;
17715 30 : typeobj.objectId = typeid;
17716 30 : typeobj.objectSubId = 0;
17717 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17718 :
17719 : /* Update pg_class.reloftype */
17720 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17721 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17722 30 : if (!HeapTupleIsValid(classtuple))
17723 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17724 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17725 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17726 :
17727 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17728 :
17729 30 : heap_freetuple(classtuple);
17730 30 : table_close(relationRelation, RowExclusiveLock);
17731 :
17732 30 : ReleaseSysCache(typetuple);
17733 :
17734 30 : return typeobj;
17735 : }
17736 :
17737 : /*
17738 : * ALTER TABLE NOT OF
17739 : *
17740 : * Detach a typed table from its originating type. Just clear reloftype and
17741 : * remove the dependency.
17742 : */
17743 : static void
17744 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
17745 : {
17746 6 : Oid relid = RelationGetRelid(rel);
17747 : Relation relationRelation;
17748 : HeapTuple tuple;
17749 :
17750 6 : if (!OidIsValid(rel->rd_rel->reloftype))
17751 0 : ereport(ERROR,
17752 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17753 : errmsg("\"%s\" is not a typed table",
17754 : RelationGetRelationName(rel))));
17755 :
17756 : /*
17757 : * We don't bother to check ownership of the type --- ownership of the
17758 : * table is presumed enough rights. No lock required on the type, either.
17759 : */
17760 :
17761 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17762 : DEPENDENCY_NORMAL);
17763 :
17764 : /* Clear pg_class.reloftype */
17765 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17766 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17767 6 : if (!HeapTupleIsValid(tuple))
17768 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17769 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17770 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17771 :
17772 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17773 :
17774 6 : heap_freetuple(tuple);
17775 6 : table_close(relationRelation, RowExclusiveLock);
17776 6 : }
17777 :
17778 : /*
17779 : * relation_mark_replica_identity: Update a table's replica identity
17780 : *
17781 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17782 : * index. Otherwise, it must be InvalidOid.
17783 : *
17784 : * Caller had better hold an exclusive lock on the relation, as the results
17785 : * of running two of these concurrently wouldn't be pretty.
17786 : */
17787 : static void
17788 460 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17789 : bool is_internal)
17790 : {
17791 : Relation pg_index;
17792 : Relation pg_class;
17793 : HeapTuple pg_class_tuple;
17794 : HeapTuple pg_index_tuple;
17795 : Form_pg_class pg_class_form;
17796 : Form_pg_index pg_index_form;
17797 : ListCell *index;
17798 :
17799 : /*
17800 : * Check whether relreplident has changed, and update it if so.
17801 : */
17802 460 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17803 460 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
17804 : ObjectIdGetDatum(RelationGetRelid(rel)));
17805 460 : if (!HeapTupleIsValid(pg_class_tuple))
17806 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
17807 : RelationGetRelationName(rel));
17808 460 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17809 460 : if (pg_class_form->relreplident != ri_type)
17810 : {
17811 410 : pg_class_form->relreplident = ri_type;
17812 410 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17813 : }
17814 460 : table_close(pg_class, RowExclusiveLock);
17815 460 : heap_freetuple(pg_class_tuple);
17816 :
17817 : /*
17818 : * Update the per-index indisreplident flags correctly.
17819 : */
17820 460 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
17821 1180 : foreach(index, RelationGetIndexList(rel))
17822 : {
17823 720 : Oid thisIndexOid = lfirst_oid(index);
17824 720 : bool dirty = false;
17825 :
17826 720 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17827 : ObjectIdGetDatum(thisIndexOid));
17828 720 : if (!HeapTupleIsValid(pg_index_tuple))
17829 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17830 720 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17831 :
17832 720 : if (thisIndexOid == indexOid)
17833 : {
17834 : /* Set the bit if not already set. */
17835 240 : if (!pg_index_form->indisreplident)
17836 : {
17837 222 : dirty = true;
17838 222 : pg_index_form->indisreplident = true;
17839 : }
17840 : }
17841 : else
17842 : {
17843 : /* Unset the bit if set. */
17844 480 : if (pg_index_form->indisreplident)
17845 : {
17846 52 : dirty = true;
17847 52 : pg_index_form->indisreplident = false;
17848 : }
17849 : }
17850 :
17851 720 : if (dirty)
17852 : {
17853 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17854 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17855 : InvalidOid, is_internal);
17856 :
17857 : /*
17858 : * Invalidate the relcache for the table, so that after we commit
17859 : * all sessions will refresh the table's replica identity index
17860 : * before attempting any UPDATE or DELETE on the table. (If we
17861 : * changed the table's pg_class row above, then a relcache inval
17862 : * is already queued due to that; but we might not have.)
17863 : */
17864 274 : CacheInvalidateRelcache(rel);
17865 : }
17866 720 : heap_freetuple(pg_index_tuple);
17867 : }
17868 :
17869 460 : table_close(pg_index, RowExclusiveLock);
17870 460 : }
17871 :
17872 : /*
17873 : * ALTER TABLE <name> REPLICA IDENTITY ...
17874 : */
17875 : static void
17876 508 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17877 : {
17878 : Oid indexOid;
17879 : Relation indexRel;
17880 : int key;
17881 :
17882 508 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17883 : {
17884 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17885 6 : return;
17886 : }
17887 502 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17888 : {
17889 166 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17890 166 : return;
17891 : }
17892 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17893 : {
17894 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17895 48 : return;
17896 : }
17897 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17898 : {
17899 : /* fallthrough */ ;
17900 : }
17901 : else
17902 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17903 :
17904 : /* Check that the index exists */
17905 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17906 288 : if (!OidIsValid(indexOid))
17907 0 : ereport(ERROR,
17908 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17909 : errmsg("index \"%s\" for table \"%s\" does not exist",
17910 : stmt->name, RelationGetRelationName(rel))));
17911 :
17912 288 : indexRel = index_open(indexOid, ShareLock);
17913 :
17914 : /* Check that the index is on the relation we're altering. */
17915 288 : if (indexRel->rd_index == NULL ||
17916 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
17917 6 : ereport(ERROR,
17918 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17919 : errmsg("\"%s\" is not an index for table \"%s\"",
17920 : RelationGetRelationName(indexRel),
17921 : RelationGetRelationName(rel))));
17922 :
17923 : /*
17924 : * The AM must support uniqueness, and the index must in fact be unique.
17925 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
17926 : * exclusion), we can use that too.
17927 : */
17928 282 : if ((!indexRel->rd_indam->amcanunique ||
17929 262 : !indexRel->rd_index->indisunique) &&
17930 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
17931 12 : ereport(ERROR,
17932 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17933 : errmsg("cannot use non-unique index \"%s\" as replica identity",
17934 : RelationGetRelationName(indexRel))));
17935 : /* Deferred indexes are not guaranteed to be always unique. */
17936 270 : if (!indexRel->rd_index->indimmediate)
17937 12 : ereport(ERROR,
17938 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17939 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
17940 : RelationGetRelationName(indexRel))));
17941 : /* Expression indexes aren't supported. */
17942 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
17943 6 : ereport(ERROR,
17944 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17945 : errmsg("cannot use expression index \"%s\" as replica identity",
17946 : RelationGetRelationName(indexRel))));
17947 : /* Predicate indexes aren't supported. */
17948 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
17949 6 : ereport(ERROR,
17950 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17951 : errmsg("cannot use partial index \"%s\" as replica identity",
17952 : RelationGetRelationName(indexRel))));
17953 :
17954 : /* Check index for nullable columns. */
17955 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17956 : {
17957 312 : int16 attno = indexRel->rd_index->indkey.values[key];
17958 : Form_pg_attribute attr;
17959 :
17960 : /*
17961 : * Reject any other system columns. (Going forward, we'll disallow
17962 : * indexes containing such columns in the first place, but they might
17963 : * exist in older branches.)
17964 : */
17965 312 : if (attno <= 0)
17966 0 : ereport(ERROR,
17967 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17968 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17969 : RelationGetRelationName(indexRel), attno)));
17970 :
17971 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
17972 312 : if (!attr->attnotnull)
17973 6 : ereport(ERROR,
17974 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17975 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17976 : RelationGetRelationName(indexRel),
17977 : NameStr(attr->attname))));
17978 : }
17979 :
17980 : /* This index is suitable for use as a replica identity. Mark it. */
17981 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17982 :
17983 240 : index_close(indexRel, NoLock);
17984 : }
17985 :
17986 : /*
17987 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17988 : */
17989 : static void
17990 300 : ATExecSetRowSecurity(Relation rel, bool rls)
17991 : {
17992 : Relation pg_class;
17993 : Oid relid;
17994 : HeapTuple tuple;
17995 :
17996 300 : relid = RelationGetRelid(rel);
17997 :
17998 : /* Pull the record for this relation and update it */
17999 300 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18000 :
18001 300 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18002 :
18003 300 : if (!HeapTupleIsValid(tuple))
18004 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18005 :
18006 300 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18007 300 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18008 :
18009 300 : InvokeObjectPostAlterHook(RelationRelationId,
18010 : RelationGetRelid(rel), 0);
18011 :
18012 300 : table_close(pg_class, RowExclusiveLock);
18013 300 : heap_freetuple(tuple);
18014 300 : }
18015 :
18016 : /*
18017 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18018 : */
18019 : static void
18020 126 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
18021 : {
18022 : Relation pg_class;
18023 : Oid relid;
18024 : HeapTuple tuple;
18025 :
18026 126 : relid = RelationGetRelid(rel);
18027 :
18028 126 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18029 :
18030 126 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18031 :
18032 126 : if (!HeapTupleIsValid(tuple))
18033 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18034 :
18035 126 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18036 126 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18037 :
18038 126 : InvokeObjectPostAlterHook(RelationRelationId,
18039 : RelationGetRelid(rel), 0);
18040 :
18041 126 : table_close(pg_class, RowExclusiveLock);
18042 126 : heap_freetuple(tuple);
18043 126 : }
18044 :
18045 : /*
18046 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
18047 : */
18048 : static void
18049 58 : ATExecGenericOptions(Relation rel, List *options)
18050 : {
18051 : Relation ftrel;
18052 : ForeignServer *server;
18053 : ForeignDataWrapper *fdw;
18054 : HeapTuple tuple;
18055 : bool isnull;
18056 : Datum repl_val[Natts_pg_foreign_table];
18057 : bool repl_null[Natts_pg_foreign_table];
18058 : bool repl_repl[Natts_pg_foreign_table];
18059 : Datum datum;
18060 : Form_pg_foreign_table tableform;
18061 :
18062 58 : if (options == NIL)
18063 0 : return;
18064 :
18065 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18066 :
18067 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18068 : ObjectIdGetDatum(rel->rd_id));
18069 58 : if (!HeapTupleIsValid(tuple))
18070 0 : ereport(ERROR,
18071 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18072 : errmsg("foreign table \"%s\" does not exist",
18073 : RelationGetRelationName(rel))));
18074 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18075 58 : server = GetForeignServer(tableform->ftserver);
18076 58 : fdw = GetForeignDataWrapper(server->fdwid);
18077 :
18078 58 : memset(repl_val, 0, sizeof(repl_val));
18079 58 : memset(repl_null, false, sizeof(repl_null));
18080 58 : memset(repl_repl, false, sizeof(repl_repl));
18081 :
18082 : /* Extract the current options */
18083 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
18084 : tuple,
18085 : Anum_pg_foreign_table_ftoptions,
18086 : &isnull);
18087 58 : if (isnull)
18088 4 : datum = PointerGetDatum(NULL);
18089 :
18090 : /* Transform the options */
18091 58 : datum = transformGenericOptions(ForeignTableRelationId,
18092 : datum,
18093 : options,
18094 : fdw->fdwvalidator);
18095 :
18096 56 : if (PointerIsValid(DatumGetPointer(datum)))
18097 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18098 : else
18099 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18100 :
18101 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18102 :
18103 : /* Everything looks good - update the tuple */
18104 :
18105 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18106 : repl_val, repl_null, repl_repl);
18107 :
18108 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18109 :
18110 : /*
18111 : * Invalidate relcache so that all sessions will refresh any cached plans
18112 : * that might depend on the old options.
18113 : */
18114 56 : CacheInvalidateRelcache(rel);
18115 :
18116 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
18117 : RelationGetRelid(rel), 0);
18118 :
18119 56 : table_close(ftrel, RowExclusiveLock);
18120 :
18121 56 : heap_freetuple(tuple);
18122 : }
18123 :
18124 : /*
18125 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18126 : *
18127 : * Return value is the address of the modified column
18128 : */
18129 : static ObjectAddress
18130 68 : ATExecSetCompression(Relation rel,
18131 : const char *column,
18132 : Node *newValue,
18133 : LOCKMODE lockmode)
18134 : {
18135 : Relation attrel;
18136 : HeapTuple tuple;
18137 : Form_pg_attribute atttableform;
18138 : AttrNumber attnum;
18139 : char *compression;
18140 : char cmethod;
18141 : ObjectAddress address;
18142 :
18143 68 : compression = strVal(newValue);
18144 :
18145 68 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18146 :
18147 : /* copy the cache entry so we can scribble on it below */
18148 68 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18149 68 : if (!HeapTupleIsValid(tuple))
18150 0 : ereport(ERROR,
18151 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18152 : errmsg("column \"%s\" of relation \"%s\" does not exist",
18153 : column, RelationGetRelationName(rel))));
18154 :
18155 : /* prevent them from altering a system attribute */
18156 68 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18157 68 : attnum = atttableform->attnum;
18158 68 : if (attnum <= 0)
18159 0 : ereport(ERROR,
18160 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18161 : errmsg("cannot alter system column \"%s\"", column)));
18162 :
18163 : /*
18164 : * Check that column type is compressible, then get the attribute
18165 : * compression method code
18166 : */
18167 68 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18168 :
18169 : /* update pg_attribute entry */
18170 62 : atttableform->attcompression = cmethod;
18171 62 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18172 :
18173 62 : InvokeObjectPostAlterHook(RelationRelationId,
18174 : RelationGetRelid(rel),
18175 : attnum);
18176 :
18177 : /*
18178 : * Apply the change to indexes as well (only for simple index columns,
18179 : * matching behavior of index.c ConstructTupleDescriptor()).
18180 : */
18181 62 : SetIndexStorageProperties(rel, attrel, attnum,
18182 : false, 0,
18183 : true, cmethod,
18184 : lockmode);
18185 :
18186 62 : heap_freetuple(tuple);
18187 :
18188 62 : table_close(attrel, RowExclusiveLock);
18189 :
18190 : /* make changes visible */
18191 62 : CommandCounterIncrement();
18192 :
18193 62 : ObjectAddressSubSet(address, RelationRelationId,
18194 : RelationGetRelid(rel), attnum);
18195 62 : return address;
18196 : }
18197 :
18198 :
18199 : /*
18200 : * Preparation phase for SET LOGGED/UNLOGGED
18201 : *
18202 : * This verifies that we're not trying to change a temp table. Also,
18203 : * existing foreign key constraints are checked to avoid ending up with
18204 : * permanent tables referencing unlogged tables.
18205 : */
18206 : static void
18207 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
18208 : {
18209 : Relation pg_constraint;
18210 : HeapTuple tuple;
18211 : SysScanDesc scan;
18212 : ScanKeyData skey[1];
18213 :
18214 : /*
18215 : * Disallow changing status for a temp table. Also verify whether we can
18216 : * get away with doing nothing; in such cases we don't need to run the
18217 : * checks below, either.
18218 : */
18219 100 : switch (rel->rd_rel->relpersistence)
18220 : {
18221 0 : case RELPERSISTENCE_TEMP:
18222 0 : ereport(ERROR,
18223 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18224 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
18225 : RelationGetRelationName(rel)),
18226 : errtable(rel)));
18227 : break;
18228 56 : case RELPERSISTENCE_PERMANENT:
18229 56 : if (toLogged)
18230 : /* nothing to do */
18231 12 : return;
18232 50 : break;
18233 44 : case RELPERSISTENCE_UNLOGGED:
18234 44 : if (!toLogged)
18235 : /* nothing to do */
18236 6 : return;
18237 38 : break;
18238 : }
18239 :
18240 : /*
18241 : * Check that the table is not part of any publication when changing to
18242 : * UNLOGGED, as UNLOGGED tables can't be published.
18243 : */
18244 138 : if (!toLogged &&
18245 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
18246 0 : ereport(ERROR,
18247 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18248 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18249 : RelationGetRelationName(rel)),
18250 : errdetail("Unlogged relations cannot be replicated.")));
18251 :
18252 : /*
18253 : * Check existing foreign key constraints to preserve the invariant that
18254 : * permanent tables cannot reference unlogged ones. Self-referencing
18255 : * foreign keys can safely be ignored.
18256 : */
18257 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18258 :
18259 : /*
18260 : * Scan conrelid if changing to permanent, else confrelid. This also
18261 : * determines whether a useful index exists.
18262 : */
18263 88 : ScanKeyInit(&skey[0],
18264 : toLogged ? Anum_pg_constraint_conrelid :
18265 : Anum_pg_constraint_confrelid,
18266 : BTEqualStrategyNumber, F_OIDEQ,
18267 : ObjectIdGetDatum(RelationGetRelid(rel)));
18268 88 : scan = systable_beginscan(pg_constraint,
18269 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18270 : true, NULL, 1, skey);
18271 :
18272 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18273 : {
18274 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
18275 :
18276 66 : if (con->contype == CONSTRAINT_FOREIGN)
18277 : {
18278 : Oid foreignrelid;
18279 : Relation foreignrel;
18280 :
18281 : /* the opposite end of what we used as scankey */
18282 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
18283 :
18284 : /* ignore if self-referencing */
18285 30 : if (RelationGetRelid(rel) == foreignrelid)
18286 12 : continue;
18287 :
18288 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
18289 :
18290 18 : if (toLogged)
18291 : {
18292 6 : if (!RelationIsPermanent(foreignrel))
18293 6 : ereport(ERROR,
18294 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18295 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18296 : RelationGetRelationName(rel),
18297 : RelationGetRelationName(foreignrel)),
18298 : errtableconstraint(rel, NameStr(con->conname))));
18299 : }
18300 : else
18301 : {
18302 12 : if (RelationIsPermanent(foreignrel))
18303 6 : ereport(ERROR,
18304 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18305 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18306 : RelationGetRelationName(rel),
18307 : RelationGetRelationName(foreignrel)),
18308 : errtableconstraint(rel, NameStr(con->conname))));
18309 : }
18310 :
18311 6 : relation_close(foreignrel, AccessShareLock);
18312 : }
18313 : }
18314 :
18315 76 : systable_endscan(scan);
18316 :
18317 76 : table_close(pg_constraint, AccessShareLock);
18318 :
18319 : /* force rewrite if necessary; see comment in ATRewriteTables */
18320 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
18321 76 : if (toLogged)
18322 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18323 : else
18324 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18325 76 : tab->chgPersistence = true;
18326 : }
18327 :
18328 : /*
18329 : * Execute ALTER TABLE SET SCHEMA
18330 : */
18331 : ObjectAddress
18332 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
18333 : {
18334 : Relation rel;
18335 : Oid relid;
18336 : Oid oldNspOid;
18337 : Oid nspOid;
18338 : RangeVar *newrv;
18339 : ObjectAddresses *objsMoved;
18340 : ObjectAddress myself;
18341 :
18342 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18343 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18344 : RangeVarCallbackForAlterRelation,
18345 : stmt);
18346 :
18347 102 : if (!OidIsValid(relid))
18348 : {
18349 12 : ereport(NOTICE,
18350 : (errmsg("relation \"%s\" does not exist, skipping",
18351 : stmt->relation->relname)));
18352 12 : return InvalidObjectAddress;
18353 : }
18354 :
18355 90 : rel = relation_open(relid, NoLock);
18356 :
18357 90 : oldNspOid = RelationGetNamespace(rel);
18358 :
18359 : /* If it's an owned sequence, disallow moving it by itself. */
18360 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18361 : {
18362 : Oid tableId;
18363 : int32 colId;
18364 :
18365 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18366 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18367 6 : ereport(ERROR,
18368 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18369 : errmsg("cannot move an owned sequence into another schema"),
18370 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
18371 : RelationGetRelationName(rel),
18372 : get_rel_name(tableId))));
18373 : }
18374 :
18375 : /* Get and lock schema OID and check its permissions. */
18376 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18377 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18378 :
18379 : /* common checks on switching namespaces */
18380 84 : CheckSetNamespace(oldNspOid, nspOid);
18381 :
18382 84 : objsMoved = new_object_addresses();
18383 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18384 84 : free_object_addresses(objsMoved);
18385 :
18386 84 : ObjectAddressSet(myself, RelationRelationId, relid);
18387 :
18388 84 : if (oldschema)
18389 84 : *oldschema = oldNspOid;
18390 :
18391 : /* close rel, but keep lock until commit */
18392 84 : relation_close(rel, NoLock);
18393 :
18394 84 : return myself;
18395 : }
18396 :
18397 : /*
18398 : * The guts of relocating a table or materialized view to another namespace:
18399 : * besides moving the relation itself, its dependent objects are relocated to
18400 : * the new schema.
18401 : */
18402 : void
18403 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
18404 : ObjectAddresses *objsMoved)
18405 : {
18406 : Relation classRel;
18407 :
18408 : Assert(objsMoved != NULL);
18409 :
18410 : /* OK, modify the pg_class row and pg_depend entry */
18411 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
18412 :
18413 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18414 : nspOid, true, objsMoved);
18415 :
18416 : /* Fix the table's row type too, if it has one */
18417 86 : if (OidIsValid(rel->rd_rel->reltype))
18418 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18419 : false, /* isImplicitArray */
18420 : false, /* ignoreDependent */
18421 : false, /* errorOnTableType */
18422 : objsMoved);
18423 :
18424 : /* Fix other dependent stuff */
18425 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18426 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18427 : objsMoved, AccessExclusiveLock);
18428 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18429 : false, objsMoved);
18430 :
18431 86 : table_close(classRel, RowExclusiveLock);
18432 86 : }
18433 :
18434 : /*
18435 : * The guts of relocating a relation to another namespace: fix the pg_class
18436 : * entry, and the pg_depend entry if any. Caller must already have
18437 : * opened and write-locked pg_class.
18438 : */
18439 : void
18440 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
18441 : Oid oldNspOid, Oid newNspOid,
18442 : bool hasDependEntry,
18443 : ObjectAddresses *objsMoved)
18444 : {
18445 : HeapTuple classTup;
18446 : Form_pg_class classForm;
18447 : ObjectAddress thisobj;
18448 188 : bool already_done = false;
18449 :
18450 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18451 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18452 188 : if (!HeapTupleIsValid(classTup))
18453 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
18454 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
18455 :
18456 : Assert(classForm->relnamespace == oldNspOid);
18457 :
18458 188 : thisobj.classId = RelationRelationId;
18459 188 : thisobj.objectId = relOid;
18460 188 : thisobj.objectSubId = 0;
18461 :
18462 : /*
18463 : * If the object has already been moved, don't move it again. If it's
18464 : * already in the right place, don't move it, but still fire the object
18465 : * access hook.
18466 : */
18467 188 : already_done = object_address_present(&thisobj, objsMoved);
18468 188 : if (!already_done && oldNspOid != newNspOid)
18469 146 : {
18470 146 : ItemPointerData otid = classTup->t_self;
18471 :
18472 : /* check for duplicate name (more friendly than unique-index failure) */
18473 146 : if (get_relname_relid(NameStr(classForm->relname),
18474 : newNspOid) != InvalidOid)
18475 0 : ereport(ERROR,
18476 : (errcode(ERRCODE_DUPLICATE_TABLE),
18477 : errmsg("relation \"%s\" already exists in schema \"%s\"",
18478 : NameStr(classForm->relname),
18479 : get_namespace_name(newNspOid))));
18480 :
18481 : /* classTup is a copy, so OK to scribble on */
18482 146 : classForm->relnamespace = newNspOid;
18483 :
18484 146 : CatalogTupleUpdate(classRel, &otid, classTup);
18485 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
18486 :
18487 :
18488 : /* Update dependency on schema if caller said so */
18489 250 : if (hasDependEntry &&
18490 104 : changeDependencyFor(RelationRelationId,
18491 : relOid,
18492 : NamespaceRelationId,
18493 : oldNspOid,
18494 : newNspOid) != 1)
18495 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
18496 : NameStr(classForm->relname));
18497 : }
18498 : else
18499 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
18500 188 : if (!already_done)
18501 : {
18502 188 : add_exact_object_address(&thisobj, objsMoved);
18503 :
18504 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18505 : }
18506 :
18507 188 : heap_freetuple(classTup);
18508 188 : }
18509 :
18510 : /*
18511 : * Move all indexes for the specified relation to another namespace.
18512 : *
18513 : * Note: we assume adequate permission checking was done by the caller,
18514 : * and that the caller has a suitable lock on the owning relation.
18515 : */
18516 : static void
18517 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
18518 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
18519 : {
18520 : List *indexList;
18521 : ListCell *l;
18522 :
18523 86 : indexList = RelationGetIndexList(rel);
18524 :
18525 132 : foreach(l, indexList)
18526 : {
18527 46 : Oid indexOid = lfirst_oid(l);
18528 : ObjectAddress thisobj;
18529 :
18530 46 : thisobj.classId = RelationRelationId;
18531 46 : thisobj.objectId = indexOid;
18532 46 : thisobj.objectSubId = 0;
18533 :
18534 : /*
18535 : * Note: currently, the index will not have its own dependency on the
18536 : * namespace, so we don't need to do changeDependencyFor(). There's no
18537 : * row type in pg_type, either.
18538 : *
18539 : * XXX this objsMoved test may be pointless -- surely we have a single
18540 : * dependency link from a relation to each index?
18541 : */
18542 46 : if (!object_address_present(&thisobj, objsMoved))
18543 : {
18544 46 : AlterRelationNamespaceInternal(classRel, indexOid,
18545 : oldNspOid, newNspOid,
18546 : false, objsMoved);
18547 46 : add_exact_object_address(&thisobj, objsMoved);
18548 : }
18549 : }
18550 :
18551 86 : list_free(indexList);
18552 86 : }
18553 :
18554 : /*
18555 : * Move all identity and SERIAL-column sequences of the specified relation to another
18556 : * namespace.
18557 : *
18558 : * Note: we assume adequate permission checking was done by the caller,
18559 : * and that the caller has a suitable lock on the owning relation.
18560 : */
18561 : static void
18562 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
18563 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
18564 : LOCKMODE lockmode)
18565 : {
18566 : Relation depRel;
18567 : SysScanDesc scan;
18568 : ScanKeyData key[2];
18569 : HeapTuple tup;
18570 :
18571 : /*
18572 : * SERIAL sequences are those having an auto dependency on one of the
18573 : * table's columns (we don't care *which* column, exactly).
18574 : */
18575 86 : depRel = table_open(DependRelationId, AccessShareLock);
18576 :
18577 86 : ScanKeyInit(&key[0],
18578 : Anum_pg_depend_refclassid,
18579 : BTEqualStrategyNumber, F_OIDEQ,
18580 : ObjectIdGetDatum(RelationRelationId));
18581 86 : ScanKeyInit(&key[1],
18582 : Anum_pg_depend_refobjid,
18583 : BTEqualStrategyNumber, F_OIDEQ,
18584 : ObjectIdGetDatum(RelationGetRelid(rel)));
18585 : /* we leave refobjsubid unspecified */
18586 :
18587 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18588 : NULL, 2, key);
18589 :
18590 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
18591 : {
18592 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18593 : Relation seqRel;
18594 :
18595 : /* skip dependencies other than auto dependencies on columns */
18596 530 : if (depForm->refobjsubid == 0 ||
18597 382 : depForm->classid != RelationRelationId ||
18598 42 : depForm->objsubid != 0 ||
18599 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18600 488 : continue;
18601 :
18602 : /* Use relation_open just in case it's an index */
18603 42 : seqRel = relation_open(depForm->objid, lockmode);
18604 :
18605 : /* skip non-sequence relations */
18606 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18607 : {
18608 : /* No need to keep the lock */
18609 0 : relation_close(seqRel, lockmode);
18610 0 : continue;
18611 : }
18612 :
18613 : /* Fix the pg_class and pg_depend entries */
18614 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
18615 : oldNspOid, newNspOid,
18616 : true, objsMoved);
18617 :
18618 : /*
18619 : * Sequences used to have entries in pg_type, but no longer do. If we
18620 : * ever re-instate that, we'll need to move the pg_type entry to the
18621 : * new namespace, too (using AlterTypeNamespaceInternal).
18622 : */
18623 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18624 :
18625 : /* Now we can close it. Keep the lock till end of transaction. */
18626 42 : relation_close(seqRel, NoLock);
18627 : }
18628 :
18629 86 : systable_endscan(scan);
18630 :
18631 86 : relation_close(depRel, AccessShareLock);
18632 86 : }
18633 :
18634 :
18635 : /*
18636 : * This code supports
18637 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18638 : *
18639 : * Because we only support this for TEMP tables, it's sufficient to remember
18640 : * the state in a backend-local data structure.
18641 : */
18642 :
18643 : /*
18644 : * Register a newly-created relation's ON COMMIT action.
18645 : */
18646 : void
18647 170 : register_on_commit_action(Oid relid, OnCommitAction action)
18648 : {
18649 : OnCommitItem *oc;
18650 : MemoryContext oldcxt;
18651 :
18652 : /*
18653 : * We needn't bother registering the relation unless there is an ON COMMIT
18654 : * action we need to take.
18655 : */
18656 170 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18657 24 : return;
18658 :
18659 146 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18660 :
18661 146 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18662 146 : oc->relid = relid;
18663 146 : oc->oncommit = action;
18664 146 : oc->creating_subid = GetCurrentSubTransactionId();
18665 146 : oc->deleting_subid = InvalidSubTransactionId;
18666 :
18667 : /*
18668 : * We use lcons() here so that ON COMMIT actions are processed in reverse
18669 : * order of registration. That might not be essential but it seems
18670 : * reasonable.
18671 : */
18672 146 : on_commits = lcons(oc, on_commits);
18673 :
18674 146 : MemoryContextSwitchTo(oldcxt);
18675 : }
18676 :
18677 : /*
18678 : * Unregister any ON COMMIT action when a relation is deleted.
18679 : *
18680 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18681 : */
18682 : void
18683 47494 : remove_on_commit_action(Oid relid)
18684 : {
18685 : ListCell *l;
18686 :
18687 47628 : foreach(l, on_commits)
18688 : {
18689 268 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18690 :
18691 268 : if (oc->relid == relid)
18692 : {
18693 134 : oc->deleting_subid = GetCurrentSubTransactionId();
18694 134 : break;
18695 : }
18696 : }
18697 47494 : }
18698 :
18699 : /*
18700 : * Perform ON COMMIT actions.
18701 : *
18702 : * This is invoked just before actually committing, since it's possible
18703 : * to encounter errors.
18704 : */
18705 : void
18706 770516 : PreCommit_on_commit_actions(void)
18707 : {
18708 : ListCell *l;
18709 770516 : List *oids_to_truncate = NIL;
18710 770516 : List *oids_to_drop = NIL;
18711 :
18712 771236 : foreach(l, on_commits)
18713 : {
18714 720 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18715 :
18716 : /* Ignore entry if already dropped in this xact */
18717 720 : if (oc->deleting_subid != InvalidSubTransactionId)
18718 68 : continue;
18719 :
18720 652 : switch (oc->oncommit)
18721 : {
18722 0 : case ONCOMMIT_NOOP:
18723 : case ONCOMMIT_PRESERVE_ROWS:
18724 : /* Do nothing (there shouldn't be such entries, actually) */
18725 0 : break;
18726 598 : case ONCOMMIT_DELETE_ROWS:
18727 :
18728 : /*
18729 : * If this transaction hasn't accessed any temporary
18730 : * relations, we can skip truncating ON COMMIT DELETE ROWS
18731 : * tables, as they must still be empty.
18732 : */
18733 598 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18734 400 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18735 598 : break;
18736 54 : case ONCOMMIT_DROP:
18737 54 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18738 54 : break;
18739 : }
18740 720 : }
18741 :
18742 : /*
18743 : * Truncate relations before dropping so that all dependencies between
18744 : * relations are removed after they are worked on. Doing it like this
18745 : * might be a waste as it is possible that a relation being truncated will
18746 : * be dropped anyway due to its parent being dropped, but this makes the
18747 : * code more robust because of not having to re-check that the relation
18748 : * exists at truncation time.
18749 : */
18750 770516 : if (oids_to_truncate != NIL)
18751 334 : heap_truncate(oids_to_truncate);
18752 :
18753 770510 : if (oids_to_drop != NIL)
18754 : {
18755 48 : ObjectAddresses *targetObjects = new_object_addresses();
18756 :
18757 102 : foreach(l, oids_to_drop)
18758 : {
18759 : ObjectAddress object;
18760 :
18761 54 : object.classId = RelationRelationId;
18762 54 : object.objectId = lfirst_oid(l);
18763 54 : object.objectSubId = 0;
18764 :
18765 : Assert(!object_address_present(&object, targetObjects));
18766 :
18767 54 : add_exact_object_address(&object, targetObjects);
18768 : }
18769 :
18770 : /*
18771 : * Object deletion might involve toast table access (to clean up
18772 : * toasted catalog entries), so ensure we have a valid snapshot.
18773 : */
18774 48 : PushActiveSnapshot(GetTransactionSnapshot());
18775 :
18776 : /*
18777 : * Since this is an automatic drop, rather than one directly initiated
18778 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18779 : */
18780 48 : performMultipleDeletions(targetObjects, DROP_CASCADE,
18781 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18782 :
18783 48 : PopActiveSnapshot();
18784 :
18785 : #ifdef USE_ASSERT_CHECKING
18786 :
18787 : /*
18788 : * Note that table deletion will call remove_on_commit_action, so the
18789 : * entry should get marked as deleted.
18790 : */
18791 : foreach(l, on_commits)
18792 : {
18793 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18794 :
18795 : if (oc->oncommit != ONCOMMIT_DROP)
18796 : continue;
18797 :
18798 : Assert(oc->deleting_subid != InvalidSubTransactionId);
18799 : }
18800 : #endif
18801 : }
18802 770510 : }
18803 :
18804 : /*
18805 : * Post-commit or post-abort cleanup for ON COMMIT management.
18806 : *
18807 : * All we do here is remove no-longer-needed OnCommitItem entries.
18808 : *
18809 : * During commit, remove entries that were deleted during this transaction;
18810 : * during abort, remove those created during this transaction.
18811 : */
18812 : void
18813 818452 : AtEOXact_on_commit_actions(bool isCommit)
18814 : {
18815 : ListCell *cur_item;
18816 :
18817 819202 : foreach(cur_item, on_commits)
18818 : {
18819 750 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18820 :
18821 852 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18822 102 : oc->creating_subid != InvalidSubTransactionId)
18823 : {
18824 : /* cur_item must be removed */
18825 146 : on_commits = foreach_delete_current(on_commits, cur_item);
18826 146 : pfree(oc);
18827 : }
18828 : else
18829 : {
18830 : /* cur_item must be preserved */
18831 604 : oc->creating_subid = InvalidSubTransactionId;
18832 604 : oc->deleting_subid = InvalidSubTransactionId;
18833 : }
18834 : }
18835 818452 : }
18836 :
18837 : /*
18838 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18839 : *
18840 : * During subabort, we can immediately remove entries created during this
18841 : * subtransaction. During subcommit, just relabel entries marked during
18842 : * this subtransaction as being the parent's responsibility.
18843 : */
18844 : void
18845 19942 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18846 : SubTransactionId parentSubid)
18847 : {
18848 : ListCell *cur_item;
18849 :
18850 19942 : foreach(cur_item, on_commits)
18851 : {
18852 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18853 :
18854 0 : if (!isCommit && oc->creating_subid == mySubid)
18855 : {
18856 : /* cur_item must be removed */
18857 0 : on_commits = foreach_delete_current(on_commits, cur_item);
18858 0 : pfree(oc);
18859 : }
18860 : else
18861 : {
18862 : /* cur_item must be preserved */
18863 0 : if (oc->creating_subid == mySubid)
18864 0 : oc->creating_subid = parentSubid;
18865 0 : if (oc->deleting_subid == mySubid)
18866 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18867 : }
18868 : }
18869 19942 : }
18870 :
18871 : /*
18872 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18873 : * the relation to be locked only if (1) it's a plain or partitioned table,
18874 : * materialized view, or TOAST table and (2) the current user is the owner (or
18875 : * the superuser) or has been granted MAINTAIN. This meets the
18876 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18877 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18878 : */
18879 : void
18880 1006 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
18881 : Oid relId, Oid oldRelId, void *arg)
18882 : {
18883 : char relkind;
18884 : AclResult aclresult;
18885 :
18886 : /* Nothing to do if the relation was not found. */
18887 1006 : if (!OidIsValid(relId))
18888 6 : return;
18889 :
18890 : /*
18891 : * If the relation does exist, check whether it's an index. But note that
18892 : * the relation might have been dropped between the time we did the name
18893 : * lookup and now. In that case, there's nothing to do.
18894 : */
18895 1000 : relkind = get_rel_relkind(relId);
18896 1000 : if (!relkind)
18897 0 : return;
18898 1000 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18899 140 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18900 28 : ereport(ERROR,
18901 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18902 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18903 :
18904 : /* Check permissions */
18905 972 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18906 972 : if (aclresult != ACLCHECK_OK)
18907 30 : aclcheck_error(aclresult,
18908 30 : get_relkind_objtype(get_rel_relkind(relId)),
18909 30 : relation->relname);
18910 : }
18911 :
18912 : /*
18913 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18914 : */
18915 : static void
18916 2030 : RangeVarCallbackForTruncate(const RangeVar *relation,
18917 : Oid relId, Oid oldRelId, void *arg)
18918 : {
18919 : HeapTuple tuple;
18920 :
18921 : /* Nothing to do if the relation was not found. */
18922 2030 : if (!OidIsValid(relId))
18923 0 : return;
18924 :
18925 2030 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18926 2030 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18927 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18928 :
18929 2030 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18930 2024 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18931 :
18932 1992 : ReleaseSysCache(tuple);
18933 : }
18934 :
18935 : /*
18936 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18937 : * the owner of the relation, or superuser.
18938 : */
18939 : void
18940 15678 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
18941 : Oid relId, Oid oldRelId, void *arg)
18942 : {
18943 : HeapTuple tuple;
18944 :
18945 : /* Nothing to do if the relation was not found. */
18946 15678 : if (!OidIsValid(relId))
18947 12 : return;
18948 :
18949 15666 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18950 15666 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18951 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18952 :
18953 15666 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18954 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18955 6 : relation->relname);
18956 :
18957 31200 : if (!allowSystemTableMods &&
18958 15540 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18959 2 : ereport(ERROR,
18960 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18961 : errmsg("permission denied: \"%s\" is a system catalog",
18962 : relation->relname)));
18963 :
18964 15658 : ReleaseSysCache(tuple);
18965 : }
18966 :
18967 : /*
18968 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
18969 : * processing.
18970 : */
18971 : static void
18972 31538 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18973 : void *arg)
18974 : {
18975 31538 : Node *stmt = (Node *) arg;
18976 : ObjectType reltype;
18977 : HeapTuple tuple;
18978 : Form_pg_class classform;
18979 : AclResult aclresult;
18980 : char relkind;
18981 :
18982 31538 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18983 31538 : if (!HeapTupleIsValid(tuple))
18984 214 : return; /* concurrently dropped */
18985 31324 : classform = (Form_pg_class) GETSTRUCT(tuple);
18986 31324 : relkind = classform->relkind;
18987 :
18988 : /* Must own relation. */
18989 31324 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18990 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18991 :
18992 : /* No system table modifications unless explicitly allowed. */
18993 31264 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
18994 30 : ereport(ERROR,
18995 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18996 : errmsg("permission denied: \"%s\" is a system catalog",
18997 : rv->relname)));
18998 :
18999 : /*
19000 : * Extract the specified relation type from the statement parse tree.
19001 : *
19002 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
19003 : * have CREATE rights on the containing namespace.
19004 : */
19005 31234 : if (IsA(stmt, RenameStmt))
19006 : {
19007 490 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19008 : GetUserId(), ACL_CREATE);
19009 490 : if (aclresult != ACLCHECK_OK)
19010 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
19011 0 : get_namespace_name(classform->relnamespace));
19012 490 : reltype = ((RenameStmt *) stmt)->renameType;
19013 : }
19014 30744 : else if (IsA(stmt, AlterObjectSchemaStmt))
19015 90 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19016 :
19017 30654 : else if (IsA(stmt, AlterTableStmt))
19018 30654 : reltype = ((AlterTableStmt *) stmt)->objtype;
19019 : else
19020 : {
19021 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19022 : reltype = OBJECT_TABLE; /* placate compiler */
19023 : }
19024 :
19025 : /*
19026 : * For compatibility with prior releases, we allow ALTER TABLE to be used
19027 : * with most other types of relations (but not composite types). We allow
19028 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
19029 : * otherwise. Otherwise, the user must select the correct form of the
19030 : * command for the relation at issue.
19031 : */
19032 31234 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19033 0 : ereport(ERROR,
19034 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19035 : errmsg("\"%s\" is not a sequence", rv->relname)));
19036 :
19037 31234 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19038 0 : ereport(ERROR,
19039 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19040 : errmsg("\"%s\" is not a view", rv->relname)));
19041 :
19042 31234 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19043 0 : ereport(ERROR,
19044 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19045 : errmsg("\"%s\" is not a materialized view", rv->relname)));
19046 :
19047 31234 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19048 0 : ereport(ERROR,
19049 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19050 : errmsg("\"%s\" is not a foreign table", rv->relname)));
19051 :
19052 31234 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19053 0 : ereport(ERROR,
19054 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19055 : errmsg("\"%s\" is not a composite type", rv->relname)));
19056 :
19057 31234 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19058 : relkind != RELKIND_PARTITIONED_INDEX
19059 38 : && !IsA(stmt, RenameStmt))
19060 6 : ereport(ERROR,
19061 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19062 : errmsg("\"%s\" is not an index", rv->relname)));
19063 :
19064 : /*
19065 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19066 : * TYPE for that.
19067 : */
19068 31228 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19069 0 : ereport(ERROR,
19070 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19071 : errmsg("\"%s\" is a composite type", rv->relname),
19072 : /* translator: %s is an SQL ALTER command */
19073 : errhint("Use %s instead.",
19074 : "ALTER TYPE")));
19075 :
19076 : /*
19077 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19078 : * to a different schema, such as indexes and TOAST tables.
19079 : */
19080 31228 : if (IsA(stmt, AlterObjectSchemaStmt))
19081 : {
19082 90 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19083 0 : ereport(ERROR,
19084 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19085 : errmsg("cannot change schema of index \"%s\"",
19086 : rv->relname),
19087 : errhint("Change the schema of the table instead.")));
19088 90 : else if (relkind == RELKIND_COMPOSITE_TYPE)
19089 0 : ereport(ERROR,
19090 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19091 : errmsg("cannot change schema of composite type \"%s\"",
19092 : rv->relname),
19093 : /* translator: %s is an SQL ALTER command */
19094 : errhint("Use %s instead.",
19095 : "ALTER TYPE")));
19096 90 : else if (relkind == RELKIND_TOASTVALUE)
19097 0 : ereport(ERROR,
19098 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19099 : errmsg("cannot change schema of TOAST table \"%s\"",
19100 : rv->relname),
19101 : errhint("Change the schema of the table instead.")));
19102 : }
19103 :
19104 31228 : ReleaseSysCache(tuple);
19105 : }
19106 :
19107 : /*
19108 : * Transform any expressions present in the partition key
19109 : *
19110 : * Returns a transformed PartitionSpec.
19111 : */
19112 : static PartitionSpec *
19113 4874 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
19114 : {
19115 : PartitionSpec *newspec;
19116 : ParseState *pstate;
19117 : ParseNamespaceItem *nsitem;
19118 : ListCell *l;
19119 :
19120 4874 : newspec = makeNode(PartitionSpec);
19121 :
19122 4874 : newspec->strategy = partspec->strategy;
19123 4874 : newspec->partParams = NIL;
19124 4874 : newspec->location = partspec->location;
19125 :
19126 : /* Check valid number of columns for strategy */
19127 7338 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19128 2464 : list_length(partspec->partParams) != 1)
19129 6 : ereport(ERROR,
19130 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19131 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19132 :
19133 : /*
19134 : * Create a dummy ParseState and insert the target relation as its sole
19135 : * rangetable entry. We need a ParseState for transformExpr.
19136 : */
19137 4868 : pstate = make_parsestate(NULL);
19138 4868 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19139 : NULL, false, true);
19140 4868 : addNSItemToQuery(pstate, nsitem, true, true, true);
19141 :
19142 : /* take care of any partition expressions */
19143 10162 : foreach(l, partspec->partParams)
19144 : {
19145 5318 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
19146 :
19147 5318 : if (pelem->expr)
19148 : {
19149 : /* Copy, to avoid scribbling on the input */
19150 304 : pelem = copyObject(pelem);
19151 :
19152 : /* Now do parse transformation of the expression */
19153 304 : pelem->expr = transformExpr(pstate, pelem->expr,
19154 : EXPR_KIND_PARTITION_EXPRESSION);
19155 :
19156 : /* we have to fix its collations too */
19157 280 : assign_expr_collations(pstate, pelem->expr);
19158 : }
19159 :
19160 5294 : newspec->partParams = lappend(newspec->partParams, pelem);
19161 : }
19162 :
19163 4844 : return newspec;
19164 : }
19165 :
19166 : /*
19167 : * Compute per-partition-column information from a list of PartitionElems.
19168 : * Expressions in the PartitionElems must be parse-analyzed already.
19169 : */
19170 : static void
19171 4844 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19172 : List **partexprs, Oid *partopclass, Oid *partcollation,
19173 : PartitionStrategy strategy)
19174 : {
19175 : int attn;
19176 : ListCell *lc;
19177 : Oid am_oid;
19178 :
19179 4844 : attn = 0;
19180 10042 : foreach(lc, partParams)
19181 : {
19182 5294 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
19183 : Oid atttype;
19184 : Oid attcollation;
19185 :
19186 5294 : if (pelem->name != NULL)
19187 : {
19188 : /* Simple attribute reference */
19189 : HeapTuple atttuple;
19190 : Form_pg_attribute attform;
19191 :
19192 5014 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
19193 5014 : pelem->name);
19194 5014 : if (!HeapTupleIsValid(atttuple))
19195 12 : ereport(ERROR,
19196 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19197 : errmsg("column \"%s\" named in partition key does not exist",
19198 : pelem->name),
19199 : parser_errposition(pstate, pelem->location)));
19200 5002 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19201 :
19202 5002 : if (attform->attnum <= 0)
19203 6 : ereport(ERROR,
19204 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19205 : errmsg("cannot use system column \"%s\" in partition key",
19206 : pelem->name),
19207 : parser_errposition(pstate, pelem->location)));
19208 :
19209 : /*
19210 : * Stored generated columns cannot work: They are computed after
19211 : * BEFORE triggers, but partition routing is done before all
19212 : * triggers. Maybe virtual generated columns could be made to
19213 : * work, but then they would need to be handled as an expression
19214 : * below.
19215 : */
19216 4996 : if (attform->attgenerated)
19217 12 : ereport(ERROR,
19218 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19219 : errmsg("cannot use generated column in partition key"),
19220 : errdetail("Column \"%s\" is a generated column.",
19221 : pelem->name),
19222 : parser_errposition(pstate, pelem->location)));
19223 :
19224 4984 : partattrs[attn] = attform->attnum;
19225 4984 : atttype = attform->atttypid;
19226 4984 : attcollation = attform->attcollation;
19227 4984 : ReleaseSysCache(atttuple);
19228 : }
19229 : else
19230 : {
19231 : /* Expression */
19232 280 : Node *expr = pelem->expr;
19233 : char partattname[16];
19234 :
19235 : Assert(expr != NULL);
19236 280 : atttype = exprType(expr);
19237 280 : attcollation = exprCollation(expr);
19238 :
19239 : /*
19240 : * The expression must be of a storable type (e.g., not RECORD).
19241 : * The test is the same as for whether a table column is of a safe
19242 : * type (which is why we needn't check for the non-expression
19243 : * case).
19244 : */
19245 280 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19246 280 : CheckAttributeType(partattname,
19247 : atttype, attcollation,
19248 : NIL, CHKATYPE_IS_PARTKEY);
19249 :
19250 : /*
19251 : * Strip any top-level COLLATE clause. This ensures that we treat
19252 : * "x COLLATE y" and "(x COLLATE y)" alike.
19253 : */
19254 268 : while (IsA(expr, CollateExpr))
19255 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
19256 :
19257 268 : if (IsA(expr, Var) &&
19258 12 : ((Var *) expr)->varattno > 0)
19259 : {
19260 : /*
19261 : * User wrote "(column)" or "(column COLLATE something)".
19262 : * Treat it like simple attribute anyway.
19263 : */
19264 6 : partattrs[attn] = ((Var *) expr)->varattno;
19265 : }
19266 : else
19267 : {
19268 262 : Bitmapset *expr_attrs = NULL;
19269 : int i;
19270 :
19271 262 : partattrs[attn] = 0; /* marks the column as expression */
19272 262 : *partexprs = lappend(*partexprs, expr);
19273 :
19274 : /*
19275 : * transformPartitionSpec() should have already rejected
19276 : * subqueries, aggregates, window functions, and SRFs, based
19277 : * on the EXPR_KIND_ for partition expressions.
19278 : */
19279 :
19280 : /*
19281 : * Cannot allow system column references, since that would
19282 : * make partition routing impossible: their values won't be
19283 : * known yet when we need to do that.
19284 : */
19285 262 : pull_varattnos(expr, 1, &expr_attrs);
19286 2096 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19287 : {
19288 1834 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
19289 : expr_attrs))
19290 0 : ereport(ERROR,
19291 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19292 : errmsg("partition key expressions cannot contain system column references")));
19293 : }
19294 :
19295 : /*
19296 : * Stored generated columns cannot work: They are computed
19297 : * after BEFORE triggers, but partition routing is done before
19298 : * all triggers. Virtual generated columns could probably
19299 : * work, but it would require more work elsewhere (for example
19300 : * SET EXPRESSION would need to check whether the column is
19301 : * used in partition keys). Seems safer to prohibit for now.
19302 : */
19303 262 : i = -1;
19304 570 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
19305 : {
19306 320 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
19307 :
19308 320 : if (attno > 0 &&
19309 314 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19310 12 : ereport(ERROR,
19311 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19312 : errmsg("cannot use generated column in partition key"),
19313 : errdetail("Column \"%s\" is a generated column.",
19314 : get_attname(RelationGetRelid(rel), attno, false)),
19315 : parser_errposition(pstate, pelem->location)));
19316 : }
19317 :
19318 : /*
19319 : * Preprocess the expression before checking for mutability.
19320 : * This is essential for the reasons described in
19321 : * contain_mutable_functions_after_planning. However, we call
19322 : * expression_planner for ourselves rather than using that
19323 : * function, because if constant-folding reduces the
19324 : * expression to a constant, we'd like to know that so we can
19325 : * complain below.
19326 : *
19327 : * Like contain_mutable_functions_after_planning, assume that
19328 : * expression_planner won't scribble on its input, so this
19329 : * won't affect the partexprs entry we saved above.
19330 : */
19331 250 : expr = (Node *) expression_planner((Expr *) expr);
19332 :
19333 : /*
19334 : * Partition expressions cannot contain mutable functions,
19335 : * because a given row must always map to the same partition
19336 : * as long as there is no change in the partition boundary
19337 : * structure.
19338 : */
19339 250 : if (contain_mutable_functions(expr))
19340 6 : ereport(ERROR,
19341 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19342 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19343 :
19344 : /*
19345 : * While it is not exactly *wrong* for a partition expression
19346 : * to be a constant, it seems better to reject such keys.
19347 : */
19348 244 : if (IsA(expr, Const))
19349 12 : ereport(ERROR,
19350 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19351 : errmsg("cannot use constant expression as partition key")));
19352 : }
19353 : }
19354 :
19355 : /*
19356 : * Apply collation override if any
19357 : */
19358 5222 : if (pelem->collation)
19359 54 : attcollation = get_collation_oid(pelem->collation, false);
19360 :
19361 : /*
19362 : * Check we have a collation iff it's a collatable type. The only
19363 : * expected failures here are (1) COLLATE applied to a noncollatable
19364 : * type, or (2) partition expression had an unresolved collation. But
19365 : * we might as well code this to be a complete consistency check.
19366 : */
19367 5222 : if (type_is_collatable(atttype))
19368 : {
19369 604 : if (!OidIsValid(attcollation))
19370 0 : ereport(ERROR,
19371 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
19372 : errmsg("could not determine which collation to use for partition expression"),
19373 : errhint("Use the COLLATE clause to set the collation explicitly.")));
19374 : }
19375 : else
19376 : {
19377 4618 : if (OidIsValid(attcollation))
19378 0 : ereport(ERROR,
19379 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19380 : errmsg("collations are not supported by type %s",
19381 : format_type_be(atttype))));
19382 : }
19383 :
19384 5222 : partcollation[attn] = attcollation;
19385 :
19386 : /*
19387 : * Identify the appropriate operator class. For list and range
19388 : * partitioning, we use a btree operator class; hash partitioning uses
19389 : * a hash operator class.
19390 : */
19391 5222 : if (strategy == PARTITION_STRATEGY_HASH)
19392 294 : am_oid = HASH_AM_OID;
19393 : else
19394 4928 : am_oid = BTREE_AM_OID;
19395 :
19396 5222 : if (!pelem->opclass)
19397 : {
19398 5090 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19399 :
19400 5090 : if (!OidIsValid(partopclass[attn]))
19401 : {
19402 12 : if (strategy == PARTITION_STRATEGY_HASH)
19403 0 : ereport(ERROR,
19404 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19405 : errmsg("data type %s has no default operator class for access method \"%s\"",
19406 : format_type_be(atttype), "hash"),
19407 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19408 : else
19409 12 : ereport(ERROR,
19410 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19411 : errmsg("data type %s has no default operator class for access method \"%s\"",
19412 : format_type_be(atttype), "btree"),
19413 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19414 : }
19415 : }
19416 : else
19417 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
19418 : atttype,
19419 : am_oid == HASH_AM_OID ? "hash" : "btree",
19420 : am_oid);
19421 :
19422 5198 : attn++;
19423 : }
19424 4748 : }
19425 :
19426 : /*
19427 : * PartConstraintImpliedByRelConstraint
19428 : * Do scanrel's existing constraints imply the partition constraint?
19429 : *
19430 : * "Existing constraints" include its check constraints and column-level
19431 : * not-null constraints. partConstraint describes the partition constraint,
19432 : * in implicit-AND form.
19433 : */
19434 : bool
19435 3106 : PartConstraintImpliedByRelConstraint(Relation scanrel,
19436 : List *partConstraint)
19437 : {
19438 3106 : List *existConstraint = NIL;
19439 3106 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19440 : int i;
19441 :
19442 3106 : if (constr && constr->has_not_null)
19443 : {
19444 770 : int natts = scanrel->rd_att->natts;
19445 :
19446 2508 : for (i = 1; i <= natts; i++)
19447 : {
19448 1738 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
19449 :
19450 1738 : if (att->attnotnull && !att->attisdropped)
19451 : {
19452 1078 : NullTest *ntest = makeNode(NullTest);
19453 :
19454 1078 : ntest->arg = (Expr *) makeVar(1,
19455 : i,
19456 : att->atttypid,
19457 : att->atttypmod,
19458 : att->attcollation,
19459 : 0);
19460 1078 : ntest->nulltesttype = IS_NOT_NULL;
19461 :
19462 : /*
19463 : * argisrow=false is correct even for a composite column,
19464 : * because attnotnull does not represent a SQL-spec IS NOT
19465 : * NULL test in such a case, just IS DISTINCT FROM NULL.
19466 : */
19467 1078 : ntest->argisrow = false;
19468 1078 : ntest->location = -1;
19469 1078 : existConstraint = lappend(existConstraint, ntest);
19470 : }
19471 : }
19472 : }
19473 :
19474 3106 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19475 : }
19476 :
19477 : /*
19478 : * ConstraintImpliedByRelConstraint
19479 : * Do scanrel's existing constraints imply the given constraint?
19480 : *
19481 : * testConstraint is the constraint to validate. provenConstraint is a
19482 : * caller-provided list of conditions which this function may assume
19483 : * to be true. Both provenConstraint and testConstraint must be in
19484 : * implicit-AND form, must only contain immutable clauses, and must
19485 : * contain only Vars with varno = 1.
19486 : */
19487 : bool
19488 4320 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
19489 : {
19490 4320 : List *existConstraint = list_copy(provenConstraint);
19491 4320 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19492 : int num_check,
19493 : i;
19494 :
19495 4320 : num_check = (constr != NULL) ? constr->num_check : 0;
19496 4836 : for (i = 0; i < num_check; i++)
19497 : {
19498 : Node *cexpr;
19499 :
19500 : /*
19501 : * If this constraint hasn't been fully validated yet, we must ignore
19502 : * it here.
19503 : */
19504 516 : if (!constr->check[i].ccvalid)
19505 6 : continue;
19506 :
19507 : /*
19508 : * NOT ENFORCED constraints are always marked as invalid, which should
19509 : * have been ignored.
19510 : */
19511 : Assert(constr->check[i].ccenforced);
19512 :
19513 510 : cexpr = stringToNode(constr->check[i].ccbin);
19514 :
19515 : /*
19516 : * Run each expression through const-simplification and
19517 : * canonicalization. It is necessary, because we will be comparing it
19518 : * to similarly-processed partition constraint expressions, and may
19519 : * fail to detect valid matches without this.
19520 : */
19521 510 : cexpr = eval_const_expressions(NULL, cexpr);
19522 510 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19523 :
19524 510 : existConstraint = list_concat(existConstraint,
19525 510 : make_ands_implicit((Expr *) cexpr));
19526 : }
19527 :
19528 : /*
19529 : * Try to make the proof. Since we are comparing CHECK constraints, we
19530 : * need to use weak implication, i.e., we assume existConstraint is
19531 : * not-false and try to prove the same for testConstraint.
19532 : *
19533 : * Note that predicate_implied_by assumes its first argument is known
19534 : * immutable. That should always be true for both NOT NULL and partition
19535 : * constraints, so we don't test it here.
19536 : */
19537 4320 : return predicate_implied_by(testConstraint, existConstraint, true);
19538 : }
19539 :
19540 : /*
19541 : * QueuePartitionConstraintValidation
19542 : *
19543 : * Add an entry to wqueue to have the given partition constraint validated by
19544 : * Phase 3, for the given relation, and all its children.
19545 : *
19546 : * We first verify whether the given constraint is implied by pre-existing
19547 : * relation constraints; if it is, there's no need to scan the table to
19548 : * validate, so don't queue in that case.
19549 : */
19550 : static void
19551 2476 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
19552 : List *partConstraint,
19553 : bool validate_default)
19554 : {
19555 : /*
19556 : * Based on the table's existing constraints, determine whether or not we
19557 : * may skip scanning the table.
19558 : */
19559 2476 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19560 : {
19561 90 : if (!validate_default)
19562 68 : ereport(DEBUG1,
19563 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19564 : RelationGetRelationName(scanrel))));
19565 : else
19566 22 : ereport(DEBUG1,
19567 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19568 : RelationGetRelationName(scanrel))));
19569 90 : return;
19570 : }
19571 :
19572 : /*
19573 : * Constraints proved insufficient. For plain relations, queue a
19574 : * validation item now; for partitioned tables, recurse to process each
19575 : * partition.
19576 : */
19577 2386 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19578 : {
19579 : AlteredTableInfo *tab;
19580 :
19581 : /* Grab a work queue entry. */
19582 1990 : tab = ATGetQueueEntry(wqueue, scanrel);
19583 : Assert(tab->partition_constraint == NULL);
19584 1990 : tab->partition_constraint = (Expr *) linitial(partConstraint);
19585 1990 : tab->validate_default = validate_default;
19586 : }
19587 396 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19588 : {
19589 348 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19590 : int i;
19591 :
19592 726 : for (i = 0; i < partdesc->nparts; i++)
19593 : {
19594 : Relation part_rel;
19595 : List *thisPartConstraint;
19596 :
19597 : /*
19598 : * This is the minimum lock we need to prevent deadlocks.
19599 : */
19600 378 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19601 :
19602 : /*
19603 : * Adjust the constraint for scanrel so that it matches this
19604 : * partition's attribute numbers.
19605 : */
19606 : thisPartConstraint =
19607 378 : map_partition_varattnos(partConstraint, 1,
19608 : part_rel, scanrel);
19609 :
19610 378 : QueuePartitionConstraintValidation(wqueue, part_rel,
19611 : thisPartConstraint,
19612 : validate_default);
19613 378 : table_close(part_rel, NoLock); /* keep lock till commit */
19614 : }
19615 : }
19616 : }
19617 :
19618 : /*
19619 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19620 : *
19621 : * Return the address of the newly attached partition.
19622 : */
19623 : static ObjectAddress
19624 2306 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19625 : AlterTableUtilityContext *context)
19626 : {
19627 : Relation attachrel,
19628 : catalog;
19629 : List *attachrel_children;
19630 : List *partConstraint;
19631 : SysScanDesc scan;
19632 : ScanKeyData skey;
19633 : AttrNumber attno;
19634 : int natts;
19635 : TupleDesc tupleDesc;
19636 : ObjectAddress address;
19637 : const char *trigger_name;
19638 : Oid defaultPartOid;
19639 : List *partBoundConstraint;
19640 2306 : ParseState *pstate = make_parsestate(NULL);
19641 :
19642 2306 : pstate->p_sourcetext = context->queryString;
19643 :
19644 : /*
19645 : * We must lock the default partition if one exists, because attaching a
19646 : * new partition will change its partition constraint.
19647 : */
19648 : defaultPartOid =
19649 2306 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19650 2306 : if (OidIsValid(defaultPartOid))
19651 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19652 :
19653 2306 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19654 :
19655 : /*
19656 : * XXX I think it'd be a good idea to grab locks on all tables referenced
19657 : * by FKs at this point also.
19658 : */
19659 :
19660 : /*
19661 : * Must be owner of both parent and source table -- parent was checked by
19662 : * ATSimplePermissions call in ATPrepCmd
19663 : */
19664 2300 : ATSimplePermissions(AT_AttachPartition, attachrel,
19665 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
19666 :
19667 : /* A partition can only have one parent */
19668 2294 : if (attachrel->rd_rel->relispartition)
19669 6 : ereport(ERROR,
19670 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19671 : errmsg("\"%s\" is already a partition",
19672 : RelationGetRelationName(attachrel))));
19673 :
19674 2288 : if (OidIsValid(attachrel->rd_rel->reloftype))
19675 6 : ereport(ERROR,
19676 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19677 : errmsg("cannot attach a typed table as partition")));
19678 :
19679 : /*
19680 : * Table being attached should not already be part of inheritance; either
19681 : * as a child table...
19682 : */
19683 2282 : catalog = table_open(InheritsRelationId, AccessShareLock);
19684 2282 : ScanKeyInit(&skey,
19685 : Anum_pg_inherits_inhrelid,
19686 : BTEqualStrategyNumber, F_OIDEQ,
19687 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19688 2282 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19689 : NULL, 1, &skey);
19690 2282 : if (HeapTupleIsValid(systable_getnext(scan)))
19691 6 : ereport(ERROR,
19692 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19693 : errmsg("cannot attach inheritance child as partition")));
19694 2276 : systable_endscan(scan);
19695 :
19696 : /* ...or as a parent table (except the case when it is partitioned) */
19697 2276 : ScanKeyInit(&skey,
19698 : Anum_pg_inherits_inhparent,
19699 : BTEqualStrategyNumber, F_OIDEQ,
19700 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19701 2276 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19702 : 1, &skey);
19703 2276 : if (HeapTupleIsValid(systable_getnext(scan)) &&
19704 256 : attachrel->rd_rel->relkind == RELKIND_RELATION)
19705 6 : ereport(ERROR,
19706 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19707 : errmsg("cannot attach inheritance parent as partition")));
19708 2270 : systable_endscan(scan);
19709 2270 : table_close(catalog, AccessShareLock);
19710 :
19711 : /*
19712 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
19713 : * particular, this disallows making a rel a partition of itself.)
19714 : *
19715 : * We do that by checking if rel is a member of the list of attachrel's
19716 : * partitions provided the latter is partitioned at all. We want to avoid
19717 : * having to construct this list again, so we request the strongest lock
19718 : * on all partitions. We need the strongest lock, because we may decide
19719 : * to scan them if we find out that the table being attached (or its leaf
19720 : * partitions) may contain rows that violate the partition constraint. If
19721 : * the table has a constraint that would prevent such rows, which by
19722 : * definition is present in all the partitions, we need not scan the
19723 : * table, nor its partitions. But we cannot risk a deadlock by taking a
19724 : * weaker lock now and the stronger one only when needed.
19725 : */
19726 2270 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19727 : AccessExclusiveLock, NULL);
19728 2270 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19729 12 : ereport(ERROR,
19730 : (errcode(ERRCODE_DUPLICATE_TABLE),
19731 : errmsg("circular inheritance not allowed"),
19732 : errdetail("\"%s\" is already a child of \"%s\".",
19733 : RelationGetRelationName(rel),
19734 : RelationGetRelationName(attachrel))));
19735 :
19736 : /* If the parent is permanent, so must be all of its partitions. */
19737 2258 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19738 2216 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19739 6 : ereport(ERROR,
19740 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19741 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19742 : RelationGetRelationName(rel))));
19743 :
19744 : /* Temp parent cannot have a partition that is itself not a temp */
19745 2252 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19746 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19747 18 : ereport(ERROR,
19748 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19749 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19750 : RelationGetRelationName(rel))));
19751 :
19752 : /* If the parent is temp, it must belong to this session */
19753 2234 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19754 24 : !rel->rd_islocaltemp)
19755 0 : ereport(ERROR,
19756 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19757 : errmsg("cannot attach as partition of temporary relation of another session")));
19758 :
19759 : /* Ditto for the partition */
19760 2234 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19761 24 : !attachrel->rd_islocaltemp)
19762 0 : ereport(ERROR,
19763 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19764 : errmsg("cannot attach temporary relation of another session as partition")));
19765 :
19766 : /*
19767 : * Check if attachrel has any identity columns or any columns that aren't
19768 : * in the parent.
19769 : */
19770 2234 : tupleDesc = RelationGetDescr(attachrel);
19771 2234 : natts = tupleDesc->natts;
19772 7662 : for (attno = 1; attno <= natts; attno++)
19773 : {
19774 5470 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19775 5470 : char *attributeName = NameStr(attribute->attname);
19776 :
19777 : /* Ignore dropped */
19778 5470 : if (attribute->attisdropped)
19779 580 : continue;
19780 :
19781 4890 : if (attribute->attidentity)
19782 24 : ereport(ERROR,
19783 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19784 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19785 : RelationGetRelationName(attachrel), attributeName),
19786 : errdetail("The new partition may not contain an identity column."));
19787 :
19788 : /* Try to find the column in parent (matching on column name) */
19789 4866 : if (!SearchSysCacheExists2(ATTNAME,
19790 : ObjectIdGetDatum(RelationGetRelid(rel)),
19791 : CStringGetDatum(attributeName)))
19792 18 : ereport(ERROR,
19793 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19794 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19795 : RelationGetRelationName(attachrel), attributeName,
19796 : RelationGetRelationName(rel)),
19797 : errdetail("The new partition may contain only the columns present in parent.")));
19798 : }
19799 :
19800 : /*
19801 : * If child_rel has row-level triggers with transition tables, we
19802 : * currently don't allow it to become a partition. See also prohibitions
19803 : * in ATExecAddInherit() and CreateTrigger().
19804 : */
19805 2192 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19806 2192 : if (trigger_name != NULL)
19807 6 : ereport(ERROR,
19808 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19809 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19810 : trigger_name, RelationGetRelationName(attachrel)),
19811 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
19812 :
19813 : /*
19814 : * Check that the new partition's bound is valid and does not overlap any
19815 : * of existing partitions of the parent - note that it does not return on
19816 : * error.
19817 : */
19818 2186 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19819 : cmd->bound, pstate);
19820 :
19821 : /* OK to create inheritance. Rest of the checks performed there */
19822 2150 : CreateInheritance(attachrel, rel, true);
19823 :
19824 : /* Update the pg_class entry. */
19825 2048 : StorePartitionBound(attachrel, rel, cmd->bound);
19826 :
19827 : /* Ensure there exists a correct set of indexes in the partition. */
19828 2048 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19829 :
19830 : /* and triggers */
19831 2018 : CloneRowTriggersToPartition(rel, attachrel);
19832 :
19833 : /*
19834 : * Clone foreign key constraints. Callee is responsible for setting up
19835 : * for phase 3 constraint verification.
19836 : */
19837 2012 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
19838 :
19839 : /*
19840 : * Generate partition constraint from the partition bound specification.
19841 : * If the parent itself is a partition, make sure to include its
19842 : * constraint as well.
19843 : */
19844 2000 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19845 :
19846 : /*
19847 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
19848 : * since it's needed later to construct the constraint expression for
19849 : * validating against the default partition, if any.
19850 : */
19851 2000 : partConstraint = list_concat_copy(partBoundConstraint,
19852 2000 : RelationGetPartitionQual(rel));
19853 :
19854 : /* Skip validation if there are no constraints to validate. */
19855 2000 : if (partConstraint)
19856 : {
19857 : /*
19858 : * Run the partition quals through const-simplification similar to
19859 : * check constraints. We skip canonicalize_qual, though, because
19860 : * partition quals should be in canonical form already.
19861 : */
19862 : partConstraint =
19863 1952 : (List *) eval_const_expressions(NULL,
19864 : (Node *) partConstraint);
19865 :
19866 : /* XXX this sure looks wrong */
19867 1952 : partConstraint = list_make1(make_ands_explicit(partConstraint));
19868 :
19869 : /*
19870 : * Adjust the generated constraint to match this partition's attribute
19871 : * numbers.
19872 : */
19873 1952 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19874 : rel);
19875 :
19876 : /* Validate partition constraints against the table being attached. */
19877 1952 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19878 : false);
19879 : }
19880 :
19881 : /*
19882 : * If we're attaching a partition other than the default partition and a
19883 : * default one exists, then that partition's partition constraint changes,
19884 : * so add an entry to the work queue to validate it, too. (We must not do
19885 : * this when the partition being attached is the default one; we already
19886 : * did it above!)
19887 : */
19888 2000 : if (OidIsValid(defaultPartOid))
19889 : {
19890 : Relation defaultrel;
19891 : List *defPartConstraint;
19892 :
19893 : Assert(!cmd->bound->is_default);
19894 :
19895 : /* we already hold a lock on the default partition */
19896 146 : defaultrel = table_open(defaultPartOid, NoLock);
19897 : defPartConstraint =
19898 146 : get_proposed_default_constraint(partBoundConstraint);
19899 :
19900 : /*
19901 : * Map the Vars in the constraint expression from rel's attnos to
19902 : * defaultrel's.
19903 : */
19904 : defPartConstraint =
19905 146 : map_partition_varattnos(defPartConstraint,
19906 : 1, defaultrel, rel);
19907 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
19908 : defPartConstraint, true);
19909 :
19910 : /* keep our lock until commit. */
19911 146 : table_close(defaultrel, NoLock);
19912 : }
19913 :
19914 2000 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19915 :
19916 : /*
19917 : * If the partition we just attached is partitioned itself, invalidate
19918 : * relcache for all descendent partitions too to ensure that their
19919 : * rd_partcheck expression trees are rebuilt; partitions already locked at
19920 : * the beginning of this function.
19921 : */
19922 2000 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19923 : {
19924 : ListCell *l;
19925 :
19926 984 : foreach(l, attachrel_children)
19927 : {
19928 660 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19929 : }
19930 : }
19931 :
19932 : /* keep our lock until commit */
19933 2000 : table_close(attachrel, NoLock);
19934 :
19935 2000 : return address;
19936 : }
19937 :
19938 : /*
19939 : * AttachPartitionEnsureIndexes
19940 : * subroutine for ATExecAttachPartition to create/match indexes
19941 : *
19942 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19943 : * PARTITION: every partition must have an index attached to each index on the
19944 : * partitioned table.
19945 : */
19946 : static void
19947 2048 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19948 : {
19949 : List *idxes;
19950 : List *attachRelIdxs;
19951 : Relation *attachrelIdxRels;
19952 : IndexInfo **attachInfos;
19953 : ListCell *cell;
19954 : MemoryContext cxt;
19955 : MemoryContext oldcxt;
19956 :
19957 2048 : cxt = AllocSetContextCreate(CurrentMemoryContext,
19958 : "AttachPartitionEnsureIndexes",
19959 : ALLOCSET_DEFAULT_SIZES);
19960 2048 : oldcxt = MemoryContextSwitchTo(cxt);
19961 :
19962 2048 : idxes = RelationGetIndexList(rel);
19963 2048 : attachRelIdxs = RelationGetIndexList(attachrel);
19964 2048 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19965 2048 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19966 :
19967 : /* Build arrays of all existing indexes and their IndexInfos */
19968 4460 : foreach_oid(cldIdxId, attachRelIdxs)
19969 : {
19970 364 : int i = foreach_current_index(cldIdxId);
19971 :
19972 364 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19973 364 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19974 : }
19975 :
19976 : /*
19977 : * If we're attaching a foreign table, we must fail if any of the indexes
19978 : * is a constraint index; otherwise, there's nothing to do here. Do this
19979 : * before starting work, to avoid wasting the effort of building a few
19980 : * non-unique indexes before coming across a unique one.
19981 : */
19982 2048 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19983 : {
19984 86 : foreach(cell, idxes)
19985 : {
19986 36 : Oid idx = lfirst_oid(cell);
19987 36 : Relation idxRel = index_open(idx, AccessShareLock);
19988 :
19989 36 : if (idxRel->rd_index->indisunique ||
19990 24 : idxRel->rd_index->indisprimary)
19991 12 : ereport(ERROR,
19992 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19993 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19994 : RelationGetRelationName(attachrel),
19995 : RelationGetRelationName(rel)),
19996 : errdetail("Partitioned table \"%s\" contains unique indexes.",
19997 : RelationGetRelationName(rel))));
19998 24 : index_close(idxRel, AccessShareLock);
19999 : }
20000 :
20001 50 : goto out;
20002 : }
20003 :
20004 : /*
20005 : * For each index on the partitioned table, find a matching one in the
20006 : * partition-to-be; if one is not found, create one.
20007 : */
20008 2406 : foreach(cell, idxes)
20009 : {
20010 438 : Oid idx = lfirst_oid(cell);
20011 438 : Relation idxRel = index_open(idx, AccessShareLock);
20012 : IndexInfo *info;
20013 : AttrMap *attmap;
20014 438 : bool found = false;
20015 : Oid constraintOid;
20016 :
20017 : /*
20018 : * Ignore indexes in the partitioned table other than partitioned
20019 : * indexes.
20020 : */
20021 438 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20022 : {
20023 0 : index_close(idxRel, AccessShareLock);
20024 0 : continue;
20025 : }
20026 :
20027 : /* construct an indexinfo to compare existing indexes against */
20028 438 : info = BuildIndexInfo(idxRel);
20029 438 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20030 : RelationGetDescr(rel),
20031 : false);
20032 438 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
20033 :
20034 : /*
20035 : * Scan the list of existing indexes in the partition-to-be, and mark
20036 : * the first matching, valid, unattached one we find, if any, as
20037 : * partition of the parent index. If we find one, we're done.
20038 : */
20039 498 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20040 : {
20041 262 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20042 262 : Oid cldConstrOid = InvalidOid;
20043 :
20044 : /* does this index have a parent? if so, can't use it */
20045 262 : if (attachrelIdxRels[i]->rd_rel->relispartition)
20046 12 : continue;
20047 :
20048 : /* If this index is invalid, can't use it */
20049 250 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
20050 6 : continue;
20051 :
20052 244 : if (CompareIndexInfo(attachInfos[i], info,
20053 244 : attachrelIdxRels[i]->rd_indcollation,
20054 244 : idxRel->rd_indcollation,
20055 244 : attachrelIdxRels[i]->rd_opfamily,
20056 244 : idxRel->rd_opfamily,
20057 : attmap))
20058 : {
20059 : /*
20060 : * If this index is being created in the parent because of a
20061 : * constraint, then the child needs to have a constraint also,
20062 : * so look for one. If there is no such constraint, this
20063 : * index is no good, so keep looking.
20064 : */
20065 208 : if (OidIsValid(constraintOid))
20066 : {
20067 : cldConstrOid =
20068 110 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
20069 : cldIdxId);
20070 : /* no dice */
20071 110 : if (!OidIsValid(cldConstrOid))
20072 6 : continue;
20073 :
20074 : /* Ensure they're both the same type of constraint */
20075 208 : if (get_constraint_type(constraintOid) !=
20076 104 : get_constraint_type(cldConstrOid))
20077 0 : continue;
20078 : }
20079 :
20080 : /* bingo. */
20081 202 : IndexSetParentIndex(attachrelIdxRels[i], idx);
20082 202 : if (OidIsValid(constraintOid))
20083 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20084 : RelationGetRelid(attachrel));
20085 202 : found = true;
20086 :
20087 202 : CommandCounterIncrement();
20088 202 : break;
20089 : }
20090 : }
20091 :
20092 : /*
20093 : * If no suitable index was found in the partition-to-be, create one
20094 : * now. Note that if this is a PK, not-null constraints must already
20095 : * exist.
20096 : */
20097 438 : if (!found)
20098 : {
20099 : IndexStmt *stmt;
20100 : Oid conOid;
20101 :
20102 236 : stmt = generateClonedIndexStmt(NULL,
20103 : idxRel, attmap,
20104 : &conOid);
20105 236 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
20106 : RelationGetRelid(idxRel),
20107 : conOid,
20108 : -1,
20109 : true, false, false, false, false);
20110 : }
20111 :
20112 420 : index_close(idxRel, AccessShareLock);
20113 : }
20114 :
20115 2018 : out:
20116 : /* Clean up. */
20117 2370 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20118 352 : index_close(attachrelIdxRels[i], AccessShareLock);
20119 2018 : MemoryContextSwitchTo(oldcxt);
20120 2018 : MemoryContextDelete(cxt);
20121 2018 : }
20122 :
20123 : /*
20124 : * CloneRowTriggersToPartition
20125 : * subroutine for ATExecAttachPartition/DefineRelation to create row
20126 : * triggers on partitions
20127 : */
20128 : static void
20129 2438 : CloneRowTriggersToPartition(Relation parent, Relation partition)
20130 : {
20131 : Relation pg_trigger;
20132 : ScanKeyData key;
20133 : SysScanDesc scan;
20134 : HeapTuple tuple;
20135 : MemoryContext perTupCxt;
20136 :
20137 2438 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20138 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20139 2438 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20140 2438 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20141 : true, NULL, 1, &key);
20142 :
20143 2438 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
20144 : "clone trig", ALLOCSET_SMALL_SIZES);
20145 :
20146 4066 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20147 : {
20148 1634 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20149 : CreateTrigStmt *trigStmt;
20150 1634 : Node *qual = NULL;
20151 : Datum value;
20152 : bool isnull;
20153 1634 : List *cols = NIL;
20154 1634 : List *trigargs = NIL;
20155 : MemoryContext oldcxt;
20156 :
20157 : /*
20158 : * Ignore statement-level triggers; those are not cloned.
20159 : */
20160 1634 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20161 1478 : continue;
20162 :
20163 : /*
20164 : * Don't clone internal triggers, because the constraint cloning code
20165 : * will.
20166 : */
20167 1634 : if (trigForm->tgisinternal)
20168 1478 : continue;
20169 :
20170 : /*
20171 : * Complain if we find an unexpected trigger type.
20172 : */
20173 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20174 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
20175 0 : elog(ERROR, "unexpected trigger \"%s\" found",
20176 : NameStr(trigForm->tgname));
20177 :
20178 : /* Use short-lived context for CREATE TRIGGER */
20179 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
20180 :
20181 : /*
20182 : * If there is a WHEN clause, generate a 'cooked' version of it that's
20183 : * appropriate for the partition.
20184 : */
20185 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20186 : RelationGetDescr(pg_trigger), &isnull);
20187 156 : if (!isnull)
20188 : {
20189 6 : qual = stringToNode(TextDatumGetCString(value));
20190 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20191 : partition, parent);
20192 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20193 : partition, parent);
20194 : }
20195 :
20196 : /*
20197 : * If there is a column list, transform it to a list of column names.
20198 : * Note we don't need to map this list in any way ...
20199 : */
20200 156 : if (trigForm->tgattr.dim1 > 0)
20201 : {
20202 : int i;
20203 :
20204 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
20205 : {
20206 : Form_pg_attribute col;
20207 :
20208 6 : col = TupleDescAttr(parent->rd_att,
20209 6 : trigForm->tgattr.values[i] - 1);
20210 6 : cols = lappend(cols,
20211 6 : makeString(pstrdup(NameStr(col->attname))));
20212 : }
20213 : }
20214 :
20215 : /* Reconstruct trigger arguments list. */
20216 156 : if (trigForm->tgnargs > 0)
20217 : {
20218 : char *p;
20219 :
20220 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20221 : RelationGetDescr(pg_trigger), &isnull);
20222 12 : if (isnull)
20223 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20224 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
20225 :
20226 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20227 :
20228 36 : for (int i = 0; i < trigForm->tgnargs; i++)
20229 : {
20230 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
20231 24 : p += strlen(p) + 1;
20232 : }
20233 : }
20234 :
20235 156 : trigStmt = makeNode(CreateTrigStmt);
20236 156 : trigStmt->replace = false;
20237 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20238 156 : trigStmt->trigname = NameStr(trigForm->tgname);
20239 156 : trigStmt->relation = NULL;
20240 156 : trigStmt->funcname = NULL; /* passed separately */
20241 156 : trigStmt->args = trigargs;
20242 156 : trigStmt->row = true;
20243 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20244 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20245 156 : trigStmt->columns = cols;
20246 156 : trigStmt->whenClause = NULL; /* passed separately */
20247 156 : trigStmt->transitionRels = NIL; /* not supported at present */
20248 156 : trigStmt->deferrable = trigForm->tgdeferrable;
20249 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
20250 156 : trigStmt->constrrel = NULL; /* passed separately */
20251 :
20252 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20253 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20254 : trigForm->tgfoid, trigForm->oid, qual,
20255 156 : false, true, trigForm->tgenabled);
20256 :
20257 150 : MemoryContextSwitchTo(oldcxt);
20258 150 : MemoryContextReset(perTupCxt);
20259 : }
20260 :
20261 2432 : MemoryContextDelete(perTupCxt);
20262 :
20263 2432 : systable_endscan(scan);
20264 2432 : table_close(pg_trigger, RowExclusiveLock);
20265 2432 : }
20266 :
20267 : /*
20268 : * ALTER TABLE DETACH PARTITION
20269 : *
20270 : * Return the address of the relation that is no longer a partition of rel.
20271 : *
20272 : * If concurrent mode is requested, we run in two transactions. A side-
20273 : * effect is that this command cannot run in a multi-part ALTER TABLE.
20274 : * Currently, that's enforced by the grammar.
20275 : *
20276 : * The strategy for concurrency is to first modify the partition's
20277 : * pg_inherit catalog row to make it visible to everyone that the
20278 : * partition is detached, lock the partition against writes, and commit
20279 : * the transaction; anyone who requests the partition descriptor from
20280 : * that point onwards has to ignore such a partition. In a second
20281 : * transaction, we wait until all transactions that could have seen the
20282 : * partition as attached are gone, then we remove the rest of partition
20283 : * metadata (pg_inherits and pg_class.relpartbounds).
20284 : */
20285 : static ObjectAddress
20286 558 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20287 : RangeVar *name, bool concurrent)
20288 : {
20289 : Relation partRel;
20290 : ObjectAddress address;
20291 : Oid defaultPartOid;
20292 :
20293 : /*
20294 : * We must lock the default partition, because detaching this partition
20295 : * will change its partition constraint.
20296 : */
20297 : defaultPartOid =
20298 558 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20299 558 : if (OidIsValid(defaultPartOid))
20300 : {
20301 : /*
20302 : * Concurrent detaching when a default partition exists is not
20303 : * supported. The main problem is that the default partition
20304 : * constraint would change. And there's a definitional problem: what
20305 : * should happen to the tuples that are being inserted that belong to
20306 : * the partition being detached? Putting them on the partition being
20307 : * detached would be wrong, since they'd become "lost" after the
20308 : * detaching completes but we cannot put them in the default partition
20309 : * either until we alter its partition constraint.
20310 : *
20311 : * I think we could solve this problem if we effected the constraint
20312 : * change before committing the first transaction. But the lock would
20313 : * have to remain AEL and it would cause concurrent query planning to
20314 : * be blocked, so changing it that way would be even worse.
20315 : */
20316 106 : if (concurrent)
20317 12 : ereport(ERROR,
20318 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20319 : errmsg("cannot detach partitions concurrently when a default partition exists")));
20320 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20321 : }
20322 :
20323 : /*
20324 : * In concurrent mode, the partition is locked with share-update-exclusive
20325 : * in the first transaction. This allows concurrent transactions to be
20326 : * doing DML to the partition.
20327 : */
20328 546 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20329 : AccessExclusiveLock);
20330 :
20331 : /*
20332 : * Check inheritance conditions and either delete the pg_inherits row (in
20333 : * non-concurrent mode) or just set the inhdetachpending flag.
20334 : */
20335 534 : if (!concurrent)
20336 388 : RemoveInheritance(partRel, rel, false);
20337 : else
20338 146 : MarkInheritDetached(partRel, rel);
20339 :
20340 : /*
20341 : * Ensure that foreign keys still hold after this detach. This keeps
20342 : * locks on the referencing tables, which prevents concurrent transactions
20343 : * from adding rows that we wouldn't see. For this to work in concurrent
20344 : * mode, it is critical that the partition appears as no longer attached
20345 : * for the RI queries as soon as the first transaction commits.
20346 : */
20347 514 : ATDetachCheckNoForeignKeyRefs(partRel);
20348 :
20349 : /*
20350 : * Concurrent mode has to work harder; first we add a new constraint to
20351 : * the partition that matches the partition constraint. Then we close our
20352 : * existing transaction, and in a new one wait for all processes to catch
20353 : * up on the catalog updates we've done so far; at that point we can
20354 : * complete the operation.
20355 : */
20356 480 : if (concurrent)
20357 : {
20358 : Oid partrelid,
20359 : parentrelid;
20360 : LOCKTAG tag;
20361 : char *parentrelname;
20362 : char *partrelname;
20363 :
20364 : /*
20365 : * Add a new constraint to the partition being detached, which
20366 : * supplants the partition constraint (unless there is one already).
20367 : */
20368 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
20369 :
20370 : /*
20371 : * We're almost done now; the only traces that remain are the
20372 : * pg_inherits tuple and the partition's relpartbounds. Before we can
20373 : * remove those, we need to wait until all transactions that know that
20374 : * this is a partition are gone.
20375 : */
20376 :
20377 : /*
20378 : * Remember relation OIDs to re-acquire them later; and relation names
20379 : * too, for error messages if something is dropped in between.
20380 : */
20381 140 : partrelid = RelationGetRelid(partRel);
20382 140 : parentrelid = RelationGetRelid(rel);
20383 140 : parentrelname = MemoryContextStrdup(PortalContext,
20384 140 : RelationGetRelationName(rel));
20385 140 : partrelname = MemoryContextStrdup(PortalContext,
20386 140 : RelationGetRelationName(partRel));
20387 :
20388 : /* Invalidate relcache entries for the parent -- must be before close */
20389 140 : CacheInvalidateRelcache(rel);
20390 :
20391 140 : table_close(partRel, NoLock);
20392 140 : table_close(rel, NoLock);
20393 140 : tab->rel = NULL;
20394 :
20395 : /* Make updated catalog entry visible */
20396 140 : PopActiveSnapshot();
20397 140 : CommitTransactionCommand();
20398 :
20399 140 : StartTransactionCommand();
20400 :
20401 : /*
20402 : * Now wait. This ensures that all queries that were planned
20403 : * including the partition are finished before we remove the rest of
20404 : * catalog entries. We don't need or indeed want to acquire this
20405 : * lock, though -- that would block later queries.
20406 : *
20407 : * We don't need to concern ourselves with waiting for a lock on the
20408 : * partition itself, since we will acquire AccessExclusiveLock below.
20409 : */
20410 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20411 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
20412 :
20413 : /*
20414 : * Now acquire locks in both relations again. Note they may have been
20415 : * removed in the meantime, so care is required.
20416 : */
20417 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20418 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
20419 :
20420 : /* If the relations aren't there, something bad happened; bail out */
20421 90 : if (rel == NULL)
20422 : {
20423 0 : if (partRel != NULL) /* shouldn't happen */
20424 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20425 : partrelname);
20426 0 : ereport(ERROR,
20427 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20428 : errmsg("partitioned table \"%s\" was removed concurrently",
20429 : parentrelname)));
20430 : }
20431 90 : if (partRel == NULL)
20432 0 : ereport(ERROR,
20433 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20434 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
20435 :
20436 90 : tab->rel = rel;
20437 : }
20438 :
20439 : /* Do the final part of detaching */
20440 430 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20441 :
20442 428 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20443 :
20444 : /* keep our lock until commit */
20445 428 : table_close(partRel, NoLock);
20446 :
20447 428 : return address;
20448 : }
20449 :
20450 : /*
20451 : * Second part of ALTER TABLE .. DETACH.
20452 : *
20453 : * This is separate so that it can be run independently when the second
20454 : * transaction of the concurrent algorithm fails (crash or abort).
20455 : */
20456 : static void
20457 444 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20458 : Oid defaultPartOid)
20459 : {
20460 : Relation classRel;
20461 : List *fks;
20462 : ListCell *cell;
20463 : List *indexes;
20464 : Datum new_val[Natts_pg_class];
20465 : bool new_null[Natts_pg_class],
20466 : new_repl[Natts_pg_class];
20467 : HeapTuple tuple,
20468 : newtuple;
20469 444 : Relation trigrel = NULL;
20470 444 : List *fkoids = NIL;
20471 :
20472 444 : if (concurrent)
20473 : {
20474 : /*
20475 : * We can remove the pg_inherits row now. (In the non-concurrent case,
20476 : * this was already done).
20477 : */
20478 104 : RemoveInheritance(partRel, rel, true);
20479 : }
20480 :
20481 : /* Drop any triggers that were cloned on creation/attach. */
20482 444 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
20483 :
20484 : /*
20485 : * Detach any foreign keys that are inherited. This includes creating
20486 : * additional action triggers.
20487 : */
20488 444 : fks = copyObject(RelationGetFKeyList(partRel));
20489 444 : if (fks != NIL)
20490 72 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20491 :
20492 : /*
20493 : * It's possible that the partition being detached has a foreign key that
20494 : * references a partitioned table. When that happens, there are multiple
20495 : * pg_constraint rows for the partition: one points to the partitioned
20496 : * table itself, while the others point to each of its partitions. Only
20497 : * the topmost one is to be considered here; the child constraints must be
20498 : * left alone, because conceptually those aren't coming from our parent
20499 : * partitioned table, but from this partition itself.
20500 : *
20501 : * We implement this by collecting all the constraint OIDs in a first scan
20502 : * of the FK array, and skipping in the loop below those constraints whose
20503 : * parents are listed here.
20504 : */
20505 1044 : foreach_node(ForeignKeyCacheInfo, fk, fks)
20506 156 : fkoids = lappend_oid(fkoids, fk->conoid);
20507 :
20508 600 : foreach(cell, fks)
20509 : {
20510 156 : ForeignKeyCacheInfo *fk = lfirst(cell);
20511 : HeapTuple contup;
20512 : Form_pg_constraint conform;
20513 : Oid insertTriggerOid,
20514 : updateTriggerOid;
20515 :
20516 156 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20517 156 : if (!HeapTupleIsValid(contup))
20518 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20519 156 : conform = (Form_pg_constraint) GETSTRUCT(contup);
20520 :
20521 : /*
20522 : * Consider only inherited foreign keys, and only if their parents
20523 : * aren't in the list.
20524 : */
20525 156 : if (conform->contype != CONSTRAINT_FOREIGN ||
20526 288 : !OidIsValid(conform->conparentid) ||
20527 132 : list_member_oid(fkoids, conform->conparentid))
20528 : {
20529 66 : ReleaseSysCache(contup);
20530 66 : continue;
20531 : }
20532 :
20533 : /*
20534 : * The constraint on this table must be marked no longer a child of
20535 : * the parent's constraint, as do its check triggers.
20536 : */
20537 90 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
20538 :
20539 : /*
20540 : * Also, look up the partition's "check" triggers corresponding to the
20541 : * constraint being detached and detach them from the parent triggers.
20542 : */
20543 90 : GetForeignKeyCheckTriggers(trigrel,
20544 : fk->conoid, fk->confrelid, fk->conrelid,
20545 : &insertTriggerOid, &updateTriggerOid);
20546 : Assert(OidIsValid(insertTriggerOid));
20547 90 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20548 : RelationGetRelid(partRel));
20549 : Assert(OidIsValid(updateTriggerOid));
20550 90 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20551 : RelationGetRelid(partRel));
20552 :
20553 : /*
20554 : * Lastly, create the action triggers on the referenced table, using
20555 : * addFkRecurseReferenced, which requires some elaborate setup (so put
20556 : * it in a separate block). While at it, if the table is partitioned,
20557 : * that function will recurse to create the pg_constraint rows and
20558 : * action triggers for each partition.
20559 : *
20560 : * Note there's no need to do addFkConstraint() here, because the
20561 : * pg_constraint row already exists.
20562 : */
20563 : {
20564 : Constraint *fkconstraint;
20565 : int numfks;
20566 : AttrNumber conkey[INDEX_MAX_KEYS];
20567 : AttrNumber confkey[INDEX_MAX_KEYS];
20568 : Oid conpfeqop[INDEX_MAX_KEYS];
20569 : Oid conppeqop[INDEX_MAX_KEYS];
20570 : Oid conffeqop[INDEX_MAX_KEYS];
20571 : int numfkdelsetcols;
20572 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
20573 : Relation refdRel;
20574 :
20575 90 : DeconstructFkConstraintRow(contup,
20576 : &numfks,
20577 : conkey,
20578 : confkey,
20579 : conpfeqop,
20580 : conppeqop,
20581 : conffeqop,
20582 : &numfkdelsetcols,
20583 : confdelsetcols);
20584 :
20585 : /* Create a synthetic node we'll use throughout */
20586 90 : fkconstraint = makeNode(Constraint);
20587 90 : fkconstraint->contype = CONSTRAINT_FOREIGN;
20588 90 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
20589 90 : fkconstraint->deferrable = conform->condeferrable;
20590 90 : fkconstraint->initdeferred = conform->condeferred;
20591 90 : fkconstraint->skip_validation = true;
20592 90 : fkconstraint->initially_valid = true;
20593 : /* a few irrelevant fields omitted here */
20594 90 : fkconstraint->pktable = NULL;
20595 90 : fkconstraint->fk_attrs = NIL;
20596 90 : fkconstraint->pk_attrs = NIL;
20597 90 : fkconstraint->fk_matchtype = conform->confmatchtype;
20598 90 : fkconstraint->fk_upd_action = conform->confupdtype;
20599 90 : fkconstraint->fk_del_action = conform->confdeltype;
20600 90 : fkconstraint->fk_del_set_cols = NIL;
20601 90 : fkconstraint->old_conpfeqop = NIL;
20602 90 : fkconstraint->old_pktable_oid = InvalidOid;
20603 90 : fkconstraint->location = -1;
20604 :
20605 : /* set up colnames, used to generate the constraint name */
20606 228 : for (int i = 0; i < numfks; i++)
20607 : {
20608 : Form_pg_attribute att;
20609 :
20610 138 : att = TupleDescAttr(RelationGetDescr(partRel),
20611 138 : conkey[i] - 1);
20612 :
20613 138 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
20614 138 : makeString(NameStr(att->attname)));
20615 : }
20616 :
20617 90 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
20618 :
20619 90 : addFkRecurseReferenced(fkconstraint, partRel,
20620 : refdRel,
20621 : conform->conindid,
20622 : fk->conoid,
20623 : numfks,
20624 : confkey,
20625 : conkey,
20626 : conpfeqop,
20627 : conppeqop,
20628 : conffeqop,
20629 : numfkdelsetcols,
20630 : confdelsetcols,
20631 : true,
20632 : InvalidOid, InvalidOid,
20633 90 : conform->conperiod);
20634 90 : table_close(refdRel, NoLock); /* keep lock till end of xact */
20635 : }
20636 :
20637 90 : ReleaseSysCache(contup);
20638 : }
20639 444 : list_free_deep(fks);
20640 444 : if (trigrel)
20641 72 : table_close(trigrel, RowExclusiveLock);
20642 :
20643 : /*
20644 : * Any sub-constraints that are in the referenced-side of a larger
20645 : * constraint have to be removed. This partition is no longer part of the
20646 : * key space of the constraint.
20647 : */
20648 480 : foreach(cell, GetParentedForeignKeyRefs(partRel))
20649 : {
20650 38 : Oid constrOid = lfirst_oid(cell);
20651 : ObjectAddress constraint;
20652 :
20653 38 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20654 38 : deleteDependencyRecordsForClass(ConstraintRelationId,
20655 : constrOid,
20656 : ConstraintRelationId,
20657 : DEPENDENCY_INTERNAL);
20658 38 : CommandCounterIncrement();
20659 :
20660 38 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20661 38 : performDeletion(&constraint, DROP_RESTRICT, 0);
20662 : }
20663 :
20664 : /* Now we can detach indexes */
20665 442 : indexes = RelationGetIndexList(partRel);
20666 624 : foreach(cell, indexes)
20667 : {
20668 182 : Oid idxid = lfirst_oid(cell);
20669 : Oid parentidx;
20670 : Relation idx;
20671 : Oid constrOid;
20672 : Oid parentConstrOid;
20673 :
20674 182 : if (!has_superclass(idxid))
20675 12 : continue;
20676 :
20677 170 : parentidx = get_partition_parent(idxid, false);
20678 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
20679 :
20680 170 : idx = index_open(idxid, AccessExclusiveLock);
20681 170 : IndexSetParentIndex(idx, InvalidOid);
20682 :
20683 : /*
20684 : * If there's a constraint associated with the index, detach it too.
20685 : * Careful: it is possible for a constraint index in a partition to be
20686 : * the child of a non-constraint index, so verify whether the parent
20687 : * index does actually have a constraint.
20688 : */
20689 170 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20690 : idxid);
20691 170 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
20692 : parentidx);
20693 170 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
20694 72 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20695 :
20696 170 : index_close(idx, NoLock);
20697 : }
20698 :
20699 : /* Update pg_class tuple */
20700 442 : classRel = table_open(RelationRelationId, RowExclusiveLock);
20701 442 : tuple = SearchSysCacheCopy1(RELOID,
20702 : ObjectIdGetDatum(RelationGetRelid(partRel)));
20703 442 : if (!HeapTupleIsValid(tuple))
20704 0 : elog(ERROR, "cache lookup failed for relation %u",
20705 : RelationGetRelid(partRel));
20706 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20707 :
20708 : /* Clear relpartbound and reset relispartition */
20709 442 : memset(new_val, 0, sizeof(new_val));
20710 442 : memset(new_null, false, sizeof(new_null));
20711 442 : memset(new_repl, false, sizeof(new_repl));
20712 442 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20713 442 : new_null[Anum_pg_class_relpartbound - 1] = true;
20714 442 : new_repl[Anum_pg_class_relpartbound - 1] = true;
20715 442 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20716 : new_val, new_null, new_repl);
20717 :
20718 442 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20719 442 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20720 442 : heap_freetuple(newtuple);
20721 442 : table_close(classRel, RowExclusiveLock);
20722 :
20723 : /*
20724 : * Drop identity property from all identity columns of partition.
20725 : */
20726 1258 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20727 : {
20728 816 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20729 :
20730 816 : if (!attr->attisdropped && attr->attidentity)
20731 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20732 : AccessExclusiveLock, true, true);
20733 : }
20734 :
20735 442 : if (OidIsValid(defaultPartOid))
20736 : {
20737 : /*
20738 : * If the relation being detached is the default partition itself,
20739 : * remove it from the parent's pg_partitioned_table entry.
20740 : *
20741 : * If not, we must invalidate default partition's relcache entry, as
20742 : * in StorePartitionBound: its partition constraint depends on every
20743 : * other partition's partition constraint.
20744 : */
20745 46 : if (RelationGetRelid(partRel) == defaultPartOid)
20746 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20747 : else
20748 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
20749 : }
20750 :
20751 : /*
20752 : * Invalidate the parent's relcache so that the partition is no longer
20753 : * included in its partition descriptor.
20754 : */
20755 442 : CacheInvalidateRelcache(rel);
20756 :
20757 : /*
20758 : * If the partition we just detached is partitioned itself, invalidate
20759 : * relcache for all descendent partitions too to ensure that their
20760 : * rd_partcheck expression trees are rebuilt; must lock partitions before
20761 : * doing so, using the same lockmode as what partRel has been locked with
20762 : * by the caller.
20763 : */
20764 442 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20765 : {
20766 : List *children;
20767 :
20768 56 : children = find_all_inheritors(RelationGetRelid(partRel),
20769 : AccessExclusiveLock, NULL);
20770 180 : foreach(cell, children)
20771 : {
20772 124 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20773 : }
20774 : }
20775 442 : }
20776 :
20777 : /*
20778 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20779 : *
20780 : * To use when a DETACH PARTITION command previously did not run to
20781 : * completion; this completes the detaching process.
20782 : */
20783 : static ObjectAddress
20784 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20785 : {
20786 : Relation partRel;
20787 : ObjectAddress address;
20788 14 : Snapshot snap = GetActiveSnapshot();
20789 :
20790 14 : partRel = table_openrv(name, AccessExclusiveLock);
20791 :
20792 : /*
20793 : * Wait until existing snapshots are gone. This is important if the
20794 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20795 : * user could immediately run DETACH FINALIZE without actually waiting for
20796 : * existing transactions. We must not complete the detach action until
20797 : * all such queries are complete (otherwise we would present them with an
20798 : * inconsistent view of catalogs).
20799 : */
20800 14 : WaitForOlderSnapshots(snap->xmin, false);
20801 :
20802 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20803 :
20804 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20805 :
20806 14 : table_close(partRel, NoLock);
20807 :
20808 14 : return address;
20809 : }
20810 :
20811 : /*
20812 : * DetachAddConstraintIfNeeded
20813 : * Subroutine for ATExecDetachPartition. Create a constraint that
20814 : * takes the place of the partition constraint, but avoid creating
20815 : * a dupe if a constraint already exists which implies the needed
20816 : * constraint.
20817 : */
20818 : static void
20819 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20820 : {
20821 : List *constraintExpr;
20822 :
20823 140 : constraintExpr = RelationGetPartitionQual(partRel);
20824 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20825 :
20826 : /*
20827 : * Avoid adding a new constraint if the needed constraint is implied by an
20828 : * existing constraint
20829 : */
20830 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20831 : {
20832 : AlteredTableInfo *tab;
20833 : Constraint *n;
20834 :
20835 134 : tab = ATGetQueueEntry(wqueue, partRel);
20836 :
20837 : /* Add constraint on partition, equivalent to the partition constraint */
20838 134 : n = makeNode(Constraint);
20839 134 : n->contype = CONSTR_CHECK;
20840 134 : n->conname = NULL;
20841 134 : n->location = -1;
20842 134 : n->is_no_inherit = false;
20843 134 : n->raw_expr = NULL;
20844 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20845 134 : n->is_enforced = true;
20846 134 : n->initially_valid = true;
20847 134 : n->skip_validation = true;
20848 : /* It's a re-add, since it nominally already exists */
20849 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20850 : true, false, true, ShareUpdateExclusiveLock);
20851 : }
20852 140 : }
20853 :
20854 : /*
20855 : * DropClonedTriggersFromPartition
20856 : * subroutine for ATExecDetachPartition to remove any triggers that were
20857 : * cloned to the partition when it was created-as-partition or attached.
20858 : * This undoes what CloneRowTriggersToPartition did.
20859 : */
20860 : static void
20861 444 : DropClonedTriggersFromPartition(Oid partitionId)
20862 : {
20863 : ScanKeyData skey;
20864 : SysScanDesc scan;
20865 : HeapTuple trigtup;
20866 : Relation tgrel;
20867 : ObjectAddresses *objects;
20868 :
20869 444 : objects = new_object_addresses();
20870 :
20871 : /*
20872 : * Scan pg_trigger to search for all triggers on this rel.
20873 : */
20874 444 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20875 : F_OIDEQ, ObjectIdGetDatum(partitionId));
20876 444 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20877 444 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20878 : true, NULL, 1, &skey);
20879 766 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20880 : {
20881 322 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20882 : ObjectAddress trig;
20883 :
20884 : /* Ignore triggers that weren't cloned */
20885 322 : if (!OidIsValid(pg_trigger->tgparentid))
20886 304 : continue;
20887 :
20888 : /*
20889 : * Ignore internal triggers that are implementation objects of foreign
20890 : * keys, because these will be detached when the foreign keys
20891 : * themselves are.
20892 : */
20893 274 : if (OidIsValid(pg_trigger->tgconstrrelid))
20894 256 : continue;
20895 :
20896 : /*
20897 : * This is ugly, but necessary: remove the dependency markings on the
20898 : * trigger so that it can be removed.
20899 : */
20900 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20901 : TriggerRelationId,
20902 : DEPENDENCY_PARTITION_PRI);
20903 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20904 : RelationRelationId,
20905 : DEPENDENCY_PARTITION_SEC);
20906 :
20907 : /* remember this trigger to remove it below */
20908 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20909 18 : add_exact_object_address(&trig, objects);
20910 : }
20911 :
20912 : /* make the dependency removal visible to the deletion below */
20913 444 : CommandCounterIncrement();
20914 444 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20915 :
20916 : /* done */
20917 444 : free_object_addresses(objects);
20918 444 : systable_endscan(scan);
20919 444 : table_close(tgrel, RowExclusiveLock);
20920 444 : }
20921 :
20922 : /*
20923 : * Before acquiring lock on an index, acquire the same lock on the owning
20924 : * table.
20925 : */
20926 : struct AttachIndexCallbackState
20927 : {
20928 : Oid partitionOid;
20929 : Oid parentTblOid;
20930 : bool lockedParentTbl;
20931 : };
20932 :
20933 : static void
20934 398 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20935 : void *arg)
20936 : {
20937 : struct AttachIndexCallbackState *state;
20938 : Form_pg_class classform;
20939 : HeapTuple tuple;
20940 :
20941 398 : state = (struct AttachIndexCallbackState *) arg;
20942 :
20943 398 : if (!state->lockedParentTbl)
20944 : {
20945 392 : LockRelationOid(state->parentTblOid, AccessShareLock);
20946 392 : state->lockedParentTbl = true;
20947 : }
20948 :
20949 : /*
20950 : * If we previously locked some other heap, and the name we're looking up
20951 : * no longer refers to an index on that relation, release the now-useless
20952 : * lock. XXX maybe we should do *after* we verify whether the index does
20953 : * not actually belong to the same relation ...
20954 : */
20955 398 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20956 : {
20957 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
20958 0 : state->partitionOid = InvalidOid;
20959 : }
20960 :
20961 : /* Didn't find a relation, so no need for locking or permission checks. */
20962 398 : if (!OidIsValid(relOid))
20963 6 : return;
20964 :
20965 392 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20966 392 : if (!HeapTupleIsValid(tuple))
20967 0 : return; /* concurrently dropped, so nothing to do */
20968 392 : classform = (Form_pg_class) GETSTRUCT(tuple);
20969 392 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20970 304 : classform->relkind != RELKIND_INDEX)
20971 6 : ereport(ERROR,
20972 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20973 : errmsg("\"%s\" is not an index", rv->relname)));
20974 386 : ReleaseSysCache(tuple);
20975 :
20976 : /*
20977 : * Since we need only examine the heap's tupledesc, an access share lock
20978 : * on it (preventing any DDL) is sufficient.
20979 : */
20980 386 : state->partitionOid = IndexGetRelation(relOid, false);
20981 386 : LockRelationOid(state->partitionOid, AccessShareLock);
20982 : }
20983 :
20984 : /*
20985 : * ALTER INDEX i1 ATTACH PARTITION i2
20986 : */
20987 : static ObjectAddress
20988 392 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20989 : {
20990 : Relation partIdx;
20991 : Relation partTbl;
20992 : Relation parentTbl;
20993 : ObjectAddress address;
20994 : Oid partIdxId;
20995 : Oid currParent;
20996 : struct AttachIndexCallbackState state;
20997 :
20998 : /*
20999 : * We need to obtain lock on the index 'name' to modify it, but we also
21000 : * need to read its owning table's tuple descriptor -- so we need to lock
21001 : * both. To avoid deadlocks, obtain lock on the table before doing so on
21002 : * the index. Furthermore, we need to examine the parent table of the
21003 : * partition, so lock that one too.
21004 : */
21005 392 : state.partitionOid = InvalidOid;
21006 392 : state.parentTblOid = parentIdx->rd_index->indrelid;
21007 392 : state.lockedParentTbl = false;
21008 : partIdxId =
21009 392 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
21010 : RangeVarCallbackForAttachIndex,
21011 : &state);
21012 : /* Not there? */
21013 380 : if (!OidIsValid(partIdxId))
21014 0 : ereport(ERROR,
21015 : (errcode(ERRCODE_UNDEFINED_OBJECT),
21016 : errmsg("index \"%s\" does not exist", name->relname)));
21017 :
21018 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21019 380 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
21020 :
21021 : /* we already hold locks on both tables, so this is safe: */
21022 380 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21023 380 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21024 :
21025 380 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21026 :
21027 : /* Silently do nothing if already in the right state */
21028 760 : currParent = partIdx->rd_rel->relispartition ?
21029 380 : get_partition_parent(partIdxId, false) : InvalidOid;
21030 380 : if (currParent != RelationGetRelid(parentIdx))
21031 : {
21032 : IndexInfo *childInfo;
21033 : IndexInfo *parentInfo;
21034 : AttrMap *attmap;
21035 : bool found;
21036 : int i;
21037 : PartitionDesc partDesc;
21038 : Oid constraintOid,
21039 356 : cldConstrId = InvalidOid;
21040 :
21041 : /*
21042 : * If this partition already has an index attached, refuse the
21043 : * operation.
21044 : */
21045 356 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21046 :
21047 350 : if (OidIsValid(currParent))
21048 0 : ereport(ERROR,
21049 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21050 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21051 : RelationGetRelationName(partIdx),
21052 : RelationGetRelationName(parentIdx)),
21053 : errdetail("Index \"%s\" is already attached to another index.",
21054 : RelationGetRelationName(partIdx))));
21055 :
21056 : /* Make sure it indexes a partition of the other index's table */
21057 350 : partDesc = RelationGetPartitionDesc(parentTbl, true);
21058 350 : found = false;
21059 552 : for (i = 0; i < partDesc->nparts; i++)
21060 : {
21061 546 : if (partDesc->oids[i] == state.partitionOid)
21062 : {
21063 344 : found = true;
21064 344 : break;
21065 : }
21066 : }
21067 350 : if (!found)
21068 6 : ereport(ERROR,
21069 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21070 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21071 : RelationGetRelationName(partIdx),
21072 : RelationGetRelationName(parentIdx)),
21073 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21074 : RelationGetRelationName(partIdx),
21075 : RelationGetRelationName(parentTbl))));
21076 :
21077 : /* Ensure the indexes are compatible */
21078 344 : childInfo = BuildIndexInfo(partIdx);
21079 344 : parentInfo = BuildIndexInfo(parentIdx);
21080 344 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21081 : RelationGetDescr(parentTbl),
21082 : false);
21083 344 : if (!CompareIndexInfo(childInfo, parentInfo,
21084 344 : partIdx->rd_indcollation,
21085 344 : parentIdx->rd_indcollation,
21086 344 : partIdx->rd_opfamily,
21087 344 : parentIdx->rd_opfamily,
21088 : attmap))
21089 42 : ereport(ERROR,
21090 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21091 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21092 : RelationGetRelationName(partIdx),
21093 : RelationGetRelationName(parentIdx)),
21094 : errdetail("The index definitions do not match.")));
21095 :
21096 : /*
21097 : * If there is a constraint in the parent, make sure there is one in
21098 : * the child too.
21099 : */
21100 302 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21101 : RelationGetRelid(parentIdx));
21102 :
21103 302 : if (OidIsValid(constraintOid))
21104 : {
21105 122 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
21106 : partIdxId);
21107 122 : if (!OidIsValid(cldConstrId))
21108 6 : ereport(ERROR,
21109 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21110 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21111 : RelationGetRelationName(partIdx),
21112 : RelationGetRelationName(parentIdx)),
21113 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21114 : RelationGetRelationName(parentIdx),
21115 : RelationGetRelationName(parentTbl),
21116 : RelationGetRelationName(partIdx))));
21117 : }
21118 :
21119 : /*
21120 : * If it's a primary key, make sure the columns in the partition are
21121 : * NOT NULL.
21122 : */
21123 296 : if (parentIdx->rd_index->indisprimary)
21124 92 : verifyPartitionIndexNotNull(childInfo, partTbl);
21125 :
21126 : /* All good -- do it */
21127 296 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21128 296 : if (OidIsValid(constraintOid))
21129 116 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
21130 : RelationGetRelid(partTbl));
21131 :
21132 296 : free_attrmap(attmap);
21133 :
21134 296 : validatePartitionedIndex(parentIdx, parentTbl);
21135 : }
21136 :
21137 320 : relation_close(parentTbl, AccessShareLock);
21138 : /* keep these locks till commit */
21139 320 : relation_close(partTbl, NoLock);
21140 320 : relation_close(partIdx, NoLock);
21141 :
21142 320 : return address;
21143 : }
21144 :
21145 : /*
21146 : * Verify whether the given partition already contains an index attached
21147 : * to the given partitioned index. If so, raise an error.
21148 : */
21149 : static void
21150 356 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21151 : {
21152 : Oid existingIdx;
21153 :
21154 356 : existingIdx = index_get_partition(partitionTbl,
21155 : RelationGetRelid(parentIdx));
21156 356 : if (OidIsValid(existingIdx))
21157 6 : ereport(ERROR,
21158 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21159 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21160 : RelationGetRelationName(partIdx),
21161 : RelationGetRelationName(parentIdx)),
21162 : errdetail("Another index is already attached for partition \"%s\".",
21163 : RelationGetRelationName(partitionTbl))));
21164 350 : }
21165 :
21166 : /*
21167 : * Verify whether the set of attached partition indexes to a parent index on
21168 : * a partitioned table is complete. If it is, mark the parent index valid.
21169 : *
21170 : * This should be called each time a partition index is attached.
21171 : */
21172 : static void
21173 338 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
21174 : {
21175 : Relation inheritsRel;
21176 : SysScanDesc scan;
21177 : ScanKeyData key;
21178 338 : int tuples = 0;
21179 : HeapTuple inhTup;
21180 338 : bool updated = false;
21181 :
21182 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21183 :
21184 : /*
21185 : * Scan pg_inherits for this parent index. Count each valid index we find
21186 : * (verifying the pg_index entry for each), and if we reach the total
21187 : * amount we expect, we can mark this parent index as valid.
21188 : */
21189 338 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21190 338 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21191 : BTEqualStrategyNumber, F_OIDEQ,
21192 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21193 338 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21194 : NULL, 1, &key);
21195 880 : while ((inhTup = systable_getnext(scan)) != NULL)
21196 : {
21197 542 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21198 : HeapTuple indTup;
21199 : Form_pg_index indexForm;
21200 :
21201 542 : indTup = SearchSysCache1(INDEXRELID,
21202 : ObjectIdGetDatum(inhForm->inhrelid));
21203 542 : if (!HeapTupleIsValid(indTup))
21204 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21205 542 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21206 542 : if (indexForm->indisvalid)
21207 484 : tuples += 1;
21208 542 : ReleaseSysCache(indTup);
21209 : }
21210 :
21211 : /* Done with pg_inherits */
21212 338 : systable_endscan(scan);
21213 338 : table_close(inheritsRel, AccessShareLock);
21214 :
21215 : /*
21216 : * If we found as many inherited indexes as the partitioned table has
21217 : * partitions, we're good; update pg_index to set indisvalid.
21218 : */
21219 338 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21220 : {
21221 : Relation idxRel;
21222 : HeapTuple indTup;
21223 : Form_pg_index indexForm;
21224 :
21225 168 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
21226 168 : indTup = SearchSysCacheCopy1(INDEXRELID,
21227 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21228 168 : if (!HeapTupleIsValid(indTup))
21229 0 : elog(ERROR, "cache lookup failed for index %u",
21230 : RelationGetRelid(partedIdx));
21231 168 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21232 :
21233 168 : indexForm->indisvalid = true;
21234 168 : updated = true;
21235 :
21236 168 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21237 :
21238 168 : table_close(idxRel, RowExclusiveLock);
21239 168 : heap_freetuple(indTup);
21240 : }
21241 :
21242 : /*
21243 : * If this index is in turn a partition of a larger index, validating it
21244 : * might cause the parent to become valid also. Try that.
21245 : */
21246 338 : if (updated && partedIdx->rd_rel->relispartition)
21247 : {
21248 : Oid parentIdxId,
21249 : parentTblId;
21250 : Relation parentIdx,
21251 : parentTbl;
21252 :
21253 : /* make sure we see the validation we just did */
21254 42 : CommandCounterIncrement();
21255 :
21256 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21257 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21258 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21259 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21260 : Assert(!parentIdx->rd_index->indisvalid);
21261 :
21262 42 : validatePartitionedIndex(parentIdx, parentTbl);
21263 :
21264 42 : relation_close(parentIdx, AccessExclusiveLock);
21265 42 : relation_close(parentTbl, AccessExclusiveLock);
21266 : }
21267 338 : }
21268 :
21269 : /*
21270 : * When attaching an index as a partition of a partitioned index which is a
21271 : * primary key, verify that all the columns in the partition are marked NOT
21272 : * NULL.
21273 : */
21274 : static void
21275 92 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
21276 : {
21277 186 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21278 : {
21279 94 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
21280 94 : iinfo->ii_IndexAttrNumbers[i] - 1);
21281 :
21282 94 : if (!att->attnotnull)
21283 0 : ereport(ERROR,
21284 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21285 : errmsg("invalid primary key definition"),
21286 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21287 : NameStr(att->attname),
21288 : RelationGetRelationName(partition)));
21289 : }
21290 92 : }
21291 :
21292 : /*
21293 : * Return an OID list of constraints that reference the given relation
21294 : * that are marked as having a parent constraints.
21295 : */
21296 : static List *
21297 958 : GetParentedForeignKeyRefs(Relation partition)
21298 : {
21299 : Relation pg_constraint;
21300 : HeapTuple tuple;
21301 : SysScanDesc scan;
21302 : ScanKeyData key[2];
21303 958 : List *constraints = NIL;
21304 :
21305 : /*
21306 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
21307 : * scan.
21308 : */
21309 1362 : if (RelationGetIndexList(partition) == NIL ||
21310 404 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
21311 : INDEX_ATTR_BITMAP_KEY)))
21312 710 : return NIL;
21313 :
21314 : /* Search for constraints referencing this table */
21315 248 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21316 248 : ScanKeyInit(&key[0],
21317 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21318 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21319 248 : ScanKeyInit(&key[1],
21320 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
21321 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21322 :
21323 : /* XXX This is a seqscan, as we don't have a usable index */
21324 248 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21325 372 : while ((tuple = systable_getnext(scan)) != NULL)
21326 : {
21327 124 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21328 :
21329 : /*
21330 : * We only need to process constraints that are part of larger ones.
21331 : */
21332 124 : if (!OidIsValid(constrForm->conparentid))
21333 0 : continue;
21334 :
21335 124 : constraints = lappend_oid(constraints, constrForm->oid);
21336 : }
21337 :
21338 248 : systable_endscan(scan);
21339 248 : table_close(pg_constraint, AccessShareLock);
21340 :
21341 248 : return constraints;
21342 : }
21343 :
21344 : /*
21345 : * During DETACH PARTITION, verify that any foreign keys pointing to the
21346 : * partitioned table would not become invalid. An error is raised if any
21347 : * referenced values exist.
21348 : */
21349 : static void
21350 514 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21351 : {
21352 : List *constraints;
21353 : ListCell *cell;
21354 :
21355 514 : constraints = GetParentedForeignKeyRefs(partition);
21356 :
21357 566 : foreach(cell, constraints)
21358 : {
21359 86 : Oid constrOid = lfirst_oid(cell);
21360 : HeapTuple tuple;
21361 : Form_pg_constraint constrForm;
21362 : Relation rel;
21363 86 : Trigger trig = {0};
21364 :
21365 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21366 86 : if (!HeapTupleIsValid(tuple))
21367 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21368 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21369 :
21370 : Assert(OidIsValid(constrForm->conparentid));
21371 : Assert(constrForm->confrelid == RelationGetRelid(partition));
21372 :
21373 : /* prevent data changes into the referencing table until commit */
21374 86 : rel = table_open(constrForm->conrelid, ShareLock);
21375 :
21376 86 : trig.tgoid = InvalidOid;
21377 86 : trig.tgname = NameStr(constrForm->conname);
21378 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
21379 86 : trig.tgisinternal = true;
21380 86 : trig.tgconstrrelid = RelationGetRelid(partition);
21381 86 : trig.tgconstrindid = constrForm->conindid;
21382 86 : trig.tgconstraint = constrForm->oid;
21383 86 : trig.tgdeferrable = false;
21384 86 : trig.tginitdeferred = false;
21385 : /* we needn't fill in remaining fields */
21386 :
21387 86 : RI_PartitionRemove_Check(&trig, rel, partition);
21388 :
21389 52 : ReleaseSysCache(tuple);
21390 :
21391 52 : table_close(rel, NoLock);
21392 : }
21393 480 : }
21394 :
21395 : /*
21396 : * resolve column compression specification to compression method.
21397 : */
21398 : static char
21399 236650 : GetAttributeCompression(Oid atttypid, const char *compression)
21400 : {
21401 : char cmethod;
21402 :
21403 236650 : if (compression == NULL || strcmp(compression, "default") == 0)
21404 236494 : return InvalidCompressionMethod;
21405 :
21406 : /*
21407 : * To specify a nondefault method, the column data type must be toastable.
21408 : * Note this says nothing about whether the column's attstorage setting
21409 : * permits compression; we intentionally allow attstorage and
21410 : * attcompression to be independent. But with a non-toastable type,
21411 : * attstorage could not be set to a value that would permit compression.
21412 : *
21413 : * We don't actually need to enforce this, since nothing bad would happen
21414 : * if attcompression were non-default; it would never be consulted. But
21415 : * it seems more user-friendly to complain about a certainly-useless
21416 : * attempt to set the property.
21417 : */
21418 156 : if (!TypeIsToastable(atttypid))
21419 6 : ereport(ERROR,
21420 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21421 : errmsg("column data type %s does not support compression",
21422 : format_type_be(atttypid))));
21423 :
21424 150 : cmethod = CompressionNameToMethod(compression);
21425 150 : if (!CompressionMethodIsValid(cmethod))
21426 12 : ereport(ERROR,
21427 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21428 : errmsg("invalid compression method \"%s\"", compression)));
21429 :
21430 138 : return cmethod;
21431 : }
21432 :
21433 : /*
21434 : * resolve column storage specification
21435 : */
21436 : static char
21437 248 : GetAttributeStorage(Oid atttypid, const char *storagemode)
21438 : {
21439 248 : char cstorage = 0;
21440 :
21441 248 : if (pg_strcasecmp(storagemode, "plain") == 0)
21442 50 : cstorage = TYPSTORAGE_PLAIN;
21443 198 : else if (pg_strcasecmp(storagemode, "external") == 0)
21444 156 : cstorage = TYPSTORAGE_EXTERNAL;
21445 42 : else if (pg_strcasecmp(storagemode, "extended") == 0)
21446 16 : cstorage = TYPSTORAGE_EXTENDED;
21447 26 : else if (pg_strcasecmp(storagemode, "main") == 0)
21448 20 : cstorage = TYPSTORAGE_MAIN;
21449 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
21450 6 : cstorage = get_typstorage(atttypid);
21451 : else
21452 0 : ereport(ERROR,
21453 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21454 : errmsg("invalid storage type \"%s\"",
21455 : storagemode)));
21456 :
21457 : /*
21458 : * safety check: do not allow toasted storage modes unless column datatype
21459 : * is TOAST-aware.
21460 : */
21461 248 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
21462 6 : ereport(ERROR,
21463 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21464 : errmsg("column data type %s can only have storage PLAIN",
21465 : format_type_be(atttypid))));
21466 :
21467 242 : return cstorage;
21468 : }
|