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, List **otherrelids, LOCKMODE lockmode);
398 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
399 : bool deferrable, bool initdeferred,
400 : List **otherrelids);
401 : static void ATExecAlterChildConstr(List **wqueue, ATAlterConstraint *cmdcon,
402 : Relation conrel, Relation tgrel, Relation rel,
403 : HeapTuple contuple, bool recurse, List **otherrelids,
404 : LOCKMODE lockmode);
405 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
406 : Relation rel, char *constrName,
407 : bool recurse, bool recursing, LOCKMODE lockmode);
408 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
409 : HeapTuple contuple, LOCKMODE lockmode);
410 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
411 : char *constrName, HeapTuple contuple,
412 : bool recurse, bool recursing, LOCKMODE lockmode);
413 : static int transformColumnNameList(Oid relId, List *colList,
414 : int16 *attnums, Oid *atttypids, Oid *attcollids);
415 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
416 : List **attnamelist,
417 : int16 *attnums, Oid *atttypids, Oid *attcollids,
418 : Oid *opclasses, bool *pk_has_without_overlaps);
419 : static Oid transformFkeyCheckAttrs(Relation pkrel,
420 : int numattrs, int16 *attnums,
421 : bool with_period, Oid *opclasses,
422 : bool *pk_has_without_overlaps);
423 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
424 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
425 : Oid *funcid);
426 : static void validateForeignKeyConstraint(char *conname,
427 : Relation rel, Relation pkrel,
428 : Oid pkindOid, Oid constraintOid, bool hasperiod);
429 : static void CheckAlterTableIsSafe(Relation rel);
430 : static void ATController(AlterTableStmt *parsetree,
431 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
432 : AlterTableUtilityContext *context);
433 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
434 : bool recurse, bool recursing, LOCKMODE lockmode,
435 : AlterTableUtilityContext *context);
436 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
437 : AlterTableUtilityContext *context);
438 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
439 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
440 : AlterTableUtilityContext *context);
441 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
442 : Relation rel, AlterTableCmd *cmd,
443 : bool recurse, LOCKMODE lockmode,
444 : AlterTablePass cur_pass,
445 : AlterTableUtilityContext *context);
446 : static void ATRewriteTables(AlterTableStmt *parsetree,
447 : List **wqueue, LOCKMODE lockmode,
448 : AlterTableUtilityContext *context);
449 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
450 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
451 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
452 : static void ATSimpleRecursion(List **wqueue, Relation rel,
453 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
454 : AlterTableUtilityContext *context);
455 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
456 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
457 : LOCKMODE lockmode,
458 : AlterTableUtilityContext *context);
459 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
460 : DropBehavior behavior);
461 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
462 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
463 : AlterTableUtilityContext *context);
464 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
465 : Relation rel, AlterTableCmd **cmd,
466 : bool recurse, bool recursing,
467 : LOCKMODE lockmode, AlterTablePass cur_pass,
468 : AlterTableUtilityContext *context);
469 : static bool check_for_column_name_collision(Relation rel, const char *colname,
470 : bool if_not_exists);
471 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
472 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
473 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
474 : LOCKMODE lockmode);
475 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
476 : LOCKMODE lockmode);
477 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
478 : char *constrname, char *colName,
479 : bool recurse, bool recursing,
480 : LOCKMODE lockmode);
481 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
482 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
483 : List *testConstraint, List *provenConstraint);
484 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
485 : Node *newDefault, LOCKMODE lockmode);
486 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
487 : Node *newDefault);
488 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
489 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
490 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
491 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
492 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
493 : bool recurse, bool recursing);
494 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
495 : Node *newExpr, LOCKMODE lockmode);
496 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
497 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
498 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
499 : Node *newValue, LOCKMODE lockmode);
500 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
501 : Node *options, bool isReset, LOCKMODE lockmode);
502 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
503 : Node *newValue, LOCKMODE lockmode);
504 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
505 : AlterTableCmd *cmd, LOCKMODE lockmode,
506 : AlterTableUtilityContext *context);
507 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
508 : DropBehavior behavior,
509 : bool recurse, bool recursing,
510 : bool missing_ok, LOCKMODE lockmode,
511 : ObjectAddresses *addrs);
512 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
513 : bool recurse, LOCKMODE lockmode,
514 : AlterTableUtilityContext *context);
515 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
516 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
517 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
518 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
519 : static ObjectAddress ATExecAddConstraint(List **wqueue,
520 : AlteredTableInfo *tab, Relation rel,
521 : Constraint *newConstraint, bool recurse, bool is_readd,
522 : LOCKMODE lockmode);
523 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
524 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
525 : IndexStmt *stmt, LOCKMODE lockmode);
526 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
527 : AlteredTableInfo *tab, Relation rel,
528 : Constraint *constr,
529 : bool recurse, bool recursing, bool is_readd,
530 : LOCKMODE lockmode);
531 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
532 : Relation rel, Constraint *fkconstraint,
533 : bool recurse, bool recursing,
534 : LOCKMODE lockmode);
535 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
536 : int numfksetcols, const int16 *fksetcolsattnums,
537 : List *fksetcols);
538 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
539 : char *constraintname,
540 : Constraint *fkconstraint, Relation rel,
541 : Relation pkrel, Oid indexOid,
542 : Oid parentConstr,
543 : int numfks, int16 *pkattnum, int16 *fkattnum,
544 : Oid *pfeqoperators, Oid *ppeqoperators,
545 : Oid *ffeqoperators, int numfkdelsetcols,
546 : int16 *fkdelsetcols, bool is_internal,
547 : bool with_period);
548 : static void addFkRecurseReferenced(Constraint *fkconstraint,
549 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
550 : int numfks, int16 *pkattnum, int16 *fkattnum,
551 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
552 : int numfkdelsetcols, int16 *fkdelsetcols,
553 : bool old_check_ok,
554 : Oid parentDelTrigger, Oid parentUpdTrigger,
555 : bool with_period);
556 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
557 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
558 : int numfks, int16 *pkattnum, int16 *fkattnum,
559 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
560 : int numfkdelsetcols, int16 *fkdelsetcols,
561 : bool old_check_ok, LOCKMODE lockmode,
562 : Oid parentInsTrigger, Oid parentUpdTrigger,
563 : bool with_period);
564 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
565 : Relation partitionRel);
566 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
567 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
568 : Relation partRel);
569 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
570 : Constraint *fkconstraint, Oid constraintOid,
571 : Oid indexOid,
572 : Oid parentInsTrigger, Oid parentUpdTrigger,
573 : Oid *insertTrigOid, Oid *updateTrigOid);
574 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
575 : Constraint *fkconstraint, Oid constraintOid,
576 : Oid indexOid,
577 : Oid parentDelTrigger, Oid parentUpdTrigger,
578 : Oid *deleteTrigOid, Oid *updateTrigOid);
579 : static bool tryAttachPartitionForeignKey(List **wqueue,
580 : ForeignKeyCacheInfo *fk,
581 : Relation partition,
582 : Oid parentConstrOid, int numfks,
583 : AttrNumber *mapped_conkey, AttrNumber *confkey,
584 : Oid *conpfeqop,
585 : Oid parentInsTrigger,
586 : Oid parentUpdTrigger,
587 : Relation trigrel);
588 : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
589 : Oid partConstrOid, Oid parentConstrOid,
590 : Oid parentInsTrigger, Oid parentUpdTrigger,
591 : Relation trigrel);
592 : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
593 : Oid conoid, Oid conrelid);
594 : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
595 : Oid confrelid, Oid conrelid);
596 : static void GetForeignKeyActionTriggers(Relation trigrel,
597 : Oid conoid, Oid confrelid, Oid conrelid,
598 : Oid *deleteTriggerOid,
599 : Oid *updateTriggerOid);
600 : static void GetForeignKeyCheckTriggers(Relation trigrel,
601 : Oid conoid, Oid confrelid, Oid conrelid,
602 : Oid *insertTriggerOid,
603 : Oid *updateTriggerOid);
604 : static void ATExecDropConstraint(Relation rel, const char *constrName,
605 : DropBehavior behavior, bool recurse,
606 : bool missing_ok, LOCKMODE lockmode);
607 : static ObjectAddress dropconstraint_internal(Relation rel,
608 : HeapTuple constraintTup, DropBehavior behavior,
609 : bool recurse, bool recursing,
610 : bool missing_ok, LOCKMODE lockmode);
611 : static void ATPrepAlterColumnType(List **wqueue,
612 : AlteredTableInfo *tab, Relation rel,
613 : bool recurse, bool recursing,
614 : AlterTableCmd *cmd, LOCKMODE lockmode,
615 : AlterTableUtilityContext *context);
616 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
617 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
618 : AlterTableCmd *cmd, LOCKMODE lockmode);
619 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
620 : Relation rel, AttrNumber attnum, const char *colName);
621 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
622 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
623 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
624 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
625 : LOCKMODE lockmode);
626 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
627 : char *cmd, List **wqueue, LOCKMODE lockmode,
628 : bool rewrite);
629 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
630 : Oid objid, Relation rel, List *domname,
631 : const char *conname);
632 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
633 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
634 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
635 : List *options, LOCKMODE lockmode);
636 : static void change_owner_fix_column_acls(Oid relationOid,
637 : Oid oldOwnerId, Oid newOwnerId);
638 : static void change_owner_recurse_to_sequences(Oid relationOid,
639 : Oid newOwnerId, LOCKMODE lockmode);
640 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
641 : LOCKMODE lockmode);
642 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
643 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
644 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
645 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
646 : bool toLogged);
647 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
648 : const char *tablespacename, LOCKMODE lockmode);
649 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
650 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
651 : static void ATExecSetRelOptions(Relation rel, List *defList,
652 : AlterTableType operation,
653 : LOCKMODE lockmode);
654 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
655 : char fires_when, bool skip_system, bool recurse,
656 : LOCKMODE lockmode);
657 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
658 : char fires_when, LOCKMODE lockmode);
659 : static void ATPrepAddInherit(Relation child_rel);
660 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
661 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
662 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
663 : DependencyType deptype);
664 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
665 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
666 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
667 : static void ATExecGenericOptions(Relation rel, List *options);
668 : static void ATExecSetRowSecurity(Relation rel, bool rls);
669 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
670 : static ObjectAddress ATExecSetCompression(Relation rel,
671 : const char *column, Node *newValue, LOCKMODE lockmode);
672 :
673 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
674 : static const char *storage_name(char c);
675 :
676 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
677 : Oid oldRelOid, void *arg);
678 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
679 : Oid oldrelid, void *arg);
680 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
681 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
682 : List **partexprs, Oid *partopclass, Oid *partcollation,
683 : PartitionStrategy strategy);
684 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
685 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
686 : bool expect_detached);
687 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
688 : PartitionCmd *cmd,
689 : AlterTableUtilityContext *context);
690 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
691 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
692 : List *partConstraint,
693 : bool validate_default);
694 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
695 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
696 : static void DropClonedTriggersFromPartition(Oid partitionId);
697 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
698 : Relation rel, RangeVar *name,
699 : bool concurrent);
700 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
701 : bool concurrent, Oid defaultPartOid);
702 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
703 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
704 : RangeVar *name);
705 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
706 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
707 : Relation partitionTbl);
708 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
709 : static List *GetParentedForeignKeyRefs(Relation partition);
710 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
711 : static char GetAttributeCompression(Oid atttypid, const char *compression);
712 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
713 :
714 :
715 : /* ----------------------------------------------------------------
716 : * DefineRelation
717 : * Creates a new relation.
718 : *
719 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
720 : * The other arguments are used to extend the behavior for other cases:
721 : * relkind: relkind to assign to the new relation
722 : * ownerId: if not InvalidOid, use this as the new relation's owner.
723 : * typaddress: if not null, it's set to the pg_type entry's address.
724 : * queryString: for error reporting
725 : *
726 : * Note that permissions checks are done against current user regardless of
727 : * ownerId. A nonzero ownerId is used when someone is creating a relation
728 : * "on behalf of" someone else, so we still want to see that the current user
729 : * has permissions to do it.
730 : *
731 : * If successful, returns the address of the new relation.
732 : * ----------------------------------------------------------------
733 : */
734 : ObjectAddress
735 59724 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
736 : ObjectAddress *typaddress, const char *queryString)
737 : {
738 : char relname[NAMEDATALEN];
739 : Oid namespaceId;
740 : Oid relationId;
741 : Oid tablespaceId;
742 : Relation rel;
743 : TupleDesc descriptor;
744 : List *inheritOids;
745 : List *old_constraints;
746 : List *old_notnulls;
747 : List *rawDefaults;
748 : List *cookedDefaults;
749 : List *nncols;
750 : Datum reloptions;
751 : ListCell *listptr;
752 : AttrNumber attnum;
753 : bool partitioned;
754 59724 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
755 : Oid ofTypeId;
756 : ObjectAddress address;
757 : LOCKMODE parentLockmode;
758 59724 : Oid accessMethodId = InvalidOid;
759 :
760 : /*
761 : * Truncate relname to appropriate length (probably a waste of time, as
762 : * parser should have done this already).
763 : */
764 59724 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
765 :
766 : /*
767 : * Check consistency of arguments
768 : */
769 59724 : if (stmt->oncommit != ONCOMMIT_NOOP
770 178 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
771 12 : ereport(ERROR,
772 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
773 : errmsg("ON COMMIT can only be used on temporary tables")));
774 :
775 59712 : if (stmt->partspec != NULL)
776 : {
777 4894 : if (relkind != RELKIND_RELATION)
778 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
779 :
780 4894 : relkind = RELKIND_PARTITIONED_TABLE;
781 4894 : partitioned = true;
782 : }
783 : else
784 54818 : partitioned = false;
785 :
786 59712 : if (relkind == RELKIND_PARTITIONED_TABLE &&
787 4894 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
788 6 : ereport(ERROR,
789 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
790 : errmsg("partitioned tables cannot be unlogged")));
791 :
792 : /*
793 : * Look up the namespace in which we are supposed to create the relation,
794 : * check we have permission to create there, lock it against concurrent
795 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
796 : * namespace is selected.
797 : */
798 : namespaceId =
799 59706 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
800 :
801 : /*
802 : * Security check: disallow creating temp tables from security-restricted
803 : * code. This is needed because calling code might not expect untrusted
804 : * tables to appear in pg_temp at the front of its search path.
805 : */
806 59706 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
807 3066 : && InSecurityRestrictedOperation())
808 0 : ereport(ERROR,
809 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
810 : errmsg("cannot create temporary table within security-restricted operation")));
811 :
812 : /*
813 : * Determine the lockmode to use when scanning parents. A self-exclusive
814 : * lock is needed here.
815 : *
816 : * For regular inheritance, if two backends attempt to add children to the
817 : * same parent simultaneously, and that parent has no pre-existing
818 : * children, then both will attempt to update the parent's relhassubclass
819 : * field, leading to a "tuple concurrently updated" error. Also, this
820 : * interlocks against a concurrent ANALYZE on the parent table, which
821 : * might otherwise be attempting to clear the parent's relhassubclass
822 : * field, if its previous children were recently dropped.
823 : *
824 : * If the child table is a partition, then we instead grab an exclusive
825 : * lock on the parent because its partition descriptor will be changed by
826 : * addition of the new partition.
827 : */
828 59706 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
829 : ShareUpdateExclusiveLock);
830 :
831 : /* Determine the list of OIDs of the parents. */
832 59706 : inheritOids = NIL;
833 69978 : foreach(listptr, stmt->inhRelations)
834 : {
835 10272 : RangeVar *rv = (RangeVar *) lfirst(listptr);
836 : Oid parentOid;
837 :
838 10272 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
839 :
840 : /*
841 : * Reject duplications in the list of parents.
842 : */
843 10272 : if (list_member_oid(inheritOids, parentOid))
844 0 : ereport(ERROR,
845 : (errcode(ERRCODE_DUPLICATE_TABLE),
846 : errmsg("relation \"%s\" would be inherited from more than once",
847 : get_rel_name(parentOid))));
848 :
849 10272 : inheritOids = lappend_oid(inheritOids, parentOid);
850 : }
851 :
852 : /*
853 : * Select tablespace to use: an explicitly indicated one, or (in the case
854 : * of a partitioned table) the parent's, if it has one.
855 : */
856 59706 : if (stmt->tablespacename)
857 : {
858 118 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
859 :
860 112 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
861 6 : ereport(ERROR,
862 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
863 : errmsg("cannot specify default tablespace for partitioned relations")));
864 : }
865 59588 : else if (stmt->partbound)
866 : {
867 : Assert(list_length(inheritOids) == 1);
868 7880 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
869 : }
870 : else
871 51708 : tablespaceId = InvalidOid;
872 :
873 : /* still nothing? use the default */
874 59694 : if (!OidIsValid(tablespaceId))
875 59566 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
876 : partitioned);
877 :
878 : /* Check permissions except when using database's default */
879 59688 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
880 : {
881 : AclResult aclresult;
882 :
883 146 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
884 : ACL_CREATE);
885 146 : if (aclresult != ACLCHECK_OK)
886 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
887 6 : get_tablespace_name(tablespaceId));
888 : }
889 :
890 : /* In all cases disallow placing user relations in pg_global */
891 59682 : if (tablespaceId == GLOBALTABLESPACE_OID)
892 18 : ereport(ERROR,
893 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
894 : errmsg("only shared relations can be placed in pg_global tablespace")));
895 :
896 : /* Identify user ID that will own the table */
897 59664 : if (!OidIsValid(ownerId))
898 59424 : ownerId = GetUserId();
899 :
900 : /*
901 : * Parse and validate reloptions, if any.
902 : */
903 59664 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
904 : true, false);
905 :
906 59646 : switch (relkind)
907 : {
908 14932 : case RELKIND_VIEW:
909 14932 : (void) view_reloptions(reloptions, true);
910 14914 : break;
911 4870 : case RELKIND_PARTITIONED_TABLE:
912 4870 : (void) partitioned_table_reloptions(reloptions, true);
913 4864 : break;
914 39844 : default:
915 39844 : (void) heap_reloptions(relkind, reloptions, true);
916 : }
917 :
918 59526 : if (stmt->ofTypename)
919 : {
920 : AclResult aclresult;
921 :
922 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
923 :
924 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
925 86 : if (aclresult != ACLCHECK_OK)
926 6 : aclcheck_error_type(aclresult, ofTypeId);
927 : }
928 : else
929 59440 : ofTypeId = InvalidOid;
930 :
931 : /*
932 : * Look up inheritance ancestors and generate relation schema, including
933 : * inherited attributes. (Note that stmt->tableElts is destructively
934 : * modified by MergeAttributes.)
935 : */
936 59280 : stmt->tableElts =
937 59520 : MergeAttributes(stmt->tableElts, inheritOids,
938 59520 : stmt->relation->relpersistence,
939 59520 : stmt->partbound != NULL,
940 : &old_constraints, &old_notnulls);
941 :
942 : /*
943 : * Create a tuple descriptor from the relation schema. Note that this
944 : * deals with column names, types, and in-descriptor NOT NULL flags, but
945 : * not default values, NOT NULL or CHECK constraints; we handle those
946 : * below.
947 : */
948 59280 : descriptor = BuildDescForRelation(stmt->tableElts);
949 :
950 : /*
951 : * Find columns with default values and prepare for insertion of the
952 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
953 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
954 : * while raw defaults go into a list of RawColumnDefault structs that will
955 : * be processed by AddRelationNewConstraints. (We can't deal with raw
956 : * expressions until we can do transformExpr.)
957 : */
958 59232 : rawDefaults = NIL;
959 59232 : cookedDefaults = NIL;
960 59232 : attnum = 0;
961 :
962 292308 : foreach(listptr, stmt->tableElts)
963 : {
964 233076 : ColumnDef *colDef = lfirst(listptr);
965 :
966 233076 : attnum++;
967 233076 : if (colDef->raw_default != NULL)
968 : {
969 : RawColumnDefault *rawEnt;
970 :
971 : Assert(colDef->cooked_default == NULL);
972 :
973 3076 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
974 3076 : rawEnt->attnum = attnum;
975 3076 : rawEnt->raw_default = colDef->raw_default;
976 3076 : rawEnt->generated = colDef->generated;
977 3076 : rawDefaults = lappend(rawDefaults, rawEnt);
978 : }
979 230000 : else if (colDef->cooked_default != NULL)
980 : {
981 : CookedConstraint *cooked;
982 :
983 372 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
984 372 : cooked->contype = CONSTR_DEFAULT;
985 372 : cooked->conoid = InvalidOid; /* until created */
986 372 : cooked->name = NULL;
987 372 : cooked->attnum = attnum;
988 372 : cooked->expr = colDef->cooked_default;
989 372 : cooked->is_enforced = true;
990 372 : cooked->skip_validation = false;
991 372 : cooked->is_local = true; /* not used for defaults */
992 372 : cooked->inhcount = 0; /* ditto */
993 372 : cooked->is_no_inherit = false;
994 372 : cookedDefaults = lappend(cookedDefaults, cooked);
995 : }
996 : }
997 :
998 : /*
999 : * For relations with table AM and partitioned tables, select access
1000 : * method to use: an explicitly indicated one, or (in the case of a
1001 : * partitioned table) the parent's, if it has one.
1002 : */
1003 59232 : if (stmt->accessMethod != NULL)
1004 : {
1005 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1006 122 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1007 : }
1008 59110 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1009 : {
1010 37462 : if (stmt->partbound)
1011 : {
1012 : Assert(list_length(inheritOids) == 1);
1013 7698 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1014 : }
1015 :
1016 37462 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1017 32584 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1018 : }
1019 :
1020 : /*
1021 : * Create the relation. Inherited defaults and CHECK constraints are
1022 : * passed in for immediate handling --- since they don't need parsing,
1023 : * they can be stored immediately.
1024 : */
1025 59214 : relationId = heap_create_with_catalog(relname,
1026 : namespaceId,
1027 : tablespaceId,
1028 : InvalidOid,
1029 : InvalidOid,
1030 : ofTypeId,
1031 : ownerId,
1032 : accessMethodId,
1033 : descriptor,
1034 : list_concat(cookedDefaults,
1035 : old_constraints),
1036 : relkind,
1037 59214 : stmt->relation->relpersistence,
1038 : false,
1039 : false,
1040 : stmt->oncommit,
1041 : reloptions,
1042 : true,
1043 : allowSystemTableMods,
1044 : false,
1045 : InvalidOid,
1046 : typaddress);
1047 :
1048 : /*
1049 : * We must bump the command counter to make the newly-created relation
1050 : * tuple visible for opening.
1051 : */
1052 59178 : CommandCounterIncrement();
1053 :
1054 : /*
1055 : * Open the new relation and acquire exclusive lock on it. This isn't
1056 : * really necessary for locking out other backends (since they can't see
1057 : * the new rel anyway until we commit), but it keeps the lock manager from
1058 : * complaining about deadlock risks.
1059 : */
1060 59178 : rel = relation_open(relationId, AccessExclusiveLock);
1061 :
1062 : /*
1063 : * Now add any newly specified column default and generation expressions
1064 : * to the new relation. These are passed to us in the form of raw
1065 : * parsetrees; we need to transform them to executable expression trees
1066 : * before they can be added. The most convenient way to do that is to
1067 : * apply the parser's transformExpr routine, but transformExpr doesn't
1068 : * work unless we have a pre-existing relation. So, the transformation has
1069 : * to be postponed to this final step of CREATE TABLE.
1070 : *
1071 : * This needs to be before processing the partitioning clauses because
1072 : * those could refer to generated columns.
1073 : */
1074 59178 : if (rawDefaults)
1075 2628 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1076 : true, true, false, queryString);
1077 :
1078 : /*
1079 : * Make column generation expressions visible for use by partitioning.
1080 : */
1081 59016 : CommandCounterIncrement();
1082 :
1083 : /* Process and store partition bound, if any. */
1084 59016 : if (stmt->partbound)
1085 : {
1086 : PartitionBoundSpec *bound;
1087 : ParseState *pstate;
1088 7802 : Oid parentId = linitial_oid(inheritOids),
1089 : defaultPartOid;
1090 : Relation parent,
1091 7802 : defaultRel = NULL;
1092 : ParseNamespaceItem *nsitem;
1093 :
1094 : /* Already have strong enough lock on the parent */
1095 7802 : parent = table_open(parentId, NoLock);
1096 :
1097 : /*
1098 : * We are going to try to validate the partition bound specification
1099 : * against the partition key of parentRel, so it better have one.
1100 : */
1101 7802 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1102 18 : ereport(ERROR,
1103 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1104 : errmsg("\"%s\" is not partitioned",
1105 : RelationGetRelationName(parent))));
1106 :
1107 : /*
1108 : * The partition constraint of the default partition depends on the
1109 : * partition bounds of every other partition. It is possible that
1110 : * another backend might be about to execute a query on the default
1111 : * partition table, and that the query relies on previously cached
1112 : * default partition constraints. We must therefore take a table lock
1113 : * strong enough to prevent all queries on the default partition from
1114 : * proceeding until we commit and send out a shared-cache-inval notice
1115 : * that will make them update their index lists.
1116 : *
1117 : * Order of locking: The relation being added won't be visible to
1118 : * other backends until it is committed, hence here in
1119 : * DefineRelation() the order of locking the default partition and the
1120 : * relation being added does not matter. But at all other places we
1121 : * need to lock the default relation before we lock the relation being
1122 : * added or removed i.e. we should take the lock in same order at all
1123 : * the places such that lock parent, lock default partition and then
1124 : * lock the partition so as to avoid a deadlock.
1125 : */
1126 : defaultPartOid =
1127 7784 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1128 : true));
1129 7784 : if (OidIsValid(defaultPartOid))
1130 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1131 :
1132 : /* Transform the bound values */
1133 7784 : pstate = make_parsestate(NULL);
1134 7784 : pstate->p_sourcetext = queryString;
1135 :
1136 : /*
1137 : * Add an nsitem containing this relation, so that transformExpr
1138 : * called on partition bound expressions is able to report errors
1139 : * using a proper context.
1140 : */
1141 7784 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1142 : NULL, false, false);
1143 7784 : addNSItemToQuery(pstate, nsitem, false, true, true);
1144 :
1145 7784 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1146 :
1147 : /*
1148 : * Check first that the new partition's bound is valid and does not
1149 : * overlap with any of existing partitions of the parent.
1150 : */
1151 7580 : check_new_partition_bound(relname, parent, bound, pstate);
1152 :
1153 : /*
1154 : * If the default partition exists, its partition constraints will
1155 : * change after the addition of this new partition such that it won't
1156 : * allow any row that qualifies for this new partition. So, check that
1157 : * the existing data in the default partition satisfies the constraint
1158 : * as it will exist after adding this partition.
1159 : */
1160 7466 : if (OidIsValid(defaultPartOid))
1161 : {
1162 348 : check_default_partition_contents(parent, defaultRel, bound);
1163 : /* Keep the lock until commit. */
1164 330 : table_close(defaultRel, NoLock);
1165 : }
1166 :
1167 : /* Update the pg_class entry. */
1168 7448 : StorePartitionBound(rel, parent, bound);
1169 :
1170 7448 : table_close(parent, NoLock);
1171 : }
1172 :
1173 : /* Store inheritance information for new rel. */
1174 58662 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1175 :
1176 : /*
1177 : * Process the partitioning specification (if any) and store the partition
1178 : * key information into the catalog.
1179 : */
1180 58662 : if (partitioned)
1181 : {
1182 : ParseState *pstate;
1183 : int partnatts;
1184 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1185 : Oid partopclass[PARTITION_MAX_KEYS];
1186 : Oid partcollation[PARTITION_MAX_KEYS];
1187 4864 : List *partexprs = NIL;
1188 :
1189 4864 : pstate = make_parsestate(NULL);
1190 4864 : pstate->p_sourcetext = queryString;
1191 :
1192 4864 : partnatts = list_length(stmt->partspec->partParams);
1193 :
1194 : /* Protect fixed-size arrays here and in executor */
1195 4864 : if (partnatts > PARTITION_MAX_KEYS)
1196 0 : ereport(ERROR,
1197 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1198 : errmsg("cannot partition using more than %d columns",
1199 : PARTITION_MAX_KEYS)));
1200 :
1201 : /*
1202 : * We need to transform the raw parsetrees corresponding to partition
1203 : * expressions into executable expression trees. Like column defaults
1204 : * and CHECK constraints, we could not have done the transformation
1205 : * earlier.
1206 : */
1207 4864 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1208 :
1209 4834 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1210 : partattrs, &partexprs, partopclass,
1211 4834 : partcollation, stmt->partspec->strategy);
1212 :
1213 4738 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1214 : partexprs,
1215 : partopclass, partcollation);
1216 :
1217 : /* make it all visible */
1218 4738 : CommandCounterIncrement();
1219 : }
1220 :
1221 : /*
1222 : * If we're creating a partition, create now all the indexes, triggers,
1223 : * FKs defined in the parent.
1224 : *
1225 : * We can't do it earlier, because DefineIndex wants to know the partition
1226 : * key which we just stored.
1227 : */
1228 58536 : if (stmt->partbound)
1229 : {
1230 7442 : Oid parentId = linitial_oid(inheritOids);
1231 : Relation parent;
1232 : List *idxlist;
1233 : ListCell *cell;
1234 :
1235 : /* Already have strong enough lock on the parent */
1236 7442 : parent = table_open(parentId, NoLock);
1237 7442 : idxlist = RelationGetIndexList(parent);
1238 :
1239 : /*
1240 : * For each index in the parent table, create one in the partition
1241 : */
1242 8828 : foreach(cell, idxlist)
1243 : {
1244 1404 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1245 : AttrMap *attmap;
1246 : IndexStmt *idxstmt;
1247 : Oid constraintOid;
1248 :
1249 1404 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1250 : {
1251 36 : if (idxRel->rd_index->indisunique)
1252 12 : ereport(ERROR,
1253 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1254 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1255 : RelationGetRelationName(parent)),
1256 : errdetail("Table \"%s\" contains indexes that are unique.",
1257 : RelationGetRelationName(parent))));
1258 : else
1259 : {
1260 24 : index_close(idxRel, AccessShareLock);
1261 24 : continue;
1262 : }
1263 : }
1264 :
1265 1368 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1266 : RelationGetDescr(parent),
1267 : false);
1268 : idxstmt =
1269 1368 : generateClonedIndexStmt(NULL, idxRel,
1270 : attmap, &constraintOid);
1271 1368 : DefineIndex(RelationGetRelid(rel),
1272 : idxstmt,
1273 : InvalidOid,
1274 : RelationGetRelid(idxRel),
1275 : constraintOid,
1276 : -1,
1277 : false, false, false, false, false);
1278 :
1279 1362 : index_close(idxRel, AccessShareLock);
1280 : }
1281 :
1282 7424 : list_free(idxlist);
1283 :
1284 : /*
1285 : * If there are any row-level triggers, clone them to the new
1286 : * partition.
1287 : */
1288 7424 : if (parent->trigdesc != NULL)
1289 420 : CloneRowTriggersToPartition(parent, rel);
1290 :
1291 : /*
1292 : * And foreign keys too. Note that because we're freshly creating the
1293 : * table, there is no need to verify these new constraints.
1294 : */
1295 7424 : CloneForeignKeyConstraints(NULL, parent, rel);
1296 :
1297 7424 : table_close(parent, NoLock);
1298 : }
1299 :
1300 : /*
1301 : * Now add any newly specified CHECK constraints to the new relation. Same
1302 : * as for defaults above, but these need to come after partitioning is set
1303 : * up.
1304 : */
1305 58518 : if (stmt->constraints)
1306 722 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1307 : true, true, false, queryString);
1308 :
1309 : /*
1310 : * Finally, merge the not-null constraints that are declared directly with
1311 : * those that come from parent relations (making sure to count inheritance
1312 : * appropriately for each), create them, and set the attnotnull flag on
1313 : * columns that don't yet have it.
1314 : */
1315 58488 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1316 : old_notnulls);
1317 131490 : foreach_int(attrnum, nncols)
1318 14706 : set_attnotnull(NULL, rel, attrnum, NoLock);
1319 :
1320 58392 : ObjectAddressSet(address, RelationRelationId, relationId);
1321 :
1322 : /*
1323 : * Clean up. We keep lock on new relation (although it shouldn't be
1324 : * visible to anyone else anyway, until commit).
1325 : */
1326 58392 : relation_close(rel, NoLock);
1327 :
1328 58392 : return address;
1329 : }
1330 :
1331 : /*
1332 : * BuildDescForRelation
1333 : *
1334 : * Given a list of ColumnDef nodes, build a TupleDesc.
1335 : *
1336 : * Note: This is only for the limited purpose of table and view creation. Not
1337 : * everything is filled in. A real tuple descriptor should be obtained from
1338 : * the relcache.
1339 : */
1340 : TupleDesc
1341 62136 : BuildDescForRelation(const List *columns)
1342 : {
1343 : int natts;
1344 : AttrNumber attnum;
1345 : ListCell *l;
1346 : TupleDesc desc;
1347 : char *attname;
1348 : Oid atttypid;
1349 : int32 atttypmod;
1350 : Oid attcollation;
1351 : int attdim;
1352 :
1353 : /*
1354 : * allocate a new tuple descriptor
1355 : */
1356 62136 : natts = list_length(columns);
1357 62136 : desc = CreateTemplateTupleDesc(natts);
1358 :
1359 62136 : attnum = 0;
1360 :
1361 298332 : foreach(l, columns)
1362 : {
1363 236256 : ColumnDef *entry = lfirst(l);
1364 : AclResult aclresult;
1365 : Form_pg_attribute att;
1366 :
1367 : /*
1368 : * for each entry in the list, get the name and type information from
1369 : * the list and have TupleDescInitEntry fill in the attribute
1370 : * information we need.
1371 : */
1372 236256 : attnum++;
1373 :
1374 236256 : attname = entry->colname;
1375 236256 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1376 :
1377 236256 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1378 236256 : if (aclresult != ACLCHECK_OK)
1379 42 : aclcheck_error_type(aclresult, atttypid);
1380 :
1381 236214 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1382 236214 : attdim = list_length(entry->typeName->arrayBounds);
1383 236214 : if (attdim > PG_INT16_MAX)
1384 0 : ereport(ERROR,
1385 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1386 : errmsg("too many array dimensions"));
1387 :
1388 236214 : if (entry->typeName->setof)
1389 0 : ereport(ERROR,
1390 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1391 : errmsg("column \"%s\" cannot be declared SETOF",
1392 : attname)));
1393 :
1394 236214 : TupleDescInitEntry(desc, attnum, attname,
1395 : atttypid, atttypmod, attdim);
1396 236214 : att = TupleDescAttr(desc, attnum - 1);
1397 :
1398 : /* Override TupleDescInitEntry's settings as requested */
1399 236214 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1400 :
1401 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1402 236214 : att->attnotnull = entry->is_not_null;
1403 236214 : att->attislocal = entry->is_local;
1404 236214 : att->attinhcount = entry->inhcount;
1405 236214 : att->attidentity = entry->identity;
1406 236214 : att->attgenerated = entry->generated;
1407 236214 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1408 236202 : if (entry->storage)
1409 19734 : att->attstorage = entry->storage;
1410 216468 : else if (entry->storage_name)
1411 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1412 :
1413 236196 : populate_compact_attribute(desc, attnum - 1);
1414 : }
1415 :
1416 62076 : return desc;
1417 : }
1418 :
1419 : /*
1420 : * Emit the right error or warning message for a "DROP" command issued on a
1421 : * non-existent relation
1422 : */
1423 : static void
1424 1074 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1425 : {
1426 : const struct dropmsgstrings *rentry;
1427 :
1428 1194 : if (rel->schemaname != NULL &&
1429 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1430 : {
1431 42 : if (!missing_ok)
1432 : {
1433 0 : ereport(ERROR,
1434 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1435 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1436 : }
1437 : else
1438 : {
1439 42 : ereport(NOTICE,
1440 : (errmsg("schema \"%s\" does not exist, skipping",
1441 : rel->schemaname)));
1442 : }
1443 42 : return;
1444 : }
1445 :
1446 1352 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1447 : {
1448 1352 : if (rentry->kind == rightkind)
1449 : {
1450 1032 : if (!missing_ok)
1451 : {
1452 132 : ereport(ERROR,
1453 : (errcode(rentry->nonexistent_code),
1454 : errmsg(rentry->nonexistent_msg, rel->relname)));
1455 : }
1456 : else
1457 : {
1458 900 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1459 900 : break;
1460 : }
1461 : }
1462 : }
1463 :
1464 : Assert(rentry->kind != '\0'); /* Should be impossible */
1465 : }
1466 :
1467 : /*
1468 : * Emit the right error message for a "DROP" command issued on a
1469 : * relation of the wrong type
1470 : */
1471 : static void
1472 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1473 : {
1474 : const struct dropmsgstrings *rentry;
1475 : const struct dropmsgstrings *wentry;
1476 :
1477 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1478 0 : if (rentry->kind == rightkind)
1479 0 : break;
1480 : Assert(rentry->kind != '\0');
1481 :
1482 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1483 0 : if (wentry->kind == wrongkind)
1484 0 : break;
1485 : /* wrongkind could be something we don't have in our table... */
1486 :
1487 0 : ereport(ERROR,
1488 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1489 : errmsg(rentry->nota_msg, relname),
1490 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1491 : }
1492 :
1493 : /*
1494 : * RemoveRelations
1495 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1496 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1497 : */
1498 : void
1499 16878 : RemoveRelations(DropStmt *drop)
1500 : {
1501 : ObjectAddresses *objects;
1502 : char relkind;
1503 : ListCell *cell;
1504 16878 : int flags = 0;
1505 16878 : LOCKMODE lockmode = AccessExclusiveLock;
1506 :
1507 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1508 16878 : if (drop->concurrent)
1509 : {
1510 : /*
1511 : * Note that for temporary relations this lock may get upgraded later
1512 : * on, but as no other session can access a temporary relation, this
1513 : * is actually fine.
1514 : */
1515 130 : lockmode = ShareUpdateExclusiveLock;
1516 : Assert(drop->removeType == OBJECT_INDEX);
1517 130 : if (list_length(drop->objects) != 1)
1518 6 : ereport(ERROR,
1519 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1520 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1521 124 : if (drop->behavior == DROP_CASCADE)
1522 0 : ereport(ERROR,
1523 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1524 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1525 : }
1526 :
1527 : /*
1528 : * First we identify all the relations, then we delete them in a single
1529 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1530 : * RESTRICT errors if one of the relations depends on another.
1531 : */
1532 :
1533 : /* Determine required relkind */
1534 16872 : switch (drop->removeType)
1535 : {
1536 14666 : case OBJECT_TABLE:
1537 14666 : relkind = RELKIND_RELATION;
1538 14666 : break;
1539 :
1540 812 : case OBJECT_INDEX:
1541 812 : relkind = RELKIND_INDEX;
1542 812 : break;
1543 :
1544 176 : case OBJECT_SEQUENCE:
1545 176 : relkind = RELKIND_SEQUENCE;
1546 176 : break;
1547 :
1548 936 : case OBJECT_VIEW:
1549 936 : relkind = RELKIND_VIEW;
1550 936 : break;
1551 :
1552 120 : case OBJECT_MATVIEW:
1553 120 : relkind = RELKIND_MATVIEW;
1554 120 : break;
1555 :
1556 162 : case OBJECT_FOREIGN_TABLE:
1557 162 : relkind = RELKIND_FOREIGN_TABLE;
1558 162 : break;
1559 :
1560 0 : default:
1561 0 : elog(ERROR, "unrecognized drop object type: %d",
1562 : (int) drop->removeType);
1563 : relkind = 0; /* keep compiler quiet */
1564 : break;
1565 : }
1566 :
1567 : /* Lock and validate each relation; build a list of object addresses */
1568 16872 : objects = new_object_addresses();
1569 :
1570 37622 : foreach(cell, drop->objects)
1571 : {
1572 20908 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1573 : Oid relOid;
1574 : ObjectAddress obj;
1575 : struct DropRelationCallbackState state;
1576 :
1577 : /*
1578 : * These next few steps are a great deal like relation_openrv, but we
1579 : * don't bother building a relcache entry since we don't need it.
1580 : *
1581 : * Check for shared-cache-inval messages before trying to access the
1582 : * relation. This is needed to cover the case where the name
1583 : * identifies a rel that has been dropped and recreated since the
1584 : * start of our transaction: if we don't flush the old syscache entry,
1585 : * then we'll latch onto that entry and suffer an error later.
1586 : */
1587 20908 : AcceptInvalidationMessages();
1588 :
1589 : /* Look up the appropriate relation using namespace search. */
1590 20908 : state.expected_relkind = relkind;
1591 41816 : state.heap_lockmode = drop->concurrent ?
1592 20908 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1593 : /* We must initialize these fields to show that no locks are held: */
1594 20908 : state.heapOid = InvalidOid;
1595 20908 : state.partParentOid = InvalidOid;
1596 :
1597 20908 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1598 : RangeVarCallbackForDropRelation,
1599 : &state);
1600 :
1601 : /* Not there? */
1602 20888 : if (!OidIsValid(relOid))
1603 : {
1604 1074 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1605 942 : continue;
1606 : }
1607 :
1608 : /*
1609 : * Decide if concurrent mode needs to be used here or not. The
1610 : * callback retrieved the rel's persistence for us.
1611 : */
1612 19814 : if (drop->concurrent &&
1613 118 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1614 : {
1615 : Assert(list_length(drop->objects) == 1 &&
1616 : drop->removeType == OBJECT_INDEX);
1617 100 : flags |= PERFORM_DELETION_CONCURRENTLY;
1618 : }
1619 :
1620 : /*
1621 : * Concurrent index drop cannot be used with partitioned indexes,
1622 : * either.
1623 : */
1624 19814 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1625 100 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1626 6 : ereport(ERROR,
1627 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1628 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1629 : rel->relname)));
1630 :
1631 : /*
1632 : * If we're told to drop a partitioned index, we must acquire lock on
1633 : * all the children of its parent partitioned table before proceeding.
1634 : * Otherwise we'd try to lock the child index partitions before their
1635 : * tables, leading to potential deadlock against other sessions that
1636 : * will lock those objects in the other order.
1637 : */
1638 19808 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1639 76 : (void) find_all_inheritors(state.heapOid,
1640 : state.heap_lockmode,
1641 : NULL);
1642 :
1643 : /* OK, we're ready to delete this one */
1644 19808 : obj.classId = RelationRelationId;
1645 19808 : obj.objectId = relOid;
1646 19808 : obj.objectSubId = 0;
1647 :
1648 19808 : add_exact_object_address(&obj, objects);
1649 : }
1650 :
1651 16714 : performMultipleDeletions(objects, drop->behavior, flags);
1652 :
1653 16572 : free_object_addresses(objects);
1654 16572 : }
1655 :
1656 : /*
1657 : * Before acquiring a table lock, check whether we have sufficient rights.
1658 : * In the case of DROP INDEX, also try to lock the table before the index.
1659 : * Also, if the table to be dropped is a partition, we try to lock the parent
1660 : * first.
1661 : */
1662 : static void
1663 21252 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1664 : void *arg)
1665 : {
1666 : HeapTuple tuple;
1667 : struct DropRelationCallbackState *state;
1668 : char expected_relkind;
1669 : bool is_partition;
1670 : Form_pg_class classform;
1671 : LOCKMODE heap_lockmode;
1672 21252 : bool invalid_system_index = false;
1673 :
1674 21252 : state = (struct DropRelationCallbackState *) arg;
1675 21252 : heap_lockmode = state->heap_lockmode;
1676 :
1677 : /*
1678 : * If we previously locked some other index's heap, and the name we're
1679 : * looking up no longer refers to that relation, release the now-useless
1680 : * lock.
1681 : */
1682 21252 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1683 : {
1684 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1685 0 : state->heapOid = InvalidOid;
1686 : }
1687 :
1688 : /*
1689 : * Similarly, if we previously locked some other partition's heap, and the
1690 : * name we're looking up no longer refers to that relation, release the
1691 : * now-useless lock.
1692 : */
1693 21252 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1694 : {
1695 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1696 0 : state->partParentOid = InvalidOid;
1697 : }
1698 :
1699 : /* Didn't find a relation, so no need for locking or permission checks. */
1700 21252 : if (!OidIsValid(relOid))
1701 1086 : return;
1702 :
1703 20166 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1704 20166 : if (!HeapTupleIsValid(tuple))
1705 0 : return; /* concurrently dropped, so nothing to do */
1706 20166 : classform = (Form_pg_class) GETSTRUCT(tuple);
1707 20166 : is_partition = classform->relispartition;
1708 :
1709 : /* Pass back some data to save lookups in RemoveRelations */
1710 20166 : state->actual_relkind = classform->relkind;
1711 20166 : state->actual_relpersistence = classform->relpersistence;
1712 :
1713 : /*
1714 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1715 : * but RemoveRelations() can only pass one relkind for a given relation.
1716 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1717 : * That means we must be careful before giving the wrong type error when
1718 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1719 : * exists with indexes.
1720 : */
1721 20166 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1722 2902 : expected_relkind = RELKIND_RELATION;
1723 17264 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1724 84 : expected_relkind = RELKIND_INDEX;
1725 : else
1726 17180 : expected_relkind = classform->relkind;
1727 :
1728 20166 : if (state->expected_relkind != expected_relkind)
1729 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1730 0 : state->expected_relkind);
1731 :
1732 : /* Allow DROP to either table owner or schema owner */
1733 20166 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1734 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1735 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1736 18 : get_relkind_objtype(classform->relkind),
1737 18 : rel->relname);
1738 :
1739 : /*
1740 : * Check the case of a system index that might have been invalidated by a
1741 : * failed concurrent process and allow its drop. For the time being, this
1742 : * only concerns indexes of toast relations that became invalid during a
1743 : * REINDEX CONCURRENTLY process.
1744 : */
1745 20148 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1746 : {
1747 : HeapTuple locTuple;
1748 : Form_pg_index indexform;
1749 : bool indisvalid;
1750 :
1751 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1752 0 : if (!HeapTupleIsValid(locTuple))
1753 : {
1754 0 : ReleaseSysCache(tuple);
1755 0 : return;
1756 : }
1757 :
1758 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1759 0 : indisvalid = indexform->indisvalid;
1760 0 : ReleaseSysCache(locTuple);
1761 :
1762 : /* Mark object as being an invalid index of system catalogs */
1763 0 : if (!indisvalid)
1764 0 : invalid_system_index = true;
1765 : }
1766 :
1767 : /* In the case of an invalid index, it is fine to bypass this check */
1768 20148 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1769 2 : ereport(ERROR,
1770 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1771 : errmsg("permission denied: \"%s\" is a system catalog",
1772 : rel->relname)));
1773 :
1774 20146 : ReleaseSysCache(tuple);
1775 :
1776 : /*
1777 : * In DROP INDEX, attempt to acquire lock on the parent table before
1778 : * locking the index. index_drop() will need this anyway, and since
1779 : * regular queries lock tables before their indexes, we risk deadlock if
1780 : * we do it the other way around. No error if we don't find a pg_index
1781 : * entry, though --- the relation may have been dropped. Note that this
1782 : * code will execute for either plain or partitioned indexes.
1783 : */
1784 20146 : if (expected_relkind == RELKIND_INDEX &&
1785 : relOid != oldRelOid)
1786 : {
1787 800 : state->heapOid = IndexGetRelation(relOid, true);
1788 800 : if (OidIsValid(state->heapOid))
1789 800 : LockRelationOid(state->heapOid, heap_lockmode);
1790 : }
1791 :
1792 : /*
1793 : * Similarly, if the relation is a partition, we must acquire lock on its
1794 : * parent before locking the partition. That's because queries lock the
1795 : * parent before its partitions, so we risk deadlock if we do it the other
1796 : * way around.
1797 : */
1798 20146 : if (is_partition && relOid != oldRelOid)
1799 : {
1800 612 : state->partParentOid = get_partition_parent(relOid, true);
1801 612 : if (OidIsValid(state->partParentOid))
1802 612 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1803 : }
1804 : }
1805 :
1806 : /*
1807 : * ExecuteTruncate
1808 : * Executes a TRUNCATE command.
1809 : *
1810 : * This is a multi-relation truncate. We first open and grab exclusive
1811 : * lock on all relations involved, checking permissions and otherwise
1812 : * verifying that the relation is OK for truncation. Note that if relations
1813 : * are foreign tables, at this stage, we have not yet checked that their
1814 : * foreign data in external data sources are OK for truncation. These are
1815 : * checked when foreign data are actually truncated later. In CASCADE mode,
1816 : * relations having FK references to the targeted relations are automatically
1817 : * added to the group; in RESTRICT mode, we check that all FK references are
1818 : * internal to the group that's being truncated. Finally all the relations
1819 : * are truncated and reindexed.
1820 : */
1821 : void
1822 1652 : ExecuteTruncate(TruncateStmt *stmt)
1823 : {
1824 1652 : List *rels = NIL;
1825 1652 : List *relids = NIL;
1826 1652 : List *relids_logged = NIL;
1827 : ListCell *cell;
1828 :
1829 : /*
1830 : * Open, exclusive-lock, and check all the explicitly-specified relations
1831 : */
1832 3516 : foreach(cell, stmt->relations)
1833 : {
1834 1920 : RangeVar *rv = lfirst(cell);
1835 : Relation rel;
1836 1920 : bool recurse = rv->inh;
1837 : Oid myrelid;
1838 1920 : LOCKMODE lockmode = AccessExclusiveLock;
1839 :
1840 1920 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1841 : 0, RangeVarCallbackForTruncate,
1842 : NULL);
1843 :
1844 : /* don't throw error for "TRUNCATE foo, foo" */
1845 1882 : if (list_member_oid(relids, myrelid))
1846 2 : continue;
1847 :
1848 : /* open the relation, we already hold a lock on it */
1849 1880 : rel = table_open(myrelid, NoLock);
1850 :
1851 : /*
1852 : * RangeVarGetRelidExtended() has done most checks with its callback,
1853 : * but other checks with the now-opened Relation remain.
1854 : */
1855 1880 : truncate_check_activity(rel);
1856 :
1857 1874 : rels = lappend(rels, rel);
1858 1874 : relids = lappend_oid(relids, myrelid);
1859 :
1860 : /* Log this relation only if needed for logical decoding */
1861 1874 : if (RelationIsLogicallyLogged(rel))
1862 68 : relids_logged = lappend_oid(relids_logged, myrelid);
1863 :
1864 1874 : if (recurse)
1865 : {
1866 : ListCell *child;
1867 : List *children;
1868 :
1869 1812 : children = find_all_inheritors(myrelid, lockmode, NULL);
1870 :
1871 5386 : foreach(child, children)
1872 : {
1873 3574 : Oid childrelid = lfirst_oid(child);
1874 :
1875 3574 : if (list_member_oid(relids, childrelid))
1876 1812 : continue;
1877 :
1878 : /* find_all_inheritors already got lock */
1879 1762 : rel = table_open(childrelid, NoLock);
1880 :
1881 : /*
1882 : * It is possible that the parent table has children that are
1883 : * temp tables of other backends. We cannot safely access
1884 : * such tables (because of buffering issues), and the best
1885 : * thing to do is to silently ignore them. Note that this
1886 : * check is the same as one of the checks done in
1887 : * truncate_check_activity() called below, still it is kept
1888 : * here for simplicity.
1889 : */
1890 1762 : if (RELATION_IS_OTHER_TEMP(rel))
1891 : {
1892 8 : table_close(rel, lockmode);
1893 8 : continue;
1894 : }
1895 :
1896 : /*
1897 : * Inherited TRUNCATE commands perform access permission
1898 : * checks on the parent table only. So we skip checking the
1899 : * children's permissions and don't call
1900 : * truncate_check_perms() here.
1901 : */
1902 1754 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1903 1754 : truncate_check_activity(rel);
1904 :
1905 1754 : rels = lappend(rels, rel);
1906 1754 : relids = lappend_oid(relids, childrelid);
1907 :
1908 : /* Log this relation only if needed for logical decoding */
1909 1754 : if (RelationIsLogicallyLogged(rel))
1910 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1911 : }
1912 : }
1913 62 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1914 12 : ereport(ERROR,
1915 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1916 : errmsg("cannot truncate only a partitioned table"),
1917 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1918 : }
1919 :
1920 1596 : ExecuteTruncateGuts(rels, relids, relids_logged,
1921 1596 : stmt->behavior, stmt->restart_seqs, false);
1922 :
1923 : /* And close the rels */
1924 4976 : foreach(cell, rels)
1925 : {
1926 3462 : Relation rel = (Relation) lfirst(cell);
1927 :
1928 3462 : table_close(rel, NoLock);
1929 : }
1930 1514 : }
1931 :
1932 : /*
1933 : * ExecuteTruncateGuts
1934 : *
1935 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1936 : * command (see above) as well as replication subscribers that execute a
1937 : * replicated TRUNCATE action.
1938 : *
1939 : * explicit_rels is the list of Relations to truncate that the command
1940 : * specified. relids is the list of Oids corresponding to explicit_rels.
1941 : * relids_logged is the list of Oids (a subset of relids) that require
1942 : * WAL-logging. This is all a bit redundant, but the existing callers have
1943 : * this information handy in this form.
1944 : */
1945 : void
1946 1634 : ExecuteTruncateGuts(List *explicit_rels,
1947 : List *relids,
1948 : List *relids_logged,
1949 : DropBehavior behavior, bool restart_seqs,
1950 : bool run_as_table_owner)
1951 : {
1952 : List *rels;
1953 1634 : List *seq_relids = NIL;
1954 1634 : HTAB *ft_htab = NULL;
1955 : EState *estate;
1956 : ResultRelInfo *resultRelInfos;
1957 : ResultRelInfo *resultRelInfo;
1958 : SubTransactionId mySubid;
1959 : ListCell *cell;
1960 : Oid *logrelids;
1961 :
1962 : /*
1963 : * Check the explicitly-specified relations.
1964 : *
1965 : * In CASCADE mode, suck in all referencing relations as well. This
1966 : * requires multiple iterations to find indirectly-dependent relations. At
1967 : * each phase, we need to exclusive-lock new rels before looking for their
1968 : * dependencies, else we might miss something. Also, we check each rel as
1969 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1970 : * time on a rel we have no permissions for.
1971 : */
1972 1634 : rels = list_copy(explicit_rels);
1973 1634 : if (behavior == DROP_CASCADE)
1974 : {
1975 : for (;;)
1976 40 : {
1977 : List *newrelids;
1978 :
1979 80 : newrelids = heap_truncate_find_FKs(relids);
1980 80 : if (newrelids == NIL)
1981 40 : break; /* nothing else to add */
1982 :
1983 134 : foreach(cell, newrelids)
1984 : {
1985 94 : Oid relid = lfirst_oid(cell);
1986 : Relation rel;
1987 :
1988 94 : rel = table_open(relid, AccessExclusiveLock);
1989 94 : ereport(NOTICE,
1990 : (errmsg("truncate cascades to table \"%s\"",
1991 : RelationGetRelationName(rel))));
1992 94 : truncate_check_rel(relid, rel->rd_rel);
1993 94 : truncate_check_perms(relid, rel->rd_rel);
1994 94 : truncate_check_activity(rel);
1995 94 : rels = lappend(rels, rel);
1996 94 : relids = lappend_oid(relids, relid);
1997 :
1998 : /* Log this relation only if needed for logical decoding */
1999 94 : if (RelationIsLogicallyLogged(rel))
2000 0 : relids_logged = lappend_oid(relids_logged, relid);
2001 : }
2002 : }
2003 : }
2004 :
2005 : /*
2006 : * Check foreign key references. In CASCADE mode, this should be
2007 : * unnecessary since we just pulled in all the references; but as a
2008 : * cross-check, do it anyway if in an Assert-enabled build.
2009 : */
2010 : #ifdef USE_ASSERT_CHECKING
2011 : heap_truncate_check_FKs(rels, false);
2012 : #else
2013 1634 : if (behavior == DROP_RESTRICT)
2014 1594 : heap_truncate_check_FKs(rels, false);
2015 : #endif
2016 :
2017 : /*
2018 : * If we are asked to restart sequences, find all the sequences, lock them
2019 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2020 : * We want to do this early since it's pointless to do all the truncation
2021 : * work only to fail on sequence permissions.
2022 : */
2023 1560 : if (restart_seqs)
2024 : {
2025 52 : foreach(cell, rels)
2026 : {
2027 26 : Relation rel = (Relation) lfirst(cell);
2028 26 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2029 : ListCell *seqcell;
2030 :
2031 62 : foreach(seqcell, seqlist)
2032 : {
2033 36 : Oid seq_relid = lfirst_oid(seqcell);
2034 : Relation seq_rel;
2035 :
2036 36 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2037 :
2038 : /* This check must match AlterSequence! */
2039 36 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2040 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2041 0 : RelationGetRelationName(seq_rel));
2042 :
2043 36 : seq_relids = lappend_oid(seq_relids, seq_relid);
2044 :
2045 36 : relation_close(seq_rel, NoLock);
2046 : }
2047 : }
2048 : }
2049 :
2050 : /* Prepare to catch AFTER triggers. */
2051 1560 : AfterTriggerBeginQuery();
2052 :
2053 : /*
2054 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2055 : * each relation. We don't need to call ExecOpenIndices, though.
2056 : *
2057 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2058 : * though we don't have a range table and don't populate the
2059 : * es_result_relations array. That's a bit bogus, but it's enough to make
2060 : * ExecGetTriggerResultRel() find them.
2061 : */
2062 1560 : estate = CreateExecutorState();
2063 : resultRelInfos = (ResultRelInfo *)
2064 1560 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2065 1560 : resultRelInfo = resultRelInfos;
2066 5194 : foreach(cell, rels)
2067 : {
2068 3634 : Relation rel = (Relation) lfirst(cell);
2069 :
2070 3634 : InitResultRelInfo(resultRelInfo,
2071 : rel,
2072 : 0, /* dummy rangetable index */
2073 : NULL,
2074 : 0);
2075 3634 : estate->es_opened_result_relations =
2076 3634 : lappend(estate->es_opened_result_relations, resultRelInfo);
2077 3634 : resultRelInfo++;
2078 : }
2079 :
2080 : /*
2081 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2082 : * truncating (this is because one of them might throw an error). Also, if
2083 : * we were to allow them to prevent statement execution, that would need
2084 : * to be handled here.
2085 : */
2086 1560 : resultRelInfo = resultRelInfos;
2087 5194 : foreach(cell, rels)
2088 : {
2089 : UserContext ucxt;
2090 :
2091 3634 : if (run_as_table_owner)
2092 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2093 : &ucxt);
2094 3634 : ExecBSTruncateTriggers(estate, resultRelInfo);
2095 3634 : if (run_as_table_owner)
2096 70 : RestoreUserContext(&ucxt);
2097 3634 : resultRelInfo++;
2098 : }
2099 :
2100 : /*
2101 : * OK, truncate each table.
2102 : */
2103 1560 : mySubid = GetCurrentSubTransactionId();
2104 :
2105 5194 : foreach(cell, rels)
2106 : {
2107 3634 : Relation rel = (Relation) lfirst(cell);
2108 :
2109 : /* Skip partitioned tables as there is nothing to do */
2110 3634 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2111 692 : continue;
2112 :
2113 : /*
2114 : * Build the lists of foreign tables belonging to each foreign server
2115 : * and pass each list to the foreign data wrapper's callback function,
2116 : * so that each server can truncate its all foreign tables in bulk.
2117 : * Each list is saved as a single entry in a hash table that uses the
2118 : * server OID as lookup key.
2119 : */
2120 2942 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2121 : {
2122 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2123 : bool found;
2124 : ForeignTruncateInfo *ft_info;
2125 :
2126 : /* First time through, initialize hashtable for foreign tables */
2127 34 : if (!ft_htab)
2128 : {
2129 : HASHCTL hctl;
2130 :
2131 30 : memset(&hctl, 0, sizeof(HASHCTL));
2132 30 : hctl.keysize = sizeof(Oid);
2133 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2134 30 : hctl.hcxt = CurrentMemoryContext;
2135 :
2136 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2137 : 32, /* start small and extend */
2138 : &hctl,
2139 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2140 : }
2141 :
2142 : /* Find or create cached entry for the foreign table */
2143 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2144 34 : if (!found)
2145 30 : ft_info->rels = NIL;
2146 :
2147 : /*
2148 : * Save the foreign table in the entry of the server that the
2149 : * foreign table belongs to.
2150 : */
2151 34 : ft_info->rels = lappend(ft_info->rels, rel);
2152 34 : continue;
2153 : }
2154 :
2155 : /*
2156 : * Normally, we need a transaction-safe truncation here. However, if
2157 : * the table was either created in the current (sub)transaction or has
2158 : * a new relfilenumber in the current (sub)transaction, then we can
2159 : * just truncate it in-place, because a rollback would cause the whole
2160 : * table or the current physical file to be thrown away anyway.
2161 : */
2162 2908 : if (rel->rd_createSubid == mySubid ||
2163 2882 : rel->rd_newRelfilelocatorSubid == mySubid)
2164 : {
2165 : /* Immediate, non-rollbackable truncation is OK */
2166 90 : heap_truncate_one_rel(rel);
2167 : }
2168 : else
2169 : {
2170 : Oid heap_relid;
2171 : Oid toast_relid;
2172 2818 : ReindexParams reindex_params = {0};
2173 :
2174 : /*
2175 : * This effectively deletes all rows in the table, and may be done
2176 : * in a serializable transaction. In that case we must record a
2177 : * rw-conflict in to this transaction from each transaction
2178 : * holding a predicate lock on the table.
2179 : */
2180 2818 : CheckTableForSerializableConflictIn(rel);
2181 :
2182 : /*
2183 : * Need the full transaction-safe pushups.
2184 : *
2185 : * Create a new empty storage file for the relation, and assign it
2186 : * as the relfilenumber value. The old storage file is scheduled
2187 : * for deletion at commit.
2188 : */
2189 2818 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2190 :
2191 2818 : heap_relid = RelationGetRelid(rel);
2192 :
2193 : /*
2194 : * The same for the toast table, if any.
2195 : */
2196 2818 : toast_relid = rel->rd_rel->reltoastrelid;
2197 2818 : if (OidIsValid(toast_relid))
2198 : {
2199 1740 : Relation toastrel = relation_open(toast_relid,
2200 : AccessExclusiveLock);
2201 :
2202 1740 : RelationSetNewRelfilenumber(toastrel,
2203 1740 : toastrel->rd_rel->relpersistence);
2204 1740 : table_close(toastrel, NoLock);
2205 : }
2206 :
2207 : /*
2208 : * Reconstruct the indexes to match, and we're done.
2209 : */
2210 2818 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2211 : &reindex_params);
2212 : }
2213 :
2214 2908 : pgstat_count_truncate(rel);
2215 : }
2216 :
2217 : /* Now go through the hash table, and truncate foreign tables */
2218 1560 : if (ft_htab)
2219 : {
2220 : ForeignTruncateInfo *ft_info;
2221 : HASH_SEQ_STATUS seq;
2222 :
2223 30 : hash_seq_init(&seq, ft_htab);
2224 :
2225 30 : PG_TRY();
2226 : {
2227 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2228 : {
2229 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2230 :
2231 : /* truncate_check_rel() has checked that already */
2232 : Assert(routine->ExecForeignTruncate != NULL);
2233 :
2234 30 : routine->ExecForeignTruncate(ft_info->rels,
2235 : behavior,
2236 : restart_seqs);
2237 : }
2238 : }
2239 8 : PG_FINALLY();
2240 : {
2241 30 : hash_destroy(ft_htab);
2242 : }
2243 30 : PG_END_TRY();
2244 : }
2245 :
2246 : /*
2247 : * Restart owned sequences if we were asked to.
2248 : */
2249 1588 : foreach(cell, seq_relids)
2250 : {
2251 36 : Oid seq_relid = lfirst_oid(cell);
2252 :
2253 36 : ResetSequence(seq_relid);
2254 : }
2255 :
2256 : /*
2257 : * Write a WAL record to allow this set of actions to be logically
2258 : * decoded.
2259 : *
2260 : * Assemble an array of relids so we can write a single WAL record for the
2261 : * whole action.
2262 : */
2263 1552 : if (relids_logged != NIL)
2264 : {
2265 : xl_heap_truncate xlrec;
2266 54 : int i = 0;
2267 :
2268 : /* should only get here if wal_level >= logical */
2269 : Assert(XLogLogicalInfoActive());
2270 :
2271 54 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2272 144 : foreach(cell, relids_logged)
2273 90 : logrelids[i++] = lfirst_oid(cell);
2274 :
2275 54 : xlrec.dbId = MyDatabaseId;
2276 54 : xlrec.nrelids = list_length(relids_logged);
2277 54 : xlrec.flags = 0;
2278 54 : if (behavior == DROP_CASCADE)
2279 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2280 54 : if (restart_seqs)
2281 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2282 :
2283 54 : XLogBeginInsert();
2284 54 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2285 54 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2286 :
2287 54 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2288 :
2289 54 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2290 : }
2291 :
2292 : /*
2293 : * Process all AFTER STATEMENT TRUNCATE triggers.
2294 : */
2295 1552 : resultRelInfo = resultRelInfos;
2296 5178 : foreach(cell, rels)
2297 : {
2298 : UserContext ucxt;
2299 :
2300 3626 : if (run_as_table_owner)
2301 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2302 : &ucxt);
2303 3626 : ExecASTruncateTriggers(estate, resultRelInfo);
2304 3626 : if (run_as_table_owner)
2305 70 : RestoreUserContext(&ucxt);
2306 3626 : resultRelInfo++;
2307 : }
2308 :
2309 : /* Handle queued AFTER triggers */
2310 1552 : AfterTriggerEndQuery(estate);
2311 :
2312 : /* We can clean up the EState now */
2313 1552 : FreeExecutorState(estate);
2314 :
2315 : /*
2316 : * Close any rels opened by CASCADE (can't do this while EState still
2317 : * holds refs)
2318 : */
2319 1552 : rels = list_difference_ptr(rels, explicit_rels);
2320 1646 : foreach(cell, rels)
2321 : {
2322 94 : Relation rel = (Relation) lfirst(cell);
2323 :
2324 94 : table_close(rel, NoLock);
2325 : }
2326 1552 : }
2327 :
2328 : /*
2329 : * Check that a given relation is safe to truncate. Subroutine for
2330 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2331 : */
2332 : static void
2333 3862 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2334 : {
2335 3862 : char *relname = NameStr(reltuple->relname);
2336 :
2337 : /*
2338 : * Only allow truncate on regular tables, foreign tables using foreign
2339 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2340 : * latter are only being included here for the following checks; no
2341 : * physical truncation will occur in their case.).
2342 : */
2343 3862 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2344 : {
2345 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2346 38 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2347 :
2348 36 : if (!fdwroutine->ExecForeignTruncate)
2349 2 : ereport(ERROR,
2350 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2351 : errmsg("cannot truncate foreign table \"%s\"",
2352 : relname)));
2353 : }
2354 3824 : else if (reltuple->relkind != RELKIND_RELATION &&
2355 710 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2356 0 : ereport(ERROR,
2357 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2358 : errmsg("\"%s\" is not a table", relname)));
2359 :
2360 : /*
2361 : * Most system catalogs can't be truncated at all, or at least not unless
2362 : * allow_system_table_mods=on. As an exception, however, we allow
2363 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2364 : * to change its relfilenode to match the old cluster, and allowing a
2365 : * TRUNCATE command to be executed is the easiest way of doing that.
2366 : */
2367 3858 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2368 44 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2369 2 : ereport(ERROR,
2370 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2371 : errmsg("permission denied: \"%s\" is a system catalog",
2372 : relname)));
2373 :
2374 3856 : InvokeObjectTruncateHook(relid);
2375 3856 : }
2376 :
2377 : /*
2378 : * Check that current user has the permission to truncate given relation.
2379 : */
2380 : static void
2381 2102 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2382 : {
2383 2102 : char *relname = NameStr(reltuple->relname);
2384 : AclResult aclresult;
2385 :
2386 : /* Permissions checks */
2387 2102 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2388 2102 : if (aclresult != ACLCHECK_OK)
2389 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2390 : relname);
2391 2070 : }
2392 :
2393 : /*
2394 : * Set of extra sanity checks to check if a given relation is safe to
2395 : * truncate. This is split with truncate_check_rel() as
2396 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2397 : */
2398 : static void
2399 3728 : truncate_check_activity(Relation rel)
2400 : {
2401 : /*
2402 : * Don't allow truncate on temp tables of other backends ... their local
2403 : * buffer manager is not going to cope.
2404 : */
2405 3728 : if (RELATION_IS_OTHER_TEMP(rel))
2406 0 : ereport(ERROR,
2407 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2408 : errmsg("cannot truncate temporary tables of other sessions")));
2409 :
2410 : /*
2411 : * Also check for active uses of the relation in the current transaction,
2412 : * including open scans and pending AFTER trigger events.
2413 : */
2414 3728 : CheckTableNotInUse(rel, "TRUNCATE");
2415 3722 : }
2416 :
2417 : /*
2418 : * storage_name
2419 : * returns the name corresponding to a typstorage/attstorage enum value
2420 : */
2421 : static const char *
2422 24 : storage_name(char c)
2423 : {
2424 24 : switch (c)
2425 : {
2426 0 : case TYPSTORAGE_PLAIN:
2427 0 : return "PLAIN";
2428 0 : case TYPSTORAGE_EXTERNAL:
2429 0 : return "EXTERNAL";
2430 12 : case TYPSTORAGE_EXTENDED:
2431 12 : return "EXTENDED";
2432 12 : case TYPSTORAGE_MAIN:
2433 12 : return "MAIN";
2434 0 : default:
2435 0 : return "???";
2436 : }
2437 : }
2438 :
2439 : /*----------
2440 : * MergeAttributes
2441 : * Returns new schema given initial schema and superclasses.
2442 : *
2443 : * Input arguments:
2444 : * 'columns' is the column/attribute definition for the table. (It's a list
2445 : * of ColumnDef's.) It is destructively changed.
2446 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2447 : * 'relpersistence' is the persistence type of the table.
2448 : * 'is_partition' tells if the table is a partition.
2449 : *
2450 : * Output arguments:
2451 : * 'supconstr' receives a list of CookedConstraint representing
2452 : * CHECK constraints belonging to parent relations, updated as
2453 : * necessary to be valid for the child.
2454 : * 'supnotnulls' receives a list of CookedConstraint representing
2455 : * not-null constraints based on those from parent relations.
2456 : *
2457 : * Return value:
2458 : * Completed schema list.
2459 : *
2460 : * Notes:
2461 : * The order in which the attributes are inherited is very important.
2462 : * Intuitively, the inherited attributes should come first. If a table
2463 : * inherits from multiple parents, the order of those attributes are
2464 : * according to the order of the parents specified in CREATE TABLE.
2465 : *
2466 : * Here's an example:
2467 : *
2468 : * create table person (name text, age int4, location point);
2469 : * create table emp (salary int4, manager text) inherits(person);
2470 : * create table student (gpa float8) inherits (person);
2471 : * create table stud_emp (percent int4) inherits (emp, student);
2472 : *
2473 : * The order of the attributes of stud_emp is:
2474 : *
2475 : * person {1:name, 2:age, 3:location}
2476 : * / \
2477 : * {6:gpa} student emp {4:salary, 5:manager}
2478 : * \ /
2479 : * stud_emp {7:percent}
2480 : *
2481 : * If the same attribute name appears multiple times, then it appears
2482 : * in the result table in the proper location for its first appearance.
2483 : *
2484 : * Constraints (including not-null constraints) for the child table
2485 : * are the union of all relevant constraints, from both the child schema
2486 : * and parent tables. In addition, in legacy inheritance, each column that
2487 : * appears in a primary key in any of the parents also gets a NOT NULL
2488 : * constraint (partitioning doesn't need this, because the PK itself gets
2489 : * inherited.)
2490 : *
2491 : * The default value for a child column is defined as:
2492 : * (1) If the child schema specifies a default, that value is used.
2493 : * (2) If neither the child nor any parent specifies a default, then
2494 : * the column will not have a default.
2495 : * (3) If conflicting defaults are inherited from different parents
2496 : * (and not overridden by the child), an error is raised.
2497 : * (4) Otherwise the inherited default is used.
2498 : *
2499 : * Note that the default-value infrastructure is used for generated
2500 : * columns' expressions too, so most of the preceding paragraph applies
2501 : * to generation expressions too. We insist that a child column be
2502 : * generated if and only if its parent(s) are, but it need not have
2503 : * the same generation expression.
2504 : *----------
2505 : */
2506 : static List *
2507 59520 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2508 : bool is_partition, List **supconstr, List **supnotnulls)
2509 : {
2510 59520 : List *inh_columns = NIL;
2511 59520 : List *constraints = NIL;
2512 59520 : List *nnconstraints = NIL;
2513 59520 : bool have_bogus_defaults = false;
2514 : int child_attno;
2515 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2516 59520 : List *saved_columns = NIL;
2517 : ListCell *lc;
2518 :
2519 : /*
2520 : * Check for and reject tables with too many columns. We perform this
2521 : * check relatively early for two reasons: (a) we don't run the risk of
2522 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2523 : * okay if we're processing <= 1600 columns, but could take minutes to
2524 : * execute if the user attempts to create a table with hundreds of
2525 : * thousands of columns.
2526 : *
2527 : * Note that we also need to check that we do not exceed this figure after
2528 : * including columns from inherited relations.
2529 : */
2530 59520 : if (list_length(columns) > MaxHeapAttributeNumber)
2531 0 : ereport(ERROR,
2532 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2533 : errmsg("tables can have at most %d columns",
2534 : MaxHeapAttributeNumber)));
2535 :
2536 : /*
2537 : * Check for duplicate names in the explicit list of attributes.
2538 : *
2539 : * Although we might consider merging such entries in the same way that we
2540 : * handle name conflicts for inherited attributes, it seems to make more
2541 : * sense to assume such conflicts are errors.
2542 : *
2543 : * We don't use foreach() here because we have two nested loops over the
2544 : * columns list, with possible element deletions in the inner one. If we
2545 : * used foreach_delete_current() it could only fix up the state of one of
2546 : * the loops, so it seems cleaner to use looping over list indexes for
2547 : * both loops. Note that any deletion will happen beyond where the outer
2548 : * loop is, so its index never needs adjustment.
2549 : */
2550 273740 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2551 : {
2552 214244 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2553 :
2554 214244 : if (!is_partition && coldef->typeName == NULL)
2555 : {
2556 : /*
2557 : * Typed table column option that does not belong to a column from
2558 : * the type. This works because the columns from the type come
2559 : * first in the list. (We omit this check for partition column
2560 : * lists; those are processed separately below.)
2561 : */
2562 6 : ereport(ERROR,
2563 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2564 : errmsg("column \"%s\" does not exist",
2565 : coldef->colname)));
2566 : }
2567 :
2568 : /* restpos scans all entries beyond coldef; incr is in loop body */
2569 6378826 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2570 : {
2571 6164606 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2572 :
2573 6164606 : if (strcmp(coldef->colname, restdef->colname) == 0)
2574 : {
2575 50 : if (coldef->is_from_type)
2576 : {
2577 : /*
2578 : * merge the column options into the column from the type
2579 : */
2580 32 : coldef->is_not_null = restdef->is_not_null;
2581 32 : coldef->raw_default = restdef->raw_default;
2582 32 : coldef->cooked_default = restdef->cooked_default;
2583 32 : coldef->constraints = restdef->constraints;
2584 32 : coldef->is_from_type = false;
2585 32 : columns = list_delete_nth_cell(columns, restpos);
2586 : }
2587 : else
2588 18 : ereport(ERROR,
2589 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2590 : errmsg("column \"%s\" specified more than once",
2591 : coldef->colname)));
2592 : }
2593 : else
2594 6164556 : restpos++;
2595 : }
2596 : }
2597 :
2598 : /*
2599 : * In case of a partition, there are no new column definitions, only dummy
2600 : * ColumnDefs created for column constraints. Set them aside for now and
2601 : * process them at the end.
2602 : */
2603 59496 : if (is_partition)
2604 : {
2605 7868 : saved_columns = columns;
2606 7868 : columns = NIL;
2607 : }
2608 :
2609 : /*
2610 : * Scan the parents left-to-right, and merge their attributes to form a
2611 : * list of inherited columns (inh_columns).
2612 : */
2613 59496 : child_attno = 0;
2614 69660 : foreach(lc, supers)
2615 : {
2616 10248 : Oid parent = lfirst_oid(lc);
2617 : Relation relation;
2618 : TupleDesc tupleDesc;
2619 : TupleConstr *constr;
2620 : AttrMap *newattmap;
2621 : List *inherited_defaults;
2622 : List *cols_with_defaults;
2623 : List *nnconstrs;
2624 : ListCell *lc1;
2625 : ListCell *lc2;
2626 10248 : Bitmapset *nncols = NULL;
2627 :
2628 : /* caller already got lock */
2629 10248 : relation = table_open(parent, NoLock);
2630 :
2631 : /*
2632 : * Check for active uses of the parent partitioned table in the
2633 : * current transaction, such as being used in some manner by an
2634 : * enclosing command.
2635 : */
2636 10248 : if (is_partition)
2637 7868 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2638 :
2639 : /*
2640 : * We do not allow partitioned tables and partitions to participate in
2641 : * regular inheritance.
2642 : */
2643 10242 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2644 6 : ereport(ERROR,
2645 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2646 : errmsg("cannot inherit from partitioned table \"%s\"",
2647 : RelationGetRelationName(relation))));
2648 10236 : if (relation->rd_rel->relispartition && !is_partition)
2649 6 : ereport(ERROR,
2650 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2651 : errmsg("cannot inherit from partition \"%s\"",
2652 : RelationGetRelationName(relation))));
2653 :
2654 10230 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2655 7864 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2656 7844 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2657 0 : ereport(ERROR,
2658 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2659 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2660 : RelationGetRelationName(relation))));
2661 :
2662 : /*
2663 : * If the parent is permanent, so must be all of its partitions. Note
2664 : * that inheritance allows that case.
2665 : */
2666 10230 : if (is_partition &&
2667 7862 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2668 : relpersistence == RELPERSISTENCE_TEMP)
2669 6 : ereport(ERROR,
2670 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2671 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2672 : RelationGetRelationName(relation))));
2673 :
2674 : /* Permanent rels cannot inherit from temporary ones */
2675 10224 : if (relpersistence != RELPERSISTENCE_TEMP &&
2676 9876 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2677 24 : ereport(ERROR,
2678 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2679 : errmsg(!is_partition
2680 : ? "cannot inherit from temporary relation \"%s\""
2681 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2682 : RelationGetRelationName(relation))));
2683 :
2684 : /* If existing rel is temp, it must belong to this session */
2685 10200 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2686 294 : !relation->rd_islocaltemp)
2687 0 : ereport(ERROR,
2688 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2689 : errmsg(!is_partition
2690 : ? "cannot inherit from temporary relation of another session"
2691 : : "cannot create as partition of temporary relation of another session")));
2692 :
2693 : /*
2694 : * We should have an UNDER permission flag for this, but for now,
2695 : * demand that creator of a child table own the parent.
2696 : */
2697 10200 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2698 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2699 0 : RelationGetRelationName(relation));
2700 :
2701 10200 : tupleDesc = RelationGetDescr(relation);
2702 10200 : constr = tupleDesc->constr;
2703 :
2704 : /*
2705 : * newattmap->attnums[] will contain the child-table attribute numbers
2706 : * for the attributes of this parent table. (They are not the same
2707 : * for parents after the first one, nor if we have dropped columns.)
2708 : */
2709 10200 : newattmap = make_attrmap(tupleDesc->natts);
2710 :
2711 : /* We can't process inherited defaults until newattmap is complete. */
2712 10200 : inherited_defaults = cols_with_defaults = NIL;
2713 :
2714 : /*
2715 : * Request attnotnull on columns that have a not-null constraint
2716 : * that's not marked NO INHERIT.
2717 : */
2718 10200 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2719 : true, false);
2720 22696 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2721 2296 : nncols = bms_add_member(nncols, cc->attnum);
2722 :
2723 30698 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2724 20498 : parent_attno++)
2725 : {
2726 20534 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2727 : parent_attno - 1);
2728 20534 : char *attributeName = NameStr(attribute->attname);
2729 : int exist_attno;
2730 : ColumnDef *newdef;
2731 : ColumnDef *mergeddef;
2732 :
2733 : /*
2734 : * Ignore dropped columns in the parent.
2735 : */
2736 20534 : if (attribute->attisdropped)
2737 192 : continue; /* leave newattmap->attnums entry as zero */
2738 :
2739 : /*
2740 : * Create new column definition
2741 : */
2742 20342 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2743 : attribute->atttypmod, attribute->attcollation);
2744 20342 : newdef->storage = attribute->attstorage;
2745 20342 : newdef->generated = attribute->attgenerated;
2746 20342 : if (CompressionMethodIsValid(attribute->attcompression))
2747 30 : newdef->compression =
2748 30 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2749 :
2750 : /*
2751 : * Regular inheritance children are independent enough not to
2752 : * inherit identity columns. But partitions are integral part of
2753 : * a partitioned table and inherit identity column.
2754 : */
2755 20342 : if (is_partition)
2756 15940 : newdef->identity = attribute->attidentity;
2757 :
2758 : /*
2759 : * Does it match some previously considered column from another
2760 : * parent?
2761 : */
2762 20342 : exist_attno = findAttrByName(attributeName, inh_columns);
2763 20342 : if (exist_attno > 0)
2764 : {
2765 : /*
2766 : * Yes, try to merge the two column definitions.
2767 : */
2768 350 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2769 :
2770 314 : newattmap->attnums[parent_attno - 1] = exist_attno;
2771 :
2772 : /*
2773 : * Partitions have only one parent, so conflict should never
2774 : * occur.
2775 : */
2776 : Assert(!is_partition);
2777 : }
2778 : else
2779 : {
2780 : /*
2781 : * No, create a new inherited column
2782 : */
2783 19992 : newdef->inhcount = 1;
2784 19992 : newdef->is_local = false;
2785 19992 : inh_columns = lappend(inh_columns, newdef);
2786 :
2787 19992 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2788 19992 : mergeddef = newdef;
2789 : }
2790 :
2791 : /*
2792 : * mark attnotnull if parent has it
2793 : */
2794 20306 : if (bms_is_member(parent_attno, nncols))
2795 2296 : mergeddef->is_not_null = true;
2796 :
2797 : /*
2798 : * Locate default/generation expression if any
2799 : */
2800 20306 : if (attribute->atthasdef)
2801 : {
2802 : Node *this_default;
2803 :
2804 674 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2805 674 : if (this_default == NULL)
2806 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2807 : parent_attno, RelationGetRelationName(relation));
2808 :
2809 : /*
2810 : * If it's a GENERATED default, it might contain Vars that
2811 : * need to be mapped to the inherited column(s)' new numbers.
2812 : * We can't do that till newattmap is ready, so just remember
2813 : * all the inherited default expressions for the moment.
2814 : */
2815 674 : inherited_defaults = lappend(inherited_defaults, this_default);
2816 674 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2817 : }
2818 : }
2819 :
2820 : /*
2821 : * Now process any inherited default expressions, adjusting attnos
2822 : * using the completed newattmap map.
2823 : */
2824 10838 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2825 : {
2826 674 : Node *this_default = (Node *) lfirst(lc1);
2827 674 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2828 : bool found_whole_row;
2829 :
2830 : /* Adjust Vars to match new table's column numbering */
2831 674 : this_default = map_variable_attnos(this_default,
2832 : 1, 0,
2833 : newattmap,
2834 : InvalidOid, &found_whole_row);
2835 :
2836 : /*
2837 : * For the moment we have to reject whole-row variables. We could
2838 : * convert them, if we knew the new table's rowtype OID, but that
2839 : * hasn't been assigned yet. (A variable could only appear in a
2840 : * generation expression, so the error message is correct.)
2841 : */
2842 674 : if (found_whole_row)
2843 0 : ereport(ERROR,
2844 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2845 : errmsg("cannot convert whole-row table reference"),
2846 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2847 : def->colname,
2848 : RelationGetRelationName(relation))));
2849 :
2850 : /*
2851 : * If we already had a default from some prior parent, check to
2852 : * see if they are the same. If so, no problem; if not, mark the
2853 : * column as having a bogus default. Below, we will complain if
2854 : * the bogus default isn't overridden by the child columns.
2855 : */
2856 : Assert(def->raw_default == NULL);
2857 674 : if (def->cooked_default == NULL)
2858 632 : def->cooked_default = this_default;
2859 42 : else if (!equal(def->cooked_default, this_default))
2860 : {
2861 36 : def->cooked_default = &bogus_marker;
2862 36 : have_bogus_defaults = true;
2863 : }
2864 : }
2865 :
2866 : /*
2867 : * Now copy the CHECK constraints of this parent, adjusting attnos
2868 : * using the completed newattmap map. Identically named constraints
2869 : * are merged if possible, else we throw error.
2870 : */
2871 10164 : if (constr && constr->num_check > 0)
2872 : {
2873 334 : ConstrCheck *check = constr->check;
2874 :
2875 968 : for (int i = 0; i < constr->num_check; i++)
2876 : {
2877 634 : char *name = check[i].ccname;
2878 : Node *expr;
2879 : bool found_whole_row;
2880 :
2881 : /* ignore if the constraint is non-inheritable */
2882 634 : if (check[i].ccnoinherit)
2883 48 : continue;
2884 :
2885 : /* Adjust Vars to match new table's column numbering */
2886 586 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2887 : 1, 0,
2888 : newattmap,
2889 : InvalidOid, &found_whole_row);
2890 :
2891 : /*
2892 : * For the moment we have to reject whole-row variables. We
2893 : * could convert them, if we knew the new table's rowtype OID,
2894 : * but that hasn't been assigned yet.
2895 : */
2896 586 : if (found_whole_row)
2897 0 : ereport(ERROR,
2898 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2899 : errmsg("cannot convert whole-row table reference"),
2900 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2901 : name,
2902 : RelationGetRelationName(relation))));
2903 :
2904 586 : constraints = MergeCheckConstraint(constraints, name, expr,
2905 586 : check[i].ccenforced);
2906 : }
2907 : }
2908 :
2909 : /*
2910 : * Also copy the not-null constraints from this parent. The
2911 : * attnotnull markings were already installed above.
2912 : */
2913 22624 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2914 : {
2915 : Assert(nn->contype == CONSTR_NOTNULL);
2916 :
2917 2296 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2918 :
2919 2296 : nnconstraints = lappend(nnconstraints, nn);
2920 : }
2921 :
2922 10164 : free_attrmap(newattmap);
2923 :
2924 : /*
2925 : * Close the parent rel, but keep our lock on it until xact commit.
2926 : * That will prevent someone else from deleting or ALTERing the parent
2927 : * before the child is committed.
2928 : */
2929 10164 : table_close(relation, NoLock);
2930 : }
2931 :
2932 : /*
2933 : * If we had no inherited attributes, the result columns are just the
2934 : * explicitly declared columns. Otherwise, we need to merge the declared
2935 : * columns into the inherited column list. Although, we never have any
2936 : * explicitly declared columns if the table is a partition.
2937 : */
2938 59412 : if (inh_columns != NIL)
2939 : {
2940 9754 : int newcol_attno = 0;
2941 :
2942 10702 : foreach(lc, columns)
2943 : {
2944 1026 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2945 1026 : char *attributeName = newdef->colname;
2946 : int exist_attno;
2947 :
2948 : /*
2949 : * Partitions have only one parent and have no column definitions
2950 : * of their own, so conflict should never occur.
2951 : */
2952 : Assert(!is_partition);
2953 :
2954 1026 : newcol_attno++;
2955 :
2956 : /*
2957 : * Does it match some inherited column?
2958 : */
2959 1026 : exist_attno = findAttrByName(attributeName, inh_columns);
2960 1026 : if (exist_attno > 0)
2961 : {
2962 : /*
2963 : * Yes, try to merge the two column definitions.
2964 : */
2965 354 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2966 : }
2967 : else
2968 : {
2969 : /*
2970 : * No, attach new column unchanged to result columns.
2971 : */
2972 672 : inh_columns = lappend(inh_columns, newdef);
2973 : }
2974 : }
2975 :
2976 9676 : columns = inh_columns;
2977 :
2978 : /*
2979 : * Check that we haven't exceeded the legal # of columns after merging
2980 : * in inherited columns.
2981 : */
2982 9676 : if (list_length(columns) > MaxHeapAttributeNumber)
2983 0 : ereport(ERROR,
2984 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2985 : errmsg("tables can have at most %d columns",
2986 : MaxHeapAttributeNumber)));
2987 : }
2988 :
2989 : /*
2990 : * Now that we have the column definition list for a partition, we can
2991 : * check whether the columns referenced in the column constraint specs
2992 : * actually exist. Also, merge column defaults.
2993 : */
2994 59334 : if (is_partition)
2995 : {
2996 8044 : foreach(lc, saved_columns)
2997 : {
2998 242 : ColumnDef *restdef = lfirst(lc);
2999 242 : bool found = false;
3000 : ListCell *l;
3001 :
3002 900 : foreach(l, columns)
3003 : {
3004 694 : ColumnDef *coldef = lfirst(l);
3005 :
3006 694 : if (strcmp(coldef->colname, restdef->colname) == 0)
3007 : {
3008 242 : found = true;
3009 :
3010 : /*
3011 : * Check for conflicts related to generated columns.
3012 : *
3013 : * Same rules as above: generated-ness has to match the
3014 : * parent, but the contents of the generation expression
3015 : * can be different.
3016 : */
3017 242 : if (coldef->generated)
3018 : {
3019 134 : if (restdef->raw_default && !restdef->generated)
3020 12 : ereport(ERROR,
3021 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3022 : errmsg("column \"%s\" inherits from generated column but specifies default",
3023 : restdef->colname)));
3024 122 : if (restdef->identity)
3025 0 : ereport(ERROR,
3026 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3027 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3028 : restdef->colname)));
3029 : }
3030 : else
3031 : {
3032 108 : if (restdef->generated)
3033 12 : ereport(ERROR,
3034 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3035 : errmsg("child column \"%s\" specifies generation expression",
3036 : restdef->colname),
3037 : errhint("A child table column cannot be generated unless its parent column is.")));
3038 : }
3039 :
3040 218 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3041 12 : ereport(ERROR,
3042 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3043 : errmsg("column \"%s\" inherits from generated column of different kind",
3044 : restdef->colname),
3045 : errdetail("Parent column is %s, child column is %s.",
3046 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3047 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3048 :
3049 : /*
3050 : * Override the parent's default value for this column
3051 : * (coldef->cooked_default) with the partition's local
3052 : * definition (restdef->raw_default), if there's one. It
3053 : * should be physically impossible to get a cooked default
3054 : * in the local definition or a raw default in the
3055 : * inherited definition, but make sure they're nulls, for
3056 : * future-proofing.
3057 : */
3058 : Assert(restdef->cooked_default == NULL);
3059 : Assert(coldef->raw_default == NULL);
3060 206 : if (restdef->raw_default)
3061 : {
3062 134 : coldef->raw_default = restdef->raw_default;
3063 134 : coldef->cooked_default = NULL;
3064 : }
3065 : }
3066 : }
3067 :
3068 : /* complain for constraints on columns not in parent */
3069 206 : if (!found)
3070 0 : ereport(ERROR,
3071 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3072 : errmsg("column \"%s\" does not exist",
3073 : restdef->colname)));
3074 : }
3075 : }
3076 :
3077 : /*
3078 : * If we found any conflicting parent default values, check to make sure
3079 : * they were overridden by the child.
3080 : */
3081 59298 : if (have_bogus_defaults)
3082 : {
3083 90 : foreach(lc, columns)
3084 : {
3085 72 : ColumnDef *def = lfirst(lc);
3086 :
3087 72 : if (def->cooked_default == &bogus_marker)
3088 : {
3089 18 : if (def->generated)
3090 12 : ereport(ERROR,
3091 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3092 : errmsg("column \"%s\" inherits conflicting generation expressions",
3093 : def->colname),
3094 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3095 : else
3096 6 : ereport(ERROR,
3097 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3098 : errmsg("column \"%s\" inherits conflicting default values",
3099 : def->colname),
3100 : errhint("To resolve the conflict, specify a default explicitly.")));
3101 : }
3102 : }
3103 : }
3104 :
3105 59280 : *supconstr = constraints;
3106 59280 : *supnotnulls = nnconstraints;
3107 :
3108 59280 : return columns;
3109 : }
3110 :
3111 :
3112 : /*
3113 : * MergeCheckConstraint
3114 : * Try to merge an inherited CHECK constraint with previous ones
3115 : *
3116 : * If we inherit identically-named constraints from multiple parents, we must
3117 : * merge them, or throw an error if they don't have identical definitions.
3118 : *
3119 : * constraints is a list of CookedConstraint structs for previous constraints.
3120 : *
3121 : * If the new constraint matches an existing one, then the existing
3122 : * constraint's inheritance count is updated. If there is a conflict (same
3123 : * name but different expression), throw an error. If the constraint neither
3124 : * matches nor conflicts with an existing one, a new constraint is appended to
3125 : * the list.
3126 : */
3127 : static List *
3128 586 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3129 : {
3130 : ListCell *lc;
3131 : CookedConstraint *newcon;
3132 :
3133 1486 : foreach(lc, constraints)
3134 : {
3135 1026 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3136 :
3137 : Assert(ccon->contype == CONSTR_CHECK);
3138 :
3139 : /* Non-matching names never conflict */
3140 1026 : if (strcmp(ccon->name, name) != 0)
3141 900 : continue;
3142 :
3143 126 : if (equal(expr, ccon->expr))
3144 : {
3145 : /* OK to merge constraint with existing */
3146 126 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3147 : &ccon->inhcount))
3148 0 : ereport(ERROR,
3149 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3150 : errmsg("too many inheritance parents"));
3151 :
3152 : /*
3153 : * When enforceability differs, the merged constraint should be
3154 : * marked as ENFORCED because one of the parents is ENFORCED.
3155 : */
3156 126 : if (!ccon->is_enforced && is_enforced)
3157 : {
3158 24 : ccon->is_enforced = true;
3159 24 : ccon->skip_validation = false;
3160 : }
3161 :
3162 126 : return constraints;
3163 : }
3164 :
3165 0 : ereport(ERROR,
3166 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3167 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3168 : name)));
3169 : }
3170 :
3171 : /*
3172 : * Constraint couldn't be merged with an existing one and also didn't
3173 : * conflict with an existing one, so add it as a new one to the list.
3174 : */
3175 460 : newcon = palloc0_object(CookedConstraint);
3176 460 : newcon->contype = CONSTR_CHECK;
3177 460 : newcon->name = pstrdup(name);
3178 460 : newcon->expr = expr;
3179 460 : newcon->inhcount = 1;
3180 460 : newcon->is_enforced = is_enforced;
3181 460 : newcon->skip_validation = !is_enforced;
3182 460 : return lappend(constraints, newcon);
3183 : }
3184 :
3185 : /*
3186 : * MergeChildAttribute
3187 : * Merge given child attribute definition into given inherited attribute.
3188 : *
3189 : * Input arguments:
3190 : * 'inh_columns' is the list of inherited ColumnDefs.
3191 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3192 : * 'newcol_attno' is the attribute number in child table's schema definition
3193 : * 'newdef' is the column/attribute definition from the child table.
3194 : *
3195 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3196 : * ColumnDef remains unchanged.
3197 : *
3198 : * Notes:
3199 : * - The attribute is merged according to the rules laid out in the prologue
3200 : * of MergeAttributes().
3201 : * - If matching inherited attribute exists but the child attribute can not be
3202 : * merged into it, the function throws respective errors.
3203 : * - A partition can not have its own column definitions. Hence this function
3204 : * is applicable only to a regular inheritance child.
3205 : */
3206 : static void
3207 354 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3208 : {
3209 354 : char *attributeName = newdef->colname;
3210 : ColumnDef *inhdef;
3211 : Oid inhtypeid,
3212 : newtypeid;
3213 : int32 inhtypmod,
3214 : newtypmod;
3215 : Oid inhcollid,
3216 : newcollid;
3217 :
3218 354 : if (exist_attno == newcol_attno)
3219 320 : ereport(NOTICE,
3220 : (errmsg("merging column \"%s\" with inherited definition",
3221 : attributeName)));
3222 : else
3223 34 : ereport(NOTICE,
3224 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3225 : errdetail("User-specified column moved to the position of the inherited column.")));
3226 :
3227 354 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3228 :
3229 : /*
3230 : * Must have the same type and typmod
3231 : */
3232 354 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3233 354 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3234 354 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3235 12 : ereport(ERROR,
3236 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3237 : errmsg("column \"%s\" has a type conflict",
3238 : attributeName),
3239 : errdetail("%s versus %s",
3240 : format_type_with_typemod(inhtypeid, inhtypmod),
3241 : format_type_with_typemod(newtypeid, newtypmod))));
3242 :
3243 : /*
3244 : * Must have the same collation
3245 : */
3246 342 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3247 342 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3248 342 : if (inhcollid != newcollid)
3249 6 : ereport(ERROR,
3250 : (errcode(ERRCODE_COLLATION_MISMATCH),
3251 : errmsg("column \"%s\" has a collation conflict",
3252 : attributeName),
3253 : errdetail("\"%s\" versus \"%s\"",
3254 : get_collation_name(inhcollid),
3255 : get_collation_name(newcollid))));
3256 :
3257 : /*
3258 : * Identity is never inherited by a regular inheritance child. Pick
3259 : * child's identity definition if there's one.
3260 : */
3261 336 : inhdef->identity = newdef->identity;
3262 :
3263 : /*
3264 : * Copy storage parameter
3265 : */
3266 336 : if (inhdef->storage == 0)
3267 0 : inhdef->storage = newdef->storage;
3268 336 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3269 6 : ereport(ERROR,
3270 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3271 : errmsg("column \"%s\" has a storage parameter conflict",
3272 : attributeName),
3273 : errdetail("%s versus %s",
3274 : storage_name(inhdef->storage),
3275 : storage_name(newdef->storage))));
3276 :
3277 : /*
3278 : * Copy compression parameter
3279 : */
3280 330 : if (inhdef->compression == NULL)
3281 324 : inhdef->compression = newdef->compression;
3282 6 : else if (newdef->compression != NULL)
3283 : {
3284 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3285 6 : ereport(ERROR,
3286 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3287 : errmsg("column \"%s\" has a compression method conflict",
3288 : attributeName),
3289 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3290 : }
3291 :
3292 : /*
3293 : * Merge of not-null constraints = OR 'em together
3294 : */
3295 324 : inhdef->is_not_null |= newdef->is_not_null;
3296 :
3297 : /*
3298 : * Check for conflicts related to generated columns.
3299 : *
3300 : * If the parent column is generated, the child column will be made a
3301 : * generated column if it isn't already. If it is a generated column,
3302 : * we'll take its generation expression in preference to the parent's. We
3303 : * must check that the child column doesn't specify a default value or
3304 : * identity, which matches the rules for a single column in
3305 : * parse_utilcmd.c.
3306 : *
3307 : * Conversely, if the parent column is not generated, the child column
3308 : * can't be either. (We used to allow that, but it results in being able
3309 : * to override the generation expression via UPDATEs through the parent.)
3310 : */
3311 324 : if (inhdef->generated)
3312 : {
3313 62 : if (newdef->raw_default && !newdef->generated)
3314 12 : ereport(ERROR,
3315 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3316 : errmsg("column \"%s\" inherits from generated column but specifies default",
3317 : inhdef->colname)));
3318 50 : if (newdef->identity)
3319 12 : ereport(ERROR,
3320 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3321 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3322 : inhdef->colname)));
3323 : }
3324 : else
3325 : {
3326 262 : if (newdef->generated)
3327 12 : ereport(ERROR,
3328 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3329 : errmsg("child column \"%s\" specifies generation expression",
3330 : inhdef->colname),
3331 : errhint("A child table column cannot be generated unless its parent column is.")));
3332 : }
3333 :
3334 288 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3335 12 : ereport(ERROR,
3336 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3337 : errmsg("column \"%s\" inherits from generated column of different kind",
3338 : inhdef->colname),
3339 : errdetail("Parent column is %s, child column is %s.",
3340 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3341 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3342 :
3343 : /*
3344 : * If new def has a default, override previous default
3345 : */
3346 276 : if (newdef->raw_default != NULL)
3347 : {
3348 30 : inhdef->raw_default = newdef->raw_default;
3349 30 : inhdef->cooked_default = newdef->cooked_default;
3350 : }
3351 :
3352 : /* Mark the column as locally defined */
3353 276 : inhdef->is_local = true;
3354 276 : }
3355 :
3356 : /*
3357 : * MergeInheritedAttribute
3358 : * Merge given parent attribute definition into specified attribute
3359 : * inherited from the previous parents.
3360 : *
3361 : * Input arguments:
3362 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3363 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3364 : * 'newdef' is the new parent column/attribute definition to be merged.
3365 : *
3366 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3367 : *
3368 : * Notes:
3369 : * - The attribute is merged according to the rules laid out in the prologue
3370 : * of MergeAttributes().
3371 : * - If matching inherited attribute exists but the new attribute can not be
3372 : * merged into it, the function throws respective errors.
3373 : * - A partition inherits from only a single parent. Hence this function is
3374 : * applicable only to a regular inheritance.
3375 : */
3376 : static ColumnDef *
3377 350 : MergeInheritedAttribute(List *inh_columns,
3378 : int exist_attno,
3379 : const ColumnDef *newdef)
3380 : {
3381 350 : char *attributeName = newdef->colname;
3382 : ColumnDef *prevdef;
3383 : Oid prevtypeid,
3384 : newtypeid;
3385 : int32 prevtypmod,
3386 : newtypmod;
3387 : Oid prevcollid,
3388 : newcollid;
3389 :
3390 350 : ereport(NOTICE,
3391 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3392 : attributeName)));
3393 350 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3394 :
3395 : /*
3396 : * Must have the same type and typmod
3397 : */
3398 350 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3399 350 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3400 350 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3401 0 : ereport(ERROR,
3402 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3403 : errmsg("inherited column \"%s\" has a type conflict",
3404 : attributeName),
3405 : errdetail("%s versus %s",
3406 : format_type_with_typemod(prevtypeid, prevtypmod),
3407 : format_type_with_typemod(newtypeid, newtypmod))));
3408 :
3409 : /*
3410 : * Must have the same collation
3411 : */
3412 350 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3413 350 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3414 350 : if (prevcollid != newcollid)
3415 0 : ereport(ERROR,
3416 : (errcode(ERRCODE_COLLATION_MISMATCH),
3417 : errmsg("inherited column \"%s\" has a collation conflict",
3418 : attributeName),
3419 : errdetail("\"%s\" versus \"%s\"",
3420 : get_collation_name(prevcollid),
3421 : get_collation_name(newcollid))));
3422 :
3423 : /*
3424 : * Copy/check storage parameter
3425 : */
3426 350 : if (prevdef->storage == 0)
3427 0 : prevdef->storage = newdef->storage;
3428 350 : else if (prevdef->storage != newdef->storage)
3429 6 : ereport(ERROR,
3430 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3431 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3432 : attributeName),
3433 : errdetail("%s versus %s",
3434 : storage_name(prevdef->storage),
3435 : storage_name(newdef->storage))));
3436 :
3437 : /*
3438 : * Copy/check compression parameter
3439 : */
3440 344 : if (prevdef->compression == NULL)
3441 332 : prevdef->compression = newdef->compression;
3442 12 : else if (newdef->compression != NULL)
3443 : {
3444 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3445 6 : ereport(ERROR,
3446 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3447 : errmsg("column \"%s\" has a compression method conflict",
3448 : attributeName),
3449 : errdetail("%s versus %s",
3450 : prevdef->compression, newdef->compression)));
3451 : }
3452 :
3453 : /*
3454 : * Check for GENERATED conflicts
3455 : */
3456 338 : if (prevdef->generated != newdef->generated)
3457 24 : ereport(ERROR,
3458 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3459 : errmsg("inherited column \"%s\" has a generation conflict",
3460 : attributeName)));
3461 :
3462 : /*
3463 : * Default and other constraints are handled by the caller.
3464 : */
3465 :
3466 314 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3467 : &prevdef->inhcount))
3468 0 : ereport(ERROR,
3469 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3470 : errmsg("too many inheritance parents"));
3471 :
3472 314 : return prevdef;
3473 : }
3474 :
3475 : /*
3476 : * StoreCatalogInheritance
3477 : * Updates the system catalogs with proper inheritance information.
3478 : *
3479 : * supers is a list of the OIDs of the new relation's direct ancestors.
3480 : */
3481 : static void
3482 58662 : StoreCatalogInheritance(Oid relationId, List *supers,
3483 : bool child_is_partition)
3484 : {
3485 : Relation relation;
3486 : int32 seqNumber;
3487 : ListCell *entry;
3488 :
3489 : /*
3490 : * sanity checks
3491 : */
3492 : Assert(OidIsValid(relationId));
3493 :
3494 58662 : if (supers == NIL)
3495 49352 : return;
3496 :
3497 : /*
3498 : * Store INHERITS information in pg_inherits using direct ancestors only.
3499 : * Also enter dependencies on the direct ancestors, and make sure they are
3500 : * marked with relhassubclass = true.
3501 : *
3502 : * (Once upon a time, both direct and indirect ancestors were found here
3503 : * and then entered into pg_ipl. Since that catalog doesn't exist
3504 : * anymore, there's no need to look for indirect ancestors.)
3505 : */
3506 9310 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3507 :
3508 9310 : seqNumber = 1;
3509 18934 : foreach(entry, supers)
3510 : {
3511 9624 : Oid parentOid = lfirst_oid(entry);
3512 :
3513 9624 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3514 : child_is_partition);
3515 9624 : seqNumber++;
3516 : }
3517 :
3518 9310 : table_close(relation, RowExclusiveLock);
3519 : }
3520 :
3521 : /*
3522 : * Make catalog entries showing relationId as being an inheritance child
3523 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3524 : */
3525 : static void
3526 11952 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3527 : int32 seqNumber, Relation inhRelation,
3528 : bool child_is_partition)
3529 : {
3530 : ObjectAddress childobject,
3531 : parentobject;
3532 :
3533 : /* store the pg_inherits row */
3534 11952 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3535 :
3536 : /*
3537 : * Store a dependency too
3538 : */
3539 11952 : parentobject.classId = RelationRelationId;
3540 11952 : parentobject.objectId = parentOid;
3541 11952 : parentobject.objectSubId = 0;
3542 11952 : childobject.classId = RelationRelationId;
3543 11952 : childobject.objectId = relationId;
3544 11952 : childobject.objectSubId = 0;
3545 :
3546 11952 : recordDependencyOn(&childobject, &parentobject,
3547 : child_dependency_type(child_is_partition));
3548 :
3549 : /*
3550 : * Post creation hook of this inheritance. Since object_access_hook
3551 : * doesn't take multiple object identifiers, we relay oid of parent
3552 : * relation using auxiliary_id argument.
3553 : */
3554 11952 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3555 : relationId, 0,
3556 : parentOid, false);
3557 :
3558 : /*
3559 : * Mark the parent as having subclasses.
3560 : */
3561 11952 : SetRelationHasSubclass(parentOid, true);
3562 11952 : }
3563 :
3564 : /*
3565 : * Look for an existing column entry with the given name.
3566 : *
3567 : * Returns the index (starting with 1) if attribute already exists in columns,
3568 : * 0 if it doesn't.
3569 : */
3570 : static int
3571 21368 : findAttrByName(const char *attributeName, const List *columns)
3572 : {
3573 : ListCell *lc;
3574 21368 : int i = 1;
3575 :
3576 38232 : foreach(lc, columns)
3577 : {
3578 17568 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3579 704 : return i;
3580 :
3581 16864 : i++;
3582 : }
3583 20664 : return 0;
3584 : }
3585 :
3586 :
3587 : /*
3588 : * SetRelationHasSubclass
3589 : * Set the value of the relation's relhassubclass field in pg_class.
3590 : *
3591 : * It's always safe to set this field to true, because all SQL commands are
3592 : * ready to see true and then find no children. On the other hand, commands
3593 : * generally assume zero children if this is false.
3594 : *
3595 : * Caller must hold any self-exclusive lock until end of transaction. If the
3596 : * new value is false, caller must have acquired that lock before reading the
3597 : * evidence that justified the false value. That way, it properly waits if
3598 : * another backend is simultaneously concluding no need to change the tuple
3599 : * (new and old values are true).
3600 : *
3601 : * NOTE: an important side-effect of this operation is that an SI invalidation
3602 : * message is sent out to all backends --- including me --- causing plans
3603 : * referencing the relation to be rebuilt with the new list of children.
3604 : * This must happen even if we find that no change is needed in the pg_class
3605 : * row.
3606 : */
3607 : void
3608 14952 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3609 : {
3610 : Relation relationRelation;
3611 : HeapTuple tuple;
3612 : Form_pg_class classtuple;
3613 :
3614 : Assert(CheckRelationOidLockedByMe(relationId,
3615 : ShareUpdateExclusiveLock, false) ||
3616 : CheckRelationOidLockedByMe(relationId,
3617 : ShareRowExclusiveLock, true));
3618 :
3619 : /*
3620 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3621 : */
3622 14952 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3623 14952 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3624 14952 : if (!HeapTupleIsValid(tuple))
3625 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3626 14952 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3627 :
3628 14952 : if (classtuple->relhassubclass != relhassubclass)
3629 : {
3630 7548 : classtuple->relhassubclass = relhassubclass;
3631 7548 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3632 : }
3633 : else
3634 : {
3635 : /* no need to change tuple, but force relcache rebuild anyway */
3636 7404 : CacheInvalidateRelcacheByTuple(tuple);
3637 : }
3638 :
3639 14952 : heap_freetuple(tuple);
3640 14952 : table_close(relationRelation, RowExclusiveLock);
3641 14952 : }
3642 :
3643 : /*
3644 : * CheckRelationTableSpaceMove
3645 : * Check if relation can be moved to new tablespace.
3646 : *
3647 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3648 : *
3649 : * Returns true if the relation can be moved to the new tablespace; raises
3650 : * an error if it is not possible to do the move; returns false if the move
3651 : * would have no effect.
3652 : */
3653 : bool
3654 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3655 : {
3656 : Oid oldTableSpaceId;
3657 :
3658 : /*
3659 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3660 : * stored as 0.
3661 : */
3662 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3663 226 : if (newTableSpaceId == oldTableSpaceId ||
3664 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3665 10 : return false;
3666 :
3667 : /*
3668 : * We cannot support moving mapped relations into different tablespaces.
3669 : * (In particular this eliminates all shared catalogs.)
3670 : */
3671 216 : if (RelationIsMapped(rel))
3672 0 : ereport(ERROR,
3673 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3674 : errmsg("cannot move system relation \"%s\"",
3675 : RelationGetRelationName(rel))));
3676 :
3677 : /* Cannot move a non-shared relation into pg_global */
3678 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3679 12 : ereport(ERROR,
3680 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3681 : errmsg("only shared relations can be placed in pg_global tablespace")));
3682 :
3683 : /*
3684 : * Do not allow moving temp tables of other backends ... their local
3685 : * buffer manager is not going to cope.
3686 : */
3687 204 : if (RELATION_IS_OTHER_TEMP(rel))
3688 0 : ereport(ERROR,
3689 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3690 : errmsg("cannot move temporary tables of other sessions")));
3691 :
3692 204 : return true;
3693 : }
3694 :
3695 : /*
3696 : * SetRelationTableSpace
3697 : * Set new reltablespace and relfilenumber in pg_class entry.
3698 : *
3699 : * newTableSpaceId is the new tablespace for the relation, and
3700 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3701 : * InvalidRelFileNumber, this field is not updated.
3702 : *
3703 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3704 : *
3705 : * The caller of this routine had better check if a relation can be
3706 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3707 : * first, and is responsible for making the change visible with
3708 : * CommandCounterIncrement().
3709 : */
3710 : void
3711 204 : SetRelationTableSpace(Relation rel,
3712 : Oid newTableSpaceId,
3713 : RelFileNumber newRelFilenumber)
3714 : {
3715 : Relation pg_class;
3716 : HeapTuple tuple;
3717 : ItemPointerData otid;
3718 : Form_pg_class rd_rel;
3719 204 : Oid reloid = RelationGetRelid(rel);
3720 :
3721 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3722 :
3723 : /* Get a modifiable copy of the relation's pg_class row. */
3724 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3725 :
3726 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3727 204 : if (!HeapTupleIsValid(tuple))
3728 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3729 204 : otid = tuple->t_self;
3730 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3731 :
3732 : /* Update the pg_class row. */
3733 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3734 204 : InvalidOid : newTableSpaceId;
3735 204 : if (RelFileNumberIsValid(newRelFilenumber))
3736 160 : rd_rel->relfilenode = newRelFilenumber;
3737 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3738 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3739 :
3740 : /*
3741 : * Record dependency on tablespace. This is only required for relations
3742 : * that have no physical storage.
3743 : */
3744 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3745 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3746 : rd_rel->reltablespace);
3747 :
3748 204 : heap_freetuple(tuple);
3749 204 : table_close(pg_class, RowExclusiveLock);
3750 204 : }
3751 :
3752 : /*
3753 : * renameatt_check - basic sanity checks before attribute rename
3754 : */
3755 : static void
3756 1018 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3757 : {
3758 1018 : char relkind = classform->relkind;
3759 :
3760 1018 : if (classform->reloftype && !recursing)
3761 6 : ereport(ERROR,
3762 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3763 : errmsg("cannot rename column of typed table")));
3764 :
3765 : /*
3766 : * Renaming the columns of sequences or toast tables doesn't actually
3767 : * break anything from the system's point of view, since internal
3768 : * references are by attnum. But it doesn't seem right to allow users to
3769 : * change names that are hardcoded into the system, hence the following
3770 : * restriction.
3771 : */
3772 1012 : if (relkind != RELKIND_RELATION &&
3773 84 : relkind != RELKIND_VIEW &&
3774 84 : relkind != RELKIND_MATVIEW &&
3775 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3776 36 : relkind != RELKIND_INDEX &&
3777 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3778 0 : relkind != RELKIND_FOREIGN_TABLE &&
3779 : relkind != RELKIND_PARTITIONED_TABLE)
3780 0 : ereport(ERROR,
3781 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3782 : errmsg("cannot rename columns of relation \"%s\"",
3783 : NameStr(classform->relname)),
3784 : errdetail_relkind_not_supported(relkind)));
3785 :
3786 : /*
3787 : * permissions checking. only the owner of a class can change its schema.
3788 : */
3789 1012 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3790 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3791 0 : NameStr(classform->relname));
3792 1012 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3793 2 : ereport(ERROR,
3794 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3795 : errmsg("permission denied: \"%s\" is a system catalog",
3796 : NameStr(classform->relname))));
3797 1010 : }
3798 :
3799 : /*
3800 : * renameatt_internal - workhorse for renameatt
3801 : *
3802 : * Return value is the attribute number in the 'myrelid' relation.
3803 : */
3804 : static AttrNumber
3805 552 : renameatt_internal(Oid myrelid,
3806 : const char *oldattname,
3807 : const char *newattname,
3808 : bool recurse,
3809 : bool recursing,
3810 : int expected_parents,
3811 : DropBehavior behavior)
3812 : {
3813 : Relation targetrelation;
3814 : Relation attrelation;
3815 : HeapTuple atttup;
3816 : Form_pg_attribute attform;
3817 : AttrNumber attnum;
3818 :
3819 : /*
3820 : * Grab an exclusive lock on the target table, which we will NOT release
3821 : * until end of transaction.
3822 : */
3823 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3824 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3825 :
3826 : /*
3827 : * if the 'recurse' flag is set then we are supposed to rename this
3828 : * attribute in all classes that inherit from 'relname' (as well as in
3829 : * 'relname').
3830 : *
3831 : * any permissions or problems with duplicate attributes will cause the
3832 : * whole transaction to abort, which is what we want -- all or nothing.
3833 : */
3834 552 : if (recurse)
3835 : {
3836 : List *child_oids,
3837 : *child_numparents;
3838 : ListCell *lo,
3839 : *li;
3840 :
3841 : /*
3842 : * we need the number of parents for each child so that the recursive
3843 : * calls to renameatt() can determine whether there are any parents
3844 : * outside the inheritance hierarchy being processed.
3845 : */
3846 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3847 : &child_numparents);
3848 :
3849 : /*
3850 : * find_all_inheritors does the recursive search of the inheritance
3851 : * hierarchy, so all we have to do is process all of the relids in the
3852 : * list that it returns.
3853 : */
3854 734 : forboth(lo, child_oids, li, child_numparents)
3855 : {
3856 516 : Oid childrelid = lfirst_oid(lo);
3857 516 : int numparents = lfirst_int(li);
3858 :
3859 516 : if (childrelid == myrelid)
3860 248 : continue;
3861 : /* note we need not recurse again */
3862 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3863 : }
3864 : }
3865 : else
3866 : {
3867 : /*
3868 : * If we are told not to recurse, there had better not be any child
3869 : * tables; else the rename would put them out of step.
3870 : *
3871 : * expected_parents will only be 0 if we are not already recursing.
3872 : */
3873 340 : if (expected_parents == 0 &&
3874 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3875 12 : ereport(ERROR,
3876 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3877 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3878 : oldattname)));
3879 : }
3880 :
3881 : /* rename attributes in typed tables of composite type */
3882 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3883 : {
3884 : List *child_oids;
3885 : ListCell *lo;
3886 :
3887 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3888 24 : RelationGetRelationName(targetrelation),
3889 : behavior);
3890 :
3891 24 : foreach(lo, child_oids)
3892 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3893 : }
3894 :
3895 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3896 :
3897 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3898 504 : if (!HeapTupleIsValid(atttup))
3899 24 : ereport(ERROR,
3900 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3901 : errmsg("column \"%s\" does not exist",
3902 : oldattname)));
3903 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3904 :
3905 480 : attnum = attform->attnum;
3906 480 : if (attnum <= 0)
3907 0 : ereport(ERROR,
3908 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3909 : errmsg("cannot rename system column \"%s\"",
3910 : oldattname)));
3911 :
3912 : /*
3913 : * if the attribute is inherited, forbid the renaming. if this is a
3914 : * top-level call to renameatt(), then expected_parents will be 0, so the
3915 : * effect of this code will be to prohibit the renaming if the attribute
3916 : * is inherited at all. if this is a recursive call to renameatt(),
3917 : * expected_parents will be the number of parents the current relation has
3918 : * within the inheritance hierarchy being processed, so we'll prohibit the
3919 : * renaming only if there are additional parents from elsewhere.
3920 : */
3921 480 : if (attform->attinhcount > expected_parents)
3922 30 : ereport(ERROR,
3923 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3924 : errmsg("cannot rename inherited column \"%s\"",
3925 : oldattname)));
3926 :
3927 : /* new name should not already exist */
3928 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3929 :
3930 : /* apply the update */
3931 438 : namestrcpy(&(attform->attname), newattname);
3932 :
3933 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3934 :
3935 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3936 :
3937 438 : heap_freetuple(atttup);
3938 :
3939 438 : table_close(attrelation, RowExclusiveLock);
3940 :
3941 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3942 :
3943 438 : return attnum;
3944 : }
3945 :
3946 : /*
3947 : * Perform permissions and integrity checks before acquiring a relation lock.
3948 : */
3949 : static void
3950 420 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3951 : void *arg)
3952 : {
3953 : HeapTuple tuple;
3954 : Form_pg_class form;
3955 :
3956 420 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3957 420 : if (!HeapTupleIsValid(tuple))
3958 38 : return; /* concurrently dropped */
3959 382 : form = (Form_pg_class) GETSTRUCT(tuple);
3960 382 : renameatt_check(relid, form, false);
3961 374 : ReleaseSysCache(tuple);
3962 : }
3963 :
3964 : /*
3965 : * renameatt - changes the name of an attribute in a relation
3966 : *
3967 : * The returned ObjectAddress is that of the renamed column.
3968 : */
3969 : ObjectAddress
3970 316 : renameatt(RenameStmt *stmt)
3971 : {
3972 : Oid relid;
3973 : AttrNumber attnum;
3974 : ObjectAddress address;
3975 :
3976 : /* lock level taken here should match renameatt_internal */
3977 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3978 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
3979 : RangeVarCallbackForRenameAttribute,
3980 : NULL);
3981 :
3982 302 : if (!OidIsValid(relid))
3983 : {
3984 24 : ereport(NOTICE,
3985 : (errmsg("relation \"%s\" does not exist, skipping",
3986 : stmt->relation->relname)));
3987 24 : return InvalidObjectAddress;
3988 : }
3989 :
3990 : attnum =
3991 278 : renameatt_internal(relid,
3992 278 : stmt->subname, /* old att name */
3993 278 : stmt->newname, /* new att name */
3994 278 : stmt->relation->inh, /* recursive? */
3995 : false, /* recursing? */
3996 : 0, /* expected inhcount */
3997 : stmt->behavior);
3998 :
3999 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4000 :
4001 194 : return address;
4002 : }
4003 :
4004 : /*
4005 : * same logic as renameatt_internal
4006 : */
4007 : static ObjectAddress
4008 90 : rename_constraint_internal(Oid myrelid,
4009 : Oid mytypid,
4010 : const char *oldconname,
4011 : const char *newconname,
4012 : bool recurse,
4013 : bool recursing,
4014 : int expected_parents)
4015 : {
4016 90 : Relation targetrelation = NULL;
4017 : Oid constraintOid;
4018 : HeapTuple tuple;
4019 : Form_pg_constraint con;
4020 : ObjectAddress address;
4021 :
4022 : Assert(!myrelid || !mytypid);
4023 :
4024 90 : if (mytypid)
4025 : {
4026 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4027 : }
4028 : else
4029 : {
4030 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4031 :
4032 : /*
4033 : * don't tell it whether we're recursing; we allow changing typed
4034 : * tables here
4035 : */
4036 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4037 :
4038 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4039 : }
4040 :
4041 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4042 90 : if (!HeapTupleIsValid(tuple))
4043 0 : elog(ERROR, "cache lookup failed for constraint %u",
4044 : constraintOid);
4045 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4046 :
4047 90 : if (myrelid &&
4048 84 : (con->contype == CONSTRAINT_CHECK ||
4049 24 : con->contype == CONSTRAINT_NOTNULL) &&
4050 66 : !con->connoinherit)
4051 : {
4052 54 : if (recurse)
4053 : {
4054 : List *child_oids,
4055 : *child_numparents;
4056 : ListCell *lo,
4057 : *li;
4058 :
4059 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4060 : &child_numparents);
4061 :
4062 84 : forboth(lo, child_oids, li, child_numparents)
4063 : {
4064 48 : Oid childrelid = lfirst_oid(lo);
4065 48 : int numparents = lfirst_int(li);
4066 :
4067 48 : if (childrelid == myrelid)
4068 36 : continue;
4069 :
4070 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4071 : }
4072 : }
4073 : else
4074 : {
4075 24 : if (expected_parents == 0 &&
4076 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4077 6 : ereport(ERROR,
4078 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4079 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4080 : oldconname)));
4081 : }
4082 :
4083 48 : if (con->coninhcount > expected_parents)
4084 6 : ereport(ERROR,
4085 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4086 : errmsg("cannot rename inherited constraint \"%s\"",
4087 : oldconname)));
4088 : }
4089 :
4090 78 : if (con->conindid
4091 18 : && (con->contype == CONSTRAINT_PRIMARY
4092 6 : || con->contype == CONSTRAINT_UNIQUE
4093 0 : || con->contype == CONSTRAINT_EXCLUSION))
4094 : /* rename the index; this renames the constraint as well */
4095 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4096 : else
4097 60 : RenameConstraintById(constraintOid, newconname);
4098 :
4099 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4100 :
4101 78 : ReleaseSysCache(tuple);
4102 :
4103 78 : if (targetrelation)
4104 : {
4105 : /*
4106 : * Invalidate relcache so as others can see the new constraint name.
4107 : */
4108 72 : CacheInvalidateRelcache(targetrelation);
4109 :
4110 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4111 : }
4112 :
4113 78 : return address;
4114 : }
4115 :
4116 : ObjectAddress
4117 84 : RenameConstraint(RenameStmt *stmt)
4118 : {
4119 84 : Oid relid = InvalidOid;
4120 84 : Oid typid = InvalidOid;
4121 :
4122 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4123 : {
4124 : Relation rel;
4125 : HeapTuple tup;
4126 :
4127 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4128 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4129 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4130 6 : if (!HeapTupleIsValid(tup))
4131 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4132 6 : checkDomainOwner(tup);
4133 6 : ReleaseSysCache(tup);
4134 6 : table_close(rel, NoLock);
4135 : }
4136 : else
4137 : {
4138 : /* lock level taken here should match rename_constraint_internal */
4139 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4140 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4141 : RangeVarCallbackForRenameAttribute,
4142 : NULL);
4143 78 : if (!OidIsValid(relid))
4144 : {
4145 6 : ereport(NOTICE,
4146 : (errmsg("relation \"%s\" does not exist, skipping",
4147 : stmt->relation->relname)));
4148 6 : return InvalidObjectAddress;
4149 : }
4150 : }
4151 :
4152 : return
4153 78 : rename_constraint_internal(relid, typid,
4154 78 : stmt->subname,
4155 78 : stmt->newname,
4156 150 : (stmt->relation &&
4157 72 : stmt->relation->inh), /* recursive? */
4158 : false, /* recursing? */
4159 : 0 /* expected inhcount */ );
4160 : }
4161 :
4162 : /*
4163 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4164 : * RENAME
4165 : */
4166 : ObjectAddress
4167 510 : RenameRelation(RenameStmt *stmt)
4168 : {
4169 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4170 : Oid relid;
4171 : ObjectAddress address;
4172 :
4173 : /*
4174 : * Grab an exclusive lock on the target table, index, sequence, view,
4175 : * materialized view, or foreign table, which we will NOT release until
4176 : * end of transaction.
4177 : *
4178 : * Lock level used here should match RenameRelationInternal, to avoid lock
4179 : * escalation. However, because ALTER INDEX can be used with any relation
4180 : * type, we mustn't believe without verification.
4181 : */
4182 : for (;;)
4183 12 : {
4184 : LOCKMODE lockmode;
4185 : char relkind;
4186 : bool obj_is_index;
4187 :
4188 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4189 :
4190 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4191 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4192 : RangeVarCallbackForAlterRelation,
4193 : stmt);
4194 :
4195 472 : if (!OidIsValid(relid))
4196 : {
4197 18 : ereport(NOTICE,
4198 : (errmsg("relation \"%s\" does not exist, skipping",
4199 : stmt->relation->relname)));
4200 18 : return InvalidObjectAddress;
4201 : }
4202 :
4203 : /*
4204 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4205 : * to rename a table), but we might've used the wrong lock level. If
4206 : * that happens, retry with the correct lock level. We don't bother
4207 : * if we already acquired AccessExclusiveLock with an index, however.
4208 : */
4209 454 : relkind = get_rel_relkind(relid);
4210 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4211 : relkind == RELKIND_PARTITIONED_INDEX);
4212 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4213 : break;
4214 :
4215 12 : UnlockRelationOid(relid, lockmode);
4216 12 : is_index_stmt = obj_is_index;
4217 : }
4218 :
4219 : /* Do the work */
4220 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4221 :
4222 430 : ObjectAddressSet(address, RelationRelationId, relid);
4223 :
4224 430 : return address;
4225 : }
4226 :
4227 : /*
4228 : * RenameRelationInternal - change the name of a relation
4229 : */
4230 : void
4231 1662 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4232 : {
4233 : Relation targetrelation;
4234 : Relation relrelation; /* for RELATION relation */
4235 : ItemPointerData otid;
4236 : HeapTuple reltup;
4237 : Form_pg_class relform;
4238 : Oid namespaceId;
4239 :
4240 : /*
4241 : * Grab a lock on the target relation, which we will NOT release until end
4242 : * of transaction. We need at least a self-exclusive lock so that
4243 : * concurrent DDL doesn't overwrite the rename if they start updating
4244 : * while still seeing the old version. The lock also guards against
4245 : * triggering relcache reloads in concurrent sessions, which might not
4246 : * handle this information changing under them. For indexes, we can use a
4247 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4248 : * specially.
4249 : */
4250 1662 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4251 1662 : namespaceId = RelationGetNamespace(targetrelation);
4252 :
4253 : /*
4254 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4255 : */
4256 1662 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4257 :
4258 1662 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4259 1662 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4260 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4261 1662 : otid = reltup->t_self;
4262 1662 : relform = (Form_pg_class) GETSTRUCT(reltup);
4263 :
4264 1662 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4265 12 : ereport(ERROR,
4266 : (errcode(ERRCODE_DUPLICATE_TABLE),
4267 : errmsg("relation \"%s\" already exists",
4268 : newrelname)));
4269 :
4270 : /*
4271 : * RenameRelation is careful not to believe the caller's idea of the
4272 : * relation kind being handled. We don't have to worry about this, but
4273 : * let's not be totally oblivious to it. We can process an index as
4274 : * not-an-index, but not the other way around.
4275 : */
4276 : Assert(!is_index ||
4277 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4278 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4279 :
4280 : /*
4281 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4282 : * because it's a copy...)
4283 : */
4284 1650 : namestrcpy(&(relform->relname), newrelname);
4285 :
4286 1650 : CatalogTupleUpdate(relrelation, &otid, reltup);
4287 1650 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4288 :
4289 1650 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4290 : InvalidOid, is_internal);
4291 :
4292 1650 : heap_freetuple(reltup);
4293 1650 : table_close(relrelation, RowExclusiveLock);
4294 :
4295 : /*
4296 : * Also rename the associated type, if any.
4297 : */
4298 1650 : if (OidIsValid(targetrelation->rd_rel->reltype))
4299 124 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4300 : newrelname, namespaceId);
4301 :
4302 : /*
4303 : * Also rename the associated constraint, if any.
4304 : */
4305 1650 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4306 866 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4307 : {
4308 802 : Oid constraintId = get_index_constraint(myrelid);
4309 :
4310 802 : if (OidIsValid(constraintId))
4311 36 : RenameConstraintById(constraintId, newrelname);
4312 : }
4313 :
4314 : /*
4315 : * Close rel, but keep lock!
4316 : */
4317 1650 : relation_close(targetrelation, NoLock);
4318 1650 : }
4319 :
4320 : /*
4321 : * ResetRelRewrite - reset relrewrite
4322 : */
4323 : void
4324 590 : ResetRelRewrite(Oid myrelid)
4325 : {
4326 : Relation relrelation; /* for RELATION relation */
4327 : HeapTuple reltup;
4328 : Form_pg_class relform;
4329 :
4330 : /*
4331 : * Find relation's pg_class tuple.
4332 : */
4333 590 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4334 :
4335 590 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4336 590 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4337 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4338 590 : relform = (Form_pg_class) GETSTRUCT(reltup);
4339 :
4340 : /*
4341 : * Update pg_class tuple.
4342 : */
4343 590 : relform->relrewrite = InvalidOid;
4344 :
4345 590 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4346 :
4347 590 : heap_freetuple(reltup);
4348 590 : table_close(relrelation, RowExclusiveLock);
4349 590 : }
4350 :
4351 : /*
4352 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4353 : * any open reference to the target table besides the one just acquired by
4354 : * the calling command; this implies there's an open cursor or active plan.
4355 : * We need this check because our lock doesn't protect us against stomping
4356 : * on our own foot, only other people's feet!
4357 : *
4358 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4359 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4360 : * possibly be relaxed to only error out for certain types of alterations.
4361 : * But the use-case for allowing any of these things is not obvious, so we
4362 : * won't work hard at it for now.
4363 : *
4364 : * We also reject these commands if there are any pending AFTER trigger events
4365 : * for the rel. This is certainly necessary for the rewriting variants of
4366 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4367 : * events would try to fetch the wrong tuples. It might be overly cautious
4368 : * in other cases, but again it seems better to err on the side of paranoia.
4369 : *
4370 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4371 : * we are worried about active indexscans on the index. The trigger-event
4372 : * check can be skipped, since we are doing no damage to the parent table.
4373 : *
4374 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4375 : */
4376 : void
4377 162860 : CheckTableNotInUse(Relation rel, const char *stmt)
4378 : {
4379 : int expected_refcnt;
4380 :
4381 162860 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4382 162860 : if (rel->rd_refcnt != expected_refcnt)
4383 42 : ereport(ERROR,
4384 : (errcode(ERRCODE_OBJECT_IN_USE),
4385 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4386 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4387 : stmt, RelationGetRelationName(rel))));
4388 :
4389 162818 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4390 264148 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4391 131036 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4392 18 : 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 has pending trigger events",
4396 : stmt, RelationGetRelationName(rel))));
4397 162800 : }
4398 :
4399 : /*
4400 : * CheckAlterTableIsSafe
4401 : * Verify that it's safe to allow ALTER TABLE on this relation.
4402 : *
4403 : * This consists of CheckTableNotInUse() plus a check that the relation
4404 : * isn't another session's temp table. We must split out the temp-table
4405 : * check because there are callers of CheckTableNotInUse() that don't want
4406 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4407 : * an orphaned temp schema.) Compare truncate_check_activity().
4408 : */
4409 : static void
4410 57068 : CheckAlterTableIsSafe(Relation rel)
4411 : {
4412 : /*
4413 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4414 : * manager is not going to cope if we need to change the table's contents.
4415 : * Even if we don't, there may be optimizations that assume temp tables
4416 : * aren't subject to such interference.
4417 : */
4418 57068 : if (RELATION_IS_OTHER_TEMP(rel))
4419 0 : ereport(ERROR,
4420 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4421 : errmsg("cannot alter temporary tables of other sessions")));
4422 :
4423 : /*
4424 : * Also check for active uses of the relation in the current transaction,
4425 : * including open scans and pending AFTER trigger events.
4426 : */
4427 57068 : CheckTableNotInUse(rel, "ALTER TABLE");
4428 57032 : }
4429 :
4430 : /*
4431 : * AlterTableLookupRelation
4432 : * Look up, and lock, the OID for the relation named by an alter table
4433 : * statement.
4434 : */
4435 : Oid
4436 30126 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4437 : {
4438 60164 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4439 30126 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4440 : RangeVarCallbackForAlterRelation,
4441 : stmt);
4442 : }
4443 :
4444 : /*
4445 : * AlterTable
4446 : * Execute ALTER TABLE, which can be a list of subcommands
4447 : *
4448 : * ALTER TABLE is performed in three phases:
4449 : * 1. Examine subcommands and perform pre-transformation checking.
4450 : * 2. Validate and transform subcommands, and update system catalogs.
4451 : * 3. Scan table(s) to check new constraints, and optionally recopy
4452 : * the data into new table(s).
4453 : * Phase 3 is not performed unless one or more of the subcommands requires
4454 : * it. The intention of this design is to allow multiple independent
4455 : * updates of the table schema to be performed with only one pass over the
4456 : * data.
4457 : *
4458 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4459 : * each table to be affected (there may be multiple affected tables if the
4460 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4461 : * validation of the subcommands. Because earlier subcommands may change
4462 : * the catalog state seen by later commands, there are limits to what can
4463 : * be done in this phase. Generally, this phase acquires table locks,
4464 : * checks permissions and relkind, and recurses to find child tables.
4465 : *
4466 : * ATRewriteCatalogs performs phase 2 for each affected table.
4467 : * Certain subcommands need to be performed before others to avoid
4468 : * unnecessary conflicts; for example, DROP COLUMN should come before
4469 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4470 : * lists, one for each logical "pass" of phase 2.
4471 : *
4472 : * ATRewriteTables performs phase 3 for those tables that need it.
4473 : *
4474 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4475 : * since phase 1 already does it. However, for certain subcommand types
4476 : * it is only possible to determine how to recurse at phase 2 time; for
4477 : * those cases, phase 1 sets the cmd->recurse flag.
4478 : *
4479 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4480 : * the whole operation; we don't have to do anything special to clean up.
4481 : *
4482 : * The caller must lock the relation, with an appropriate lock level
4483 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4484 : * or higher. We pass the lock level down
4485 : * so that we can apply it recursively to inherited tables. Note that the
4486 : * lock level we want as we recurse might well be higher than required for
4487 : * that specific subcommand. So we pass down the overall lock requirement,
4488 : * rather than reassess it at lower levels.
4489 : *
4490 : * The caller also provides a "context" which is to be passed back to
4491 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4492 : * Some of the fields therein, such as the relid, are used here as well.
4493 : */
4494 : void
4495 29900 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4496 : AlterTableUtilityContext *context)
4497 : {
4498 : Relation rel;
4499 :
4500 : /* Caller is required to provide an adequate lock. */
4501 29900 : rel = relation_open(context->relid, NoLock);
4502 :
4503 29900 : CheckAlterTableIsSafe(rel);
4504 :
4505 29882 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4506 26366 : }
4507 :
4508 : /*
4509 : * AlterTableInternal
4510 : *
4511 : * ALTER TABLE with target specified by OID
4512 : *
4513 : * We do not reject if the relation is already open, because it's quite
4514 : * likely that one or more layers of caller have it open. That means it
4515 : * is unsafe to use this entry point for alterations that could break
4516 : * existing query plans. On the assumption it's not used for such, we
4517 : * don't have to reject pending AFTER triggers, either.
4518 : *
4519 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4520 : * used for any subcommand types that require parse transformation or
4521 : * could generate subcommands that have to be passed to ProcessUtility.
4522 : */
4523 : void
4524 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4525 : {
4526 : Relation rel;
4527 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4528 :
4529 278 : rel = relation_open(relid, lockmode);
4530 :
4531 278 : EventTriggerAlterTableRelid(relid);
4532 :
4533 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4534 278 : }
4535 :
4536 : /*
4537 : * AlterTableGetLockLevel
4538 : *
4539 : * Sets the overall lock level required for the supplied list of subcommands.
4540 : * Policy for doing this set according to needs of AlterTable(), see
4541 : * comments there for overall explanation.
4542 : *
4543 : * Function is called before and after parsing, so it must give same
4544 : * answer each time it is called. Some subcommands are transformed
4545 : * into other subcommand types, so the transform must never be made to a
4546 : * lower lock level than previously assigned. All transforms are noted below.
4547 : *
4548 : * Since this is called before we lock the table we cannot use table metadata
4549 : * to influence the type of lock we acquire.
4550 : *
4551 : * There should be no lockmodes hardcoded into the subcommand functions. All
4552 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4553 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4554 : * and does not travel through this section of code and cannot be combined with
4555 : * any of the subcommands given here.
4556 : *
4557 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4558 : * so any changes that might affect SELECTs running on standbys need to use
4559 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4560 : * have a solution for that also.
4561 : *
4562 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4563 : * that takes a lock less than AccessExclusiveLock can change object definitions
4564 : * while pg_dump is running. Be careful to check that the appropriate data is
4565 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4566 : * otherwise we might end up with an inconsistent dump that can't restore.
4567 : */
4568 : LOCKMODE
4569 30404 : AlterTableGetLockLevel(List *cmds)
4570 : {
4571 : /*
4572 : * This only works if we read catalog tables using MVCC snapshots.
4573 : */
4574 : ListCell *lcmd;
4575 30404 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4576 :
4577 61950 : foreach(lcmd, cmds)
4578 : {
4579 31546 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4580 31546 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4581 :
4582 31546 : switch (cmd->subtype)
4583 : {
4584 : /*
4585 : * These subcommands rewrite the heap, so require full locks.
4586 : */
4587 3506 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4588 : * to SELECT */
4589 : case AT_SetAccessMethod: /* must rewrite heap */
4590 : case AT_SetTableSpace: /* must rewrite heap */
4591 : case AT_AlterColumnType: /* must rewrite heap */
4592 3506 : cmd_lockmode = AccessExclusiveLock;
4593 3506 : break;
4594 :
4595 : /*
4596 : * These subcommands may require addition of toast tables. If
4597 : * we add a toast table to a table currently being scanned, we
4598 : * might miss data added to the new toast table by concurrent
4599 : * insert transactions.
4600 : */
4601 218 : case AT_SetStorage: /* may add toast tables, see
4602 : * ATRewriteCatalogs() */
4603 218 : cmd_lockmode = AccessExclusiveLock;
4604 218 : break;
4605 :
4606 : /*
4607 : * Removing constraints can affect SELECTs that have been
4608 : * optimized assuming the constraint holds true. See also
4609 : * CloneFkReferenced.
4610 : */
4611 1086 : case AT_DropConstraint: /* as DROP INDEX */
4612 : case AT_DropNotNull: /* may change some SQL plans */
4613 1086 : cmd_lockmode = AccessExclusiveLock;
4614 1086 : break;
4615 :
4616 : /*
4617 : * Subcommands that may be visible to concurrent SELECTs
4618 : */
4619 1744 : case AT_DropColumn: /* change visible to SELECT */
4620 : case AT_AddColumnToView: /* CREATE VIEW */
4621 : case AT_DropOids: /* used to equiv to DropColumn */
4622 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4623 : case AT_EnableReplicaRule: /* may change SELECT rules */
4624 : case AT_EnableRule: /* may change SELECT rules */
4625 : case AT_DisableRule: /* may change SELECT rules */
4626 1744 : cmd_lockmode = AccessExclusiveLock;
4627 1744 : break;
4628 :
4629 : /*
4630 : * Changing owner may remove implicit SELECT privileges
4631 : */
4632 1938 : case AT_ChangeOwner: /* change visible to SELECT */
4633 1938 : cmd_lockmode = AccessExclusiveLock;
4634 1938 : break;
4635 :
4636 : /*
4637 : * Changing foreign table options may affect optimization.
4638 : */
4639 254 : case AT_GenericOptions:
4640 : case AT_AlterColumnGenericOptions:
4641 254 : cmd_lockmode = AccessExclusiveLock;
4642 254 : break;
4643 :
4644 : /*
4645 : * These subcommands affect write operations only.
4646 : */
4647 340 : case AT_EnableTrig:
4648 : case AT_EnableAlwaysTrig:
4649 : case AT_EnableReplicaTrig:
4650 : case AT_EnableTrigAll:
4651 : case AT_EnableTrigUser:
4652 : case AT_DisableTrig:
4653 : case AT_DisableTrigAll:
4654 : case AT_DisableTrigUser:
4655 340 : cmd_lockmode = ShareRowExclusiveLock;
4656 340 : break;
4657 :
4658 : /*
4659 : * These subcommands affect write operations only. XXX
4660 : * Theoretically, these could be ShareRowExclusiveLock.
4661 : */
4662 2688 : case AT_ColumnDefault:
4663 : case AT_CookedColumnDefault:
4664 : case AT_AlterConstraint:
4665 : case AT_AddIndex: /* from ADD CONSTRAINT */
4666 : case AT_AddIndexConstraint:
4667 : case AT_ReplicaIdentity:
4668 : case AT_SetNotNull:
4669 : case AT_EnableRowSecurity:
4670 : case AT_DisableRowSecurity:
4671 : case AT_ForceRowSecurity:
4672 : case AT_NoForceRowSecurity:
4673 : case AT_AddIdentity:
4674 : case AT_DropIdentity:
4675 : case AT_SetIdentity:
4676 : case AT_SetExpression:
4677 : case AT_DropExpression:
4678 : case AT_SetCompression:
4679 2688 : cmd_lockmode = AccessExclusiveLock;
4680 2688 : break;
4681 :
4682 14158 : case AT_AddConstraint:
4683 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4684 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4685 14158 : if (IsA(cmd->def, Constraint))
4686 : {
4687 14158 : Constraint *con = (Constraint *) cmd->def;
4688 :
4689 14158 : switch (con->contype)
4690 : {
4691 10776 : case CONSTR_EXCLUSION:
4692 : case CONSTR_PRIMARY:
4693 : case CONSTR_UNIQUE:
4694 :
4695 : /*
4696 : * Cases essentially the same as CREATE INDEX. We
4697 : * could reduce the lock strength to ShareLock if
4698 : * we can work out how to allow concurrent catalog
4699 : * updates. XXX Might be set down to
4700 : * ShareRowExclusiveLock but requires further
4701 : * analysis.
4702 : */
4703 10776 : cmd_lockmode = AccessExclusiveLock;
4704 10776 : break;
4705 2504 : case CONSTR_FOREIGN:
4706 :
4707 : /*
4708 : * We add triggers to both tables when we add a
4709 : * Foreign Key, so the lock level must be at least
4710 : * as strong as CREATE TRIGGER.
4711 : */
4712 2504 : cmd_lockmode = ShareRowExclusiveLock;
4713 2504 : break;
4714 :
4715 878 : default:
4716 878 : cmd_lockmode = AccessExclusiveLock;
4717 : }
4718 0 : }
4719 14158 : break;
4720 :
4721 : /*
4722 : * These subcommands affect inheritance behaviour. Queries
4723 : * started before us will continue to see the old inheritance
4724 : * behaviour, while queries started after we commit will see
4725 : * new behaviour. No need to prevent reads or writes to the
4726 : * subtable while we hook it up though. Changing the TupDesc
4727 : * may be a problem, so keep highest lock.
4728 : */
4729 496 : case AT_AddInherit:
4730 : case AT_DropInherit:
4731 496 : cmd_lockmode = AccessExclusiveLock;
4732 496 : break;
4733 :
4734 : /*
4735 : * These subcommands affect implicit row type conversion. They
4736 : * have affects similar to CREATE/DROP CAST on queries. don't
4737 : * provide for invalidating parse trees as a result of such
4738 : * changes, so we keep these at AccessExclusiveLock.
4739 : */
4740 72 : case AT_AddOf:
4741 : case AT_DropOf:
4742 72 : cmd_lockmode = AccessExclusiveLock;
4743 72 : break;
4744 :
4745 : /*
4746 : * Only used by CREATE OR REPLACE VIEW which must conflict
4747 : * with an SELECTs currently using the view.
4748 : */
4749 194 : case AT_ReplaceRelOptions:
4750 194 : cmd_lockmode = AccessExclusiveLock;
4751 194 : break;
4752 :
4753 : /*
4754 : * These subcommands affect general strategies for performance
4755 : * and maintenance, though don't change the semantic results
4756 : * from normal data reads and writes. Delaying an ALTER TABLE
4757 : * behind currently active writes only delays the point where
4758 : * the new strategy begins to take effect, so there is no
4759 : * benefit in waiting. In this case the minimum restriction
4760 : * applies: we don't currently allow concurrent catalog
4761 : * updates.
4762 : */
4763 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4764 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4765 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4766 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4767 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4768 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4769 234 : break;
4770 :
4771 112 : case AT_SetLogged:
4772 : case AT_SetUnLogged:
4773 112 : cmd_lockmode = AccessExclusiveLock;
4774 112 : break;
4775 :
4776 412 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4777 412 : cmd_lockmode = ShareUpdateExclusiveLock;
4778 412 : break;
4779 :
4780 : /*
4781 : * Rel options are more complex than first appears. Options
4782 : * are set here for tables, views and indexes; for historical
4783 : * reasons these can all be used with ALTER TABLE, so we can't
4784 : * decide between them using the basic grammar.
4785 : */
4786 762 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4787 : * getTables() */
4788 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4789 : * getTables() */
4790 762 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4791 762 : break;
4792 :
4793 2736 : case AT_AttachPartition:
4794 2736 : cmd_lockmode = ShareUpdateExclusiveLock;
4795 2736 : break;
4796 :
4797 576 : case AT_DetachPartition:
4798 576 : if (((PartitionCmd *) cmd->def)->concurrent)
4799 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4800 : else
4801 412 : cmd_lockmode = AccessExclusiveLock;
4802 576 : break;
4803 :
4804 20 : case AT_DetachPartitionFinalize:
4805 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4806 20 : break;
4807 :
4808 0 : default: /* oops */
4809 0 : elog(ERROR, "unrecognized alter table type: %d",
4810 : (int) cmd->subtype);
4811 : break;
4812 : }
4813 :
4814 : /*
4815 : * Take the greatest lockmode from any subcommand
4816 : */
4817 31546 : if (cmd_lockmode > lockmode)
4818 26262 : lockmode = cmd_lockmode;
4819 : }
4820 :
4821 30404 : return lockmode;
4822 : }
4823 :
4824 : /*
4825 : * ATController provides top level control over the phases.
4826 : *
4827 : * parsetree is passed in to allow it to be passed to event triggers
4828 : * when requested.
4829 : */
4830 : static void
4831 30160 : ATController(AlterTableStmt *parsetree,
4832 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4833 : AlterTableUtilityContext *context)
4834 : {
4835 30160 : List *wqueue = NIL;
4836 : ListCell *lcmd;
4837 :
4838 : /* Phase 1: preliminary examination of commands, create work queue */
4839 61080 : foreach(lcmd, cmds)
4840 : {
4841 31296 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4842 :
4843 31296 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4844 : }
4845 :
4846 : /* Close the relation, but keep lock until commit */
4847 29784 : relation_close(rel, NoLock);
4848 :
4849 : /* Phase 2: update system catalogs */
4850 29784 : ATRewriteCatalogs(&wqueue, lockmode, context);
4851 :
4852 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4853 27016 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4854 26644 : }
4855 :
4856 : /*
4857 : * ATPrepCmd
4858 : *
4859 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4860 : * recursion and permission checks.
4861 : *
4862 : * Caller must have acquired appropriate lock type on relation already.
4863 : * This lock should be held until commit.
4864 : */
4865 : static void
4866 32712 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4867 : bool recurse, bool recursing, LOCKMODE lockmode,
4868 : AlterTableUtilityContext *context)
4869 : {
4870 : AlteredTableInfo *tab;
4871 32712 : AlterTablePass pass = AT_PASS_UNSET;
4872 :
4873 : /* Find or create work queue entry for this table */
4874 32712 : tab = ATGetQueueEntry(wqueue, rel);
4875 :
4876 : /*
4877 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4878 : * partitions that are pending detach.
4879 : */
4880 32712 : if (rel->rd_rel->relispartition &&
4881 2772 : cmd->subtype != AT_DetachPartitionFinalize &&
4882 1386 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4883 2 : ereport(ERROR,
4884 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4885 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4886 : RelationGetRelationName(rel)),
4887 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4888 :
4889 : /*
4890 : * Copy the original subcommand for each table, so we can scribble on it.
4891 : * This avoids conflicts when different child tables need to make
4892 : * different parse transformations (for example, the same column may have
4893 : * different column numbers in different children).
4894 : */
4895 32710 : cmd = copyObject(cmd);
4896 :
4897 : /*
4898 : * Do permissions and relkind checking, recursion to child tables if
4899 : * needed, and any additional phase-1 processing needed. (But beware of
4900 : * adding any processing that looks at table details that another
4901 : * subcommand could change. In some cases we reject multiple subcommands
4902 : * that could try to change the same state in contrary ways.)
4903 : */
4904 32710 : switch (cmd->subtype)
4905 : {
4906 2122 : case AT_AddColumn: /* ADD COLUMN */
4907 2122 : ATSimplePermissions(cmd->subtype, rel,
4908 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4909 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4910 2122 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4911 : lockmode, context);
4912 : /* Recursion occurs during execution phase */
4913 2110 : pass = AT_PASS_ADD_COL;
4914 2110 : break;
4915 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4916 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4917 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4918 : lockmode, context);
4919 : /* Recursion occurs during execution phase */
4920 24 : pass = AT_PASS_ADD_COL;
4921 24 : break;
4922 620 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4923 :
4924 : /*
4925 : * We allow defaults on views so that INSERT into a view can have
4926 : * default-ish behavior. This works because the rewriter
4927 : * substitutes default values into INSERTs before it expands
4928 : * rules.
4929 : */
4930 620 : ATSimplePermissions(cmd->subtype, rel,
4931 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4932 : ATT_FOREIGN_TABLE);
4933 620 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4934 : /* No command-specific prep needed */
4935 620 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4936 620 : break;
4937 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4938 : /* This is currently used only in CREATE TABLE */
4939 : /* (so the permission check really isn't necessary) */
4940 80 : ATSimplePermissions(cmd->subtype, rel,
4941 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4942 : /* This command never recurses */
4943 80 : pass = AT_PASS_ADD_OTHERCONSTR;
4944 80 : break;
4945 166 : case AT_AddIdentity:
4946 166 : ATSimplePermissions(cmd->subtype, rel,
4947 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4948 : ATT_FOREIGN_TABLE);
4949 : /* Set up recursion for phase 2; no other prep needed */
4950 166 : if (recurse)
4951 160 : cmd->recurse = true;
4952 166 : pass = AT_PASS_ADD_OTHERCONSTR;
4953 166 : break;
4954 62 : case AT_SetIdentity:
4955 62 : 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 62 : if (recurse)
4960 56 : cmd->recurse = true;
4961 : /* This should run after AddIdentity, so do it in MISC pass */
4962 62 : pass = AT_PASS_MISC;
4963 62 : break;
4964 56 : case AT_DropIdentity:
4965 56 : ATSimplePermissions(cmd->subtype, rel,
4966 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4967 : ATT_FOREIGN_TABLE);
4968 : /* Set up recursion for phase 2; no other prep needed */
4969 56 : if (recurse)
4970 50 : cmd->recurse = true;
4971 56 : pass = AT_PASS_DROP;
4972 56 : break;
4973 268 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4974 268 : ATSimplePermissions(cmd->subtype, rel,
4975 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4976 : /* Set up recursion for phase 2; no other prep needed */
4977 262 : if (recurse)
4978 244 : cmd->recurse = true;
4979 262 : pass = AT_PASS_DROP;
4980 262 : break;
4981 390 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4982 390 : ATSimplePermissions(cmd->subtype, rel,
4983 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4984 : /* Set up recursion for phase 2; no other prep needed */
4985 384 : if (recurse)
4986 360 : cmd->recurse = true;
4987 384 : pass = AT_PASS_COL_ATTRS;
4988 384 : break;
4989 156 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4990 156 : ATSimplePermissions(cmd->subtype, rel,
4991 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4992 156 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4993 156 : pass = AT_PASS_SET_EXPRESSION;
4994 156 : break;
4995 86 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4996 86 : ATSimplePermissions(cmd->subtype, rel,
4997 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4998 86 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4999 86 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5000 62 : pass = AT_PASS_DROP;
5001 62 : break;
5002 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5003 164 : ATSimplePermissions(cmd->subtype, rel,
5004 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5005 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5006 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5007 : /* No command-specific prep needed */
5008 164 : pass = AT_PASS_MISC;
5009 164 : break;
5010 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5011 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5012 44 : ATSimplePermissions(cmd->subtype, rel,
5013 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5014 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5015 : /* This command never recurses */
5016 32 : pass = AT_PASS_MISC;
5017 32 : break;
5018 240 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5019 240 : ATSimplePermissions(cmd->subtype, rel,
5020 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5021 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5022 240 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5023 : /* No command-specific prep needed */
5024 240 : pass = AT_PASS_MISC;
5025 240 : break;
5026 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5027 68 : ATSimplePermissions(cmd->subtype, rel,
5028 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5029 : /* This command never recurses */
5030 : /* No command-specific prep needed */
5031 68 : pass = AT_PASS_MISC;
5032 68 : break;
5033 1650 : case AT_DropColumn: /* DROP COLUMN */
5034 1650 : ATSimplePermissions(cmd->subtype, rel,
5035 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5036 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5037 1644 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5038 : lockmode, context);
5039 : /* Recursion occurs during execution phase */
5040 1632 : pass = AT_PASS_DROP;
5041 1632 : break;
5042 0 : case AT_AddIndex: /* ADD INDEX */
5043 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5044 : /* This command never recurses */
5045 : /* No command-specific prep needed */
5046 0 : pass = AT_PASS_ADD_INDEX;
5047 0 : break;
5048 15110 : case AT_AddConstraint: /* ADD CONSTRAINT */
5049 15110 : ATSimplePermissions(cmd->subtype, rel,
5050 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5051 15110 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5052 15104 : if (recurse)
5053 : {
5054 : /* recurses at exec time; lock descendants and set flag */
5055 14724 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5056 14724 : cmd->recurse = true;
5057 : }
5058 15104 : pass = AT_PASS_ADD_CONSTR;
5059 15104 : break;
5060 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5061 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5062 : /* This command never recurses */
5063 : /* No command-specific prep needed */
5064 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5065 0 : break;
5066 780 : case AT_DropConstraint: /* DROP CONSTRAINT */
5067 780 : ATSimplePermissions(cmd->subtype, rel,
5068 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5069 780 : ATCheckPartitionsNotInUse(rel, lockmode);
5070 : /* Other recursion occurs during execution phase */
5071 : /* No command-specific prep needed except saving recurse flag */
5072 774 : if (recurse)
5073 738 : cmd->recurse = true;
5074 774 : pass = AT_PASS_DROP;
5075 774 : break;
5076 1270 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5077 1270 : ATSimplePermissions(cmd->subtype, rel,
5078 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5079 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5080 : /* See comments for ATPrepAlterColumnType */
5081 1270 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5082 : AT_PASS_UNSET, context);
5083 : Assert(cmd != NULL);
5084 : /* Performs own recursion */
5085 1264 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5086 : lockmode, context);
5087 1072 : pass = AT_PASS_ALTER_TYPE;
5088 1072 : break;
5089 172 : case AT_AlterColumnGenericOptions:
5090 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5091 : /* This command never recurses */
5092 : /* No command-specific prep needed */
5093 172 : pass = AT_PASS_MISC;
5094 172 : break;
5095 1914 : case AT_ChangeOwner: /* ALTER OWNER */
5096 : /* This command never recurses */
5097 : /* No command-specific prep needed */
5098 1914 : pass = AT_PASS_MISC;
5099 1914 : break;
5100 64 : case AT_ClusterOn: /* CLUSTER ON */
5101 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5102 64 : ATSimplePermissions(cmd->subtype, rel,
5103 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5104 : /* These commands never recurse */
5105 : /* No command-specific prep needed */
5106 64 : pass = AT_PASS_MISC;
5107 64 : break;
5108 112 : case AT_SetLogged: /* SET LOGGED */
5109 : case AT_SetUnLogged: /* SET UNLOGGED */
5110 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5111 100 : if (tab->chgPersistence)
5112 0 : ereport(ERROR,
5113 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5114 : errmsg("cannot change persistence setting twice")));
5115 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5116 88 : pass = AT_PASS_MISC;
5117 88 : break;
5118 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5119 6 : ATSimplePermissions(cmd->subtype, rel,
5120 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5121 6 : pass = AT_PASS_DROP;
5122 6 : break;
5123 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5124 128 : ATSimplePermissions(cmd->subtype, rel,
5125 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5126 :
5127 : /* check if another access method change was already requested */
5128 128 : if (tab->chgAccessMethod)
5129 18 : ereport(ERROR,
5130 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5131 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5132 :
5133 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5134 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5135 110 : break;
5136 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5137 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5138 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5139 : /* This command never recurses */
5140 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5141 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5142 158 : break;
5143 954 : case AT_SetRelOptions: /* SET (...) */
5144 : case AT_ResetRelOptions: /* RESET (...) */
5145 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5146 954 : ATSimplePermissions(cmd->subtype, rel,
5147 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5148 : ATT_MATVIEW | ATT_INDEX);
5149 : /* This command never recurses */
5150 : /* No command-specific prep needed */
5151 952 : pass = AT_PASS_MISC;
5152 952 : break;
5153 410 : case AT_AddInherit: /* INHERIT */
5154 410 : ATSimplePermissions(cmd->subtype, rel,
5155 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5156 : /* This command never recurses */
5157 410 : ATPrepAddInherit(rel);
5158 392 : pass = AT_PASS_MISC;
5159 392 : break;
5160 86 : case AT_DropInherit: /* NO INHERIT */
5161 86 : ATSimplePermissions(cmd->subtype, rel,
5162 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5163 : /* This command never recurses */
5164 : /* No command-specific prep needed */
5165 86 : pass = AT_PASS_MISC;
5166 86 : break;
5167 186 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5168 186 : ATSimplePermissions(cmd->subtype, rel,
5169 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5170 : /* Recursion occurs during execution phase */
5171 180 : if (recurse)
5172 180 : cmd->recurse = true;
5173 180 : pass = AT_PASS_MISC;
5174 180 : break;
5175 412 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5176 412 : ATSimplePermissions(cmd->subtype, rel,
5177 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5178 : /* Recursion occurs during execution phase */
5179 : /* No command-specific prep needed except saving recurse flag */
5180 412 : if (recurse)
5181 412 : cmd->recurse = true;
5182 412 : pass = AT_PASS_MISC;
5183 412 : break;
5184 490 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5185 490 : ATSimplePermissions(cmd->subtype, rel,
5186 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5187 490 : pass = AT_PASS_MISC;
5188 : /* This command never recurses */
5189 : /* No command-specific prep needed */
5190 490 : break;
5191 340 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5192 : case AT_EnableAlwaysTrig:
5193 : case AT_EnableReplicaTrig:
5194 : case AT_EnableTrigAll:
5195 : case AT_EnableTrigUser:
5196 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5197 : case AT_DisableTrigAll:
5198 : case AT_DisableTrigUser:
5199 340 : ATSimplePermissions(cmd->subtype, rel,
5200 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5201 : /* Set up recursion for phase 2; no other prep needed */
5202 340 : if (recurse)
5203 312 : cmd->recurse = true;
5204 340 : pass = AT_PASS_MISC;
5205 340 : break;
5206 544 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5207 : case AT_EnableAlwaysRule:
5208 : case AT_EnableReplicaRule:
5209 : case AT_DisableRule:
5210 : case AT_AddOf: /* OF */
5211 : case AT_DropOf: /* NOT OF */
5212 : case AT_EnableRowSecurity:
5213 : case AT_DisableRowSecurity:
5214 : case AT_ForceRowSecurity:
5215 : case AT_NoForceRowSecurity:
5216 544 : ATSimplePermissions(cmd->subtype, rel,
5217 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5218 : /* These commands never recurse */
5219 : /* No command-specific prep needed */
5220 544 : pass = AT_PASS_MISC;
5221 544 : break;
5222 58 : case AT_GenericOptions:
5223 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5224 : /* No command-specific prep needed */
5225 58 : pass = AT_PASS_MISC;
5226 58 : break;
5227 2724 : case AT_AttachPartition:
5228 2724 : ATSimplePermissions(cmd->subtype, rel,
5229 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5230 : /* No command-specific prep needed */
5231 2718 : pass = AT_PASS_MISC;
5232 2718 : break;
5233 576 : case AT_DetachPartition:
5234 576 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5235 : /* No command-specific prep needed */
5236 558 : pass = AT_PASS_MISC;
5237 558 : break;
5238 20 : case AT_DetachPartitionFinalize:
5239 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5240 : /* No command-specific prep needed */
5241 14 : pass = AT_PASS_MISC;
5242 14 : break;
5243 0 : default: /* oops */
5244 0 : elog(ERROR, "unrecognized alter table type: %d",
5245 : (int) cmd->subtype);
5246 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5247 : break;
5248 : }
5249 : Assert(pass > AT_PASS_UNSET);
5250 :
5251 : /* Add the subcommand to the appropriate list for phase 2 */
5252 32324 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5253 32324 : }
5254 :
5255 : /*
5256 : * ATRewriteCatalogs
5257 : *
5258 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5259 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5260 : * conflicts).
5261 : */
5262 : static void
5263 29784 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5264 : AlterTableUtilityContext *context)
5265 : {
5266 : ListCell *ltab;
5267 :
5268 : /*
5269 : * We process all the tables "in parallel", one pass at a time. This is
5270 : * needed because we may have to propagate work from one table to another
5271 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5272 : * re-adding of the foreign key constraint to the other table). Work can
5273 : * only be propagated into later passes, however.
5274 : */
5275 374728 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5276 : {
5277 : /* Go through each table that needs to be processed */
5278 707170 : foreach(ltab, *wqueue)
5279 : {
5280 362226 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5281 362226 : List *subcmds = tab->subcmds[pass];
5282 : ListCell *lcmd;
5283 :
5284 362226 : if (subcmds == NIL)
5285 310582 : continue;
5286 :
5287 : /*
5288 : * Open the relation and store it in tab. This allows subroutines
5289 : * close and reopen, if necessary. Appropriate lock was obtained
5290 : * by phase 1, needn't get it again.
5291 : */
5292 51644 : tab->rel = relation_open(tab->relid, NoLock);
5293 :
5294 104550 : foreach(lcmd, subcmds)
5295 55674 : ATExecCmd(wqueue, tab,
5296 55674 : lfirst_node(AlterTableCmd, lcmd),
5297 : lockmode, pass, context);
5298 :
5299 : /*
5300 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5301 : * (this is not done in ATExecAlterColumnType since it should be
5302 : * done only once if multiple columns of a table are altered).
5303 : */
5304 48876 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5305 1084 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5306 :
5307 48876 : if (tab->rel)
5308 : {
5309 48876 : relation_close(tab->rel, NoLock);
5310 48876 : tab->rel = NULL;
5311 : }
5312 : }
5313 : }
5314 :
5315 : /* Check to see if a toast table must be added. */
5316 57930 : foreach(ltab, *wqueue)
5317 : {
5318 30914 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5319 :
5320 : /*
5321 : * If the table is source table of ATTACH PARTITION command, we did
5322 : * not modify anything about it that will change its toasting
5323 : * requirement, so no need to check.
5324 : */
5325 30914 : if (((tab->relkind == RELKIND_RELATION ||
5326 5894 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5327 29010 : tab->partition_constraint == NULL) ||
5328 3890 : tab->relkind == RELKIND_MATVIEW)
5329 27074 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5330 : }
5331 27016 : }
5332 :
5333 : /*
5334 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5335 : */
5336 : static void
5337 55674 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5338 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5339 : AlterTableUtilityContext *context)
5340 : {
5341 55674 : ObjectAddress address = InvalidObjectAddress;
5342 55674 : Relation rel = tab->rel;
5343 :
5344 55674 : switch (cmd->subtype)
5345 : {
5346 2128 : case AT_AddColumn: /* ADD COLUMN */
5347 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5348 2128 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5349 2128 : cmd->recurse, false,
5350 : lockmode, cur_pass, context);
5351 1996 : break;
5352 584 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5353 584 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5354 518 : break;
5355 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5356 80 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5357 80 : break;
5358 166 : case AT_AddIdentity:
5359 166 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5360 : cur_pass, context);
5361 : Assert(cmd != NULL);
5362 154 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5363 106 : break;
5364 62 : case AT_SetIdentity:
5365 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5366 : cur_pass, context);
5367 : Assert(cmd != NULL);
5368 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5369 38 : break;
5370 56 : case AT_DropIdentity:
5371 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5372 38 : break;
5373 262 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5374 262 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5375 160 : break;
5376 384 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5377 384 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5378 384 : cmd->recurse, false, lockmode);
5379 348 : break;
5380 156 : case AT_SetExpression:
5381 156 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5382 126 : break;
5383 56 : case AT_DropExpression:
5384 56 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5385 32 : break;
5386 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5387 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5388 116 : break;
5389 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5390 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5391 26 : break;
5392 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5393 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5394 6 : break;
5395 240 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5396 240 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5397 228 : break;
5398 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5399 68 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5400 : lockmode);
5401 62 : break;
5402 1632 : case AT_DropColumn: /* DROP COLUMN */
5403 1632 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5404 1632 : cmd->behavior, cmd->recurse, false,
5405 1632 : cmd->missing_ok, lockmode,
5406 : NULL);
5407 1452 : break;
5408 1178 : case AT_AddIndex: /* ADD INDEX */
5409 1178 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5410 : lockmode);
5411 1008 : break;
5412 444 : case AT_ReAddIndex: /* ADD INDEX */
5413 444 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5414 : lockmode);
5415 444 : break;
5416 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5417 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5418 : true, lockmode);
5419 14 : break;
5420 26926 : case AT_AddConstraint: /* ADD CONSTRAINT */
5421 : /* Transform the command only during initial examination */
5422 26926 : if (cur_pass == AT_PASS_ADD_CONSTR)
5423 15074 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5424 15104 : cmd->recurse, lockmode,
5425 : cur_pass, context);
5426 : /* Depending on constraint type, might be no more work to do now */
5427 26896 : if (cmd != NULL)
5428 : address =
5429 11822 : ATExecAddConstraint(wqueue, tab, rel,
5430 11822 : (Constraint *) cmd->def,
5431 11822 : cmd->recurse, false, lockmode);
5432 26228 : break;
5433 326 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5434 : address =
5435 326 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5436 : true, true, lockmode);
5437 314 : break;
5438 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5439 : * constraint */
5440 : address =
5441 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5442 14 : ((AlterDomainStmt *) cmd->def)->def,
5443 : NULL);
5444 8 : break;
5445 78 : case AT_ReAddComment: /* Re-add existing comment */
5446 78 : address = CommentObject((CommentStmt *) cmd->def);
5447 78 : break;
5448 9540 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5449 9540 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5450 : lockmode);
5451 9528 : break;
5452 180 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5453 180 : address = ATExecAlterConstraint(wqueue, rel,
5454 180 : castNode(ATAlterConstraint, cmd->def),
5455 180 : cmd->recurse, lockmode);
5456 132 : break;
5457 412 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5458 412 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5459 : false, lockmode);
5460 406 : break;
5461 774 : case AT_DropConstraint: /* DROP CONSTRAINT */
5462 774 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5463 774 : cmd->recurse,
5464 774 : cmd->missing_ok, lockmode);
5465 564 : break;
5466 1036 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5467 : /* parse transformation was done earlier */
5468 1036 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5469 994 : break;
5470 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5471 : address =
5472 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5473 172 : (List *) cmd->def, lockmode);
5474 166 : break;
5475 1914 : case AT_ChangeOwner: /* ALTER OWNER */
5476 1908 : ATExecChangeOwner(RelationGetRelid(rel),
5477 1914 : get_rolespec_oid(cmd->newowner, false),
5478 : false, lockmode);
5479 1896 : break;
5480 64 : case AT_ClusterOn: /* CLUSTER ON */
5481 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5482 58 : break;
5483 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5484 18 : ATExecDropCluster(rel, lockmode);
5485 12 : break;
5486 88 : case AT_SetLogged: /* SET LOGGED */
5487 : case AT_SetUnLogged: /* SET UNLOGGED */
5488 88 : break;
5489 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5490 : /* nothing to do here, oid columns don't exist anymore */
5491 6 : break;
5492 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5493 :
5494 : /*
5495 : * Only do this for partitioned tables, for which this is just a
5496 : * catalog change. Tables with storage are handled by Phase 3.
5497 : */
5498 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5499 50 : tab->chgAccessMethod)
5500 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5501 92 : break;
5502 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5503 :
5504 : /*
5505 : * Only do this for partitioned tables and indexes, for which this
5506 : * is just a catalog change. Other relation types which have
5507 : * storage are handled by Phase 3.
5508 : */
5509 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5510 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5511 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5512 :
5513 152 : break;
5514 952 : case AT_SetRelOptions: /* SET (...) */
5515 : case AT_ResetRelOptions: /* RESET (...) */
5516 : case AT_ReplaceRelOptions: /* replace entire option list */
5517 952 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5518 900 : break;
5519 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5520 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5521 : TRIGGER_FIRES_ON_ORIGIN, false,
5522 122 : cmd->recurse,
5523 : lockmode);
5524 122 : break;
5525 40 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5526 40 : ATExecEnableDisableTrigger(rel, cmd->name,
5527 : TRIGGER_FIRES_ALWAYS, false,
5528 40 : cmd->recurse,
5529 : lockmode);
5530 40 : break;
5531 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5532 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5533 : TRIGGER_FIRES_ON_REPLICA, false,
5534 16 : cmd->recurse,
5535 : lockmode);
5536 16 : break;
5537 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5538 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5539 : TRIGGER_DISABLED, false,
5540 138 : cmd->recurse,
5541 : lockmode);
5542 138 : break;
5543 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5544 0 : ATExecEnableDisableTrigger(rel, NULL,
5545 : TRIGGER_FIRES_ON_ORIGIN, false,
5546 0 : cmd->recurse,
5547 : lockmode);
5548 0 : break;
5549 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5550 12 : ATExecEnableDisableTrigger(rel, NULL,
5551 : TRIGGER_DISABLED, false,
5552 12 : cmd->recurse,
5553 : lockmode);
5554 12 : break;
5555 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5556 0 : ATExecEnableDisableTrigger(rel, NULL,
5557 : TRIGGER_FIRES_ON_ORIGIN, true,
5558 0 : cmd->recurse,
5559 : lockmode);
5560 0 : break;
5561 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5562 12 : ATExecEnableDisableTrigger(rel, NULL,
5563 : TRIGGER_DISABLED, true,
5564 12 : cmd->recurse,
5565 : lockmode);
5566 12 : break;
5567 :
5568 8 : case AT_EnableRule: /* ENABLE RULE name */
5569 8 : ATExecEnableDisableRule(rel, cmd->name,
5570 : RULE_FIRES_ON_ORIGIN, lockmode);
5571 8 : break;
5572 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5573 0 : ATExecEnableDisableRule(rel, cmd->name,
5574 : RULE_FIRES_ALWAYS, lockmode);
5575 0 : break;
5576 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5577 6 : ATExecEnableDisableRule(rel, cmd->name,
5578 : RULE_FIRES_ON_REPLICA, lockmode);
5579 6 : break;
5580 32 : case AT_DisableRule: /* DISABLE RULE name */
5581 32 : ATExecEnableDisableRule(rel, cmd->name,
5582 : RULE_DISABLED, lockmode);
5583 32 : break;
5584 :
5585 392 : case AT_AddInherit:
5586 392 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5587 284 : break;
5588 86 : case AT_DropInherit:
5589 86 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5590 80 : break;
5591 66 : case AT_AddOf:
5592 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5593 30 : break;
5594 6 : case AT_DropOf:
5595 6 : ATExecDropOf(rel, lockmode);
5596 6 : break;
5597 508 : case AT_ReplicaIdentity:
5598 508 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5599 460 : break;
5600 290 : case AT_EnableRowSecurity:
5601 290 : ATExecSetRowSecurity(rel, true);
5602 290 : break;
5603 10 : case AT_DisableRowSecurity:
5604 10 : ATExecSetRowSecurity(rel, false);
5605 10 : break;
5606 94 : case AT_ForceRowSecurity:
5607 94 : ATExecForceNoForceRowSecurity(rel, true);
5608 94 : break;
5609 32 : case AT_NoForceRowSecurity:
5610 32 : ATExecForceNoForceRowSecurity(rel, false);
5611 32 : break;
5612 58 : case AT_GenericOptions:
5613 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5614 56 : break;
5615 2718 : case AT_AttachPartition:
5616 2718 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5617 : cur_pass, context);
5618 : Assert(cmd != NULL);
5619 2694 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5620 2302 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5621 : context);
5622 : else
5623 392 : address = ATExecAttachPartitionIdx(wqueue, rel,
5624 392 : ((PartitionCmd *) cmd->def)->name);
5625 2316 : break;
5626 558 : case AT_DetachPartition:
5627 558 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5628 : cur_pass, context);
5629 : Assert(cmd != NULL);
5630 : /* ATPrepCmd ensures it must be a table */
5631 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5632 558 : address = ATExecDetachPartition(wqueue, tab, rel,
5633 558 : ((PartitionCmd *) cmd->def)->name,
5634 558 : ((PartitionCmd *) cmd->def)->concurrent);
5635 428 : break;
5636 14 : case AT_DetachPartitionFinalize:
5637 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5638 14 : break;
5639 0 : default: /* oops */
5640 0 : elog(ERROR, "unrecognized alter table type: %d",
5641 : (int) cmd->subtype);
5642 : break;
5643 : }
5644 :
5645 : /*
5646 : * Report the subcommand to interested event triggers.
5647 : */
5648 52906 : if (cmd)
5649 37832 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5650 :
5651 : /*
5652 : * Bump the command counter to ensure the next subcommand in the sequence
5653 : * can see the changes so far
5654 : */
5655 52906 : CommandCounterIncrement();
5656 52906 : }
5657 :
5658 : /*
5659 : * ATParseTransformCmd: perform parse transformation for one subcommand
5660 : *
5661 : * Returns the transformed subcommand tree, if there is one, else NULL.
5662 : *
5663 : * The parser may hand back additional AlterTableCmd(s) and/or other
5664 : * utility statements, either before or after the original subcommand.
5665 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5666 : * AlteredTableInfo (they had better be for later passes than the current one).
5667 : * Utility statements that are supposed to happen before the AlterTableCmd
5668 : * are executed immediately. Those that are supposed to happen afterwards
5669 : * are added to the tab->afterStmts list to be done at the very end.
5670 : */
5671 : static AlterTableCmd *
5672 21886 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5673 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5674 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5675 : {
5676 21886 : AlterTableCmd *newcmd = NULL;
5677 21886 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5678 : List *beforeStmts;
5679 : List *afterStmts;
5680 : ListCell *lc;
5681 :
5682 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5683 21886 : atstmt->relation =
5684 21886 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5685 21886 : pstrdup(RelationGetRelationName(rel)),
5686 : -1);
5687 21886 : atstmt->relation->inh = recurse;
5688 21886 : atstmt->cmds = list_make1(cmd);
5689 21886 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5690 21886 : atstmt->missing_ok = false;
5691 :
5692 : /* Transform the AlterTableStmt */
5693 21886 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5694 : atstmt,
5695 : context->queryString,
5696 : &beforeStmts,
5697 : &afterStmts);
5698 :
5699 : /* Execute any statements that should happen before these subcommand(s) */
5700 22294 : foreach(lc, beforeStmts)
5701 : {
5702 486 : Node *stmt = (Node *) lfirst(lc);
5703 :
5704 486 : ProcessUtilityForAlterTable(stmt, context);
5705 474 : CommandCounterIncrement();
5706 : }
5707 :
5708 : /* Examine the transformed subcommands and schedule them appropriately */
5709 51118 : foreach(lc, atstmt->cmds)
5710 : {
5711 29310 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5712 : AlterTablePass pass;
5713 :
5714 : /*
5715 : * This switch need only cover the subcommand types that can be added
5716 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5717 : * executing the subcommand immediately, as a substitute for the
5718 : * original subcommand. (Note, however, that this does cause
5719 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5720 : * which is important for index and foreign key constraints.)
5721 : *
5722 : * We assume we needn't do any phase-1 checks for added subcommands.
5723 : */
5724 29310 : switch (cmd2->subtype)
5725 : {
5726 1202 : case AT_AddIndex:
5727 1202 : pass = AT_PASS_ADD_INDEX;
5728 1202 : break;
5729 9540 : case AT_AddIndexConstraint:
5730 9540 : pass = AT_PASS_ADD_INDEXCONSTR;
5731 9540 : break;
5732 11834 : case AT_AddConstraint:
5733 : /* Recursion occurs during execution phase */
5734 11834 : if (recurse)
5735 11786 : cmd2->recurse = true;
5736 11834 : switch (castNode(Constraint, cmd2->def)->contype)
5737 : {
5738 8458 : case CONSTR_NOTNULL:
5739 8458 : pass = AT_PASS_COL_ATTRS;
5740 8458 : break;
5741 0 : case CONSTR_PRIMARY:
5742 : case CONSTR_UNIQUE:
5743 : case CONSTR_EXCLUSION:
5744 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5745 0 : break;
5746 3376 : default:
5747 3376 : pass = AT_PASS_ADD_OTHERCONSTR;
5748 3376 : break;
5749 : }
5750 11834 : break;
5751 0 : case AT_AlterColumnGenericOptions:
5752 : /* This command never recurses */
5753 : /* No command-specific prep needed */
5754 0 : pass = AT_PASS_MISC;
5755 0 : break;
5756 6734 : default:
5757 6734 : pass = cur_pass;
5758 6734 : break;
5759 : }
5760 :
5761 29310 : if (pass < cur_pass)
5762 : {
5763 : /* Cannot schedule into a pass we already finished */
5764 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5765 : pass);
5766 : }
5767 29310 : else if (pass > cur_pass)
5768 : {
5769 : /* OK, queue it up for later */
5770 22576 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5771 : }
5772 : else
5773 : {
5774 : /*
5775 : * We should see at most one subcommand for the current pass,
5776 : * which is the transformed version of the original subcommand.
5777 : */
5778 6734 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5779 : {
5780 : /* Found the transformed version of our subcommand */
5781 6734 : newcmd = cmd2;
5782 : }
5783 : else
5784 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5785 : pass);
5786 : }
5787 : }
5788 :
5789 : /* Queue up any after-statements to happen at the end */
5790 21808 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5791 :
5792 21808 : return newcmd;
5793 : }
5794 :
5795 : /*
5796 : * ATRewriteTables: ALTER TABLE phase 3
5797 : */
5798 : static void
5799 27016 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5800 : AlterTableUtilityContext *context)
5801 : {
5802 : ListCell *ltab;
5803 :
5804 : /* Go through each table that needs to be checked or rewritten */
5805 57632 : foreach(ltab, *wqueue)
5806 : {
5807 30902 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5808 :
5809 : /* Relations without storage may be ignored here */
5810 30902 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5811 5590 : continue;
5812 :
5813 : /*
5814 : * If we change column data types, the operation has to be propagated
5815 : * to tables that use this table's rowtype as a column type.
5816 : * tab->newvals will also be non-NULL in the case where we're adding a
5817 : * column with a default. We choose to forbid that case as well,
5818 : * since composite types might eventually support defaults.
5819 : *
5820 : * (Eventually we'll probably need to check for composite type
5821 : * dependencies even when we're just scanning the table without a
5822 : * rewrite, but at the moment a composite type does not enforce any
5823 : * constraints, so it's not necessary/appropriate to enforce them just
5824 : * during ALTER.)
5825 : */
5826 25312 : if (tab->newvals != NIL || tab->rewrite > 0)
5827 : {
5828 : Relation rel;
5829 :
5830 1608 : rel = table_open(tab->relid, NoLock);
5831 1608 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5832 1590 : table_close(rel, NoLock);
5833 : }
5834 :
5835 : /*
5836 : * We only need to rewrite the table if at least one column needs to
5837 : * be recomputed, or we are changing its persistence or access method.
5838 : *
5839 : * There are two reasons for requiring a rewrite when changing
5840 : * persistence: on one hand, we need to ensure that the buffers
5841 : * belonging to each of the two relations are marked with or without
5842 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5843 : * and assigns a new relfilenumber, we automatically create or drop an
5844 : * init fork for the relation as appropriate.
5845 : */
5846 25294 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5847 904 : {
5848 : /* Build a temporary relation and copy data */
5849 : Relation OldHeap;
5850 : Oid OIDNewHeap;
5851 : Oid NewAccessMethod;
5852 : Oid NewTableSpace;
5853 : char persistence;
5854 :
5855 954 : OldHeap = table_open(tab->relid, NoLock);
5856 :
5857 : /*
5858 : * We don't support rewriting of system catalogs; there are too
5859 : * many corner cases and too little benefit. In particular this
5860 : * is certainly not going to work for mapped catalogs.
5861 : */
5862 954 : if (IsSystemRelation(OldHeap))
5863 0 : ereport(ERROR,
5864 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5865 : errmsg("cannot rewrite system relation \"%s\"",
5866 : RelationGetRelationName(OldHeap))));
5867 :
5868 954 : if (RelationIsUsedAsCatalogTable(OldHeap))
5869 2 : ereport(ERROR,
5870 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5871 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5872 : RelationGetRelationName(OldHeap))));
5873 :
5874 : /*
5875 : * Don't allow rewrite on temp tables of other backends ... their
5876 : * local buffer manager is not going to cope. (This is redundant
5877 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5878 : * check here too.)
5879 : */
5880 952 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5881 0 : ereport(ERROR,
5882 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5883 : errmsg("cannot rewrite temporary tables of other sessions")));
5884 :
5885 : /*
5886 : * Select destination tablespace (same as original unless user
5887 : * requested a change)
5888 : */
5889 952 : if (tab->newTableSpace)
5890 0 : NewTableSpace = tab->newTableSpace;
5891 : else
5892 952 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5893 :
5894 : /*
5895 : * Select destination access method (same as original unless user
5896 : * requested a change)
5897 : */
5898 952 : if (tab->chgAccessMethod)
5899 36 : NewAccessMethod = tab->newAccessMethod;
5900 : else
5901 916 : NewAccessMethod = OldHeap->rd_rel->relam;
5902 :
5903 : /*
5904 : * Select persistence of transient table (same as original unless
5905 : * user requested a change)
5906 : */
5907 952 : persistence = tab->chgPersistence ?
5908 900 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5909 :
5910 952 : table_close(OldHeap, NoLock);
5911 :
5912 : /*
5913 : * Fire off an Event Trigger now, before actually rewriting the
5914 : * table.
5915 : *
5916 : * We don't support Event Trigger for nested commands anywhere,
5917 : * here included, and parsetree is given NULL when coming from
5918 : * AlterTableInternal.
5919 : *
5920 : * And fire it only once.
5921 : */
5922 952 : if (parsetree)
5923 952 : EventTriggerTableRewrite((Node *) parsetree,
5924 : tab->relid,
5925 : tab->rewrite);
5926 :
5927 : /*
5928 : * Create transient table that will receive the modified data.
5929 : *
5930 : * Ensure it is marked correctly as logged or unlogged. We have
5931 : * to do this here so that buffers for the new relfilenumber will
5932 : * have the right persistence set, and at the same time ensure
5933 : * that the original filenumbers's buffers will get read in with
5934 : * the correct setting (i.e. the original one). Otherwise a
5935 : * rollback after the rewrite would possibly result with buffers
5936 : * for the original filenumbers having the wrong persistence
5937 : * setting.
5938 : *
5939 : * NB: This relies on swap_relation_files() also swapping the
5940 : * persistence. That wouldn't work for pg_class, but that can't be
5941 : * unlogged anyway.
5942 : */
5943 946 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5944 : persistence, lockmode);
5945 :
5946 : /*
5947 : * Copy the heap data into the new table with the desired
5948 : * modifications, and test the current data within the table
5949 : * against new constraints generated by ALTER TABLE commands.
5950 : */
5951 946 : ATRewriteTable(tab, OIDNewHeap);
5952 :
5953 : /*
5954 : * Swap the physical files of the old and new heaps, then rebuild
5955 : * indexes and discard the old heap. We can use RecentXmin for
5956 : * the table's new relfrozenxid because we rewrote all the tuples
5957 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5958 : * we never try to swap toast tables by content, since we have no
5959 : * interest in letting this code work on system catalogs.
5960 : */
5961 910 : finish_heap_swap(tab->relid, OIDNewHeap,
5962 : false, false, true,
5963 910 : !OidIsValid(tab->newTableSpace),
5964 : RecentXmin,
5965 : ReadNextMultiXactId(),
5966 : persistence);
5967 :
5968 904 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5969 : }
5970 24340 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5971 : {
5972 24 : if (tab->chgPersistence)
5973 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5974 : }
5975 : else
5976 : {
5977 : /*
5978 : * If required, test the current data within the table against new
5979 : * constraints generated by ALTER TABLE commands, but don't
5980 : * rebuild data.
5981 : */
5982 24316 : if (tab->constraints != NIL || tab->verify_new_notnull ||
5983 21694 : tab->partition_constraint != NULL)
5984 4448 : ATRewriteTable(tab, InvalidOid);
5985 :
5986 : /*
5987 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5988 : * just do a block-by-block copy.
5989 : */
5990 24098 : if (tab->newTableSpace)
5991 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5992 : }
5993 :
5994 : /*
5995 : * Also change persistence of owned sequences, so that it matches the
5996 : * table persistence.
5997 : */
5998 25026 : if (tab->chgPersistence)
5999 : {
6000 76 : List *seqlist = getOwnedSequences(tab->relid);
6001 : ListCell *lc;
6002 :
6003 124 : foreach(lc, seqlist)
6004 : {
6005 48 : Oid seq_relid = lfirst_oid(lc);
6006 :
6007 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6008 : }
6009 : }
6010 : }
6011 :
6012 : /*
6013 : * Foreign key constraints are checked in a final pass, since (a) it's
6014 : * generally best to examine each one separately, and (b) it's at least
6015 : * theoretically possible that we have changed both relations of the
6016 : * foreign key, and we'd better have finished both rewrites before we try
6017 : * to read the tables.
6018 : */
6019 57124 : foreach(ltab, *wqueue)
6020 : {
6021 30480 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6022 30480 : Relation rel = NULL;
6023 : ListCell *lcon;
6024 :
6025 : /* Relations without storage may be ignored here too */
6026 30480 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6027 5510 : continue;
6028 :
6029 26696 : foreach(lcon, tab->constraints)
6030 : {
6031 1812 : NewConstraint *con = lfirst(lcon);
6032 :
6033 1812 : if (con->contype == CONSTR_FOREIGN)
6034 : {
6035 1100 : Constraint *fkconstraint = (Constraint *) con->qual;
6036 : Relation refrel;
6037 :
6038 1100 : if (rel == NULL)
6039 : {
6040 : /* Long since locked, no need for another */
6041 1082 : rel = table_open(tab->relid, NoLock);
6042 : }
6043 :
6044 1100 : refrel = table_open(con->refrelid, RowShareLock);
6045 :
6046 1100 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6047 : con->refindid,
6048 : con->conid,
6049 1100 : con->conwithperiod);
6050 :
6051 : /*
6052 : * No need to mark the constraint row as validated, we did
6053 : * that when we inserted the row earlier.
6054 : */
6055 :
6056 1014 : table_close(refrel, NoLock);
6057 : }
6058 : }
6059 :
6060 24884 : if (rel)
6061 996 : table_close(rel, NoLock);
6062 : }
6063 :
6064 : /* Finally, run any afterStmts that were queued up */
6065 56994 : foreach(ltab, *wqueue)
6066 : {
6067 30350 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6068 : ListCell *lc;
6069 :
6070 30436 : foreach(lc, tab->afterStmts)
6071 : {
6072 86 : Node *stmt = (Node *) lfirst(lc);
6073 :
6074 86 : ProcessUtilityForAlterTable(stmt, context);
6075 86 : CommandCounterIncrement();
6076 : }
6077 : }
6078 26644 : }
6079 :
6080 : /*
6081 : * ATRewriteTable: scan or rewrite one table
6082 : *
6083 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6084 : * must already hold AccessExclusiveLock on it.
6085 : */
6086 : static void
6087 5394 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6088 : {
6089 : Relation oldrel;
6090 : Relation newrel;
6091 : TupleDesc oldTupDesc;
6092 : TupleDesc newTupDesc;
6093 5394 : bool needscan = false;
6094 : List *notnull_attrs;
6095 : int i;
6096 : ListCell *l;
6097 : EState *estate;
6098 : CommandId mycid;
6099 : BulkInsertState bistate;
6100 : int ti_options;
6101 5394 : ExprState *partqualstate = NULL;
6102 :
6103 : /*
6104 : * Open the relation(s). We have surely already locked the existing
6105 : * table.
6106 : */
6107 5394 : oldrel = table_open(tab->relid, NoLock);
6108 5394 : oldTupDesc = tab->oldDesc;
6109 5394 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6110 :
6111 5394 : if (OidIsValid(OIDNewHeap))
6112 : {
6113 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6114 : false));
6115 946 : newrel = table_open(OIDNewHeap, NoLock);
6116 : }
6117 : else
6118 4448 : newrel = NULL;
6119 :
6120 : /*
6121 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6122 : * is empty, so don't bother using it.
6123 : */
6124 5394 : if (newrel)
6125 : {
6126 946 : mycid = GetCurrentCommandId(true);
6127 946 : bistate = GetBulkInsertState();
6128 946 : ti_options = TABLE_INSERT_SKIP_FSM;
6129 : }
6130 : else
6131 : {
6132 : /* keep compiler quiet about using these uninitialized */
6133 4448 : mycid = 0;
6134 4448 : bistate = NULL;
6135 4448 : ti_options = 0;
6136 : }
6137 :
6138 : /*
6139 : * Generate the constraint and default execution states
6140 : */
6141 :
6142 5394 : estate = CreateExecutorState();
6143 :
6144 : /* Build the needed expression execution states */
6145 7326 : foreach(l, tab->constraints)
6146 : {
6147 1932 : NewConstraint *con = lfirst(l);
6148 :
6149 1932 : switch (con->contype)
6150 : {
6151 826 : case CONSTR_CHECK:
6152 826 : needscan = true;
6153 826 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6154 826 : break;
6155 1106 : case CONSTR_FOREIGN:
6156 : /* Nothing to do here */
6157 1106 : break;
6158 0 : default:
6159 0 : elog(ERROR, "unrecognized constraint type: %d",
6160 : (int) con->contype);
6161 : }
6162 : }
6163 :
6164 : /* Build expression execution states for partition check quals */
6165 5394 : if (tab->partition_constraint)
6166 : {
6167 1980 : needscan = true;
6168 1980 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6169 : }
6170 :
6171 6360 : foreach(l, tab->newvals)
6172 : {
6173 966 : NewColumnValue *ex = lfirst(l);
6174 :
6175 : /* expr already planned */
6176 966 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6177 : }
6178 :
6179 5394 : notnull_attrs = NIL;
6180 5394 : if (newrel || tab->verify_new_notnull)
6181 : {
6182 : /*
6183 : * If we are rebuilding the tuples OR if we added any new but not
6184 : * verified not-null constraints, check all not-null constraints. This
6185 : * is a bit of overkill but it minimizes risk of bugs.
6186 : */
6187 6810 : for (i = 0; i < newTupDesc->natts; i++)
6188 : {
6189 4998 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6190 :
6191 4998 : if (attr->attnotnull && !attr->attisdropped)
6192 1886 : notnull_attrs = lappend_int(notnull_attrs, attr->attnum);
6193 : }
6194 1812 : if (notnull_attrs)
6195 1366 : needscan = true;
6196 : }
6197 :
6198 5394 : if (newrel || needscan)
6199 : {
6200 : ExprContext *econtext;
6201 : TupleTableSlot *oldslot;
6202 : TupleTableSlot *newslot;
6203 : TableScanDesc scan;
6204 : MemoryContext oldCxt;
6205 4502 : List *dropped_attrs = NIL;
6206 : ListCell *lc;
6207 : Snapshot snapshot;
6208 :
6209 4502 : if (newrel)
6210 946 : ereport(DEBUG1,
6211 : (errmsg_internal("rewriting table \"%s\"",
6212 : RelationGetRelationName(oldrel))));
6213 : else
6214 3556 : ereport(DEBUG1,
6215 : (errmsg_internal("verifying table \"%s\"",
6216 : RelationGetRelationName(oldrel))));
6217 :
6218 4502 : if (newrel)
6219 : {
6220 : /*
6221 : * All predicate locks on the tuples or pages are about to be made
6222 : * invalid, because we move tuples around. Promote them to
6223 : * relation locks.
6224 : */
6225 946 : TransferPredicateLocksToHeapRelation(oldrel);
6226 : }
6227 :
6228 4502 : econtext = GetPerTupleExprContext(estate);
6229 :
6230 : /*
6231 : * Create necessary tuple slots. When rewriting, two slots are needed,
6232 : * otherwise one suffices. In the case where one slot suffices, we
6233 : * need to use the new tuple descriptor, otherwise some constraints
6234 : * can't be evaluated. Note that even when the tuple layout is the
6235 : * same and no rewrite is required, the tupDescs might not be
6236 : * (consider ADD COLUMN without a default).
6237 : */
6238 4502 : if (tab->rewrite)
6239 : {
6240 : Assert(newrel != NULL);
6241 946 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6242 : table_slot_callbacks(oldrel));
6243 946 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6244 : table_slot_callbacks(newrel));
6245 :
6246 : /*
6247 : * Set all columns in the new slot to NULL initially, to ensure
6248 : * columns added as part of the rewrite are initialized to NULL.
6249 : * That is necessary as tab->newvals will not contain an
6250 : * expression for columns with a NULL default, e.g. when adding a
6251 : * column without a default together with a column with a default
6252 : * requiring an actual rewrite.
6253 : */
6254 946 : ExecStoreAllNullTuple(newslot);
6255 : }
6256 : else
6257 : {
6258 3556 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6259 : table_slot_callbacks(oldrel));
6260 3556 : newslot = NULL;
6261 : }
6262 :
6263 : /*
6264 : * Any attributes that are dropped according to the new tuple
6265 : * descriptor can be set to NULL. We precompute the list of dropped
6266 : * attributes to avoid needing to do so in the per-tuple loop.
6267 : */
6268 15970 : for (i = 0; i < newTupDesc->natts; i++)
6269 : {
6270 11468 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6271 784 : dropped_attrs = lappend_int(dropped_attrs, i);
6272 : }
6273 :
6274 : /*
6275 : * Scan through the rows, generating a new row if needed and then
6276 : * checking all the constraints.
6277 : */
6278 4502 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6279 4502 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6280 :
6281 : /*
6282 : * Switch to per-tuple memory context and reset it for each tuple
6283 : * produced, so we don't leak memory.
6284 : */
6285 4502 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6286 :
6287 769260 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6288 : {
6289 : TupleTableSlot *insertslot;
6290 :
6291 765012 : if (tab->rewrite > 0)
6292 : {
6293 : /* Extract data from old tuple */
6294 99786 : slot_getallattrs(oldslot);
6295 99786 : ExecClearTuple(newslot);
6296 :
6297 : /* copy attributes */
6298 99786 : memcpy(newslot->tts_values, oldslot->tts_values,
6299 99786 : sizeof(Datum) * oldslot->tts_nvalid);
6300 99786 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6301 99786 : sizeof(bool) * oldslot->tts_nvalid);
6302 :
6303 : /* Set dropped attributes to null in new tuple */
6304 99884 : foreach(lc, dropped_attrs)
6305 98 : newslot->tts_isnull[lfirst_int(lc)] = true;
6306 :
6307 : /*
6308 : * Constraints and GENERATED expressions might reference the
6309 : * tableoid column, so fill tts_tableOid with the desired
6310 : * value. (We must do this each time, because it gets
6311 : * overwritten with newrel's OID during storing.)
6312 : */
6313 99786 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6314 :
6315 : /*
6316 : * Process supplied expressions to replace selected columns.
6317 : *
6318 : * First, evaluate expressions whose inputs come from the old
6319 : * tuple.
6320 : */
6321 99786 : econtext->ecxt_scantuple = oldslot;
6322 :
6323 205488 : foreach(l, tab->newvals)
6324 : {
6325 105714 : NewColumnValue *ex = lfirst(l);
6326 :
6327 105714 : if (ex->is_generated)
6328 294 : continue;
6329 :
6330 105420 : newslot->tts_values[ex->attnum - 1]
6331 105408 : = ExecEvalExpr(ex->exprstate,
6332 : econtext,
6333 105420 : &newslot->tts_isnull[ex->attnum - 1]);
6334 : }
6335 :
6336 99774 : ExecStoreVirtualTuple(newslot);
6337 :
6338 : /*
6339 : * Now, evaluate any expressions whose inputs come from the
6340 : * new tuple. We assume these columns won't reference each
6341 : * other, so that there's no ordering dependency.
6342 : */
6343 99774 : econtext->ecxt_scantuple = newslot;
6344 :
6345 205476 : foreach(l, tab->newvals)
6346 : {
6347 105702 : NewColumnValue *ex = lfirst(l);
6348 :
6349 105702 : if (!ex->is_generated)
6350 105408 : continue;
6351 :
6352 294 : newslot->tts_values[ex->attnum - 1]
6353 294 : = ExecEvalExpr(ex->exprstate,
6354 : econtext,
6355 294 : &newslot->tts_isnull[ex->attnum - 1]);
6356 : }
6357 :
6358 99774 : insertslot = newslot;
6359 : }
6360 : else
6361 : {
6362 : /*
6363 : * If there's no rewrite, old and new table are guaranteed to
6364 : * have the same AM, so we can just use the old slot to verify
6365 : * new constraints etc.
6366 : */
6367 665226 : insertslot = oldslot;
6368 : }
6369 :
6370 : /* Now check any constraints on the possibly-changed tuple */
6371 765000 : econtext->ecxt_scantuple = insertslot;
6372 :
6373 4106270 : foreach_int(attn, notnull_attrs)
6374 : {
6375 2576414 : if (slot_attisnull(insertslot, attn))
6376 : {
6377 72 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6378 :
6379 72 : ereport(ERROR,
6380 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6381 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6382 : NameStr(attr->attname),
6383 : RelationGetRelationName(oldrel)),
6384 : errtablecol(oldrel, attn)));
6385 : }
6386 : }
6387 :
6388 773082 : foreach(l, tab->constraints)
6389 : {
6390 8250 : NewConstraint *con = lfirst(l);
6391 :
6392 8250 : switch (con->contype)
6393 : {
6394 8144 : case CONSTR_CHECK:
6395 8144 : if (!ExecCheck(con->qualstate, econtext))
6396 96 : ereport(ERROR,
6397 : (errcode(ERRCODE_CHECK_VIOLATION),
6398 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6399 : con->name,
6400 : RelationGetRelationName(oldrel)),
6401 : errtableconstraint(oldrel, con->name)));
6402 8048 : break;
6403 106 : case CONSTR_NOTNULL:
6404 : case CONSTR_FOREIGN:
6405 : /* Nothing to do here */
6406 106 : break;
6407 0 : default:
6408 0 : elog(ERROR, "unrecognized constraint type: %d",
6409 : (int) con->contype);
6410 : }
6411 : }
6412 :
6413 764832 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6414 : {
6415 74 : if (tab->validate_default)
6416 26 : ereport(ERROR,
6417 : (errcode(ERRCODE_CHECK_VIOLATION),
6418 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6419 : RelationGetRelationName(oldrel)),
6420 : errtable(oldrel)));
6421 : else
6422 48 : ereport(ERROR,
6423 : (errcode(ERRCODE_CHECK_VIOLATION),
6424 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6425 : RelationGetRelationName(oldrel)),
6426 : errtable(oldrel)));
6427 : }
6428 :
6429 : /* Write the tuple out to the new relation */
6430 764758 : if (newrel)
6431 99750 : table_tuple_insert(newrel, insertslot, mycid,
6432 : ti_options, bistate);
6433 :
6434 764758 : ResetExprContext(econtext);
6435 :
6436 764758 : CHECK_FOR_INTERRUPTS();
6437 : }
6438 :
6439 4248 : MemoryContextSwitchTo(oldCxt);
6440 4248 : table_endscan(scan);
6441 4248 : UnregisterSnapshot(snapshot);
6442 :
6443 4248 : ExecDropSingleTupleTableSlot(oldslot);
6444 4248 : if (newslot)
6445 910 : ExecDropSingleTupleTableSlot(newslot);
6446 : }
6447 :
6448 5140 : FreeExecutorState(estate);
6449 :
6450 5140 : table_close(oldrel, NoLock);
6451 5140 : if (newrel)
6452 : {
6453 910 : FreeBulkInsertState(bistate);
6454 :
6455 910 : table_finish_bulk_insert(newrel, ti_options);
6456 :
6457 910 : table_close(newrel, NoLock);
6458 : }
6459 5140 : }
6460 :
6461 : /*
6462 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6463 : */
6464 : static AlteredTableInfo *
6465 39352 : ATGetQueueEntry(List **wqueue, Relation rel)
6466 : {
6467 39352 : Oid relid = RelationGetRelid(rel);
6468 : AlteredTableInfo *tab;
6469 : ListCell *ltab;
6470 :
6471 48510 : foreach(ltab, *wqueue)
6472 : {
6473 14256 : tab = (AlteredTableInfo *) lfirst(ltab);
6474 14256 : if (tab->relid == relid)
6475 5098 : return tab;
6476 : }
6477 :
6478 : /*
6479 : * Not there, so add it. Note that we make a copy of the relation's
6480 : * existing descriptor before anything interesting can happen to it.
6481 : */
6482 34254 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6483 34254 : tab->relid = relid;
6484 34254 : tab->rel = NULL; /* set later */
6485 34254 : tab->relkind = rel->rd_rel->relkind;
6486 34254 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6487 34254 : tab->newAccessMethod = InvalidOid;
6488 34254 : tab->chgAccessMethod = false;
6489 34254 : tab->newTableSpace = InvalidOid;
6490 34254 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6491 34254 : tab->chgPersistence = false;
6492 :
6493 34254 : *wqueue = lappend(*wqueue, tab);
6494 :
6495 34254 : return tab;
6496 : }
6497 :
6498 : static const char *
6499 80 : alter_table_type_to_string(AlterTableType cmdtype)
6500 : {
6501 80 : switch (cmdtype)
6502 : {
6503 0 : case AT_AddColumn:
6504 : case AT_AddColumnToView:
6505 0 : return "ADD COLUMN";
6506 0 : case AT_ColumnDefault:
6507 : case AT_CookedColumnDefault:
6508 0 : return "ALTER COLUMN ... SET DEFAULT";
6509 6 : case AT_DropNotNull:
6510 6 : return "ALTER COLUMN ... DROP NOT NULL";
6511 6 : case AT_SetNotNull:
6512 6 : return "ALTER COLUMN ... SET NOT NULL";
6513 0 : case AT_SetExpression:
6514 0 : return "ALTER COLUMN ... SET EXPRESSION";
6515 0 : case AT_DropExpression:
6516 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6517 0 : case AT_SetStatistics:
6518 0 : return "ALTER COLUMN ... SET STATISTICS";
6519 12 : case AT_SetOptions:
6520 12 : return "ALTER COLUMN ... SET";
6521 0 : case AT_ResetOptions:
6522 0 : return "ALTER COLUMN ... RESET";
6523 0 : case AT_SetStorage:
6524 0 : return "ALTER COLUMN ... SET STORAGE";
6525 0 : case AT_SetCompression:
6526 0 : return "ALTER COLUMN ... SET COMPRESSION";
6527 6 : case AT_DropColumn:
6528 6 : return "DROP COLUMN";
6529 0 : case AT_AddIndex:
6530 : case AT_ReAddIndex:
6531 0 : return NULL; /* not real grammar */
6532 0 : case AT_AddConstraint:
6533 : case AT_ReAddConstraint:
6534 : case AT_ReAddDomainConstraint:
6535 : case AT_AddIndexConstraint:
6536 0 : return "ADD CONSTRAINT";
6537 6 : case AT_AlterConstraint:
6538 6 : return "ALTER CONSTRAINT";
6539 0 : case AT_ValidateConstraint:
6540 0 : return "VALIDATE CONSTRAINT";
6541 0 : case AT_DropConstraint:
6542 0 : return "DROP CONSTRAINT";
6543 0 : case AT_ReAddComment:
6544 0 : return NULL; /* not real grammar */
6545 0 : case AT_AlterColumnType:
6546 0 : return "ALTER COLUMN ... SET DATA TYPE";
6547 0 : case AT_AlterColumnGenericOptions:
6548 0 : return "ALTER COLUMN ... OPTIONS";
6549 0 : case AT_ChangeOwner:
6550 0 : return "OWNER TO";
6551 0 : case AT_ClusterOn:
6552 0 : return "CLUSTER ON";
6553 0 : case AT_DropCluster:
6554 0 : return "SET WITHOUT CLUSTER";
6555 0 : case AT_SetAccessMethod:
6556 0 : return "SET ACCESS METHOD";
6557 6 : case AT_SetLogged:
6558 6 : return "SET LOGGED";
6559 6 : case AT_SetUnLogged:
6560 6 : return "SET UNLOGGED";
6561 0 : case AT_DropOids:
6562 0 : return "SET WITHOUT OIDS";
6563 0 : case AT_SetTableSpace:
6564 0 : return "SET TABLESPACE";
6565 2 : case AT_SetRelOptions:
6566 2 : return "SET";
6567 0 : case AT_ResetRelOptions:
6568 0 : return "RESET";
6569 0 : case AT_ReplaceRelOptions:
6570 0 : return NULL; /* not real grammar */
6571 0 : case AT_EnableTrig:
6572 0 : return "ENABLE TRIGGER";
6573 0 : case AT_EnableAlwaysTrig:
6574 0 : return "ENABLE ALWAYS TRIGGER";
6575 0 : case AT_EnableReplicaTrig:
6576 0 : return "ENABLE REPLICA TRIGGER";
6577 0 : case AT_DisableTrig:
6578 0 : return "DISABLE TRIGGER";
6579 0 : case AT_EnableTrigAll:
6580 0 : return "ENABLE TRIGGER ALL";
6581 0 : case AT_DisableTrigAll:
6582 0 : return "DISABLE TRIGGER ALL";
6583 0 : case AT_EnableTrigUser:
6584 0 : return "ENABLE TRIGGER USER";
6585 0 : case AT_DisableTrigUser:
6586 0 : return "DISABLE TRIGGER USER";
6587 0 : case AT_EnableRule:
6588 0 : return "ENABLE RULE";
6589 0 : case AT_EnableAlwaysRule:
6590 0 : return "ENABLE ALWAYS RULE";
6591 0 : case AT_EnableReplicaRule:
6592 0 : return "ENABLE REPLICA RULE";
6593 0 : case AT_DisableRule:
6594 0 : return "DISABLE RULE";
6595 0 : case AT_AddInherit:
6596 0 : return "INHERIT";
6597 0 : case AT_DropInherit:
6598 0 : return "NO INHERIT";
6599 0 : case AT_AddOf:
6600 0 : return "OF";
6601 0 : case AT_DropOf:
6602 0 : return "NOT OF";
6603 0 : case AT_ReplicaIdentity:
6604 0 : return "REPLICA IDENTITY";
6605 0 : case AT_EnableRowSecurity:
6606 0 : return "ENABLE ROW SECURITY";
6607 0 : case AT_DisableRowSecurity:
6608 0 : return "DISABLE ROW SECURITY";
6609 0 : case AT_ForceRowSecurity:
6610 0 : return "FORCE ROW SECURITY";
6611 0 : case AT_NoForceRowSecurity:
6612 0 : return "NO FORCE ROW SECURITY";
6613 0 : case AT_GenericOptions:
6614 0 : return "OPTIONS";
6615 6 : case AT_AttachPartition:
6616 6 : return "ATTACH PARTITION";
6617 18 : case AT_DetachPartition:
6618 18 : return "DETACH PARTITION";
6619 6 : case AT_DetachPartitionFinalize:
6620 6 : return "DETACH PARTITION ... FINALIZE";
6621 0 : case AT_AddIdentity:
6622 0 : return "ALTER COLUMN ... ADD IDENTITY";
6623 0 : case AT_SetIdentity:
6624 0 : return "ALTER COLUMN ... SET";
6625 0 : case AT_DropIdentity:
6626 0 : return "ALTER COLUMN ... DROP IDENTITY";
6627 0 : case AT_ReAddStatistics:
6628 0 : return NULL; /* not real grammar */
6629 : }
6630 :
6631 0 : return NULL;
6632 : }
6633 :
6634 : /*
6635 : * ATSimplePermissions
6636 : *
6637 : * - Ensure that it is a relation (or possibly a view)
6638 : * - Ensure this user is the owner
6639 : * - Ensure that it is not a system table
6640 : */
6641 : static void
6642 35880 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6643 : {
6644 : int actual_target;
6645 :
6646 35880 : switch (rel->rd_rel->relkind)
6647 : {
6648 28166 : case RELKIND_RELATION:
6649 28166 : actual_target = ATT_TABLE;
6650 28166 : break;
6651 5440 : case RELKIND_PARTITIONED_TABLE:
6652 5440 : actual_target = ATT_PARTITIONED_TABLE;
6653 5440 : break;
6654 402 : case RELKIND_VIEW:
6655 402 : actual_target = ATT_VIEW;
6656 402 : break;
6657 46 : case RELKIND_MATVIEW:
6658 46 : actual_target = ATT_MATVIEW;
6659 46 : break;
6660 226 : case RELKIND_INDEX:
6661 226 : actual_target = ATT_INDEX;
6662 226 : break;
6663 434 : case RELKIND_PARTITIONED_INDEX:
6664 434 : actual_target = ATT_PARTITIONED_INDEX;
6665 434 : break;
6666 214 : case RELKIND_COMPOSITE_TYPE:
6667 214 : actual_target = ATT_COMPOSITE_TYPE;
6668 214 : break;
6669 926 : case RELKIND_FOREIGN_TABLE:
6670 926 : actual_target = ATT_FOREIGN_TABLE;
6671 926 : break;
6672 24 : case RELKIND_SEQUENCE:
6673 24 : actual_target = ATT_SEQUENCE;
6674 24 : break;
6675 2 : default:
6676 2 : actual_target = 0;
6677 2 : break;
6678 : }
6679 :
6680 : /* Wrong target type? */
6681 35880 : if ((actual_target & allowed_targets) == 0)
6682 : {
6683 80 : const char *action_str = alter_table_type_to_string(cmdtype);
6684 :
6685 80 : if (action_str)
6686 80 : ereport(ERROR,
6687 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6688 : /* translator: %s is a group of some SQL keywords */
6689 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6690 : action_str, RelationGetRelationName(rel)),
6691 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6692 : else
6693 : /* internal error? */
6694 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6695 : RelationGetRelationName(rel));
6696 : }
6697 :
6698 : /* Permissions checks */
6699 35800 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6700 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6701 12 : RelationGetRelationName(rel));
6702 :
6703 35788 : if (!allowSystemTableMods && IsSystemRelation(rel))
6704 0 : ereport(ERROR,
6705 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6706 : errmsg("permission denied: \"%s\" is a system catalog",
6707 : RelationGetRelationName(rel))));
6708 35788 : }
6709 :
6710 : /*
6711 : * ATSimpleRecursion
6712 : *
6713 : * Simple table recursion sufficient for most ALTER TABLE operations.
6714 : * All direct and indirect children are processed in an unspecified order.
6715 : * Note that if a child inherits from the original table via multiple
6716 : * inheritance paths, it will be visited just once.
6717 : */
6718 : static void
6719 1266 : ATSimpleRecursion(List **wqueue, Relation rel,
6720 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6721 : AlterTableUtilityContext *context)
6722 : {
6723 : /*
6724 : * Propagate to children, if desired and if there are (or might be) any
6725 : * children.
6726 : */
6727 1266 : if (recurse && rel->rd_rel->relhassubclass)
6728 : {
6729 78 : Oid relid = RelationGetRelid(rel);
6730 : ListCell *child;
6731 : List *children;
6732 :
6733 78 : children = find_all_inheritors(relid, lockmode, NULL);
6734 :
6735 : /*
6736 : * find_all_inheritors does the recursive search of the inheritance
6737 : * hierarchy, so all we have to do is process all of the relids in the
6738 : * list that it returns.
6739 : */
6740 342 : foreach(child, children)
6741 : {
6742 264 : Oid childrelid = lfirst_oid(child);
6743 : Relation childrel;
6744 :
6745 264 : if (childrelid == relid)
6746 78 : continue;
6747 : /* find_all_inheritors already got lock */
6748 186 : childrel = relation_open(childrelid, NoLock);
6749 186 : CheckAlterTableIsSafe(childrel);
6750 186 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6751 186 : relation_close(childrel, NoLock);
6752 : }
6753 : }
6754 1266 : }
6755 :
6756 : /*
6757 : * Obtain list of partitions of the given table, locking them all at the given
6758 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6759 : *
6760 : * This function is a no-op if the given relation is not a partitioned table;
6761 : * in particular, nothing is done if it's a legacy inheritance parent.
6762 : */
6763 : static void
6764 780 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6765 : {
6766 780 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6767 : {
6768 : List *inh;
6769 : ListCell *cell;
6770 :
6771 176 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6772 : /* first element is the parent rel; must ignore it */
6773 574 : for_each_from(cell, inh, 1)
6774 : {
6775 : Relation childrel;
6776 :
6777 : /* find_all_inheritors already got lock */
6778 404 : childrel = table_open(lfirst_oid(cell), NoLock);
6779 404 : CheckAlterTableIsSafe(childrel);
6780 398 : table_close(childrel, NoLock);
6781 : }
6782 170 : list_free(inh);
6783 : }
6784 774 : }
6785 :
6786 : /*
6787 : * ATTypedTableRecursion
6788 : *
6789 : * Propagate ALTER TYPE operations to the typed tables of that type.
6790 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6791 : * recursion to inheritance children of the typed tables.
6792 : */
6793 : static void
6794 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6795 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6796 : {
6797 : ListCell *child;
6798 : List *children;
6799 :
6800 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6801 :
6802 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6803 190 : RelationGetRelationName(rel),
6804 : cmd->behavior);
6805 :
6806 202 : foreach(child, children)
6807 : {
6808 30 : Oid childrelid = lfirst_oid(child);
6809 : Relation childrel;
6810 :
6811 30 : childrel = relation_open(childrelid, lockmode);
6812 30 : CheckAlterTableIsSafe(childrel);
6813 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6814 30 : relation_close(childrel, NoLock);
6815 : }
6816 172 : }
6817 :
6818 :
6819 : /*
6820 : * find_composite_type_dependencies
6821 : *
6822 : * Check to see if the type "typeOid" is being used as a column in some table
6823 : * (possibly nested several levels deep in composite types, arrays, etc!).
6824 : * Eventually, we'd like to propagate the check or rewrite operation
6825 : * into such tables, but for now, just error out if we find any.
6826 : *
6827 : * Caller should provide either the associated relation of a rowtype,
6828 : * or a type name (not both) for use in the error message, if any.
6829 : *
6830 : * Note that "typeOid" is not necessarily a composite type; it could also be
6831 : * another container type such as an array or range, or a domain over one of
6832 : * these things. The name of this function is therefore somewhat historical,
6833 : * but it's not worth changing.
6834 : *
6835 : * We assume that functions and views depending on the type are not reasons
6836 : * to reject the ALTER. (How safe is this really?)
6837 : */
6838 : void
6839 4272 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6840 : const char *origTypeName)
6841 : {
6842 : Relation depRel;
6843 : ScanKeyData key[2];
6844 : SysScanDesc depScan;
6845 : HeapTuple depTup;
6846 :
6847 : /* since this function recurses, it could be driven to stack overflow */
6848 4272 : check_stack_depth();
6849 :
6850 : /*
6851 : * We scan pg_depend to find those things that depend on the given type.
6852 : * (We assume we can ignore refobjsubid for a type.)
6853 : */
6854 4272 : depRel = table_open(DependRelationId, AccessShareLock);
6855 :
6856 4272 : ScanKeyInit(&key[0],
6857 : Anum_pg_depend_refclassid,
6858 : BTEqualStrategyNumber, F_OIDEQ,
6859 : ObjectIdGetDatum(TypeRelationId));
6860 4272 : ScanKeyInit(&key[1],
6861 : Anum_pg_depend_refobjid,
6862 : BTEqualStrategyNumber, F_OIDEQ,
6863 : ObjectIdGetDatum(typeOid));
6864 :
6865 4272 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6866 : NULL, 2, key);
6867 :
6868 6576 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6869 : {
6870 2424 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6871 : Relation rel;
6872 : TupleDesc tupleDesc;
6873 : Form_pg_attribute att;
6874 :
6875 : /* Check for directly dependent types */
6876 2424 : if (pg_depend->classid == TypeRelationId)
6877 : {
6878 : /*
6879 : * This must be an array, domain, or range containing the given
6880 : * type, so recursively check for uses of this type. Note that
6881 : * any error message will mention the original type not the
6882 : * container; this is intentional.
6883 : */
6884 2054 : find_composite_type_dependencies(pg_depend->objid,
6885 : origRelation, origTypeName);
6886 2030 : continue;
6887 : }
6888 :
6889 : /* Else, ignore dependees that aren't relations */
6890 370 : if (pg_depend->classid != RelationRelationId)
6891 122 : continue;
6892 :
6893 248 : rel = relation_open(pg_depend->objid, AccessShareLock);
6894 248 : tupleDesc = RelationGetDescr(rel);
6895 :
6896 : /*
6897 : * If objsubid identifies a specific column, refer to that in error
6898 : * messages. Otherwise, search to see if there's a user column of the
6899 : * type. (We assume system columns are never of interesting types.)
6900 : * The search is needed because an index containing an expression
6901 : * column of the target type will just be recorded as a whole-relation
6902 : * dependency. If we do not find a column of the type, the dependency
6903 : * must indicate that the type is transiently referenced in an index
6904 : * expression but not stored on disk, which we assume is OK, just as
6905 : * we do for references in views. (It could also be that the target
6906 : * type is embedded in some container type that is stored in an index
6907 : * column, but the previous recursion should catch such cases.)
6908 : */
6909 248 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6910 90 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6911 : else
6912 : {
6913 158 : att = NULL;
6914 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6915 : {
6916 254 : att = TupleDescAttr(tupleDesc, attno - 1);
6917 254 : if (att->atttypid == typeOid && !att->attisdropped)
6918 6 : break;
6919 248 : att = NULL;
6920 : }
6921 158 : if (att == NULL)
6922 : {
6923 : /* No such column, so assume OK */
6924 152 : relation_close(rel, AccessShareLock);
6925 152 : continue;
6926 : }
6927 : }
6928 :
6929 : /*
6930 : * We definitely should reject if the relation has storage. If it's
6931 : * partitioned, then perhaps we don't have to reject: if there are
6932 : * partitions then we'll fail when we find one, else there is no
6933 : * stored data to worry about. However, it's possible that the type
6934 : * change would affect conclusions about whether the type is sortable
6935 : * or hashable and thus (if it's a partitioning column) break the
6936 : * partitioning rule. For now, reject for partitioned rels too.
6937 : */
6938 96 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6939 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6940 : {
6941 96 : if (origTypeName)
6942 30 : ereport(ERROR,
6943 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6944 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6945 : origTypeName,
6946 : RelationGetRelationName(rel),
6947 : NameStr(att->attname))));
6948 66 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6949 18 : ereport(ERROR,
6950 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6951 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6952 : RelationGetRelationName(origRelation),
6953 : RelationGetRelationName(rel),
6954 : NameStr(att->attname))));
6955 48 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6956 6 : ereport(ERROR,
6957 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6958 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6959 : RelationGetRelationName(origRelation),
6960 : RelationGetRelationName(rel),
6961 : NameStr(att->attname))));
6962 : else
6963 42 : ereport(ERROR,
6964 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6965 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6966 : RelationGetRelationName(origRelation),
6967 : RelationGetRelationName(rel),
6968 : NameStr(att->attname))));
6969 : }
6970 0 : else if (OidIsValid(rel->rd_rel->reltype))
6971 : {
6972 : /*
6973 : * A view or composite type itself isn't a problem, but we must
6974 : * recursively check for indirect dependencies via its rowtype.
6975 : */
6976 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
6977 : origRelation, origTypeName);
6978 : }
6979 :
6980 0 : relation_close(rel, AccessShareLock);
6981 : }
6982 :
6983 4152 : systable_endscan(depScan);
6984 :
6985 4152 : relation_close(depRel, AccessShareLock);
6986 4152 : }
6987 :
6988 :
6989 : /*
6990 : * find_typed_table_dependencies
6991 : *
6992 : * Check to see if a composite type is being used as the type of a
6993 : * typed table. Abort if any are found and behavior is RESTRICT.
6994 : * Else return the list of tables.
6995 : */
6996 : static List *
6997 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6998 : {
6999 : Relation classRel;
7000 : ScanKeyData key[1];
7001 : TableScanDesc scan;
7002 : HeapTuple tuple;
7003 214 : List *result = NIL;
7004 :
7005 214 : classRel = table_open(RelationRelationId, AccessShareLock);
7006 :
7007 214 : ScanKeyInit(&key[0],
7008 : Anum_pg_class_reloftype,
7009 : BTEqualStrategyNumber, F_OIDEQ,
7010 : ObjectIdGetDatum(typeOid));
7011 :
7012 214 : scan = table_beginscan_catalog(classRel, 1, key);
7013 :
7014 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7015 : {
7016 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7017 :
7018 60 : if (behavior == DROP_RESTRICT)
7019 24 : ereport(ERROR,
7020 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7021 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7022 : typeName),
7023 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7024 : else
7025 36 : result = lappend_oid(result, classform->oid);
7026 : }
7027 :
7028 190 : table_endscan(scan);
7029 190 : table_close(classRel, AccessShareLock);
7030 :
7031 190 : return result;
7032 : }
7033 :
7034 :
7035 : /*
7036 : * check_of_type
7037 : *
7038 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7039 : * isn't suitable, throw an error. Currently, we require that the type
7040 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7041 : * would require handling a number of extra corner cases in the DDL commands.
7042 : * (Also, allowing domain-over-composite would open up a can of worms about
7043 : * whether and how the domain's constraints should apply to derived tables.)
7044 : */
7045 : void
7046 182 : check_of_type(HeapTuple typetuple)
7047 : {
7048 182 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7049 182 : bool typeOk = false;
7050 :
7051 182 : if (typ->typtype == TYPTYPE_COMPOSITE)
7052 : {
7053 : Relation typeRelation;
7054 :
7055 : Assert(OidIsValid(typ->typrelid));
7056 176 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7057 176 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7058 :
7059 : /*
7060 : * Close the parent rel, but keep our AccessShareLock on it until xact
7061 : * commit. That will prevent someone else from deleting or ALTERing
7062 : * the type before the typed table creation/conversion commits.
7063 : */
7064 176 : relation_close(typeRelation, NoLock);
7065 :
7066 176 : if (!typeOk)
7067 6 : ereport(ERROR,
7068 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7069 : errmsg("type %s is the row type of another table",
7070 : format_type_be(typ->oid)),
7071 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7072 : }
7073 : else
7074 6 : ereport(ERROR,
7075 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7076 : errmsg("type %s is not a composite type",
7077 : format_type_be(typ->oid))));
7078 170 : }
7079 :
7080 :
7081 : /*
7082 : * ALTER TABLE ADD COLUMN
7083 : *
7084 : * Adds an additional attribute to a relation making the assumption that
7085 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7086 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7087 : * AlterTableCmd's.
7088 : *
7089 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7090 : * have to decide at runtime whether to recurse or not depending on whether we
7091 : * actually add a column or merely merge with an existing column. (We can't
7092 : * check this in a static pre-pass because it won't handle multiple inheritance
7093 : * situations correctly.)
7094 : */
7095 : static void
7096 2146 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7097 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7098 : AlterTableUtilityContext *context)
7099 : {
7100 2146 : if (rel->rd_rel->reloftype && !recursing)
7101 6 : ereport(ERROR,
7102 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7103 : errmsg("cannot add column to typed table")));
7104 :
7105 2140 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7106 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7107 :
7108 2134 : if (recurse && !is_view)
7109 2034 : cmd->recurse = true;
7110 2134 : }
7111 :
7112 : /*
7113 : * Add a column to a table. The return value is the address of the
7114 : * new column in the parent relation.
7115 : *
7116 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7117 : * copy (but that happens only after we check for IF NOT EXISTS).
7118 : */
7119 : static ObjectAddress
7120 2788 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7121 : AlterTableCmd **cmd, bool recurse, bool recursing,
7122 : LOCKMODE lockmode, AlterTablePass cur_pass,
7123 : AlterTableUtilityContext *context)
7124 : {
7125 2788 : Oid myrelid = RelationGetRelid(rel);
7126 2788 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7127 2788 : bool if_not_exists = (*cmd)->missing_ok;
7128 : Relation pgclass,
7129 : attrdesc;
7130 : HeapTuple reltup;
7131 : Form_pg_class relform;
7132 : Form_pg_attribute attribute;
7133 : int newattnum;
7134 : char relkind;
7135 : Expr *defval;
7136 : List *children;
7137 : ListCell *child;
7138 : AlterTableCmd *childcmd;
7139 : ObjectAddress address;
7140 : TupleDesc tupdesc;
7141 :
7142 : /* since this function recurses, it could be driven to stack overflow */
7143 2788 : check_stack_depth();
7144 :
7145 : /* At top level, permission check was done in ATPrepCmd, else do it */
7146 2788 : if (recursing)
7147 660 : ATSimplePermissions((*cmd)->subtype, rel,
7148 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7149 :
7150 2788 : if (rel->rd_rel->relispartition && !recursing)
7151 12 : ereport(ERROR,
7152 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7153 : errmsg("cannot add column to a partition")));
7154 :
7155 2776 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7156 :
7157 : /*
7158 : * Are we adding the column to a recursion child? If so, check whether to
7159 : * merge with an existing definition for the column. If we do merge, we
7160 : * must not recurse. Children will already have the column, and recursing
7161 : * into them would mess up attinhcount.
7162 : */
7163 2776 : if (colDef->inhcount > 0)
7164 : {
7165 : HeapTuple tuple;
7166 :
7167 : /* Does child already have a column by this name? */
7168 660 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7169 660 : if (HeapTupleIsValid(tuple))
7170 : {
7171 48 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7172 : Oid ctypeId;
7173 : int32 ctypmod;
7174 : Oid ccollid;
7175 :
7176 : /* Child column must match on type, typmod, and collation */
7177 48 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7178 48 : if (ctypeId != childatt->atttypid ||
7179 48 : ctypmod != childatt->atttypmod)
7180 0 : ereport(ERROR,
7181 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7182 : errmsg("child table \"%s\" has different type for column \"%s\"",
7183 : RelationGetRelationName(rel), colDef->colname)));
7184 48 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7185 48 : if (ccollid != childatt->attcollation)
7186 0 : ereport(ERROR,
7187 : (errcode(ERRCODE_COLLATION_MISMATCH),
7188 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7189 : RelationGetRelationName(rel), colDef->colname),
7190 : errdetail("\"%s\" versus \"%s\"",
7191 : get_collation_name(ccollid),
7192 : get_collation_name(childatt->attcollation))));
7193 :
7194 : /* Bump the existing child att's inhcount */
7195 48 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7196 : &childatt->attinhcount))
7197 0 : ereport(ERROR,
7198 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7199 : errmsg("too many inheritance parents"));
7200 48 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7201 :
7202 48 : heap_freetuple(tuple);
7203 :
7204 : /* Inform the user about the merge */
7205 48 : ereport(NOTICE,
7206 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7207 : colDef->colname, RelationGetRelationName(rel))));
7208 :
7209 48 : table_close(attrdesc, RowExclusiveLock);
7210 :
7211 : /* Make the child column change visible */
7212 48 : CommandCounterIncrement();
7213 :
7214 48 : return InvalidObjectAddress;
7215 : }
7216 : }
7217 :
7218 : /* skip if the name already exists and if_not_exists is true */
7219 2728 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7220 : {
7221 54 : table_close(attrdesc, RowExclusiveLock);
7222 54 : return InvalidObjectAddress;
7223 : }
7224 :
7225 : /*
7226 : * Okay, we need to add the column, so go ahead and do parse
7227 : * transformation. This can result in queueing up, or even immediately
7228 : * executing, subsidiary operations (such as creation of unique indexes);
7229 : * so we mustn't do it until we have made the if_not_exists check.
7230 : *
7231 : * When recursing, the command was already transformed and we needn't do
7232 : * so again. Also, if context isn't given we can't transform. (That
7233 : * currently happens only for AT_AddColumnToView; we expect that view.c
7234 : * passed us a ColumnDef that doesn't need work.)
7235 : */
7236 2644 : if (context != NULL && !recursing)
7237 : {
7238 2008 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7239 : cur_pass, context);
7240 : Assert(*cmd != NULL);
7241 2002 : colDef = castNode(ColumnDef, (*cmd)->def);
7242 : }
7243 :
7244 : /*
7245 : * Regular inheritance children are independent enough not to inherit the
7246 : * identity column from parent hence cannot recursively add identity
7247 : * column if the table has inheritance children.
7248 : *
7249 : * Partitions, on the other hand, are integral part of a partitioned table
7250 : * and inherit identity column. Hence propagate identity column down the
7251 : * partition hierarchy.
7252 : */
7253 2638 : if (colDef->identity &&
7254 54 : recurse &&
7255 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7256 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7257 6 : ereport(ERROR,
7258 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7259 : errmsg("cannot recursively add identity column to table that has child tables")));
7260 :
7261 2632 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7262 :
7263 2632 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7264 2632 : if (!HeapTupleIsValid(reltup))
7265 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7266 2632 : relform = (Form_pg_class) GETSTRUCT(reltup);
7267 2632 : relkind = relform->relkind;
7268 :
7269 : /* Determine the new attribute's number */
7270 2632 : newattnum = relform->relnatts + 1;
7271 2632 : if (newattnum > MaxHeapAttributeNumber)
7272 0 : ereport(ERROR,
7273 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7274 : errmsg("tables can have at most %d columns",
7275 : MaxHeapAttributeNumber)));
7276 :
7277 : /*
7278 : * Construct new attribute's pg_attribute entry.
7279 : */
7280 2632 : tupdesc = BuildDescForRelation(list_make1(colDef));
7281 :
7282 2620 : attribute = TupleDescAttr(tupdesc, 0);
7283 :
7284 : /* Fix up attribute number */
7285 2620 : attribute->attnum = newattnum;
7286 :
7287 : /* make sure datatype is legal for a column */
7288 2620 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7289 2620 : list_make1_oid(rel->rd_rel->reltype),
7290 : 0);
7291 :
7292 2590 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7293 :
7294 2590 : table_close(attrdesc, RowExclusiveLock);
7295 :
7296 : /*
7297 : * Update pg_class tuple as appropriate
7298 : */
7299 2590 : relform->relnatts = newattnum;
7300 :
7301 2590 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7302 :
7303 2590 : heap_freetuple(reltup);
7304 :
7305 : /* Post creation hook for new attribute */
7306 2590 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7307 :
7308 2590 : table_close(pgclass, RowExclusiveLock);
7309 :
7310 : /* Make the attribute's catalog entry visible */
7311 2590 : CommandCounterIncrement();
7312 :
7313 : /*
7314 : * Store the DEFAULT, if any, in the catalogs
7315 : */
7316 2590 : if (colDef->raw_default)
7317 : {
7318 : RawColumnDefault *rawEnt;
7319 :
7320 822 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7321 822 : rawEnt->attnum = attribute->attnum;
7322 822 : rawEnt->raw_default = copyObject(colDef->raw_default);
7323 822 : rawEnt->generated = colDef->generated;
7324 :
7325 : /*
7326 : * This function is intended for CREATE TABLE, so it processes a
7327 : * _list_ of defaults, but we just do one.
7328 : */
7329 822 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7330 : false, true, false, NULL);
7331 :
7332 : /* Make the additional catalog changes visible */
7333 798 : CommandCounterIncrement();
7334 : }
7335 :
7336 : /*
7337 : * Tell Phase 3 to fill in the default expression, if there is one.
7338 : *
7339 : * If there is no default, Phase 3 doesn't have to do anything, because
7340 : * that effectively means that the default is NULL. The heap tuple access
7341 : * routines always check for attnum > # of attributes in tuple, and return
7342 : * NULL if so, so without any modification of the tuple data we will get
7343 : * the effect of NULL values in the new column.
7344 : *
7345 : * An exception occurs when the new column is of a domain type: the domain
7346 : * might have a not-null constraint, or a check constraint that indirectly
7347 : * rejects nulls. If there are any domain constraints then we construct
7348 : * an explicit NULL default value that will be passed through
7349 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7350 : * rewriting the table which we really wouldn't have to do; but we do it
7351 : * to preserve the historical behavior that such a failure will be raised
7352 : * only if the table currently contains some rows.)
7353 : *
7354 : * Note: we use build_column_default, and not just the cooked default
7355 : * returned by AddRelationNewConstraints, so that the right thing happens
7356 : * when a datatype's default applies.
7357 : *
7358 : * Note: it might seem that this should happen at the end of Phase 2, so
7359 : * that the effects of subsequent subcommands can be taken into account.
7360 : * It's intentional that we do it now, though. The new column should be
7361 : * filled according to what is said in the ADD COLUMN subcommand, so that
7362 : * the effects are the same as if this subcommand had been run by itself
7363 : * and the later subcommands had been issued in new ALTER TABLE commands.
7364 : *
7365 : * We can skip this entirely for relations without storage, since Phase 3
7366 : * is certainly not going to touch them.
7367 : */
7368 2566 : if (RELKIND_HAS_STORAGE(relkind))
7369 : {
7370 : bool has_domain_constraints;
7371 2208 : bool has_missing = false;
7372 :
7373 : /*
7374 : * For an identity column, we can't use build_column_default(),
7375 : * because the sequence ownership isn't set yet. So do it manually.
7376 : */
7377 2208 : if (colDef->identity)
7378 : {
7379 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7380 :
7381 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7382 42 : nve->typeId = attribute->atttypid;
7383 :
7384 42 : defval = (Expr *) nve;
7385 : }
7386 : else
7387 2166 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7388 :
7389 : /* Build CoerceToDomain(NULL) expression if needed */
7390 2208 : has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7391 2208 : if (!defval && has_domain_constraints)
7392 : {
7393 : Oid baseTypeId;
7394 : int32 baseTypeMod;
7395 : Oid baseTypeColl;
7396 :
7397 6 : baseTypeMod = attribute->atttypmod;
7398 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7399 6 : baseTypeColl = get_typcollation(baseTypeId);
7400 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7401 6 : defval = (Expr *) coerce_to_target_type(NULL,
7402 : (Node *) defval,
7403 : baseTypeId,
7404 : attribute->atttypid,
7405 : attribute->atttypmod,
7406 : COERCION_ASSIGNMENT,
7407 : COERCE_IMPLICIT_CAST,
7408 : -1);
7409 6 : if (defval == NULL) /* should not happen */
7410 0 : elog(ERROR, "failed to coerce base type to domain");
7411 : }
7412 :
7413 2208 : if (defval)
7414 : {
7415 : NewColumnValue *newval;
7416 :
7417 : /* Prepare defval for execution, either here or in Phase 3 */
7418 724 : defval = expression_planner(defval);
7419 :
7420 : /* Add the new default to the newvals list */
7421 724 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7422 724 : newval->attnum = attribute->attnum;
7423 724 : newval->expr = defval;
7424 724 : newval->is_generated = (colDef->generated != '\0');
7425 :
7426 724 : tab->newvals = lappend(tab->newvals, newval);
7427 :
7428 : /*
7429 : * Attempt to skip a complete table rewrite by storing the
7430 : * specified DEFAULT value outside of the heap. This is only
7431 : * allowed for plain relations and non-generated columns, and the
7432 : * default expression can't be volatile (stable is OK). Note that
7433 : * contain_volatile_functions deems CoerceToDomain immutable, but
7434 : * here we consider that coercion to a domain with constraints is
7435 : * volatile; else it might fail even when the table is empty.
7436 : */
7437 724 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7438 724 : !colDef->generated &&
7439 640 : !has_domain_constraints &&
7440 628 : !contain_volatile_functions((Node *) defval))
7441 478 : {
7442 : EState *estate;
7443 : ExprState *exprState;
7444 : Datum missingval;
7445 : bool missingIsNull;
7446 :
7447 : /* Evaluate the default expression */
7448 478 : estate = CreateExecutorState();
7449 478 : exprState = ExecPrepareExpr(defval, estate);
7450 478 : missingval = ExecEvalExpr(exprState,
7451 478 : GetPerTupleExprContext(estate),
7452 : &missingIsNull);
7453 : /* If it turns out NULL, nothing to do; else store it */
7454 478 : if (!missingIsNull)
7455 : {
7456 478 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7457 478 : has_missing = true;
7458 : }
7459 478 : FreeExecutorState(estate);
7460 : }
7461 : else
7462 : {
7463 : /*
7464 : * Failed to use missing mode. We have to do a table rewrite
7465 : * to install the value --- unless it's a virtual generated
7466 : * column.
7467 : */
7468 246 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7469 198 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7470 : }
7471 : }
7472 :
7473 2208 : if (!has_missing)
7474 : {
7475 : /*
7476 : * If the new column is NOT NULL, and there is no missing value,
7477 : * tell Phase 3 it needs to check for NULLs.
7478 : */
7479 1730 : tab->verify_new_notnull |= colDef->is_not_null;
7480 : }
7481 : }
7482 :
7483 : /*
7484 : * Add needed dependency entries for the new column.
7485 : */
7486 2566 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7487 2566 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7488 :
7489 : /*
7490 : * Propagate to children as appropriate. Unlike most other ALTER
7491 : * routines, we have to do this one level of recursion at a time; we can't
7492 : * use find_all_inheritors to do it in one pass.
7493 : */
7494 : children =
7495 2566 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7496 :
7497 : /*
7498 : * If we are told not to recurse, there had better not be any child
7499 : * tables; else the addition would put them out of step.
7500 : */
7501 2566 : if (children && !recurse)
7502 12 : ereport(ERROR,
7503 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7504 : errmsg("column must be added to child tables too")));
7505 :
7506 : /* Children should see column as singly inherited */
7507 2554 : if (!recursing)
7508 : {
7509 1942 : childcmd = copyObject(*cmd);
7510 1942 : colDef = castNode(ColumnDef, childcmd->def);
7511 1942 : colDef->inhcount = 1;
7512 1942 : colDef->is_local = false;
7513 : }
7514 : else
7515 612 : childcmd = *cmd; /* no need to copy again */
7516 :
7517 3214 : foreach(child, children)
7518 : {
7519 660 : Oid childrelid = lfirst_oid(child);
7520 : Relation childrel;
7521 : AlteredTableInfo *childtab;
7522 :
7523 : /* find_inheritance_children already got lock */
7524 660 : childrel = table_open(childrelid, NoLock);
7525 660 : CheckAlterTableIsSafe(childrel);
7526 :
7527 : /* Find or create work queue entry for this table */
7528 660 : childtab = ATGetQueueEntry(wqueue, childrel);
7529 :
7530 : /* Recurse to child; return value is ignored */
7531 660 : ATExecAddColumn(wqueue, childtab, childrel,
7532 : &childcmd, recurse, true,
7533 : lockmode, cur_pass, context);
7534 :
7535 660 : table_close(childrel, NoLock);
7536 : }
7537 :
7538 2554 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7539 2554 : return address;
7540 : }
7541 :
7542 : /*
7543 : * If a new or renamed column will collide with the name of an existing
7544 : * column and if_not_exists is false then error out, else do nothing.
7545 : */
7546 : static bool
7547 3178 : check_for_column_name_collision(Relation rel, const char *colname,
7548 : bool if_not_exists)
7549 : {
7550 : HeapTuple attTuple;
7551 : int attnum;
7552 :
7553 : /*
7554 : * this test is deliberately not attisdropped-aware, since if one tries to
7555 : * add a column matching a dropped column name, it's gonna fail anyway.
7556 : */
7557 3178 : attTuple = SearchSysCache2(ATTNAME,
7558 : ObjectIdGetDatum(RelationGetRelid(rel)),
7559 : PointerGetDatum(colname));
7560 3178 : if (!HeapTupleIsValid(attTuple))
7561 3082 : return true;
7562 :
7563 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7564 96 : ReleaseSysCache(attTuple);
7565 :
7566 : /*
7567 : * We throw a different error message for conflicts with system column
7568 : * names, since they are normally not shown and the user might otherwise
7569 : * be confused about the reason for the conflict.
7570 : */
7571 96 : if (attnum <= 0)
7572 12 : ereport(ERROR,
7573 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7574 : errmsg("column name \"%s\" conflicts with a system column name",
7575 : colname)));
7576 : else
7577 : {
7578 84 : if (if_not_exists)
7579 : {
7580 54 : ereport(NOTICE,
7581 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7582 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7583 : colname, RelationGetRelationName(rel))));
7584 54 : return false;
7585 : }
7586 :
7587 30 : ereport(ERROR,
7588 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7589 : errmsg("column \"%s\" of relation \"%s\" already exists",
7590 : colname, RelationGetRelationName(rel))));
7591 : }
7592 :
7593 : return true;
7594 : }
7595 :
7596 : /*
7597 : * Install a column's dependency on its datatype.
7598 : */
7599 : static void
7600 3560 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7601 : {
7602 : ObjectAddress myself,
7603 : referenced;
7604 :
7605 3560 : myself.classId = RelationRelationId;
7606 3560 : myself.objectId = relid;
7607 3560 : myself.objectSubId = attnum;
7608 3560 : referenced.classId = TypeRelationId;
7609 3560 : referenced.objectId = typid;
7610 3560 : referenced.objectSubId = 0;
7611 3560 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7612 3560 : }
7613 :
7614 : /*
7615 : * Install a column's dependency on its collation.
7616 : */
7617 : static void
7618 3560 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7619 : {
7620 : ObjectAddress myself,
7621 : referenced;
7622 :
7623 : /* We know the default collation is pinned, so don't bother recording it */
7624 3560 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7625 : {
7626 18 : myself.classId = RelationRelationId;
7627 18 : myself.objectId = relid;
7628 18 : myself.objectSubId = attnum;
7629 18 : referenced.classId = CollationRelationId;
7630 18 : referenced.objectId = collid;
7631 18 : referenced.objectSubId = 0;
7632 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7633 : }
7634 3560 : }
7635 :
7636 : /*
7637 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7638 : *
7639 : * Return the address of the modified column. If the column was already
7640 : * nullable, InvalidObjectAddress is returned.
7641 : */
7642 : static ObjectAddress
7643 262 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7644 : LOCKMODE lockmode)
7645 : {
7646 : HeapTuple tuple;
7647 : HeapTuple conTup;
7648 : Form_pg_attribute attTup;
7649 : AttrNumber attnum;
7650 : Relation attr_rel;
7651 : ObjectAddress address;
7652 :
7653 : /*
7654 : * lookup the attribute
7655 : */
7656 262 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7657 :
7658 262 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7659 262 : if (!HeapTupleIsValid(tuple))
7660 18 : ereport(ERROR,
7661 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7662 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7663 : colName, RelationGetRelationName(rel))));
7664 244 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7665 244 : attnum = attTup->attnum;
7666 244 : ObjectAddressSubSet(address, RelationRelationId,
7667 : RelationGetRelid(rel), attnum);
7668 :
7669 : /* If the column is already nullable there's nothing to do. */
7670 244 : if (!attTup->attnotnull)
7671 : {
7672 6 : table_close(attr_rel, RowExclusiveLock);
7673 6 : return InvalidObjectAddress;
7674 : }
7675 :
7676 : /* Prevent them from altering a system attribute */
7677 238 : if (attnum <= 0)
7678 0 : ereport(ERROR,
7679 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7680 : errmsg("cannot alter system column \"%s\"",
7681 : colName)));
7682 :
7683 238 : if (attTup->attidentity)
7684 18 : ereport(ERROR,
7685 : (errcode(ERRCODE_SYNTAX_ERROR),
7686 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7687 : colName, RelationGetRelationName(rel))));
7688 :
7689 : /*
7690 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7691 : */
7692 220 : if (rel->rd_rel->relispartition)
7693 : {
7694 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7695 12 : Relation parent = table_open(parentId, AccessShareLock);
7696 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7697 : AttrNumber parent_attnum;
7698 :
7699 12 : parent_attnum = get_attnum(parentId, colName);
7700 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7701 12 : ereport(ERROR,
7702 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7703 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7704 : colName)));
7705 0 : table_close(parent, AccessShareLock);
7706 : }
7707 :
7708 : /*
7709 : * Find the constraint that makes this column NOT NULL, and drop it.
7710 : * dropconstraint_internal() resets attnotnull.
7711 : */
7712 208 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7713 208 : if (conTup == NULL)
7714 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7715 : colName, RelationGetRelationName(rel));
7716 :
7717 : /* The normal case: we have a pg_constraint row, remove it */
7718 208 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7719 : false, lockmode);
7720 154 : heap_freetuple(conTup);
7721 :
7722 154 : InvokeObjectPostAlterHook(RelationRelationId,
7723 : RelationGetRelid(rel), attnum);
7724 :
7725 154 : table_close(attr_rel, RowExclusiveLock);
7726 :
7727 154 : return address;
7728 : }
7729 :
7730 : /*
7731 : * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7732 : * to verify it.
7733 : *
7734 : * When called to alter an existing table, 'wqueue' must be given so that we
7735 : * can queue a check that existing tuples pass the constraint. When called
7736 : * from table creation, 'wqueue' should be passed as NULL.
7737 : */
7738 : static void
7739 23464 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7740 : LOCKMODE lockmode)
7741 : {
7742 : Form_pg_attribute attr;
7743 :
7744 23464 : CheckAlterTableIsSafe(rel);
7745 :
7746 : /*
7747 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7748 : * attribute.
7749 : */
7750 23464 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7751 23464 : if (attr->attisdropped)
7752 0 : return;
7753 :
7754 23464 : if (!attr->attnotnull)
7755 : {
7756 : Relation attr_rel;
7757 : HeapTuple tuple;
7758 :
7759 1256 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7760 :
7761 1256 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7762 1256 : if (!HeapTupleIsValid(tuple))
7763 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7764 : attnum, RelationGetRelid(rel));
7765 :
7766 1256 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7767 : Assert(!attr->attnotnull);
7768 1256 : attr->attnotnull = true;
7769 1256 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7770 :
7771 : /*
7772 : * If the nullness isn't already proven by validated constraints, have
7773 : * ALTER TABLE phase 3 test for it.
7774 : */
7775 1256 : if (wqueue && !NotNullImpliedByRelConstraints(rel, attr))
7776 : {
7777 : AlteredTableInfo *tab;
7778 :
7779 1152 : tab = ATGetQueueEntry(wqueue, rel);
7780 1152 : tab->verify_new_notnull = true;
7781 : }
7782 :
7783 1256 : CommandCounterIncrement();
7784 :
7785 1256 : table_close(attr_rel, RowExclusiveLock);
7786 1256 : heap_freetuple(tuple);
7787 : }
7788 : }
7789 :
7790 : /*
7791 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7792 : *
7793 : * Add a not-null constraint to a single table and its children. Returns
7794 : * the address of the constraint added to the parent relation, if one gets
7795 : * added, or InvalidObjectAddress otherwise.
7796 : *
7797 : * We must recurse to child tables during execution, rather than using
7798 : * ALTER TABLE's normal prep-time recursion.
7799 : */
7800 : static ObjectAddress
7801 676 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7802 : bool recurse, bool recursing, LOCKMODE lockmode)
7803 : {
7804 : HeapTuple tuple;
7805 : AttrNumber attnum;
7806 : ObjectAddress address;
7807 : Constraint *constraint;
7808 : CookedConstraint *ccon;
7809 : List *cooked;
7810 676 : bool is_no_inherit = false;
7811 :
7812 : /* Guard against stack overflow due to overly deep inheritance tree. */
7813 676 : check_stack_depth();
7814 :
7815 : /* At top level, permission check was done in ATPrepCmd, else do it */
7816 676 : if (recursing)
7817 : {
7818 292 : ATSimplePermissions(AT_AddConstraint, rel,
7819 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7820 : Assert(conName != NULL);
7821 : }
7822 :
7823 676 : attnum = get_attnum(RelationGetRelid(rel), colName);
7824 676 : if (attnum == InvalidAttrNumber)
7825 18 : ereport(ERROR,
7826 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7827 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7828 : colName, RelationGetRelationName(rel))));
7829 :
7830 : /* Prevent them from altering a system attribute */
7831 658 : if (attnum <= 0)
7832 0 : ereport(ERROR,
7833 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7834 : errmsg("cannot alter system column \"%s\"",
7835 : colName)));
7836 :
7837 : /* TODO: see transformColumnDefinition() */
7838 658 : if (TupleDescAttr(RelationGetDescr(rel), attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
7839 6 : ereport(ERROR,
7840 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7841 : errmsg("not-null constraints are not supported on virtual generated columns"),
7842 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
7843 : colName, RelationGetRelationName(rel))));
7844 :
7845 : /* See if there's already a constraint */
7846 652 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7847 652 : if (HeapTupleIsValid(tuple))
7848 : {
7849 128 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7850 128 : bool changed = false;
7851 :
7852 : /*
7853 : * Don't let a NO INHERIT constraint be changed into inherit.
7854 : */
7855 128 : if (conForm->connoinherit && recurse)
7856 12 : ereport(ERROR,
7857 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7858 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7859 : NameStr(conForm->conname),
7860 : RelationGetRelationName(rel)));
7861 :
7862 : /*
7863 : * If we find an appropriate constraint, we're almost done, but just
7864 : * need to change some properties on it: if we're recursing, increment
7865 : * coninhcount; if not, set conislocal if not already set.
7866 : */
7867 116 : if (recursing)
7868 : {
7869 96 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7870 : &conForm->coninhcount))
7871 0 : ereport(ERROR,
7872 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7873 : errmsg("too many inheritance parents"));
7874 96 : changed = true;
7875 : }
7876 20 : else if (!conForm->conislocal)
7877 : {
7878 0 : conForm->conislocal = true;
7879 0 : changed = true;
7880 : }
7881 :
7882 116 : if (changed)
7883 : {
7884 : Relation constr_rel;
7885 :
7886 96 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7887 :
7888 96 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7889 96 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7890 96 : table_close(constr_rel, RowExclusiveLock);
7891 : }
7892 :
7893 116 : if (changed)
7894 96 : return address;
7895 : else
7896 20 : return InvalidObjectAddress;
7897 : }
7898 :
7899 : /*
7900 : * If we're asked not to recurse, and children exist, raise an error for
7901 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
7902 : * specified.
7903 : */
7904 548 : if (!recurse &&
7905 24 : find_inheritance_children(RelationGetRelid(rel),
7906 : NoLock) != NIL)
7907 : {
7908 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7909 6 : ereport(ERROR,
7910 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7911 : errmsg("constraint must be added to child tables too"),
7912 : errhint("Do not specify the ONLY keyword."));
7913 : else
7914 12 : is_no_inherit = true;
7915 : }
7916 :
7917 : /*
7918 : * No constraint exists; we must add one. First determine a name to use,
7919 : * if we haven't already.
7920 : */
7921 518 : if (!recursing)
7922 : {
7923 : Assert(conName == NULL);
7924 328 : conName = ChooseConstraintName(RelationGetRelationName(rel),
7925 : colName, "not_null",
7926 328 : RelationGetNamespace(rel),
7927 : NIL);
7928 : }
7929 :
7930 518 : constraint = makeNotNullConstraint(makeString(colName));
7931 518 : constraint->is_no_inherit = is_no_inherit;
7932 518 : constraint->conname = conName;
7933 :
7934 : /* and do it */
7935 518 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7936 518 : false, !recursing, false, NULL);
7937 518 : ccon = linitial(cooked);
7938 518 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7939 :
7940 518 : InvokeObjectPostAlterHook(RelationRelationId,
7941 : RelationGetRelid(rel), attnum);
7942 :
7943 : /* Mark pg_attribute.attnotnull for the column */
7944 518 : set_attnotnull(wqueue, rel, attnum, lockmode);
7945 :
7946 : /*
7947 : * Recurse to propagate the constraint to children that don't have one.
7948 : */
7949 518 : if (recurse)
7950 : {
7951 : List *children;
7952 :
7953 500 : children = find_inheritance_children(RelationGetRelid(rel),
7954 : lockmode);
7955 :
7956 1232 : foreach_oid(childoid, children)
7957 : {
7958 244 : Relation childrel = table_open(childoid, NoLock);
7959 :
7960 244 : CommandCounterIncrement();
7961 :
7962 244 : ATExecSetNotNull(wqueue, childrel, conName, colName,
7963 : recurse, true, lockmode);
7964 238 : table_close(childrel, NoLock);
7965 : }
7966 : }
7967 :
7968 512 : return address;
7969 : }
7970 :
7971 : /*
7972 : * NotNullImpliedByRelConstraints
7973 : * Does rel's existing constraints imply NOT NULL for the given attribute?
7974 : */
7975 : static bool
7976 1202 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7977 : {
7978 1202 : NullTest *nnulltest = makeNode(NullTest);
7979 :
7980 2404 : nnulltest->arg = (Expr *) makeVar(1,
7981 1202 : attr->attnum,
7982 : attr->atttypid,
7983 : attr->atttypmod,
7984 : attr->attcollation,
7985 : 0);
7986 1202 : nnulltest->nulltesttype = IS_NOT_NULL;
7987 :
7988 : /*
7989 : * argisrow = false is correct even for a composite column, because
7990 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7991 : * case, just IS DISTINCT FROM NULL.
7992 : */
7993 1202 : nnulltest->argisrow = false;
7994 1202 : nnulltest->location = -1;
7995 :
7996 1202 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7997 : {
7998 50 : ereport(DEBUG1,
7999 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8000 : RelationGetRelationName(rel), NameStr(attr->attname))));
8001 50 : return true;
8002 : }
8003 :
8004 1152 : return false;
8005 : }
8006 :
8007 : /*
8008 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8009 : *
8010 : * Return the address of the affected column.
8011 : */
8012 : static ObjectAddress
8013 584 : ATExecColumnDefault(Relation rel, const char *colName,
8014 : Node *newDefault, LOCKMODE lockmode)
8015 : {
8016 584 : TupleDesc tupdesc = RelationGetDescr(rel);
8017 : AttrNumber attnum;
8018 : ObjectAddress address;
8019 :
8020 : /*
8021 : * get the number of the attribute
8022 : */
8023 584 : attnum = get_attnum(RelationGetRelid(rel), colName);
8024 584 : if (attnum == InvalidAttrNumber)
8025 30 : ereport(ERROR,
8026 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8027 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8028 : colName, RelationGetRelationName(rel))));
8029 :
8030 : /* Prevent them from altering a system attribute */
8031 554 : if (attnum <= 0)
8032 0 : ereport(ERROR,
8033 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8034 : errmsg("cannot alter system column \"%s\"",
8035 : colName)));
8036 :
8037 554 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8038 18 : ereport(ERROR,
8039 : (errcode(ERRCODE_SYNTAX_ERROR),
8040 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8041 : colName, RelationGetRelationName(rel)),
8042 : /* translator: %s is an SQL ALTER command */
8043 : newDefault ? 0 : errhint("Use %s instead.",
8044 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8045 :
8046 536 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8047 12 : ereport(ERROR,
8048 : (errcode(ERRCODE_SYNTAX_ERROR),
8049 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8050 : colName, RelationGetRelationName(rel)),
8051 : newDefault ?
8052 : /* translator: %s is an SQL ALTER command */
8053 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8054 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8055 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8056 :
8057 : /*
8058 : * Remove any old default for the column. We use RESTRICT here for
8059 : * safety, but at present we do not expect anything to depend on the
8060 : * default.
8061 : *
8062 : * We treat removing the existing default as an internal operation when it
8063 : * is preparatory to adding a new default, but as a user-initiated
8064 : * operation when the user asked for a drop.
8065 : */
8066 524 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8067 : newDefault != NULL);
8068 :
8069 524 : if (newDefault)
8070 : {
8071 : /* SET DEFAULT */
8072 : RawColumnDefault *rawEnt;
8073 :
8074 350 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8075 350 : rawEnt->attnum = attnum;
8076 350 : rawEnt->raw_default = newDefault;
8077 350 : rawEnt->generated = '\0';
8078 :
8079 : /*
8080 : * This function is intended for CREATE TABLE, so it processes a
8081 : * _list_ of defaults, but we just do one.
8082 : */
8083 350 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8084 : false, true, false, NULL);
8085 : }
8086 :
8087 518 : ObjectAddressSubSet(address, RelationRelationId,
8088 : RelationGetRelid(rel), attnum);
8089 518 : return address;
8090 : }
8091 :
8092 : /*
8093 : * Add a pre-cooked default expression.
8094 : *
8095 : * Return the address of the affected column.
8096 : */
8097 : static ObjectAddress
8098 80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8099 : Node *newDefault)
8100 : {
8101 : ObjectAddress address;
8102 :
8103 : /* We assume no checking is required */
8104 :
8105 : /*
8106 : * Remove any old default for the column. We use RESTRICT here for
8107 : * safety, but at present we do not expect anything to depend on the
8108 : * default. (In ordinary cases, there could not be a default in place
8109 : * anyway, but it's possible when combining LIKE with inheritance.)
8110 : */
8111 80 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8112 : true);
8113 :
8114 80 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8115 :
8116 80 : ObjectAddressSubSet(address, RelationRelationId,
8117 : RelationGetRelid(rel), attnum);
8118 80 : return address;
8119 : }
8120 :
8121 : /*
8122 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8123 : *
8124 : * Return the address of the affected column.
8125 : */
8126 : static ObjectAddress
8127 160 : ATExecAddIdentity(Relation rel, const char *colName,
8128 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8129 : {
8130 : Relation attrelation;
8131 : HeapTuple tuple;
8132 : Form_pg_attribute attTup;
8133 : AttrNumber attnum;
8134 : ObjectAddress address;
8135 160 : ColumnDef *cdef = castNode(ColumnDef, def);
8136 : bool ispartitioned;
8137 :
8138 160 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8139 160 : if (ispartitioned && !recurse)
8140 6 : ereport(ERROR,
8141 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8142 : errmsg("cannot add identity to a column of only the partitioned table"),
8143 : errhint("Do not specify the ONLY keyword.")));
8144 :
8145 154 : if (rel->rd_rel->relispartition && !recursing)
8146 12 : ereport(ERROR,
8147 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8148 : errmsg("cannot add identity to a column of a partition"));
8149 :
8150 142 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8151 :
8152 142 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8153 142 : if (!HeapTupleIsValid(tuple))
8154 0 : ereport(ERROR,
8155 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8156 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8157 : colName, RelationGetRelationName(rel))));
8158 142 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8159 142 : attnum = attTup->attnum;
8160 :
8161 : /* Can't alter a system attribute */
8162 142 : if (attnum <= 0)
8163 0 : ereport(ERROR,
8164 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8165 : errmsg("cannot alter system column \"%s\"",
8166 : colName)));
8167 :
8168 : /*
8169 : * Creating a column as identity implies NOT NULL, so adding the identity
8170 : * to an existing column that is not NOT NULL would create a state that
8171 : * cannot be reproduced without contortions.
8172 : */
8173 142 : if (!attTup->attnotnull)
8174 6 : ereport(ERROR,
8175 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8176 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8177 : colName, RelationGetRelationName(rel))));
8178 :
8179 136 : if (attTup->attidentity)
8180 18 : ereport(ERROR,
8181 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8182 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8183 : colName, RelationGetRelationName(rel))));
8184 :
8185 118 : if (attTup->atthasdef)
8186 6 : ereport(ERROR,
8187 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8188 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8189 : colName, RelationGetRelationName(rel))));
8190 :
8191 112 : attTup->attidentity = cdef->identity;
8192 112 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8193 :
8194 112 : InvokeObjectPostAlterHook(RelationRelationId,
8195 : RelationGetRelid(rel),
8196 : attTup->attnum);
8197 112 : ObjectAddressSubSet(address, RelationRelationId,
8198 : RelationGetRelid(rel), attnum);
8199 112 : heap_freetuple(tuple);
8200 :
8201 112 : table_close(attrelation, RowExclusiveLock);
8202 :
8203 : /*
8204 : * Recurse to propagate the identity column to partitions. Identity is
8205 : * not inherited in regular inheritance children.
8206 : */
8207 112 : if (recurse && ispartitioned)
8208 : {
8209 : List *children;
8210 : ListCell *lc;
8211 :
8212 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8213 :
8214 16 : foreach(lc, children)
8215 : {
8216 : Relation childrel;
8217 :
8218 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8219 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8220 6 : table_close(childrel, NoLock);
8221 : }
8222 : }
8223 :
8224 112 : return address;
8225 : }
8226 :
8227 : /*
8228 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8229 : *
8230 : * Return the address of the affected column.
8231 : */
8232 : static ObjectAddress
8233 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8234 : LOCKMODE lockmode, bool recurse, bool recursing)
8235 : {
8236 : ListCell *option;
8237 74 : DefElem *generatedEl = NULL;
8238 : HeapTuple tuple;
8239 : Form_pg_attribute attTup;
8240 : AttrNumber attnum;
8241 : Relation attrelation;
8242 : ObjectAddress address;
8243 : bool ispartitioned;
8244 :
8245 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8246 74 : if (ispartitioned && !recurse)
8247 6 : ereport(ERROR,
8248 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8249 : errmsg("cannot change identity column of only the partitioned table"),
8250 : errhint("Do not specify the ONLY keyword.")));
8251 :
8252 68 : if (rel->rd_rel->relispartition && !recursing)
8253 12 : ereport(ERROR,
8254 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8255 : errmsg("cannot change identity column of a partition"));
8256 :
8257 100 : foreach(option, castNode(List, def))
8258 : {
8259 44 : DefElem *defel = lfirst_node(DefElem, option);
8260 :
8261 44 : if (strcmp(defel->defname, "generated") == 0)
8262 : {
8263 44 : if (generatedEl)
8264 0 : ereport(ERROR,
8265 : (errcode(ERRCODE_SYNTAX_ERROR),
8266 : errmsg("conflicting or redundant options")));
8267 44 : generatedEl = defel;
8268 : }
8269 : else
8270 0 : elog(ERROR, "option \"%s\" not recognized",
8271 : defel->defname);
8272 : }
8273 :
8274 : /*
8275 : * Even if there is nothing to change here, we run all the checks. There
8276 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8277 : * there.
8278 : */
8279 :
8280 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8281 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8282 56 : if (!HeapTupleIsValid(tuple))
8283 0 : ereport(ERROR,
8284 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8285 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8286 : colName, RelationGetRelationName(rel))));
8287 :
8288 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8289 56 : attnum = attTup->attnum;
8290 :
8291 56 : if (attnum <= 0)
8292 0 : ereport(ERROR,
8293 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8294 : errmsg("cannot alter system column \"%s\"",
8295 : colName)));
8296 :
8297 56 : if (!attTup->attidentity)
8298 6 : ereport(ERROR,
8299 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8300 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8301 : colName, RelationGetRelationName(rel))));
8302 :
8303 50 : if (generatedEl)
8304 : {
8305 44 : attTup->attidentity = defGetInt32(generatedEl);
8306 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8307 :
8308 44 : InvokeObjectPostAlterHook(RelationRelationId,
8309 : RelationGetRelid(rel),
8310 : attTup->attnum);
8311 44 : ObjectAddressSubSet(address, RelationRelationId,
8312 : RelationGetRelid(rel), attnum);
8313 : }
8314 : else
8315 6 : address = InvalidObjectAddress;
8316 :
8317 50 : heap_freetuple(tuple);
8318 50 : table_close(attrelation, RowExclusiveLock);
8319 :
8320 : /*
8321 : * Recurse to propagate the identity change to partitions. Identity is not
8322 : * inherited in regular inheritance children.
8323 : */
8324 50 : if (generatedEl && recurse && ispartitioned)
8325 : {
8326 : List *children;
8327 : ListCell *lc;
8328 :
8329 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8330 :
8331 18 : foreach(lc, children)
8332 : {
8333 : Relation childrel;
8334 :
8335 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8336 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8337 12 : table_close(childrel, NoLock);
8338 : }
8339 : }
8340 :
8341 50 : return address;
8342 : }
8343 :
8344 : /*
8345 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8346 : *
8347 : * Return the address of the affected column.
8348 : */
8349 : static ObjectAddress
8350 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8351 : bool recurse, bool recursing)
8352 : {
8353 : HeapTuple tuple;
8354 : Form_pg_attribute attTup;
8355 : AttrNumber attnum;
8356 : Relation attrelation;
8357 : ObjectAddress address;
8358 : Oid seqid;
8359 : ObjectAddress seqaddress;
8360 : bool ispartitioned;
8361 :
8362 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8363 68 : if (ispartitioned && !recurse)
8364 6 : ereport(ERROR,
8365 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8366 : errmsg("cannot drop identity from a column of only the partitioned table"),
8367 : errhint("Do not specify the ONLY keyword.")));
8368 :
8369 62 : if (rel->rd_rel->relispartition && !recursing)
8370 6 : ereport(ERROR,
8371 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8372 : errmsg("cannot drop identity from a column of a partition"));
8373 :
8374 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8375 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8376 56 : if (!HeapTupleIsValid(tuple))
8377 0 : ereport(ERROR,
8378 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8379 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8380 : colName, RelationGetRelationName(rel))));
8381 :
8382 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8383 56 : attnum = attTup->attnum;
8384 :
8385 56 : if (attnum <= 0)
8386 0 : ereport(ERROR,
8387 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8388 : errmsg("cannot alter system column \"%s\"",
8389 : colName)));
8390 :
8391 56 : if (!attTup->attidentity)
8392 : {
8393 12 : if (!missing_ok)
8394 6 : ereport(ERROR,
8395 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8396 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8397 : colName, RelationGetRelationName(rel))));
8398 : else
8399 : {
8400 6 : ereport(NOTICE,
8401 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8402 : colName, RelationGetRelationName(rel))));
8403 6 : heap_freetuple(tuple);
8404 6 : table_close(attrelation, RowExclusiveLock);
8405 6 : return InvalidObjectAddress;
8406 : }
8407 : }
8408 :
8409 44 : attTup->attidentity = '\0';
8410 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8411 :
8412 44 : InvokeObjectPostAlterHook(RelationRelationId,
8413 : RelationGetRelid(rel),
8414 : attTup->attnum);
8415 44 : ObjectAddressSubSet(address, RelationRelationId,
8416 : RelationGetRelid(rel), attnum);
8417 44 : heap_freetuple(tuple);
8418 :
8419 44 : table_close(attrelation, RowExclusiveLock);
8420 :
8421 : /*
8422 : * Recurse to drop the identity from column in partitions. Identity is
8423 : * not inherited in regular inheritance children so ignore them.
8424 : */
8425 44 : if (recurse && ispartitioned)
8426 : {
8427 : List *children;
8428 : ListCell *lc;
8429 :
8430 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8431 :
8432 12 : foreach(lc, children)
8433 : {
8434 : Relation childrel;
8435 :
8436 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8437 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8438 6 : table_close(childrel, NoLock);
8439 : }
8440 : }
8441 :
8442 44 : if (!recursing)
8443 : {
8444 : /* drop the internal sequence */
8445 32 : seqid = getIdentitySequence(rel, attnum, false);
8446 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8447 : RelationRelationId, DEPENDENCY_INTERNAL);
8448 32 : CommandCounterIncrement();
8449 32 : seqaddress.classId = RelationRelationId;
8450 32 : seqaddress.objectId = seqid;
8451 32 : seqaddress.objectSubId = 0;
8452 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8453 : }
8454 :
8455 44 : return address;
8456 : }
8457 :
8458 : /*
8459 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8460 : *
8461 : * Return the address of the affected column.
8462 : */
8463 : static ObjectAddress
8464 156 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8465 : Node *newExpr, LOCKMODE lockmode)
8466 : {
8467 : HeapTuple tuple;
8468 : Form_pg_attribute attTup;
8469 : AttrNumber attnum;
8470 : char attgenerated;
8471 : bool rewrite;
8472 : Oid attrdefoid;
8473 : ObjectAddress address;
8474 : Expr *defval;
8475 : NewColumnValue *newval;
8476 : RawColumnDefault *rawEnt;
8477 :
8478 156 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8479 156 : if (!HeapTupleIsValid(tuple))
8480 0 : ereport(ERROR,
8481 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8482 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8483 : colName, RelationGetRelationName(rel))));
8484 :
8485 156 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8486 :
8487 156 : attnum = attTup->attnum;
8488 156 : if (attnum <= 0)
8489 0 : ereport(ERROR,
8490 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8491 : errmsg("cannot alter system column \"%s\"",
8492 : colName)));
8493 :
8494 156 : attgenerated = attTup->attgenerated;
8495 156 : if (!attgenerated)
8496 12 : ereport(ERROR,
8497 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8498 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8499 : colName, RelationGetRelationName(rel))));
8500 :
8501 : /*
8502 : * TODO: This could be done, just need to recheck any constraints
8503 : * afterwards.
8504 : */
8505 144 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8506 66 : rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8507 12 : ereport(ERROR,
8508 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8509 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables with check constraints"),
8510 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8511 : colName, RelationGetRelationName(rel))));
8512 :
8513 : /*
8514 : * We need to prevent this because a change of expression could affect a
8515 : * row filter and inject expressions that are not permitted in a row
8516 : * filter. XXX We could try to have a more precise check to catch only
8517 : * publications with row filters, or even re-verify the row filter
8518 : * expressions.
8519 : */
8520 186 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8521 54 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8522 6 : ereport(ERROR,
8523 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8524 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables that are part of a publication"),
8525 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8526 : colName, RelationGetRelationName(rel))));
8527 :
8528 126 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8529 :
8530 126 : ReleaseSysCache(tuple);
8531 :
8532 126 : if (rewrite)
8533 : {
8534 : /*
8535 : * Clear all the missing values if we're rewriting the table, since
8536 : * this renders them pointless.
8537 : */
8538 78 : RelationClearMissing(rel);
8539 :
8540 : /* make sure we don't conflict with later attribute modifications */
8541 78 : CommandCounterIncrement();
8542 :
8543 : /*
8544 : * Find everything that depends on the column (constraints, indexes,
8545 : * etc), and record enough information to let us recreate the objects
8546 : * after rewrite.
8547 : */
8548 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8549 : }
8550 :
8551 : /*
8552 : * Drop the dependency records of the GENERATED expression, in particular
8553 : * its INTERNAL dependency on the column, which would otherwise cause
8554 : * dependency.c to refuse to perform the deletion.
8555 : */
8556 126 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8557 126 : if (!OidIsValid(attrdefoid))
8558 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8559 : RelationGetRelid(rel), attnum);
8560 126 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8561 :
8562 : /* Make above changes visible */
8563 126 : CommandCounterIncrement();
8564 :
8565 : /*
8566 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8567 : * safety, but at present we do not expect anything to depend on the
8568 : * expression.
8569 : */
8570 126 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8571 : false, false);
8572 :
8573 : /* Prepare to store the new expression, in the catalogs */
8574 126 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8575 126 : rawEnt->attnum = attnum;
8576 126 : rawEnt->raw_default = newExpr;
8577 126 : rawEnt->generated = attgenerated;
8578 :
8579 : /* Store the generated expression */
8580 126 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8581 : false, true, false, NULL);
8582 :
8583 : /* Make above new expression visible */
8584 126 : CommandCounterIncrement();
8585 :
8586 126 : if (rewrite)
8587 : {
8588 : /* Prepare for table rewrite */
8589 78 : defval = (Expr *) build_column_default(rel, attnum);
8590 :
8591 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8592 78 : newval->attnum = attnum;
8593 78 : newval->expr = expression_planner(defval);
8594 78 : newval->is_generated = true;
8595 :
8596 78 : tab->newvals = lappend(tab->newvals, newval);
8597 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8598 : }
8599 :
8600 : /* Drop any pg_statistic entry for the column */
8601 126 : RemoveStatistics(RelationGetRelid(rel), attnum);
8602 :
8603 126 : InvokeObjectPostAlterHook(RelationRelationId,
8604 : RelationGetRelid(rel), attnum);
8605 :
8606 126 : ObjectAddressSubSet(address, RelationRelationId,
8607 : RelationGetRelid(rel), attnum);
8608 126 : return address;
8609 : }
8610 :
8611 : /*
8612 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8613 : */
8614 : static void
8615 86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8616 : {
8617 : /*
8618 : * Reject ONLY if there are child tables. We could implement this, but it
8619 : * is a bit complicated. GENERATED clauses must be attached to the column
8620 : * definition and cannot be added later like DEFAULT, so if a child table
8621 : * has a generation expression that the parent does not have, the child
8622 : * column will necessarily be an attislocal column. So to implement ONLY
8623 : * here, we'd need extra code to update attislocal of the direct child
8624 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8625 : * resulting state can be properly dumped and restored.
8626 : */
8627 110 : if (!recurse &&
8628 24 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8629 12 : ereport(ERROR,
8630 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8631 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8632 :
8633 : /*
8634 : * Cannot drop generation expression from inherited columns.
8635 : */
8636 74 : if (!recursing)
8637 : {
8638 : HeapTuple tuple;
8639 : Form_pg_attribute attTup;
8640 :
8641 62 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8642 62 : if (!HeapTupleIsValid(tuple))
8643 0 : ereport(ERROR,
8644 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8645 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8646 : cmd->name, RelationGetRelationName(rel))));
8647 :
8648 62 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8649 :
8650 62 : if (attTup->attinhcount > 0)
8651 12 : ereport(ERROR,
8652 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8653 : errmsg("cannot drop generation expression from inherited column")));
8654 : }
8655 62 : }
8656 :
8657 : /*
8658 : * Return the address of the affected column.
8659 : */
8660 : static ObjectAddress
8661 56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8662 : {
8663 : HeapTuple tuple;
8664 : Form_pg_attribute attTup;
8665 : AttrNumber attnum;
8666 : Relation attrelation;
8667 : Oid attrdefoid;
8668 : ObjectAddress address;
8669 :
8670 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8671 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8672 56 : if (!HeapTupleIsValid(tuple))
8673 0 : ereport(ERROR,
8674 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8675 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8676 : colName, RelationGetRelationName(rel))));
8677 :
8678 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8679 56 : attnum = attTup->attnum;
8680 :
8681 56 : if (attnum <= 0)
8682 0 : ereport(ERROR,
8683 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8684 : errmsg("cannot alter system column \"%s\"",
8685 : colName)));
8686 :
8687 : /*
8688 : * TODO: This could be done, but it would need a table rewrite to
8689 : * materialize the generated values. Note that for the time being, we
8690 : * still error with missing_ok, so that we don't silently leave the column
8691 : * as generated.
8692 : */
8693 56 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8694 12 : ereport(ERROR,
8695 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8696 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8697 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8698 : colName, RelationGetRelationName(rel))));
8699 :
8700 44 : if (!attTup->attgenerated)
8701 : {
8702 24 : if (!missing_ok)
8703 12 : ereport(ERROR,
8704 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8705 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8706 : colName, RelationGetRelationName(rel))));
8707 : else
8708 : {
8709 12 : ereport(NOTICE,
8710 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8711 : colName, RelationGetRelationName(rel))));
8712 12 : heap_freetuple(tuple);
8713 12 : table_close(attrelation, RowExclusiveLock);
8714 12 : return InvalidObjectAddress;
8715 : }
8716 : }
8717 :
8718 : /*
8719 : * Mark the column as no longer generated. (The atthasdef flag needs to
8720 : * get cleared too, but RemoveAttrDefault will handle that.)
8721 : */
8722 20 : attTup->attgenerated = '\0';
8723 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8724 :
8725 20 : InvokeObjectPostAlterHook(RelationRelationId,
8726 : RelationGetRelid(rel),
8727 : attnum);
8728 20 : heap_freetuple(tuple);
8729 :
8730 20 : table_close(attrelation, RowExclusiveLock);
8731 :
8732 : /*
8733 : * Drop the dependency records of the GENERATED expression, in particular
8734 : * its INTERNAL dependency on the column, which would otherwise cause
8735 : * dependency.c to refuse to perform the deletion.
8736 : */
8737 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8738 20 : if (!OidIsValid(attrdefoid))
8739 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8740 : RelationGetRelid(rel), attnum);
8741 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8742 :
8743 : /* Make above changes visible */
8744 20 : CommandCounterIncrement();
8745 :
8746 : /*
8747 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8748 : * safety, but at present we do not expect anything to depend on the
8749 : * default.
8750 : */
8751 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8752 : false, false);
8753 :
8754 20 : ObjectAddressSubSet(address, RelationRelationId,
8755 : RelationGetRelid(rel), attnum);
8756 20 : return address;
8757 : }
8758 :
8759 : /*
8760 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8761 : *
8762 : * Return value is the address of the modified column
8763 : */
8764 : static ObjectAddress
8765 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8766 : {
8767 164 : int newtarget = 0;
8768 : bool newtarget_default;
8769 : Relation attrelation;
8770 : HeapTuple tuple,
8771 : newtuple;
8772 : Form_pg_attribute attrtuple;
8773 : AttrNumber attnum;
8774 : ObjectAddress address;
8775 : Datum repl_val[Natts_pg_attribute];
8776 : bool repl_null[Natts_pg_attribute];
8777 : bool repl_repl[Natts_pg_attribute];
8778 :
8779 : /*
8780 : * We allow referencing columns by numbers only for indexes, since table
8781 : * column numbers could contain gaps if columns are later dropped.
8782 : */
8783 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8784 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8785 : !colName)
8786 0 : ereport(ERROR,
8787 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8788 : errmsg("cannot refer to non-index column by number")));
8789 :
8790 : /* -1 was used in previous versions for the default setting */
8791 164 : if (newValue && intVal(newValue) != -1)
8792 : {
8793 120 : newtarget = intVal(newValue);
8794 120 : newtarget_default = false;
8795 : }
8796 : else
8797 44 : newtarget_default = true;
8798 :
8799 164 : if (!newtarget_default)
8800 : {
8801 : /*
8802 : * Limit target to a sane range
8803 : */
8804 120 : if (newtarget < 0)
8805 : {
8806 0 : ereport(ERROR,
8807 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8808 : errmsg("statistics target %d is too low",
8809 : newtarget)));
8810 : }
8811 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8812 : {
8813 0 : newtarget = MAX_STATISTICS_TARGET;
8814 0 : ereport(WARNING,
8815 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8816 : errmsg("lowering statistics target to %d",
8817 : newtarget)));
8818 : }
8819 : }
8820 :
8821 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8822 :
8823 164 : if (colName)
8824 : {
8825 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8826 :
8827 100 : if (!HeapTupleIsValid(tuple))
8828 12 : ereport(ERROR,
8829 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8830 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8831 : colName, RelationGetRelationName(rel))));
8832 : }
8833 : else
8834 : {
8835 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8836 :
8837 64 : if (!HeapTupleIsValid(tuple))
8838 12 : ereport(ERROR,
8839 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8840 : errmsg("column number %d of relation \"%s\" does not exist",
8841 : colNum, RelationGetRelationName(rel))));
8842 : }
8843 :
8844 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8845 :
8846 140 : attnum = attrtuple->attnum;
8847 140 : if (attnum <= 0)
8848 0 : ereport(ERROR,
8849 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8850 : errmsg("cannot alter system column \"%s\"",
8851 : colName)));
8852 :
8853 : /*
8854 : * Prevent this as long as the ANALYZE code skips virtual generated
8855 : * columns.
8856 : */
8857 140 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8858 0 : ereport(ERROR,
8859 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8860 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
8861 : colName)));
8862 :
8863 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8864 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8865 : {
8866 52 : if (attnum > rel->rd_index->indnkeyatts)
8867 6 : ereport(ERROR,
8868 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8869 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8870 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8871 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8872 18 : ereport(ERROR,
8873 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8874 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8875 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8876 : errhint("Alter statistics on table column instead.")));
8877 : }
8878 :
8879 : /* Build new tuple. */
8880 116 : memset(repl_null, false, sizeof(repl_null));
8881 116 : memset(repl_repl, false, sizeof(repl_repl));
8882 116 : if (!newtarget_default)
8883 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8884 : else
8885 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8886 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8887 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8888 : repl_val, repl_null, repl_repl);
8889 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8890 :
8891 116 : InvokeObjectPostAlterHook(RelationRelationId,
8892 : RelationGetRelid(rel),
8893 : attrtuple->attnum);
8894 116 : ObjectAddressSubSet(address, RelationRelationId,
8895 : RelationGetRelid(rel), attnum);
8896 :
8897 116 : heap_freetuple(newtuple);
8898 :
8899 116 : ReleaseSysCache(tuple);
8900 :
8901 116 : table_close(attrelation, RowExclusiveLock);
8902 :
8903 116 : return address;
8904 : }
8905 :
8906 : /*
8907 : * Return value is the address of the modified column
8908 : */
8909 : static ObjectAddress
8910 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8911 : bool isReset, LOCKMODE lockmode)
8912 : {
8913 : Relation attrelation;
8914 : HeapTuple tuple,
8915 : newtuple;
8916 : Form_pg_attribute attrtuple;
8917 : AttrNumber attnum;
8918 : Datum datum,
8919 : newOptions;
8920 : bool isnull;
8921 : ObjectAddress address;
8922 : Datum repl_val[Natts_pg_attribute];
8923 : bool repl_null[Natts_pg_attribute];
8924 : bool repl_repl[Natts_pg_attribute];
8925 :
8926 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8927 :
8928 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8929 :
8930 32 : if (!HeapTupleIsValid(tuple))
8931 0 : ereport(ERROR,
8932 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8933 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8934 : colName, RelationGetRelationName(rel))));
8935 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8936 :
8937 32 : attnum = attrtuple->attnum;
8938 32 : if (attnum <= 0)
8939 0 : ereport(ERROR,
8940 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8941 : errmsg("cannot alter system column \"%s\"",
8942 : colName)));
8943 :
8944 : /* Generate new proposed attoptions (text array) */
8945 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8946 : &isnull);
8947 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8948 : castNode(List, options), NULL, NULL,
8949 : false, isReset);
8950 : /* Validate new options */
8951 32 : (void) attribute_reloptions(newOptions, true);
8952 :
8953 : /* Build new tuple. */
8954 32 : memset(repl_null, false, sizeof(repl_null));
8955 32 : memset(repl_repl, false, sizeof(repl_repl));
8956 32 : if (newOptions != (Datum) 0)
8957 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8958 : else
8959 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
8960 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8961 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8962 : repl_val, repl_null, repl_repl);
8963 :
8964 : /* Update system catalog. */
8965 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8966 :
8967 32 : InvokeObjectPostAlterHook(RelationRelationId,
8968 : RelationGetRelid(rel),
8969 : attrtuple->attnum);
8970 32 : ObjectAddressSubSet(address, RelationRelationId,
8971 : RelationGetRelid(rel), attnum);
8972 :
8973 32 : heap_freetuple(newtuple);
8974 :
8975 32 : ReleaseSysCache(tuple);
8976 :
8977 32 : table_close(attrelation, RowExclusiveLock);
8978 :
8979 32 : return address;
8980 : }
8981 :
8982 : /*
8983 : * Helper function for ATExecSetStorage and ATExecSetCompression
8984 : *
8985 : * Set the attstorage and/or attcompression fields for index columns
8986 : * associated with the specified table column.
8987 : */
8988 : static void
8989 290 : SetIndexStorageProperties(Relation rel, Relation attrelation,
8990 : AttrNumber attnum,
8991 : bool setstorage, char newstorage,
8992 : bool setcompression, char newcompression,
8993 : LOCKMODE lockmode)
8994 : {
8995 : ListCell *lc;
8996 :
8997 374 : foreach(lc, RelationGetIndexList(rel))
8998 : {
8999 84 : Oid indexoid = lfirst_oid(lc);
9000 : Relation indrel;
9001 84 : AttrNumber indattnum = 0;
9002 : HeapTuple tuple;
9003 :
9004 84 : indrel = index_open(indexoid, lockmode);
9005 :
9006 144 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9007 : {
9008 90 : if (indrel->rd_index->indkey.values[i] == attnum)
9009 : {
9010 30 : indattnum = i + 1;
9011 30 : break;
9012 : }
9013 : }
9014 :
9015 84 : if (indattnum == 0)
9016 : {
9017 54 : index_close(indrel, lockmode);
9018 54 : continue;
9019 : }
9020 :
9021 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9022 :
9023 30 : if (HeapTupleIsValid(tuple))
9024 : {
9025 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9026 :
9027 30 : if (setstorage)
9028 24 : attrtuple->attstorage = newstorage;
9029 :
9030 30 : if (setcompression)
9031 6 : attrtuple->attcompression = newcompression;
9032 :
9033 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9034 :
9035 30 : InvokeObjectPostAlterHook(RelationRelationId,
9036 : RelationGetRelid(rel),
9037 : attrtuple->attnum);
9038 :
9039 30 : heap_freetuple(tuple);
9040 : }
9041 :
9042 30 : index_close(indrel, lockmode);
9043 : }
9044 290 : }
9045 :
9046 : /*
9047 : * ALTER TABLE ALTER COLUMN SET STORAGE
9048 : *
9049 : * Return value is the address of the modified column
9050 : */
9051 : static ObjectAddress
9052 240 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9053 : {
9054 : Relation attrelation;
9055 : HeapTuple tuple;
9056 : Form_pg_attribute attrtuple;
9057 : AttrNumber attnum;
9058 : ObjectAddress address;
9059 :
9060 240 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9061 :
9062 240 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9063 :
9064 240 : if (!HeapTupleIsValid(tuple))
9065 12 : ereport(ERROR,
9066 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9067 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9068 : colName, RelationGetRelationName(rel))));
9069 228 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9070 :
9071 228 : attnum = attrtuple->attnum;
9072 228 : if (attnum <= 0)
9073 0 : ereport(ERROR,
9074 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9075 : errmsg("cannot alter system column \"%s\"",
9076 : colName)));
9077 :
9078 228 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9079 :
9080 228 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9081 :
9082 228 : InvokeObjectPostAlterHook(RelationRelationId,
9083 : RelationGetRelid(rel),
9084 : attrtuple->attnum);
9085 :
9086 : /*
9087 : * Apply the change to indexes as well (only for simple index columns,
9088 : * matching behavior of index.c ConstructTupleDescriptor()).
9089 : */
9090 228 : SetIndexStorageProperties(rel, attrelation, attnum,
9091 228 : true, attrtuple->attstorage,
9092 : false, 0,
9093 : lockmode);
9094 :
9095 228 : heap_freetuple(tuple);
9096 :
9097 228 : table_close(attrelation, RowExclusiveLock);
9098 :
9099 228 : ObjectAddressSubSet(address, RelationRelationId,
9100 : RelationGetRelid(rel), attnum);
9101 228 : return address;
9102 : }
9103 :
9104 :
9105 : /*
9106 : * ALTER TABLE DROP COLUMN
9107 : *
9108 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9109 : * because we have to decide at runtime whether to recurse or not depending
9110 : * on whether attinhcount goes to zero or not. (We can't check this in a
9111 : * static pre-pass because it won't handle multiple inheritance situations
9112 : * correctly.)
9113 : */
9114 : static void
9115 1644 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9116 : AlterTableCmd *cmd, LOCKMODE lockmode,
9117 : AlterTableUtilityContext *context)
9118 : {
9119 1644 : if (rel->rd_rel->reloftype && !recursing)
9120 6 : ereport(ERROR,
9121 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9122 : errmsg("cannot drop column from typed table")));
9123 :
9124 1638 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9125 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9126 :
9127 1632 : if (recurse)
9128 1354 : cmd->recurse = true;
9129 1632 : }
9130 :
9131 : /*
9132 : * Drops column 'colName' from relation 'rel' and returns the address of the
9133 : * dropped column. The column is also dropped (or marked as no longer
9134 : * inherited from relation) from the relation's inheritance children, if any.
9135 : *
9136 : * In the recursive invocations for inheritance child relations, instead of
9137 : * dropping the column directly (if to be dropped at all), its object address
9138 : * is added to 'addrs', which must be non-NULL in such invocations. All
9139 : * columns are dropped at the same time after all the children have been
9140 : * checked recursively.
9141 : */
9142 : static ObjectAddress
9143 2188 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9144 : DropBehavior behavior,
9145 : bool recurse, bool recursing,
9146 : bool missing_ok, LOCKMODE lockmode,
9147 : ObjectAddresses *addrs)
9148 : {
9149 : HeapTuple tuple;
9150 : Form_pg_attribute targetatt;
9151 : AttrNumber attnum;
9152 : List *children;
9153 : ObjectAddress object;
9154 : bool is_expr;
9155 :
9156 : /* At top level, permission check was done in ATPrepCmd, else do it */
9157 2188 : if (recursing)
9158 556 : ATSimplePermissions(AT_DropColumn, rel,
9159 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9160 :
9161 : /* Initialize addrs on the first invocation */
9162 : Assert(!recursing || addrs != NULL);
9163 :
9164 : /* since this function recurses, it could be driven to stack overflow */
9165 2188 : check_stack_depth();
9166 :
9167 2188 : if (!recursing)
9168 1632 : addrs = new_object_addresses();
9169 :
9170 : /*
9171 : * get the number of the attribute
9172 : */
9173 2188 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9174 2188 : if (!HeapTupleIsValid(tuple))
9175 : {
9176 54 : if (!missing_ok)
9177 : {
9178 36 : ereport(ERROR,
9179 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9180 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9181 : colName, RelationGetRelationName(rel))));
9182 : }
9183 : else
9184 : {
9185 18 : ereport(NOTICE,
9186 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9187 : colName, RelationGetRelationName(rel))));
9188 18 : return InvalidObjectAddress;
9189 : }
9190 : }
9191 2134 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9192 :
9193 2134 : attnum = targetatt->attnum;
9194 :
9195 : /* Can't drop a system attribute */
9196 2134 : if (attnum <= 0)
9197 6 : ereport(ERROR,
9198 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9199 : errmsg("cannot drop system column \"%s\"",
9200 : colName)));
9201 :
9202 : /*
9203 : * Don't drop inherited columns, unless recursing (presumably from a drop
9204 : * of the parent column)
9205 : */
9206 2128 : if (targetatt->attinhcount > 0 && !recursing)
9207 48 : ereport(ERROR,
9208 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9209 : errmsg("cannot drop inherited column \"%s\"",
9210 : colName)));
9211 :
9212 : /*
9213 : * Don't drop columns used in the partition key, either. (If we let this
9214 : * go through, the key column's dependencies would cause a cascaded drop
9215 : * of the whole table, which is surely not what the user expected.)
9216 : */
9217 2080 : if (has_partition_attrs(rel,
9218 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9219 : &is_expr))
9220 30 : ereport(ERROR,
9221 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9222 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9223 : colName, RelationGetRelationName(rel))));
9224 :
9225 2050 : ReleaseSysCache(tuple);
9226 :
9227 : /*
9228 : * Propagate to children as appropriate. Unlike most other ALTER
9229 : * routines, we have to do this one level of recursion at a time; we can't
9230 : * use find_all_inheritors to do it in one pass.
9231 : */
9232 : children =
9233 2050 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9234 :
9235 2050 : if (children)
9236 : {
9237 : Relation attr_rel;
9238 : ListCell *child;
9239 :
9240 : /*
9241 : * In case of a partitioned table, the column must be dropped from the
9242 : * partitions as well.
9243 : */
9244 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9245 6 : ereport(ERROR,
9246 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9247 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9248 : errhint("Do not specify the ONLY keyword.")));
9249 :
9250 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9251 882 : foreach(child, children)
9252 : {
9253 592 : Oid childrelid = lfirst_oid(child);
9254 : Relation childrel;
9255 : Form_pg_attribute childatt;
9256 :
9257 : /* find_inheritance_children already got lock */
9258 592 : childrel = table_open(childrelid, NoLock);
9259 592 : CheckAlterTableIsSafe(childrel);
9260 :
9261 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9262 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9263 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9264 : colName, childrelid);
9265 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9266 :
9267 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9268 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9269 : childrelid, colName);
9270 :
9271 592 : if (recurse)
9272 : {
9273 : /*
9274 : * If the child column has other definition sources, just
9275 : * decrement its inheritance count; if not, recurse to delete
9276 : * it.
9277 : */
9278 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9279 : {
9280 : /* Time to delete this child column, too */
9281 556 : ATExecDropColumn(wqueue, childrel, colName,
9282 : behavior, true, true,
9283 : false, lockmode, addrs);
9284 : }
9285 : else
9286 : {
9287 : /* Child column must survive my deletion */
9288 12 : childatt->attinhcount--;
9289 :
9290 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9291 :
9292 : /* Make update visible */
9293 12 : CommandCounterIncrement();
9294 : }
9295 : }
9296 : else
9297 : {
9298 : /*
9299 : * If we were told to drop ONLY in this table (no recursion),
9300 : * we need to mark the inheritors' attributes as locally
9301 : * defined rather than inherited.
9302 : */
9303 24 : childatt->attinhcount--;
9304 24 : childatt->attislocal = true;
9305 :
9306 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9307 :
9308 : /* Make update visible */
9309 24 : CommandCounterIncrement();
9310 : }
9311 :
9312 586 : heap_freetuple(tuple);
9313 :
9314 586 : table_close(childrel, NoLock);
9315 : }
9316 290 : table_close(attr_rel, RowExclusiveLock);
9317 : }
9318 :
9319 : /* Add object to delete */
9320 2038 : object.classId = RelationRelationId;
9321 2038 : object.objectId = RelationGetRelid(rel);
9322 2038 : object.objectSubId = attnum;
9323 2038 : add_exact_object_address(&object, addrs);
9324 :
9325 2038 : if (!recursing)
9326 : {
9327 : /* Recursion has ended, drop everything that was collected */
9328 1488 : performMultipleDeletions(addrs, behavior, 0);
9329 1434 : free_object_addresses(addrs);
9330 : }
9331 :
9332 1984 : return object;
9333 : }
9334 :
9335 : /*
9336 : * Prepare to add a primary key on table, by adding not-null constraints
9337 : * on all columns.
9338 : */
9339 : static void
9340 15110 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9341 : bool recurse, LOCKMODE lockmode,
9342 : AlterTableUtilityContext *context)
9343 : {
9344 : ListCell *lc;
9345 : Constraint *pkconstr;
9346 :
9347 15110 : pkconstr = castNode(Constraint, cmd->def);
9348 15110 : if (pkconstr->contype != CONSTR_PRIMARY)
9349 8918 : return;
9350 :
9351 : /*
9352 : * If not recursing, we must ensure that all children have a NOT NULL
9353 : * constraint on the columns, and error out if not.
9354 : */
9355 6192 : if (!recurse)
9356 : {
9357 : List *children;
9358 :
9359 284 : children = find_inheritance_children(RelationGetRelid(rel),
9360 : lockmode);
9361 698 : foreach_oid(childrelid, children)
9362 : {
9363 280 : foreach(lc, pkconstr->keys)
9364 : {
9365 : HeapTuple tup;
9366 : Form_pg_attribute attrForm;
9367 144 : char *attname = strVal(lfirst(lc));
9368 :
9369 144 : tup = SearchSysCacheAttName(childrelid, attname);
9370 144 : if (!tup)
9371 0 : elog(ERROR, "cache lookup failed for attribute %s of relation %u",
9372 : attname, childrelid);
9373 144 : attrForm = (Form_pg_attribute) GETSTRUCT(tup);
9374 144 : if (!attrForm->attnotnull)
9375 6 : ereport(ERROR,
9376 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9377 : attname, get_rel_name(childrelid)));
9378 138 : ReleaseSysCache(tup);
9379 : }
9380 : }
9381 : }
9382 :
9383 : /* Insert not-null constraints in the queue for the PK columns */
9384 7166 : foreach(lc, pkconstr->keys)
9385 : {
9386 : AlterTableCmd *newcmd;
9387 : Constraint *nnconstr;
9388 :
9389 980 : nnconstr = makeNotNullConstraint(lfirst(lc));
9390 :
9391 980 : newcmd = makeNode(AlterTableCmd);
9392 980 : newcmd->subtype = AT_AddConstraint;
9393 980 : newcmd->recurse = true;
9394 980 : newcmd->def = (Node *) nnconstr;
9395 :
9396 980 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9397 : }
9398 : }
9399 :
9400 : /*
9401 : * ALTER TABLE ADD INDEX
9402 : *
9403 : * There is no such command in the grammar, but parse_utilcmd.c converts
9404 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9405 : * us schedule creation of the index at the appropriate time during ALTER.
9406 : *
9407 : * Return value is the address of the new index.
9408 : */
9409 : static ObjectAddress
9410 1622 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9411 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9412 : {
9413 : bool check_rights;
9414 : bool skip_build;
9415 : bool quiet;
9416 : ObjectAddress address;
9417 :
9418 : Assert(IsA(stmt, IndexStmt));
9419 : Assert(!stmt->concurrent);
9420 :
9421 : /* The IndexStmt has already been through transformIndexStmt */
9422 : Assert(stmt->transformed);
9423 :
9424 : /* suppress schema rights check when rebuilding existing index */
9425 1622 : check_rights = !is_rebuild;
9426 : /* skip index build if phase 3 will do it or we're reusing an old one */
9427 1622 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9428 : /* suppress notices when rebuilding existing index */
9429 1622 : quiet = is_rebuild;
9430 :
9431 1622 : address = DefineIndex(RelationGetRelid(rel),
9432 : stmt,
9433 : InvalidOid, /* no predefined OID */
9434 : InvalidOid, /* no parent index */
9435 : InvalidOid, /* no parent constraint */
9436 : -1, /* total_parts unknown */
9437 : true, /* is_alter_table */
9438 : check_rights,
9439 : false, /* check_not_in_use - we did it already */
9440 : skip_build,
9441 : quiet);
9442 :
9443 : /*
9444 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9445 : * new index instead of building from scratch. Restore associated fields.
9446 : * This may store InvalidSubTransactionId in both fields, in which case
9447 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9448 : * this after the CCI that made catalog rows visible to any rebuild. The
9449 : * DROP of the old edition of this index will have scheduled the storage
9450 : * for deletion at commit, so cancel that pending deletion.
9451 : */
9452 1452 : if (RelFileNumberIsValid(stmt->oldNumber))
9453 : {
9454 74 : Relation irel = index_open(address.objectId, NoLock);
9455 :
9456 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9457 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9458 74 : RelationPreserveStorage(irel->rd_locator, true);
9459 74 : index_close(irel, NoLock);
9460 : }
9461 :
9462 1452 : return address;
9463 : }
9464 :
9465 : /*
9466 : * ALTER TABLE ADD STATISTICS
9467 : *
9468 : * This is no such command in the grammar, but we use this internally to add
9469 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9470 : * column type change.
9471 : */
9472 : static ObjectAddress
9473 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9474 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9475 : {
9476 : ObjectAddress address;
9477 :
9478 : Assert(IsA(stmt, CreateStatsStmt));
9479 :
9480 : /* The CreateStatsStmt has already been through transformStatsStmt */
9481 : Assert(stmt->transformed);
9482 :
9483 14 : address = CreateStatistics(stmt);
9484 :
9485 14 : return address;
9486 : }
9487 :
9488 : /*
9489 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9490 : *
9491 : * Returns the address of the new constraint.
9492 : */
9493 : static ObjectAddress
9494 9540 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9495 : IndexStmt *stmt, LOCKMODE lockmode)
9496 : {
9497 9540 : Oid index_oid = stmt->indexOid;
9498 : Relation indexRel;
9499 : char *indexName;
9500 : IndexInfo *indexInfo;
9501 : char *constraintName;
9502 : char constraintType;
9503 : ObjectAddress address;
9504 : bits16 flags;
9505 :
9506 : Assert(IsA(stmt, IndexStmt));
9507 : Assert(OidIsValid(index_oid));
9508 : Assert(stmt->isconstraint);
9509 :
9510 : /*
9511 : * Doing this on partitioned tables is not a simple feature to implement,
9512 : * so let's punt for now.
9513 : */
9514 9540 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9515 6 : ereport(ERROR,
9516 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9517 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9518 :
9519 9534 : indexRel = index_open(index_oid, AccessShareLock);
9520 :
9521 9534 : indexName = pstrdup(RelationGetRelationName(indexRel));
9522 :
9523 9534 : indexInfo = BuildIndexInfo(indexRel);
9524 :
9525 : /* this should have been checked at parse time */
9526 9534 : if (!indexInfo->ii_Unique)
9527 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9528 :
9529 : /*
9530 : * Determine name to assign to constraint. We require a constraint to
9531 : * have the same name as the underlying index; therefore, use the index's
9532 : * existing name as the default constraint name, and if the user
9533 : * explicitly gives some other name for the constraint, rename the index
9534 : * to match.
9535 : */
9536 9534 : constraintName = stmt->idxname;
9537 9534 : if (constraintName == NULL)
9538 9508 : constraintName = indexName;
9539 26 : else if (strcmp(constraintName, indexName) != 0)
9540 : {
9541 20 : ereport(NOTICE,
9542 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9543 : indexName, constraintName)));
9544 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9545 : }
9546 :
9547 : /* Extra checks needed if making primary key */
9548 9534 : if (stmt->primary)
9549 5386 : index_check_primary_key(rel, indexInfo, true, stmt);
9550 :
9551 : /* Note we currently don't support EXCLUSION constraints here */
9552 9528 : if (stmt->primary)
9553 5380 : constraintType = CONSTRAINT_PRIMARY;
9554 : else
9555 4148 : constraintType = CONSTRAINT_UNIQUE;
9556 :
9557 : /* Create the catalog entries for the constraint */
9558 9528 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9559 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9560 19056 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9561 9528 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9562 9528 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9563 :
9564 9528 : address = index_constraint_create(rel,
9565 : index_oid,
9566 : InvalidOid,
9567 : indexInfo,
9568 : constraintName,
9569 : constraintType,
9570 : flags,
9571 : allowSystemTableMods,
9572 : false); /* is_internal */
9573 :
9574 9528 : index_close(indexRel, NoLock);
9575 :
9576 9528 : return address;
9577 : }
9578 :
9579 : /*
9580 : * ALTER TABLE ADD CONSTRAINT
9581 : *
9582 : * Return value is the address of the new constraint; if no constraint was
9583 : * added, InvalidObjectAddress is returned.
9584 : */
9585 : static ObjectAddress
9586 12148 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9587 : Constraint *newConstraint, bool recurse, bool is_readd,
9588 : LOCKMODE lockmode)
9589 : {
9590 12148 : ObjectAddress address = InvalidObjectAddress;
9591 :
9592 : Assert(IsA(newConstraint, Constraint));
9593 :
9594 : /*
9595 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9596 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9597 : * parse_utilcmd.c).
9598 : */
9599 12148 : switch (newConstraint->contype)
9600 : {
9601 9566 : case CONSTR_CHECK:
9602 : case CONSTR_NOTNULL:
9603 : address =
9604 9566 : ATAddCheckNNConstraint(wqueue, tab, rel,
9605 : newConstraint, recurse, false, is_readd,
9606 : lockmode);
9607 9434 : break;
9608 :
9609 2582 : case CONSTR_FOREIGN:
9610 :
9611 : /*
9612 : * Assign or validate constraint name
9613 : */
9614 2582 : if (newConstraint->conname)
9615 : {
9616 1152 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9617 : RelationGetRelid(rel),
9618 1152 : newConstraint->conname))
9619 0 : ereport(ERROR,
9620 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9621 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9622 : newConstraint->conname,
9623 : RelationGetRelationName(rel))));
9624 : }
9625 : else
9626 1430 : newConstraint->conname =
9627 1430 : ChooseConstraintName(RelationGetRelationName(rel),
9628 1430 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9629 : "fkey",
9630 1430 : RelationGetNamespace(rel),
9631 : NIL);
9632 :
9633 2582 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9634 : newConstraint,
9635 : recurse, false,
9636 : lockmode);
9637 2034 : break;
9638 :
9639 0 : default:
9640 0 : elog(ERROR, "unrecognized constraint type: %d",
9641 : (int) newConstraint->contype);
9642 : }
9643 :
9644 11468 : return address;
9645 : }
9646 :
9647 : /*
9648 : * Generate the column-name portion of the constraint name for a new foreign
9649 : * key given the list of column names that reference the referenced
9650 : * table. This will be passed to ChooseConstraintName along with the parent
9651 : * table name and the "fkey" suffix.
9652 : *
9653 : * We know that less than NAMEDATALEN characters will actually be used, so we
9654 : * can truncate the result once we've generated that many.
9655 : *
9656 : * XXX see also ChooseExtendedStatisticNameAddition and
9657 : * ChooseIndexNameAddition.
9658 : */
9659 : static char *
9660 2414 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9661 : {
9662 : char buf[NAMEDATALEN * 2];
9663 2414 : int buflen = 0;
9664 : ListCell *lc;
9665 :
9666 2414 : buf[0] = '\0';
9667 5400 : foreach(lc, colnames)
9668 : {
9669 2986 : const char *name = strVal(lfirst(lc));
9670 :
9671 2986 : if (buflen > 0)
9672 572 : buf[buflen++] = '_'; /* insert _ between names */
9673 :
9674 : /*
9675 : * At this point we have buflen <= NAMEDATALEN. name should be less
9676 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9677 : */
9678 2986 : strlcpy(buf + buflen, name, NAMEDATALEN);
9679 2986 : buflen += strlen(buf + buflen);
9680 2986 : if (buflen >= NAMEDATALEN)
9681 0 : break;
9682 : }
9683 2414 : return pstrdup(buf);
9684 : }
9685 :
9686 : /*
9687 : * Add a check or not-null constraint to a single table and its children.
9688 : * Returns the address of the constraint added to the parent relation,
9689 : * if one gets added, or InvalidObjectAddress otherwise.
9690 : *
9691 : * Subroutine for ATExecAddConstraint.
9692 : *
9693 : * We must recurse to child tables during execution, rather than using
9694 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9695 : * constraints *must* be given the same name, else they won't be seen as
9696 : * related later. If the user didn't explicitly specify a name, then
9697 : * AddRelationNewConstraints would normally assign different names to the
9698 : * child constraints. To fix that, we must capture the name assigned at
9699 : * the parent table and pass that down.
9700 : */
9701 : static ObjectAddress
9702 10378 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9703 : Constraint *constr, bool recurse, bool recursing,
9704 : bool is_readd, LOCKMODE lockmode)
9705 : {
9706 : List *newcons;
9707 : ListCell *lcon;
9708 : List *children;
9709 : ListCell *child;
9710 10378 : ObjectAddress address = InvalidObjectAddress;
9711 :
9712 : /* Guard against stack overflow due to overly deep inheritance tree. */
9713 10378 : check_stack_depth();
9714 :
9715 : /* At top level, permission check was done in ATPrepCmd, else do it */
9716 10378 : if (recursing)
9717 678 : ATSimplePermissions(AT_AddConstraint, rel,
9718 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9719 :
9720 : /*
9721 : * Call AddRelationNewConstraints to do the work, making sure it works on
9722 : * a copy of the Constraint so transformExpr can't modify the original. It
9723 : * returns a list of cooked constraints.
9724 : *
9725 : * If the constraint ends up getting merged with a pre-existing one, it's
9726 : * omitted from the returned list, which is what we want: we do not need
9727 : * to do any validation work. That can only happen at child tables,
9728 : * though, since we disallow merging at the top level.
9729 : */
9730 10378 : newcons = AddRelationNewConstraints(rel, NIL,
9731 10378 : list_make1(copyObject(constr)),
9732 10378 : recursing || is_readd, /* allow_merge */
9733 10378 : !recursing, /* is_local */
9734 : is_readd, /* is_internal */
9735 10378 : NULL); /* queryString not available
9736 : * here */
9737 :
9738 : /* we don't expect more than one constraint here */
9739 : Assert(list_length(newcons) <= 1);
9740 :
9741 : /* Add each to-be-validated constraint to Phase 3's queue */
9742 19862 : foreach(lcon, newcons)
9743 : {
9744 9610 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9745 :
9746 9610 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9747 : {
9748 : NewConstraint *newcon;
9749 :
9750 880 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9751 880 : newcon->name = ccon->name;
9752 880 : newcon->contype = ccon->contype;
9753 880 : newcon->qual = ccon->expr;
9754 :
9755 880 : tab->constraints = lappend(tab->constraints, newcon);
9756 : }
9757 :
9758 : /* Save the actually assigned name if it was defaulted */
9759 9610 : if (constr->conname == NULL)
9760 8158 : constr->conname = ccon->name;
9761 :
9762 : /*
9763 : * If adding a not-null constraint, set the pg_attribute flag and tell
9764 : * phase 3 to verify existing rows, if needed.
9765 : */
9766 9610 : if (constr->contype == CONSTR_NOTNULL)
9767 8240 : set_attnotnull(wqueue, rel, ccon->attnum, lockmode);
9768 :
9769 9610 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9770 : }
9771 :
9772 : /* At this point we must have a locked-down name to use */
9773 : Assert(newcons == NIL || constr->conname != NULL);
9774 :
9775 : /* Advance command counter in case same table is visited multiple times */
9776 10252 : CommandCounterIncrement();
9777 :
9778 : /*
9779 : * If the constraint got merged with an existing constraint, we're done.
9780 : * We mustn't recurse to child tables in this case, because they've
9781 : * already got the constraint, and visiting them again would lead to an
9782 : * incorrect value for coninhcount.
9783 : */
9784 10252 : if (newcons == NIL)
9785 642 : return address;
9786 :
9787 : /*
9788 : * If adding a NO INHERIT constraint, no need to find our children.
9789 : */
9790 9610 : if (constr->is_no_inherit)
9791 72 : return address;
9792 :
9793 : /*
9794 : * Propagate to children as appropriate. Unlike most other ALTER
9795 : * routines, we have to do this one level of recursion at a time; we can't
9796 : * use find_all_inheritors to do it in one pass.
9797 : */
9798 : children =
9799 9538 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9800 :
9801 : /*
9802 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9803 : * constraint creation only if there are no children currently. Error out
9804 : * otherwise.
9805 : */
9806 9538 : if (!recurse && children != NIL)
9807 6 : ereport(ERROR,
9808 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9809 : errmsg("constraint must be added to child tables too")));
9810 :
9811 : /*
9812 : * Recurse to create the constraint on each child.
9813 : */
9814 10180 : foreach(child, children)
9815 : {
9816 678 : Oid childrelid = lfirst_oid(child);
9817 : Relation childrel;
9818 : AlteredTableInfo *childtab;
9819 :
9820 : /* find_inheritance_children already got lock */
9821 678 : childrel = table_open(childrelid, NoLock);
9822 678 : CheckAlterTableIsSafe(childrel);
9823 :
9824 : /* Find or create work queue entry for this table */
9825 678 : childtab = ATGetQueueEntry(wqueue, childrel);
9826 :
9827 : /* Recurse to this child */
9828 678 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
9829 : constr, recurse, true, is_readd, lockmode);
9830 :
9831 648 : table_close(childrel, NoLock);
9832 : }
9833 :
9834 9502 : return address;
9835 : }
9836 :
9837 : /*
9838 : * Add a foreign-key constraint to a single table; return the new constraint's
9839 : * address.
9840 : *
9841 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9842 : * lock on the rel, and have done appropriate validity checks for it.
9843 : * We do permissions checks here, however.
9844 : *
9845 : * When the referenced or referencing tables (or both) are partitioned,
9846 : * multiple pg_constraint rows are required -- one for each partitioned table
9847 : * and each partition on each side (fortunately, not one for every combination
9848 : * thereof). We also need action triggers on each leaf partition on the
9849 : * referenced side, and check triggers on each leaf partition on the
9850 : * referencing side.
9851 : */
9852 : static ObjectAddress
9853 2582 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9854 : Constraint *fkconstraint,
9855 : bool recurse, bool recursing, LOCKMODE lockmode)
9856 : {
9857 : Relation pkrel;
9858 2582 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9859 2582 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9860 2582 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9861 2582 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9862 2582 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
9863 2582 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
9864 2582 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9865 2582 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9866 2582 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9867 2582 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9868 2582 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9869 : bool with_period;
9870 : bool pk_has_without_overlaps;
9871 : int i;
9872 : int numfks,
9873 : numpks,
9874 : numfkdelsetcols;
9875 : Oid indexOid;
9876 : bool old_check_ok;
9877 : ObjectAddress address;
9878 2582 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9879 :
9880 : /*
9881 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9882 : * delete rows out from under us.
9883 : */
9884 2582 : if (OidIsValid(fkconstraint->old_pktable_oid))
9885 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9886 : else
9887 2510 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9888 :
9889 : /*
9890 : * Validity checks (permission checks wait till we have the column
9891 : * numbers)
9892 : */
9893 2576 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9894 6 : ereport(ERROR,
9895 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
9896 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9897 : RelationGetRelationName(rel),
9898 : RelationGetRelationName(pkrel)));
9899 :
9900 2570 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9901 332 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9902 0 : ereport(ERROR,
9903 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9904 : errmsg("referenced relation \"%s\" is not a table",
9905 : RelationGetRelationName(pkrel))));
9906 :
9907 2570 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
9908 2 : ereport(ERROR,
9909 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9910 : errmsg("permission denied: \"%s\" is a system catalog",
9911 : RelationGetRelationName(pkrel))));
9912 :
9913 : /*
9914 : * References from permanent or unlogged tables to temp tables, and from
9915 : * permanent tables to unlogged tables, are disallowed because the
9916 : * referenced data can vanish out from under us. References from temp
9917 : * tables to any other table type are also disallowed, because other
9918 : * backends might need to run the RI triggers on the perm table, but they
9919 : * can't reliably see tuples in the local buffers of other backends.
9920 : */
9921 2568 : switch (rel->rd_rel->relpersistence)
9922 : {
9923 2278 : case RELPERSISTENCE_PERMANENT:
9924 2278 : if (!RelationIsPermanent(pkrel))
9925 0 : ereport(ERROR,
9926 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9927 : errmsg("constraints on permanent tables may reference only permanent tables")));
9928 2278 : break;
9929 12 : case RELPERSISTENCE_UNLOGGED:
9930 12 : if (!RelationIsPermanent(pkrel)
9931 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9932 0 : ereport(ERROR,
9933 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9934 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9935 12 : break;
9936 278 : case RELPERSISTENCE_TEMP:
9937 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9938 0 : ereport(ERROR,
9939 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9940 : errmsg("constraints on temporary tables may reference only temporary tables")));
9941 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9942 0 : ereport(ERROR,
9943 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9944 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
9945 278 : break;
9946 : }
9947 :
9948 : /*
9949 : * Look up the referencing attributes to make sure they exist, and record
9950 : * their attnums and type and collation OIDs.
9951 : */
9952 2568 : numfks = transformColumnNameList(RelationGetRelid(rel),
9953 : fkconstraint->fk_attrs,
9954 : fkattnum, fktypoid, fkcolloid);
9955 2538 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9956 2538 : if (with_period && !fkconstraint->fk_with_period)
9957 24 : ereport(ERROR,
9958 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9959 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9960 :
9961 2514 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9962 : fkconstraint->fk_del_set_cols,
9963 : fkdelsetcols, NULL, NULL);
9964 2508 : validateFkOnDeleteSetColumns(numfks, fkattnum,
9965 : numfkdelsetcols, fkdelsetcols,
9966 : fkconstraint->fk_del_set_cols);
9967 :
9968 : /*
9969 : * If the attribute list for the referenced table was omitted, lookup the
9970 : * definition of the primary key and use it. Otherwise, validate the
9971 : * supplied attribute list. In either case, discover the index OID and
9972 : * index opclasses, and the attnums and type and collation OIDs of the
9973 : * attributes.
9974 : */
9975 2502 : if (fkconstraint->pk_attrs == NIL)
9976 : {
9977 1166 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9978 : &fkconstraint->pk_attrs,
9979 : pkattnum, pktypoid, pkcolloid,
9980 : opclasses, &pk_has_without_overlaps);
9981 :
9982 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9983 1166 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
9984 24 : ereport(ERROR,
9985 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9986 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9987 : }
9988 : else
9989 : {
9990 1336 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
9991 : fkconstraint->pk_attrs,
9992 : pkattnum, pktypoid, pkcolloid);
9993 :
9994 : /* Since we got pk_attrs, one should be a period. */
9995 1306 : if (with_period && !fkconstraint->pk_with_period)
9996 24 : ereport(ERROR,
9997 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
9998 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9999 :
10000 : /* Look for an index matching the column list */
10001 1282 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10002 : with_period, opclasses, &pk_has_without_overlaps);
10003 : }
10004 :
10005 : /*
10006 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10007 : * must use PERIOD.
10008 : */
10009 2388 : if (pk_has_without_overlaps && !with_period)
10010 12 : ereport(ERROR,
10011 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10012 : errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10013 :
10014 : /*
10015 : * Now we can check permissions.
10016 : */
10017 2376 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10018 :
10019 : /*
10020 : * Check some things for generated columns.
10021 : */
10022 5550 : for (i = 0; i < numfks; i++)
10023 : {
10024 3204 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10025 :
10026 3204 : if (attgenerated)
10027 : {
10028 : /*
10029 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10030 : */
10031 48 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10032 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10033 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10034 12 : ereport(ERROR,
10035 : (errcode(ERRCODE_SYNTAX_ERROR),
10036 : errmsg("invalid %s action for foreign key constraint containing generated column",
10037 : "ON UPDATE")));
10038 36 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10039 24 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10040 12 : ereport(ERROR,
10041 : (errcode(ERRCODE_SYNTAX_ERROR),
10042 : errmsg("invalid %s action for foreign key constraint containing generated column",
10043 : "ON DELETE")));
10044 : }
10045 :
10046 : /*
10047 : * FKs on virtual columns are not supported. This would require
10048 : * various additional support in ri_triggers.c, including special
10049 : * handling in ri_NullCheck(), ri_KeysEqual(),
10050 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10051 : * as NULL there). Also not really practical as long as you can't
10052 : * index virtual columns.
10053 : */
10054 3180 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10055 6 : ereport(ERROR,
10056 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10057 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10058 : }
10059 :
10060 : /*
10061 : * Some actions are currently unsupported for foreign keys using PERIOD.
10062 : */
10063 2346 : if (fkconstraint->fk_with_period)
10064 : {
10065 242 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10066 230 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10067 212 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10068 194 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10069 66 : ereport(ERROR,
10070 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10071 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10072 : "ON UPDATE"));
10073 :
10074 176 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10075 170 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10076 170 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10077 170 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10078 6 : ereport(ERROR,
10079 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10080 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10081 : "ON DELETE"));
10082 : }
10083 :
10084 : /*
10085 : * Look up the equality operators to use in the constraint.
10086 : *
10087 : * Note that we have to be careful about the difference between the actual
10088 : * PK column type and the opclass' declared input type, which might be
10089 : * only binary-compatible with it. The declared opcintype is the right
10090 : * thing to probe pg_amop with.
10091 : */
10092 2274 : if (numfks != numpks)
10093 0 : ereport(ERROR,
10094 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10095 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10096 :
10097 : /*
10098 : * On the strength of a previous constraint, we might avoid scanning
10099 : * tables to validate this one. See below.
10100 : */
10101 2274 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10102 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10103 :
10104 4920 : for (i = 0; i < numpks; i++)
10105 : {
10106 2886 : Oid pktype = pktypoid[i];
10107 2886 : Oid fktype = fktypoid[i];
10108 : Oid fktyped;
10109 2886 : Oid pkcoll = pkcolloid[i];
10110 2886 : Oid fkcoll = fkcolloid[i];
10111 : HeapTuple cla_ht;
10112 : Form_pg_opclass cla_tup;
10113 : Oid amid;
10114 : Oid opfamily;
10115 : Oid opcintype;
10116 : bool for_overlaps;
10117 : CompareType cmptype;
10118 : Oid pfeqop;
10119 : Oid ppeqop;
10120 : Oid ffeqop;
10121 : int16 eqstrategy;
10122 : Oid pfeqop_right;
10123 :
10124 : /* We need several fields out of the pg_opclass entry */
10125 2886 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10126 2886 : if (!HeapTupleIsValid(cla_ht))
10127 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10128 2886 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10129 2886 : amid = cla_tup->opcmethod;
10130 2886 : opfamily = cla_tup->opcfamily;
10131 2886 : opcintype = cla_tup->opcintype;
10132 2886 : ReleaseSysCache(cla_ht);
10133 :
10134 : /*
10135 : * Get strategy number from index AM.
10136 : *
10137 : * For a normal foreign-key constraint, this should not fail, since we
10138 : * already checked that the index is unique and should therefore have
10139 : * appropriate equal operators. For a period foreign key, this could
10140 : * fail if we selected a non-matching exclusion constraint earlier.
10141 : * (XXX Maybe we should do these lookups earlier so we don't end up
10142 : * doing that.)
10143 : */
10144 2886 : for_overlaps = with_period && i == numpks - 1;
10145 2886 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10146 2886 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10147 2886 : if (eqstrategy == InvalidStrategy)
10148 0 : ereport(ERROR,
10149 : errcode(ERRCODE_UNDEFINED_OBJECT),
10150 : for_overlaps
10151 : ? errmsg("could not identify an overlaps operator for foreign key")
10152 : : errmsg("could not identify an equality operator for foreign key"),
10153 : errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
10154 : cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
10155 :
10156 : /*
10157 : * There had better be a primary equality operator for the index.
10158 : * We'll use it for PK = PK comparisons.
10159 : */
10160 2886 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10161 : eqstrategy);
10162 :
10163 2886 : if (!OidIsValid(ppeqop))
10164 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10165 : eqstrategy, opcintype, opcintype, opfamily);
10166 :
10167 : /*
10168 : * Are there equality operators that take exactly the FK type? Assume
10169 : * we should look through any domain here.
10170 : */
10171 2886 : fktyped = getBaseType(fktype);
10172 :
10173 2886 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10174 : eqstrategy);
10175 2886 : if (OidIsValid(pfeqop))
10176 : {
10177 2262 : pfeqop_right = fktyped;
10178 2262 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10179 : eqstrategy);
10180 : }
10181 : else
10182 : {
10183 : /* keep compiler quiet */
10184 624 : pfeqop_right = InvalidOid;
10185 624 : ffeqop = InvalidOid;
10186 : }
10187 :
10188 2886 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10189 : {
10190 : /*
10191 : * Otherwise, look for an implicit cast from the FK type to the
10192 : * opcintype, and if found, use the primary equality operator.
10193 : * This is a bit tricky because opcintype might be a polymorphic
10194 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10195 : * whether the two actual column types can be concurrently cast to
10196 : * that type. (Otherwise, we'd fail to reject combinations such
10197 : * as int[] and point[].)
10198 : */
10199 : Oid input_typeids[2];
10200 : Oid target_typeids[2];
10201 :
10202 624 : input_typeids[0] = pktype;
10203 624 : input_typeids[1] = fktype;
10204 624 : target_typeids[0] = opcintype;
10205 624 : target_typeids[1] = opcintype;
10206 624 : if (can_coerce_type(2, input_typeids, target_typeids,
10207 : COERCION_IMPLICIT))
10208 : {
10209 396 : pfeqop = ffeqop = ppeqop;
10210 396 : pfeqop_right = opcintype;
10211 : }
10212 : }
10213 :
10214 2886 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10215 228 : ereport(ERROR,
10216 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10217 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10218 : fkconstraint->conname),
10219 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10220 : "are of incompatible types: %s and %s.",
10221 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10222 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10223 : format_type_be(fktype),
10224 : format_type_be(pktype))));
10225 :
10226 : /*
10227 : * This shouldn't be possible, but better check to make sure we have a
10228 : * consistent state for the check below.
10229 : */
10230 2658 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10231 0 : elog(ERROR, "key columns are not both collatable");
10232 :
10233 2658 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10234 : {
10235 : bool pkcolldet;
10236 : bool fkcolldet;
10237 :
10238 104 : pkcolldet = get_collation_isdeterministic(pkcoll);
10239 104 : fkcolldet = get_collation_isdeterministic(fkcoll);
10240 :
10241 : /*
10242 : * SQL requires that both collations are the same. This is
10243 : * because we need a consistent notion of equality on both
10244 : * columns. We relax this by allowing different collations if
10245 : * they are both deterministic. (This is also for backward
10246 : * compatibility, because PostgreSQL has always allowed this.)
10247 : */
10248 104 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10249 12 : ereport(ERROR,
10250 : (errcode(ERRCODE_COLLATION_MISMATCH),
10251 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10252 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10253 : "have incompatible collations: \"%s\" and \"%s\". "
10254 : "If either collation is nondeterministic, then both collations have to be the same.",
10255 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10256 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10257 : get_collation_name(fkcoll),
10258 : get_collation_name(pkcoll))));
10259 : }
10260 :
10261 2646 : if (old_check_ok)
10262 : {
10263 : /*
10264 : * When a pfeqop changes, revalidate the constraint. We could
10265 : * permit intra-opfamily changes, but that adds subtle complexity
10266 : * without any concrete benefit for core types. We need not
10267 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10268 : */
10269 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10270 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10271 : old_pfeqop_item);
10272 : }
10273 2646 : if (old_check_ok)
10274 : {
10275 : Oid old_fktype;
10276 : Oid new_fktype;
10277 : CoercionPathType old_pathtype;
10278 : CoercionPathType new_pathtype;
10279 : Oid old_castfunc;
10280 : Oid new_castfunc;
10281 : Oid old_fkcoll;
10282 : Oid new_fkcoll;
10283 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10284 6 : fkattnum[i] - 1);
10285 :
10286 : /*
10287 : * Identify coercion pathways from each of the old and new FK-side
10288 : * column types to the right (foreign) operand type of the pfeqop.
10289 : * We may assume that pg_constraint.conkey is not changing.
10290 : */
10291 6 : old_fktype = attr->atttypid;
10292 6 : new_fktype = fktype;
10293 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10294 : &old_castfunc);
10295 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10296 : &new_castfunc);
10297 :
10298 6 : old_fkcoll = attr->attcollation;
10299 6 : new_fkcoll = fkcoll;
10300 :
10301 : /*
10302 : * Upon a change to the cast from the FK column to its pfeqop
10303 : * operand, revalidate the constraint. For this evaluation, a
10304 : * binary coercion cast is equivalent to no cast at all. While
10305 : * type implementors should design implicit casts with an eye
10306 : * toward consistency of operations like equality, we cannot
10307 : * assume here that they have done so.
10308 : *
10309 : * A function with a polymorphic argument could change behavior
10310 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10311 : * when the cast destination is polymorphic, we only avoid
10312 : * revalidation if the input type has not changed at all. Given
10313 : * just the core data types and operator classes, this requirement
10314 : * prevents no would-be optimizations.
10315 : *
10316 : * If the cast converts from a base type to a domain thereon, then
10317 : * that domain type must be the opcintype of the unique index.
10318 : * Necessarily, the primary key column must then be of the domain
10319 : * type. Since the constraint was previously valid, all values on
10320 : * the foreign side necessarily exist on the primary side and in
10321 : * turn conform to the domain. Consequently, we need not treat
10322 : * domains specially here.
10323 : *
10324 : * If the collation changes, revalidation is required, unless both
10325 : * collations are deterministic, because those share the same
10326 : * notion of equality (because texteq reduces to bitwise
10327 : * equality).
10328 : *
10329 : * We need not directly consider the PK type. It's necessarily
10330 : * binary coercible to the opcintype of the unique index column,
10331 : * and ri_triggers.c will only deal with PK datums in terms of
10332 : * that opcintype. Changing the opcintype also changes pfeqop.
10333 : */
10334 6 : old_check_ok = (new_pathtype == old_pathtype &&
10335 6 : new_castfunc == old_castfunc &&
10336 6 : (!IsPolymorphicType(pfeqop_right) ||
10337 12 : new_fktype == old_fktype) &&
10338 0 : (new_fkcoll == old_fkcoll ||
10339 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10340 : }
10341 :
10342 2646 : pfeqoperators[i] = pfeqop;
10343 2646 : ppeqoperators[i] = ppeqop;
10344 2646 : ffeqoperators[i] = ffeqop;
10345 : }
10346 :
10347 : /*
10348 : * For FKs with PERIOD we need additional operators to check whether the
10349 : * referencing row's range is contained by the aggregated ranges of the
10350 : * referenced row(s). For rangetypes and multirangetypes this is
10351 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10352 : * support for now. FKs will look these up at "runtime", but we should
10353 : * make sure the lookup works here, even if we don't use the values.
10354 : */
10355 2034 : if (with_period)
10356 : {
10357 : Oid periodoperoid;
10358 : Oid aggedperiodoperoid;
10359 : Oid intersectoperoid;
10360 :
10361 152 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10362 : &intersectoperoid);
10363 : }
10364 :
10365 : /* First, create the constraint catalog entry itself. */
10366 2034 : address = addFkConstraint(addFkBothSides,
10367 : fkconstraint->conname, fkconstraint, rel, pkrel,
10368 : indexOid,
10369 : InvalidOid, /* no parent constraint */
10370 : numfks,
10371 : pkattnum,
10372 : fkattnum,
10373 : pfeqoperators,
10374 : ppeqoperators,
10375 : ffeqoperators,
10376 : numfkdelsetcols,
10377 : fkdelsetcols,
10378 : false,
10379 : with_period);
10380 :
10381 : /* Next process the action triggers at the referenced side and recurse */
10382 2034 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10383 : indexOid,
10384 : address.objectId,
10385 : numfks,
10386 : pkattnum,
10387 : fkattnum,
10388 : pfeqoperators,
10389 : ppeqoperators,
10390 : ffeqoperators,
10391 : numfkdelsetcols,
10392 : fkdelsetcols,
10393 : old_check_ok,
10394 : InvalidOid, InvalidOid,
10395 : with_period);
10396 :
10397 : /* Lastly create the check triggers at the referencing side and recurse */
10398 2034 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10399 : indexOid,
10400 : address.objectId,
10401 : numfks,
10402 : pkattnum,
10403 : fkattnum,
10404 : pfeqoperators,
10405 : ppeqoperators,
10406 : ffeqoperators,
10407 : numfkdelsetcols,
10408 : fkdelsetcols,
10409 : old_check_ok,
10410 : lockmode,
10411 : InvalidOid, InvalidOid,
10412 : with_period);
10413 :
10414 : /*
10415 : * Done. Close pk table, but keep lock until we've committed.
10416 : */
10417 2034 : table_close(pkrel, NoLock);
10418 :
10419 2034 : return address;
10420 : }
10421 :
10422 : /*
10423 : * validateFkOnDeleteSetColumns
10424 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10425 : * column lists are valid.
10426 : */
10427 : void
10428 2508 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10429 : int numfksetcols, const int16 *fksetcolsattnums,
10430 : List *fksetcols)
10431 : {
10432 2532 : for (int i = 0; i < numfksetcols; i++)
10433 : {
10434 30 : int16 setcol_attnum = fksetcolsattnums[i];
10435 30 : bool seen = false;
10436 :
10437 54 : for (int j = 0; j < numfks; j++)
10438 : {
10439 48 : if (fkattnums[j] == setcol_attnum)
10440 : {
10441 24 : seen = true;
10442 24 : break;
10443 : }
10444 : }
10445 :
10446 30 : if (!seen)
10447 : {
10448 6 : char *col = strVal(list_nth(fksetcols, i));
10449 :
10450 6 : ereport(ERROR,
10451 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10452 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10453 : }
10454 : }
10455 2502 : }
10456 :
10457 : /*
10458 : * addFkConstraint
10459 : * Install pg_constraint entries to implement a foreign key constraint.
10460 : * Caller must separately invoke addFkRecurseReferenced and
10461 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10462 : * and (for partitioned tables) recurse to partitions.
10463 : *
10464 : * fkside: the side of the FK (or both) to create. Caller should
10465 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10466 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10467 : * addFkBothSides.
10468 : * constraintname: the base name for the constraint being added,
10469 : * copied to fkconstraint->conname if the latter is not set
10470 : * fkconstraint: the constraint being added
10471 : * rel: the root referencing relation
10472 : * pkrel: the referenced relation; might be a partition, if recursing
10473 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10474 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10475 : * top-level constraint
10476 : * numfks: the number of columns in the foreign key
10477 : * pkattnum: the attnum array of referenced attributes
10478 : * fkattnum: the attnum array of referencing attributes
10479 : * pf/pp/ffeqoperators: OID array of operators between columns
10480 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10481 : * (...) clause
10482 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10483 : * NULL/DEFAULT clause
10484 : * with_period: true if this is a temporal FK
10485 : */
10486 : static ObjectAddress
10487 3788 : addFkConstraint(addFkConstraintSides fkside,
10488 : char *constraintname, Constraint *fkconstraint,
10489 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10490 : int numfks, int16 *pkattnum,
10491 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10492 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10493 : bool is_internal, bool with_period)
10494 : {
10495 : ObjectAddress address;
10496 : Oid constrOid;
10497 : char *conname;
10498 : bool conislocal;
10499 : int16 coninhcount;
10500 : bool connoinherit;
10501 :
10502 : /*
10503 : * Verify relkind for each referenced partition. At the top level, this
10504 : * is redundant with a previous check, but we need it when recursing.
10505 : */
10506 3788 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10507 790 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10508 0 : ereport(ERROR,
10509 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10510 : errmsg("referenced relation \"%s\" is not a table",
10511 : RelationGetRelationName(pkrel))));
10512 :
10513 : /*
10514 : * Caller supplies us with a constraint name; however, it may be used in
10515 : * this partition, so come up with a different one in that case.
10516 : */
10517 3788 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10518 : RelationGetRelid(rel),
10519 : constraintname))
10520 984 : conname = ChooseConstraintName(RelationGetRelationName(rel),
10521 984 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10522 : "fkey",
10523 984 : RelationGetNamespace(rel), NIL);
10524 : else
10525 2804 : conname = constraintname;
10526 :
10527 3788 : if (fkconstraint->conname == NULL)
10528 418 : fkconstraint->conname = pstrdup(conname);
10529 :
10530 3788 : if (OidIsValid(parentConstr))
10531 : {
10532 1754 : conislocal = false;
10533 1754 : coninhcount = 1;
10534 1754 : connoinherit = false;
10535 : }
10536 : else
10537 : {
10538 2034 : conislocal = true;
10539 2034 : coninhcount = 0;
10540 :
10541 : /*
10542 : * always inherit for partitioned tables, never for legacy inheritance
10543 : */
10544 2034 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10545 : }
10546 :
10547 : /*
10548 : * Record the FK constraint in pg_constraint.
10549 : */
10550 3788 : constrOid = CreateConstraintEntry(conname,
10551 3788 : RelationGetNamespace(rel),
10552 : CONSTRAINT_FOREIGN,
10553 3788 : fkconstraint->deferrable,
10554 3788 : fkconstraint->initdeferred,
10555 : true, /* Is Enforced */
10556 3788 : fkconstraint->initially_valid,
10557 : parentConstr,
10558 : RelationGetRelid(rel),
10559 : fkattnum,
10560 : numfks,
10561 : numfks,
10562 : InvalidOid, /* not a domain constraint */
10563 : indexOid,
10564 : RelationGetRelid(pkrel),
10565 : pkattnum,
10566 : pfeqoperators,
10567 : ppeqoperators,
10568 : ffeqoperators,
10569 : numfks,
10570 3788 : fkconstraint->fk_upd_action,
10571 3788 : fkconstraint->fk_del_action,
10572 : fkdelsetcols,
10573 : numfkdelsetcols,
10574 3788 : fkconstraint->fk_matchtype,
10575 : NULL, /* no exclusion constraint */
10576 : NULL, /* no check constraint */
10577 : NULL,
10578 : conislocal, /* islocal */
10579 : coninhcount, /* inhcount */
10580 : connoinherit, /* conNoInherit */
10581 : with_period, /* conPeriod */
10582 : is_internal); /* is_internal */
10583 :
10584 3788 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10585 :
10586 : /*
10587 : * In partitioning cases, create the dependency entries for this
10588 : * constraint. (For non-partitioned cases, relevant entries were created
10589 : * by CreateConstraintEntry.)
10590 : *
10591 : * On the referenced side, we need the constraint to have an internal
10592 : * dependency on its parent constraint; this means that this constraint
10593 : * cannot be dropped on its own -- only through the parent constraint. It
10594 : * also means the containing partition cannot be dropped on its own, but
10595 : * it can be detached, at which point this dependency is removed (after
10596 : * verifying that no rows are referenced via this FK.)
10597 : *
10598 : * When processing the referencing side, we link the constraint via the
10599 : * special partitioning dependencies: the parent constraint is the primary
10600 : * dependent, and the partition on which the foreign key exists is the
10601 : * secondary dependency. That way, this constraint is dropped if either
10602 : * of these objects is.
10603 : *
10604 : * Note that this is only necessary for the subsidiary pg_constraint rows
10605 : * in partitions; the topmost row doesn't need any of this.
10606 : */
10607 3788 : if (OidIsValid(parentConstr))
10608 : {
10609 : ObjectAddress referenced;
10610 :
10611 1754 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10612 :
10613 : Assert(fkside != addFkBothSides);
10614 1754 : if (fkside == addFkReferencedSide)
10615 978 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10616 : else
10617 : {
10618 776 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10619 776 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10620 776 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10621 : }
10622 : }
10623 :
10624 : /* make new constraint visible, in case we add more */
10625 3788 : CommandCounterIncrement();
10626 :
10627 3788 : return address;
10628 : }
10629 :
10630 : /*
10631 : * addFkRecurseReferenced
10632 : * Recursive helper for the referenced side of foreign key creation,
10633 : * which creates the action triggers and recurses
10634 : *
10635 : * If the referenced relation is a plain relation, create the necessary action
10636 : * triggers that implement the constraint. If the referenced relation is a
10637 : * partitioned table, then we create a pg_constraint row referencing the parent
10638 : * of the referencing side for it and recurse on this routine for each
10639 : * partition.
10640 : *
10641 : * fkconstraint: the constraint being added
10642 : * rel: the root referencing relation
10643 : * pkrel: the referenced relation; might be a partition, if recursing
10644 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10645 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10646 : * top-level constraint
10647 : * numfks: the number of columns in the foreign key
10648 : * pkattnum: the attnum array of referenced attributes
10649 : * fkattnum: the attnum array of referencing attributes
10650 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10651 : * NULL/DEFAULT (...) clause
10652 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10653 : * NULL/DEFAULT clause
10654 : * pf/pp/ffeqoperators: OID array of operators between columns
10655 : * old_check_ok: true if this constraint replaces an existing one that
10656 : * was already validated (thus this one doesn't need validation)
10657 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10658 : * partition, the OIDs of the parent action triggers for DELETE and
10659 : * UPDATE respectively.
10660 : * with_period: true if this is a temporal FK
10661 : */
10662 : static void
10663 3102 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10664 : Relation pkrel, Oid indexOid, Oid parentConstr,
10665 : int numfks,
10666 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10667 : Oid *ppeqoperators, Oid *ffeqoperators,
10668 : int numfkdelsetcols, int16 *fkdelsetcols,
10669 : bool old_check_ok,
10670 : Oid parentDelTrigger, Oid parentUpdTrigger,
10671 : bool with_period)
10672 : {
10673 : Oid deleteTriggerOid,
10674 : updateTriggerOid;
10675 :
10676 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10677 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10678 :
10679 : /*
10680 : * Create the action triggers that enforce the constraint.
10681 : */
10682 3102 : createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10683 : fkconstraint,
10684 : parentConstr, indexOid,
10685 : parentDelTrigger, parentUpdTrigger,
10686 : &deleteTriggerOid, &updateTriggerOid);
10687 :
10688 : /*
10689 : * If the referenced table is partitioned, recurse on ourselves to handle
10690 : * each partition. We need one pg_constraint row created for each
10691 : * partition in addition to the pg_constraint row for the parent table.
10692 : */
10693 3102 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10694 : {
10695 492 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10696 :
10697 1344 : for (int i = 0; i < pd->nparts; i++)
10698 : {
10699 : Relation partRel;
10700 : AttrMap *map;
10701 : AttrNumber *mapped_pkattnum;
10702 : Oid partIndexId;
10703 : ObjectAddress address;
10704 :
10705 : /* XXX would it be better to acquire these locks beforehand? */
10706 852 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10707 :
10708 : /*
10709 : * Map the attribute numbers in the referenced side of the FK
10710 : * definition to match the partition's column layout.
10711 : */
10712 852 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10713 : RelationGetDescr(pkrel),
10714 : false);
10715 852 : if (map)
10716 : {
10717 130 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10718 272 : for (int j = 0; j < numfks; j++)
10719 142 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10720 : }
10721 : else
10722 722 : mapped_pkattnum = pkattnum;
10723 :
10724 : /* Determine the index to use at this level */
10725 852 : partIndexId = index_get_partition(partRel, indexOid);
10726 852 : if (!OidIsValid(partIndexId))
10727 0 : elog(ERROR, "index for %u not found in partition %s",
10728 : indexOid, RelationGetRelationName(partRel));
10729 :
10730 : /* Create entry at this level ... */
10731 852 : address = addFkConstraint(addFkReferencedSide,
10732 : fkconstraint->conname, fkconstraint, rel,
10733 : partRel, partIndexId, parentConstr,
10734 : numfks, mapped_pkattnum,
10735 : fkattnum, pfeqoperators, ppeqoperators,
10736 : ffeqoperators, numfkdelsetcols,
10737 : fkdelsetcols, true, with_period);
10738 : /* ... and recurse to our children */
10739 852 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10740 : partIndexId, address.objectId, numfks,
10741 : mapped_pkattnum, fkattnum,
10742 : pfeqoperators, ppeqoperators, ffeqoperators,
10743 : numfkdelsetcols, fkdelsetcols,
10744 : old_check_ok,
10745 : deleteTriggerOid, updateTriggerOid,
10746 : with_period);
10747 :
10748 : /* Done -- clean up (but keep the lock) */
10749 852 : table_close(partRel, NoLock);
10750 852 : if (map)
10751 : {
10752 130 : pfree(mapped_pkattnum);
10753 130 : free_attrmap(map);
10754 : }
10755 : }
10756 : }
10757 3102 : }
10758 :
10759 : /*
10760 : * addFkRecurseReferencing
10761 : * Recursive helper for the referencing side of foreign key creation,
10762 : * which creates the check triggers and recurses
10763 : *
10764 : * If the referencing relation is a plain relation, create the necessary check
10765 : * triggers that implement the constraint, and set up for Phase 3 constraint
10766 : * verification. If the referencing relation is a partitioned table, then
10767 : * we create a pg_constraint row for it and recurse on this routine for each
10768 : * partition.
10769 : *
10770 : * We assume that the referenced relation is locked against concurrent
10771 : * deletions. If it's a partitioned relation, every partition must be so
10772 : * locked.
10773 : *
10774 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
10775 : * of an ALTER TABLE sequence.
10776 : * fkconstraint: the constraint being added
10777 : * rel: the referencing relation; might be a partition, if recursing
10778 : * pkrel: the root referenced relation
10779 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10780 : * parentConstr: the OID of the parent constraint (there is always one)
10781 : * numfks: the number of columns in the foreign key
10782 : * pkattnum: the attnum array of referenced attributes
10783 : * fkattnum: the attnum array of referencing attributes
10784 : * pf/pp/ffeqoperators: OID array of operators between columns
10785 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10786 : * (...) clause
10787 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10788 : * NULL/DEFAULT clause
10789 : * old_check_ok: true if this constraint replaces an existing one that
10790 : * was already validated (thus this one doesn't need validation)
10791 : * lockmode: the lockmode to acquire on partitions when recursing
10792 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
10793 : * a partition, the OIDs of the parent check triggers for INSERT and
10794 : * UPDATE respectively.
10795 : * with_period: true if this is a temporal FK
10796 : */
10797 : static void
10798 2810 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10799 : Relation pkrel, Oid indexOid, Oid parentConstr,
10800 : int numfks, int16 *pkattnum, int16 *fkattnum,
10801 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10802 : int numfkdelsetcols, int16 *fkdelsetcols,
10803 : bool old_check_ok, LOCKMODE lockmode,
10804 : Oid parentInsTrigger, Oid parentUpdTrigger,
10805 : bool with_period)
10806 : {
10807 : Oid insertTriggerOid,
10808 : updateTriggerOid;
10809 :
10810 : Assert(OidIsValid(parentConstr));
10811 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10812 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10813 :
10814 2810 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10815 0 : ereport(ERROR,
10816 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10817 : errmsg("foreign key constraints are not supported on foreign tables")));
10818 :
10819 : /*
10820 : * Add the check triggers to it and, if necessary, schedule it to be
10821 : * checked in Phase 3.
10822 : *
10823 : * If the relation is partitioned, drill down to do it to its partitions.
10824 : */
10825 2810 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10826 : RelationGetRelid(pkrel),
10827 : fkconstraint,
10828 : parentConstr,
10829 : indexOid,
10830 : parentInsTrigger, parentUpdTrigger,
10831 : &insertTriggerOid, &updateTriggerOid);
10832 :
10833 2810 : if (rel->rd_rel->relkind == RELKIND_RELATION)
10834 : {
10835 : /*
10836 : * Tell Phase 3 to check that the constraint is satisfied by existing
10837 : * rows. We can skip this during table creation, when requested
10838 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10839 : * and when we're recreating a constraint following a SET DATA TYPE
10840 : * operation that did not impugn its validity.
10841 : */
10842 2344 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10843 : {
10844 : NewConstraint *newcon;
10845 : AlteredTableInfo *tab;
10846 :
10847 784 : tab = ATGetQueueEntry(wqueue, rel);
10848 :
10849 784 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10850 784 : newcon->name = get_constraint_name(parentConstr);
10851 784 : newcon->contype = CONSTR_FOREIGN;
10852 784 : newcon->refrelid = RelationGetRelid(pkrel);
10853 784 : newcon->refindid = indexOid;
10854 784 : newcon->conid = parentConstr;
10855 784 : newcon->conwithperiod = fkconstraint->fk_with_period;
10856 784 : newcon->qual = (Node *) fkconstraint;
10857 :
10858 784 : tab->constraints = lappend(tab->constraints, newcon);
10859 : }
10860 : }
10861 466 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10862 : {
10863 466 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10864 : Relation trigrel;
10865 :
10866 : /*
10867 : * Triggers of the foreign keys will be manipulated a bunch of times
10868 : * in the loop below. To avoid repeatedly opening/closing the trigger
10869 : * catalog relation, we open it here and pass it to the subroutines
10870 : * called below.
10871 : */
10872 466 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10873 :
10874 : /*
10875 : * Recurse to take appropriate action on each partition; either we
10876 : * find an existing constraint to reparent to ours, or we create a new
10877 : * one.
10878 : */
10879 836 : for (int i = 0; i < pd->nparts; i++)
10880 : {
10881 376 : Relation partition = table_open(pd->oids[i], lockmode);
10882 : List *partFKs;
10883 : AttrMap *attmap;
10884 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10885 : bool attached;
10886 : ObjectAddress address;
10887 :
10888 376 : CheckAlterTableIsSafe(partition);
10889 :
10890 370 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10891 : RelationGetDescr(rel),
10892 : false);
10893 950 : for (int j = 0; j < numfks; j++)
10894 580 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10895 :
10896 : /* Check whether an existing constraint can be repurposed */
10897 370 : partFKs = copyObject(RelationGetFKeyList(partition));
10898 370 : attached = false;
10899 758 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
10900 : {
10901 30 : if (tryAttachPartitionForeignKey(wqueue,
10902 : fk,
10903 : partition,
10904 : parentConstr,
10905 : numfks,
10906 : mapped_fkattnum,
10907 : pkattnum,
10908 : pfeqoperators,
10909 : insertTriggerOid,
10910 : updateTriggerOid,
10911 : trigrel))
10912 : {
10913 12 : attached = true;
10914 12 : break;
10915 : }
10916 : }
10917 370 : if (attached)
10918 : {
10919 12 : table_close(partition, NoLock);
10920 12 : continue;
10921 : }
10922 :
10923 : /*
10924 : * No luck finding a good constraint to reuse; create our own.
10925 : */
10926 358 : address = addFkConstraint(addFkReferencingSide,
10927 : fkconstraint->conname, fkconstraint,
10928 : partition, pkrel, indexOid, parentConstr,
10929 : numfks, pkattnum,
10930 : mapped_fkattnum, pfeqoperators,
10931 : ppeqoperators, ffeqoperators,
10932 : numfkdelsetcols, fkdelsetcols, true,
10933 : with_period);
10934 :
10935 : /* call ourselves to finalize the creation and we're done */
10936 358 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10937 : indexOid,
10938 : address.objectId,
10939 : numfks,
10940 : pkattnum,
10941 : mapped_fkattnum,
10942 : pfeqoperators,
10943 : ppeqoperators,
10944 : ffeqoperators,
10945 : numfkdelsetcols,
10946 : fkdelsetcols,
10947 : old_check_ok,
10948 : lockmode,
10949 : insertTriggerOid,
10950 : updateTriggerOid,
10951 : with_period);
10952 :
10953 358 : table_close(partition, NoLock);
10954 : }
10955 :
10956 460 : table_close(trigrel, RowExclusiveLock);
10957 : }
10958 2804 : }
10959 :
10960 : /*
10961 : * CloneForeignKeyConstraints
10962 : * Clone foreign keys from a partitioned table to a newly acquired
10963 : * partition.
10964 : *
10965 : * partitionRel is a partition of parentRel, so we can be certain that it has
10966 : * the same columns with the same datatypes. The columns may be in different
10967 : * order, though.
10968 : *
10969 : * wqueue must be passed to set up phase 3 constraint checking, unless the
10970 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
10971 : * PARTITION OF).
10972 : */
10973 : static void
10974 9432 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10975 : Relation partitionRel)
10976 : {
10977 : /* This only works for declarative partitioning */
10978 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10979 :
10980 : /*
10981 : * Clone constraints for which the parent is on the referenced side.
10982 : */
10983 9432 : CloneFkReferenced(parentRel, partitionRel);
10984 :
10985 : /*
10986 : * Now clone constraints where the parent is on the referencing side.
10987 : */
10988 9432 : CloneFkReferencing(wqueue, parentRel, partitionRel);
10989 9420 : }
10990 :
10991 : /*
10992 : * CloneFkReferenced
10993 : * Subroutine for CloneForeignKeyConstraints
10994 : *
10995 : * Find all the FKs that have the parent relation on the referenced side;
10996 : * clone those constraints to the given partition. This is to be called
10997 : * when the partition is being created or attached.
10998 : *
10999 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
11000 : *
11001 : * This recurses to partitions, if the relation being attached is partitioned.
11002 : * Recursion is done by calling addFkRecurseReferenced.
11003 : */
11004 : static void
11005 9432 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11006 : {
11007 : Relation pg_constraint;
11008 : AttrMap *attmap;
11009 : ListCell *cell;
11010 : SysScanDesc scan;
11011 : ScanKeyData key[2];
11012 : HeapTuple tuple;
11013 9432 : List *clone = NIL;
11014 : Relation trigrel;
11015 :
11016 : /*
11017 : * Search for any constraints where this partition's parent is in the
11018 : * referenced side. However, we must not clone any constraint whose
11019 : * parent constraint is also going to be cloned, to avoid duplicates. So
11020 : * do it in two steps: first construct the list of constraints to clone,
11021 : * then go over that list cloning those whose parents are not in the list.
11022 : * (We must not rely on the parent being seen first, since the catalog
11023 : * scan could return children first.)
11024 : */
11025 9432 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11026 9432 : ScanKeyInit(&key[0],
11027 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11028 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11029 9432 : ScanKeyInit(&key[1],
11030 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11031 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11032 : /* This is a seqscan, as we don't have a usable index ... */
11033 9432 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11034 : NULL, 2, key);
11035 9738 : while ((tuple = systable_getnext(scan)) != NULL)
11036 : {
11037 306 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11038 :
11039 306 : clone = lappend_oid(clone, constrForm->oid);
11040 : }
11041 9432 : systable_endscan(scan);
11042 9432 : table_close(pg_constraint, RowShareLock);
11043 :
11044 : /*
11045 : * Triggers of the foreign keys will be manipulated a bunch of times in
11046 : * the loop below. To avoid repeatedly opening/closing the trigger
11047 : * catalog relation, we open it here and pass it to the subroutines called
11048 : * below.
11049 : */
11050 9432 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11051 :
11052 9432 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11053 : RelationGetDescr(parentRel),
11054 : false);
11055 9738 : foreach(cell, clone)
11056 : {
11057 306 : Oid constrOid = lfirst_oid(cell);
11058 : Form_pg_constraint constrForm;
11059 : Relation fkRel;
11060 : Oid indexOid;
11061 : Oid partIndexId;
11062 : int numfks;
11063 : AttrNumber conkey[INDEX_MAX_KEYS];
11064 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11065 : AttrNumber confkey[INDEX_MAX_KEYS];
11066 : Oid conpfeqop[INDEX_MAX_KEYS];
11067 : Oid conppeqop[INDEX_MAX_KEYS];
11068 : Oid conffeqop[INDEX_MAX_KEYS];
11069 : int numfkdelsetcols;
11070 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11071 : Constraint *fkconstraint;
11072 : ObjectAddress address;
11073 : Oid deleteTriggerOid,
11074 : updateTriggerOid;
11075 :
11076 306 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11077 306 : if (!HeapTupleIsValid(tuple))
11078 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11079 306 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11080 :
11081 : /*
11082 : * As explained above: don't try to clone a constraint for which we're
11083 : * going to clone the parent.
11084 : */
11085 306 : if (list_member_oid(clone, constrForm->conparentid))
11086 : {
11087 126 : ReleaseSysCache(tuple);
11088 180 : continue;
11089 : }
11090 :
11091 : /*
11092 : * Don't clone self-referencing foreign keys, which can be in the
11093 : * partitioned table or in the partition-to-be.
11094 : */
11095 180 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11096 138 : constrForm->conrelid == RelationGetRelid(partitionRel))
11097 : {
11098 54 : ReleaseSysCache(tuple);
11099 54 : continue;
11100 : }
11101 :
11102 : /* We need the same lock level that CreateTrigger will acquire */
11103 126 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11104 :
11105 126 : indexOid = constrForm->conindid;
11106 126 : DeconstructFkConstraintRow(tuple,
11107 : &numfks,
11108 : conkey,
11109 : confkey,
11110 : conpfeqop,
11111 : conppeqop,
11112 : conffeqop,
11113 : &numfkdelsetcols,
11114 : confdelsetcols);
11115 :
11116 258 : for (int i = 0; i < numfks; i++)
11117 132 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11118 :
11119 126 : fkconstraint = makeNode(Constraint);
11120 126 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11121 126 : fkconstraint->conname = NameStr(constrForm->conname);
11122 126 : fkconstraint->deferrable = constrForm->condeferrable;
11123 126 : fkconstraint->initdeferred = constrForm->condeferred;
11124 126 : fkconstraint->location = -1;
11125 126 : fkconstraint->pktable = NULL;
11126 : /* ->fk_attrs determined below */
11127 126 : fkconstraint->pk_attrs = NIL;
11128 126 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11129 126 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11130 126 : fkconstraint->fk_del_action = constrForm->confdeltype;
11131 126 : fkconstraint->fk_del_set_cols = NIL;
11132 126 : fkconstraint->old_conpfeqop = NIL;
11133 126 : fkconstraint->old_pktable_oid = InvalidOid;
11134 126 : fkconstraint->skip_validation = false;
11135 126 : fkconstraint->initially_valid = true;
11136 :
11137 : /* set up colnames that are used to generate the constraint name */
11138 258 : for (int i = 0; i < numfks; i++)
11139 : {
11140 : Form_pg_attribute att;
11141 :
11142 132 : att = TupleDescAttr(RelationGetDescr(fkRel),
11143 132 : conkey[i] - 1);
11144 132 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11145 132 : makeString(NameStr(att->attname)));
11146 : }
11147 :
11148 : /*
11149 : * Add the new foreign key constraint pointing to the new partition.
11150 : * Because this new partition appears in the referenced side of the
11151 : * constraint, we don't need to set up for Phase 3 check.
11152 : */
11153 126 : partIndexId = index_get_partition(partitionRel, indexOid);
11154 126 : if (!OidIsValid(partIndexId))
11155 0 : elog(ERROR, "index for %u not found in partition %s",
11156 : indexOid, RelationGetRelationName(partitionRel));
11157 :
11158 : /*
11159 : * Get the "action" triggers belonging to the constraint to pass as
11160 : * parent OIDs for similar triggers that will be created on the
11161 : * partition in addFkRecurseReferenced().
11162 : */
11163 126 : GetForeignKeyActionTriggers(trigrel, constrOid,
11164 : constrForm->confrelid, constrForm->conrelid,
11165 : &deleteTriggerOid, &updateTriggerOid);
11166 :
11167 : /* Add this constraint ... */
11168 126 : address = addFkConstraint(addFkReferencedSide,
11169 : fkconstraint->conname, fkconstraint, fkRel,
11170 : partitionRel, partIndexId, constrOid,
11171 : numfks, mapped_confkey,
11172 : conkey, conpfeqop, conppeqop, conffeqop,
11173 : numfkdelsetcols, confdelsetcols, false,
11174 126 : constrForm->conperiod);
11175 : /* ... and recurse */
11176 126 : addFkRecurseReferenced(fkconstraint,
11177 : fkRel,
11178 : partitionRel,
11179 : partIndexId,
11180 : address.objectId,
11181 : numfks,
11182 : mapped_confkey,
11183 : conkey,
11184 : conpfeqop,
11185 : conppeqop,
11186 : conffeqop,
11187 : numfkdelsetcols,
11188 : confdelsetcols,
11189 : true,
11190 : deleteTriggerOid,
11191 : updateTriggerOid,
11192 126 : constrForm->conperiod);
11193 :
11194 126 : table_close(fkRel, NoLock);
11195 126 : ReleaseSysCache(tuple);
11196 : }
11197 :
11198 9432 : table_close(trigrel, RowExclusiveLock);
11199 9432 : }
11200 :
11201 : /*
11202 : * CloneFkReferencing
11203 : * Subroutine for CloneForeignKeyConstraints
11204 : *
11205 : * For each FK constraint of the parent relation in the given list, find an
11206 : * equivalent constraint in its partition relation that can be reparented;
11207 : * if one cannot be found, create a new constraint in the partition as its
11208 : * child.
11209 : *
11210 : * If wqueue is given, it is used to set up phase-3 verification for each
11211 : * cloned constraint; omit it if such verification is not needed
11212 : * (example: the partition is being created anew).
11213 : */
11214 : static void
11215 9432 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11216 : {
11217 : AttrMap *attmap;
11218 : List *partFKs;
11219 9432 : List *clone = NIL;
11220 : ListCell *cell;
11221 : Relation trigrel;
11222 :
11223 : /* obtain a list of constraints that we need to clone */
11224 10604 : foreach(cell, RelationGetFKeyList(parentRel))
11225 : {
11226 1178 : ForeignKeyCacheInfo *fk = lfirst(cell);
11227 :
11228 : /*
11229 : * Refuse to attach a table as partition that this partitioned table
11230 : * already has a foreign key to. This isn't useful schema, which is
11231 : * proven by the fact that there have been no user complaints that
11232 : * it's already impossible to achieve this in the opposite direction,
11233 : * i.e., creating a foreign key that references a partition. This
11234 : * restriction allows us to dodge some complexities around
11235 : * pg_constraint and pg_trigger row creations that would be needed
11236 : * during ATTACH/DETACH for this kind of relationship.
11237 : */
11238 1178 : if (fk->confrelid == RelationGetRelid(partRel))
11239 6 : ereport(ERROR,
11240 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11241 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11242 : RelationGetRelationName(partRel),
11243 : get_constraint_name(fk->conoid))));
11244 :
11245 1172 : clone = lappend_oid(clone, fk->conoid);
11246 : }
11247 :
11248 : /*
11249 : * Silently do nothing if there's nothing to do. In particular, this
11250 : * avoids throwing a spurious error for foreign tables.
11251 : */
11252 9426 : if (clone == NIL)
11253 8924 : return;
11254 :
11255 502 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11256 0 : ereport(ERROR,
11257 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11258 : errmsg("foreign key constraints are not supported on foreign tables")));
11259 :
11260 : /*
11261 : * Triggers of the foreign keys will be manipulated a bunch of times in
11262 : * the loop below. To avoid repeatedly opening/closing the trigger
11263 : * catalog relation, we open it here and pass it to the subroutines called
11264 : * below.
11265 : */
11266 502 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11267 :
11268 : /*
11269 : * The constraint key may differ, if the columns in the partition are
11270 : * different. This map is used to convert them.
11271 : */
11272 502 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11273 : RelationGetDescr(parentRel),
11274 : false);
11275 :
11276 502 : partFKs = copyObject(RelationGetFKeyList(partRel));
11277 :
11278 1668 : foreach(cell, clone)
11279 : {
11280 1172 : Oid parentConstrOid = lfirst_oid(cell);
11281 : Form_pg_constraint constrForm;
11282 : Relation pkrel;
11283 : HeapTuple tuple;
11284 : int numfks;
11285 : AttrNumber conkey[INDEX_MAX_KEYS];
11286 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11287 : AttrNumber confkey[INDEX_MAX_KEYS];
11288 : Oid conpfeqop[INDEX_MAX_KEYS];
11289 : Oid conppeqop[INDEX_MAX_KEYS];
11290 : Oid conffeqop[INDEX_MAX_KEYS];
11291 : int numfkdelsetcols;
11292 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11293 : Constraint *fkconstraint;
11294 : bool attached;
11295 : Oid indexOid;
11296 : ObjectAddress address;
11297 : ListCell *lc;
11298 : Oid insertTriggerOid,
11299 : updateTriggerOid;
11300 : bool with_period;
11301 :
11302 1172 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11303 1172 : if (!HeapTupleIsValid(tuple))
11304 0 : elog(ERROR, "cache lookup failed for constraint %u",
11305 : parentConstrOid);
11306 1172 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11307 :
11308 : /* Don't clone constraints whose parents are being cloned */
11309 1172 : if (list_member_oid(clone, constrForm->conparentid))
11310 : {
11311 634 : ReleaseSysCache(tuple);
11312 754 : continue;
11313 : }
11314 :
11315 : /*
11316 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11317 : * relation, that means to lock all partitions.
11318 : */
11319 538 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11320 538 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11321 226 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11322 : ShareRowExclusiveLock, NULL);
11323 :
11324 538 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11325 : conpfeqop, conppeqop, conffeqop,
11326 : &numfkdelsetcols, confdelsetcols);
11327 1280 : for (int i = 0; i < numfks; i++)
11328 742 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11329 :
11330 : /*
11331 : * Get the "check" triggers belonging to the constraint to pass as
11332 : * parent OIDs for similar triggers that will be created on the
11333 : * partition in addFkRecurseReferencing(). They are also passed to
11334 : * tryAttachPartitionForeignKey() below to simply assign as parents to
11335 : * the partition's existing "check" triggers, that is, if the
11336 : * corresponding constraints is deemed attachable to the parent
11337 : * constraint.
11338 : */
11339 538 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11340 : constrForm->confrelid, constrForm->conrelid,
11341 : &insertTriggerOid, &updateTriggerOid);
11342 :
11343 : /*
11344 : * Before creating a new constraint, see whether any existing FKs are
11345 : * fit for the purpose. If one is, attach the parent constraint to
11346 : * it, and don't clone anything. This way we avoid the expensive
11347 : * verification step and don't end up with a duplicate FK, and we
11348 : * don't need to recurse to partitions for this constraint.
11349 : */
11350 538 : attached = false;
11351 748 : foreach(lc, partFKs)
11352 : {
11353 330 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11354 :
11355 330 : if (tryAttachPartitionForeignKey(wqueue,
11356 : fk,
11357 : partRel,
11358 : parentConstrOid,
11359 : numfks,
11360 : mapped_conkey,
11361 : confkey,
11362 : conpfeqop,
11363 : insertTriggerOid,
11364 : updateTriggerOid,
11365 : trigrel))
11366 : {
11367 120 : attached = true;
11368 120 : table_close(pkrel, NoLock);
11369 120 : break;
11370 : }
11371 : }
11372 538 : if (attached)
11373 : {
11374 120 : ReleaseSysCache(tuple);
11375 120 : continue;
11376 : }
11377 :
11378 : /* No dice. Set up to create our own constraint */
11379 418 : fkconstraint = makeNode(Constraint);
11380 418 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11381 : /* ->conname determined below */
11382 418 : fkconstraint->deferrable = constrForm->condeferrable;
11383 418 : fkconstraint->initdeferred = constrForm->condeferred;
11384 418 : fkconstraint->location = -1;
11385 418 : fkconstraint->pktable = NULL;
11386 : /* ->fk_attrs determined below */
11387 418 : fkconstraint->pk_attrs = NIL;
11388 418 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11389 418 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11390 418 : fkconstraint->fk_del_action = constrForm->confdeltype;
11391 418 : fkconstraint->fk_del_set_cols = NIL;
11392 418 : fkconstraint->old_conpfeqop = NIL;
11393 418 : fkconstraint->old_pktable_oid = InvalidOid;
11394 418 : fkconstraint->skip_validation = false;
11395 418 : fkconstraint->initially_valid = constrForm->convalidated;
11396 950 : for (int i = 0; i < numfks; i++)
11397 : {
11398 : Form_pg_attribute att;
11399 :
11400 532 : att = TupleDescAttr(RelationGetDescr(partRel),
11401 532 : mapped_conkey[i] - 1);
11402 532 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11403 532 : makeString(NameStr(att->attname)));
11404 : }
11405 :
11406 418 : indexOid = constrForm->conindid;
11407 418 : with_period = constrForm->conperiod;
11408 :
11409 : /* Create the pg_constraint entry at this level */
11410 418 : address = addFkConstraint(addFkReferencingSide,
11411 418 : NameStr(constrForm->conname), fkconstraint,
11412 : partRel, pkrel, indexOid, parentConstrOid,
11413 : numfks, confkey,
11414 : mapped_conkey, conpfeqop,
11415 : conppeqop, conffeqop,
11416 : numfkdelsetcols, confdelsetcols,
11417 : false, with_period);
11418 :
11419 : /* Done with the cloned constraint's tuple */
11420 418 : ReleaseSysCache(tuple);
11421 :
11422 : /* Create the check triggers, and recurse to partitions, if any */
11423 418 : addFkRecurseReferencing(wqueue,
11424 : fkconstraint,
11425 : partRel,
11426 : pkrel,
11427 : indexOid,
11428 : address.objectId,
11429 : numfks,
11430 : confkey,
11431 : mapped_conkey,
11432 : conpfeqop,
11433 : conppeqop,
11434 : conffeqop,
11435 : numfkdelsetcols,
11436 : confdelsetcols,
11437 : false, /* no old check exists */
11438 : AccessExclusiveLock,
11439 : insertTriggerOid,
11440 : updateTriggerOid,
11441 : with_period);
11442 412 : table_close(pkrel, NoLock);
11443 : }
11444 :
11445 496 : table_close(trigrel, RowExclusiveLock);
11446 : }
11447 :
11448 : /*
11449 : * When the parent of a partition receives [the referencing side of] a foreign
11450 : * key, we must propagate that foreign key to the partition. However, the
11451 : * partition might already have an equivalent foreign key; this routine
11452 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11453 : * by the other parameters. If they are equivalent, create the link between
11454 : * the two constraints and return true.
11455 : *
11456 : * If the given FK does not match the one defined by rest of the params,
11457 : * return false.
11458 : */
11459 : static bool
11460 360 : tryAttachPartitionForeignKey(List **wqueue,
11461 : ForeignKeyCacheInfo *fk,
11462 : Relation partition,
11463 : Oid parentConstrOid,
11464 : int numfks,
11465 : AttrNumber *mapped_conkey,
11466 : AttrNumber *confkey,
11467 : Oid *conpfeqop,
11468 : Oid parentInsTrigger,
11469 : Oid parentUpdTrigger,
11470 : Relation trigrel)
11471 : {
11472 : HeapTuple parentConstrTup;
11473 : Form_pg_constraint parentConstr;
11474 : HeapTuple partcontup;
11475 : Form_pg_constraint partConstr;
11476 :
11477 360 : parentConstrTup = SearchSysCache1(CONSTROID,
11478 : ObjectIdGetDatum(parentConstrOid));
11479 360 : if (!HeapTupleIsValid(parentConstrTup))
11480 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11481 360 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11482 :
11483 : /*
11484 : * Do some quick & easy initial checks. If any of these fail, we cannot
11485 : * use this constraint.
11486 : */
11487 360 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11488 : {
11489 120 : ReleaseSysCache(parentConstrTup);
11490 120 : return false;
11491 : }
11492 672 : for (int i = 0; i < numfks; i++)
11493 : {
11494 432 : if (fk->conkey[i] != mapped_conkey[i] ||
11495 432 : fk->confkey[i] != confkey[i] ||
11496 432 : fk->conpfeqop[i] != conpfeqop[i])
11497 : {
11498 0 : ReleaseSysCache(parentConstrTup);
11499 0 : return false;
11500 : }
11501 : }
11502 :
11503 : /* Looks good so far; perform more extensive checks. */
11504 240 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11505 240 : if (!HeapTupleIsValid(partcontup))
11506 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11507 240 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11508 240 : if (OidIsValid(partConstr->conparentid) ||
11509 210 : partConstr->condeferrable != parentConstr->condeferrable ||
11510 182 : partConstr->condeferred != parentConstr->condeferred ||
11511 182 : partConstr->confupdtype != parentConstr->confupdtype ||
11512 146 : partConstr->confdeltype != parentConstr->confdeltype ||
11513 146 : partConstr->confmatchtype != parentConstr->confmatchtype)
11514 : {
11515 108 : ReleaseSysCache(parentConstrTup);
11516 108 : ReleaseSysCache(partcontup);
11517 108 : return false;
11518 : }
11519 :
11520 132 : ReleaseSysCache(parentConstrTup);
11521 132 : ReleaseSysCache(partcontup);
11522 :
11523 : /* Looks good! Attach this constraint. */
11524 132 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11525 : parentConstrOid, parentInsTrigger,
11526 : parentUpdTrigger, trigrel);
11527 :
11528 132 : return true;
11529 : }
11530 :
11531 : /*
11532 : * AttachPartitionForeignKey
11533 : *
11534 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11535 : * attaching the constraint, removing redundant triggers and entries from
11536 : * pg_constraint, and setting the constraint's parent.
11537 : */
11538 : static void
11539 132 : AttachPartitionForeignKey(List **wqueue,
11540 : Relation partition,
11541 : Oid partConstrOid,
11542 : Oid parentConstrOid,
11543 : Oid parentInsTrigger,
11544 : Oid parentUpdTrigger,
11545 : Relation trigrel)
11546 : {
11547 : HeapTuple parentConstrTup;
11548 : Form_pg_constraint parentConstr;
11549 : HeapTuple partcontup;
11550 : Form_pg_constraint partConstr;
11551 : bool queueValidation;
11552 : Oid partConstrFrelid;
11553 : Oid partConstrRelid;
11554 : Oid insertTriggerOid,
11555 : updateTriggerOid;
11556 :
11557 : /* Fetch the parent constraint tuple */
11558 132 : parentConstrTup = SearchSysCache1(CONSTROID,
11559 : ObjectIdGetDatum(parentConstrOid));
11560 132 : if (!HeapTupleIsValid(parentConstrTup))
11561 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11562 132 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11563 :
11564 : /* Fetch the child constraint tuple */
11565 132 : partcontup = SearchSysCache1(CONSTROID,
11566 : ObjectIdGetDatum(partConstrOid));
11567 132 : if (!HeapTupleIsValid(partcontup))
11568 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11569 132 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11570 132 : partConstrFrelid = partConstr->confrelid;
11571 132 : partConstrRelid = partConstr->conrelid;
11572 :
11573 : /*
11574 : * If the referenced table is partitioned, then the partition we're
11575 : * attaching now has extra pg_constraint rows and action triggers that are
11576 : * no longer needed. Remove those.
11577 : */
11578 132 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11579 : {
11580 24 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11581 :
11582 24 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11583 : partConstrRelid);
11584 :
11585 24 : table_close(pg_constraint, RowShareLock);
11586 : }
11587 :
11588 : /*
11589 : * Will we need to validate this constraint? A valid parent constraint
11590 : * implies that all child constraints have been validated, so if this one
11591 : * isn't, we must trigger phase 3 validation.
11592 : */
11593 132 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11594 :
11595 132 : ReleaseSysCache(partcontup);
11596 132 : ReleaseSysCache(parentConstrTup);
11597 :
11598 : /*
11599 : * The action triggers in the new partition become redundant -- the parent
11600 : * table already has equivalent ones, and those will be able to reach the
11601 : * partition. Remove the ones in the partition. We identify them because
11602 : * they have our constraint OID, as well as being on the referenced rel.
11603 : */
11604 132 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11605 : partConstrRelid);
11606 :
11607 132 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11608 : RelationGetRelid(partition));
11609 :
11610 : /*
11611 : * Like the constraint, attach partition's "check" triggers to the
11612 : * corresponding parent triggers.
11613 : */
11614 132 : GetForeignKeyCheckTriggers(trigrel,
11615 : partConstrOid, partConstrFrelid, partConstrRelid,
11616 : &insertTriggerOid, &updateTriggerOid);
11617 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11618 132 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11619 : RelationGetRelid(partition));
11620 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11621 132 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11622 : RelationGetRelid(partition));
11623 :
11624 : /*
11625 : * We updated this pg_constraint row above to set its parent; validating
11626 : * it will cause its convalidated flag to change, so we need CCI here. In
11627 : * addition, we need it unconditionally for the rare case where the parent
11628 : * table has *two* identical constraints; when reaching this function for
11629 : * the second one, we must have made our changes visible, otherwise we
11630 : * would try to attach both to this one.
11631 : */
11632 132 : CommandCounterIncrement();
11633 :
11634 : /* If validation is needed, put it in the queue now. */
11635 132 : if (queueValidation)
11636 : {
11637 : Relation conrel;
11638 :
11639 18 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11640 :
11641 18 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11642 18 : if (!HeapTupleIsValid(partcontup))
11643 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11644 :
11645 : /* Use the same lock as for AT_ValidateConstraint */
11646 18 : QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11647 : ShareUpdateExclusiveLock);
11648 18 : ReleaseSysCache(partcontup);
11649 18 : table_close(conrel, RowExclusiveLock);
11650 : }
11651 132 : }
11652 :
11653 : /*
11654 : * RemoveInheritedConstraint
11655 : *
11656 : * Removes the constraint and its associated trigger from the specified
11657 : * relation, which inherited the given constraint.
11658 : */
11659 : static void
11660 24 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11661 : Oid conrelid)
11662 : {
11663 : ObjectAddresses *objs;
11664 : HeapTuple consttup;
11665 : ScanKeyData key;
11666 : SysScanDesc scan;
11667 : HeapTuple trigtup;
11668 :
11669 24 : ScanKeyInit(&key,
11670 : Anum_pg_constraint_conrelid,
11671 : BTEqualStrategyNumber, F_OIDEQ,
11672 : ObjectIdGetDatum(conrelid));
11673 :
11674 24 : scan = systable_beginscan(conrel,
11675 : ConstraintRelidTypidNameIndexId,
11676 : true, NULL, 1, &key);
11677 24 : objs = new_object_addresses();
11678 240 : while ((consttup = systable_getnext(scan)) != NULL)
11679 : {
11680 216 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11681 :
11682 216 : if (conform->conparentid != conoid)
11683 168 : continue;
11684 : else
11685 : {
11686 : ObjectAddress addr;
11687 : SysScanDesc scan2;
11688 : ScanKeyData key2;
11689 : int n PG_USED_FOR_ASSERTS_ONLY;
11690 :
11691 48 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11692 48 : add_exact_object_address(&addr, objs);
11693 :
11694 : /*
11695 : * First we must delete the dependency record that binds the
11696 : * constraint records together.
11697 : */
11698 48 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11699 : conform->oid,
11700 : DEPENDENCY_INTERNAL,
11701 : ConstraintRelationId,
11702 : conoid);
11703 : Assert(n == 1); /* actually only one is expected */
11704 :
11705 : /*
11706 : * Now search for the triggers for this constraint and set them up
11707 : * for deletion too
11708 : */
11709 48 : ScanKeyInit(&key2,
11710 : Anum_pg_trigger_tgconstraint,
11711 : BTEqualStrategyNumber, F_OIDEQ,
11712 : ObjectIdGetDatum(conform->oid));
11713 48 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11714 : true, NULL, 1, &key2);
11715 144 : while ((trigtup = systable_getnext(scan2)) != NULL)
11716 : {
11717 96 : ObjectAddressSet(addr, TriggerRelationId,
11718 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11719 96 : add_exact_object_address(&addr, objs);
11720 : }
11721 48 : systable_endscan(scan2);
11722 : }
11723 : }
11724 : /* make the dependency deletions visible */
11725 24 : CommandCounterIncrement();
11726 24 : performMultipleDeletions(objs, DROP_RESTRICT,
11727 : PERFORM_DELETION_INTERNAL);
11728 24 : systable_endscan(scan);
11729 24 : }
11730 :
11731 : /*
11732 : * DropForeignKeyConstraintTriggers
11733 : *
11734 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11735 : * action triggers for the foreign key constraint.
11736 : */
11737 : static void
11738 132 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
11739 : Oid conrelid)
11740 : {
11741 : ScanKeyData key;
11742 : SysScanDesc scan;
11743 : HeapTuple trigtup;
11744 :
11745 132 : ScanKeyInit(&key,
11746 : Anum_pg_trigger_tgconstraint,
11747 : BTEqualStrategyNumber, F_OIDEQ,
11748 : ObjectIdGetDatum(conoid));
11749 132 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11750 : NULL, 1, &key);
11751 660 : while ((trigtup = systable_getnext(scan)) != NULL)
11752 : {
11753 528 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11754 : ObjectAddress trigger;
11755 :
11756 528 : if (trgform->tgconstrrelid != conrelid)
11757 264 : continue;
11758 264 : if (trgform->tgrelid != confrelid)
11759 0 : continue;
11760 :
11761 : /*
11762 : * The constraint is originally set up to contain this trigger as an
11763 : * implementation object, so there's a dependency record that links
11764 : * the two; however, since the trigger is no longer needed, we remove
11765 : * the dependency link in order to be able to drop the trigger while
11766 : * keeping the constraint intact.
11767 : */
11768 264 : deleteDependencyRecordsFor(TriggerRelationId,
11769 : trgform->oid,
11770 : false);
11771 : /* make dependency deletion visible to performDeletion */
11772 264 : CommandCounterIncrement();
11773 264 : ObjectAddressSet(trigger, TriggerRelationId,
11774 : trgform->oid);
11775 264 : performDeletion(&trigger, DROP_RESTRICT, 0);
11776 : /* make trigger drop visible, in case the loop iterates */
11777 264 : CommandCounterIncrement();
11778 : }
11779 :
11780 132 : systable_endscan(scan);
11781 132 : }
11782 :
11783 : /*
11784 : * GetForeignKeyActionTriggers
11785 : * Returns delete and update "action" triggers of the given relation
11786 : * belonging to the given constraint
11787 : */
11788 : static void
11789 126 : GetForeignKeyActionTriggers(Relation trigrel,
11790 : Oid conoid, Oid confrelid, Oid conrelid,
11791 : Oid *deleteTriggerOid,
11792 : Oid *updateTriggerOid)
11793 : {
11794 : ScanKeyData key;
11795 : SysScanDesc scan;
11796 : HeapTuple trigtup;
11797 :
11798 126 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11799 126 : ScanKeyInit(&key,
11800 : Anum_pg_trigger_tgconstraint,
11801 : BTEqualStrategyNumber, F_OIDEQ,
11802 : ObjectIdGetDatum(conoid));
11803 :
11804 126 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11805 : NULL, 1, &key);
11806 276 : while ((trigtup = systable_getnext(scan)) != NULL)
11807 : {
11808 276 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11809 :
11810 276 : if (trgform->tgconstrrelid != conrelid)
11811 24 : continue;
11812 252 : if (trgform->tgrelid != confrelid)
11813 0 : continue;
11814 : /* Only ever look at "action" triggers on the PK side. */
11815 252 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11816 0 : continue;
11817 252 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11818 : {
11819 : Assert(*deleteTriggerOid == InvalidOid);
11820 126 : *deleteTriggerOid = trgform->oid;
11821 : }
11822 126 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11823 : {
11824 : Assert(*updateTriggerOid == InvalidOid);
11825 126 : *updateTriggerOid = trgform->oid;
11826 : }
11827 : #ifndef USE_ASSERT_CHECKING
11828 : /* In an assert-enabled build, continue looking to find duplicates */
11829 252 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11830 126 : break;
11831 : #endif
11832 : }
11833 :
11834 126 : if (!OidIsValid(*deleteTriggerOid))
11835 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11836 : conoid);
11837 126 : if (!OidIsValid(*updateTriggerOid))
11838 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11839 : conoid);
11840 :
11841 126 : systable_endscan(scan);
11842 126 : }
11843 :
11844 : /*
11845 : * GetForeignKeyCheckTriggers
11846 : * Returns insert and update "check" triggers of the given relation
11847 : * belonging to the given constraint
11848 : */
11849 : static void
11850 760 : GetForeignKeyCheckTriggers(Relation trigrel,
11851 : Oid conoid, Oid confrelid, Oid conrelid,
11852 : Oid *insertTriggerOid,
11853 : Oid *updateTriggerOid)
11854 : {
11855 : ScanKeyData key;
11856 : SysScanDesc scan;
11857 : HeapTuple trigtup;
11858 :
11859 760 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11860 760 : ScanKeyInit(&key,
11861 : Anum_pg_trigger_tgconstraint,
11862 : BTEqualStrategyNumber, F_OIDEQ,
11863 : ObjectIdGetDatum(conoid));
11864 :
11865 760 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11866 : NULL, 1, &key);
11867 2452 : while ((trigtup = systable_getnext(scan)) != NULL)
11868 : {
11869 2452 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11870 :
11871 2452 : if (trgform->tgconstrrelid != confrelid)
11872 848 : continue;
11873 1604 : if (trgform->tgrelid != conrelid)
11874 0 : continue;
11875 : /* Only ever look at "check" triggers on the FK side. */
11876 1604 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11877 84 : continue;
11878 1520 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
11879 : {
11880 : Assert(*insertTriggerOid == InvalidOid);
11881 760 : *insertTriggerOid = trgform->oid;
11882 : }
11883 760 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11884 : {
11885 : Assert(*updateTriggerOid == InvalidOid);
11886 760 : *updateTriggerOid = trgform->oid;
11887 : }
11888 : #ifndef USE_ASSERT_CHECKING
11889 : /* In an assert-enabled build, continue looking to find duplicates. */
11890 1520 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11891 760 : break;
11892 : #endif
11893 : }
11894 :
11895 760 : if (!OidIsValid(*insertTriggerOid))
11896 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11897 : conoid);
11898 760 : if (!OidIsValid(*updateTriggerOid))
11899 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11900 : conoid);
11901 :
11902 760 : systable_endscan(scan);
11903 760 : }
11904 :
11905 : /*
11906 : * ALTER TABLE ALTER CONSTRAINT
11907 : *
11908 : * Update the attributes of a constraint.
11909 : *
11910 : * Currently only works for Foreign Key and not null constraints.
11911 : *
11912 : * If the constraint is modified, returns its address; otherwise, return
11913 : * InvalidObjectAddress.
11914 : */
11915 : static ObjectAddress
11916 180 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
11917 : bool recurse, LOCKMODE lockmode)
11918 : {
11919 : Relation conrel;
11920 : Relation tgrel;
11921 : SysScanDesc scan;
11922 : ScanKeyData skey[3];
11923 : HeapTuple contuple;
11924 : Form_pg_constraint currcon;
11925 : ObjectAddress address;
11926 180 : List *otherrelids = NIL;
11927 :
11928 : /*
11929 : * Disallow altering ONLY a partitioned table, as it would make no sense.
11930 : * This is okay for legacy inheritance.
11931 : */
11932 180 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
11933 0 : ereport(ERROR,
11934 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11935 : errmsg("constraint must be altered in child tables too"),
11936 : errhint("Do not specify the ONLY keyword."));
11937 :
11938 :
11939 180 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11940 180 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11941 :
11942 : /*
11943 : * Find and check the target constraint
11944 : */
11945 180 : ScanKeyInit(&skey[0],
11946 : Anum_pg_constraint_conrelid,
11947 : BTEqualStrategyNumber, F_OIDEQ,
11948 : ObjectIdGetDatum(RelationGetRelid(rel)));
11949 180 : ScanKeyInit(&skey[1],
11950 : Anum_pg_constraint_contypid,
11951 : BTEqualStrategyNumber, F_OIDEQ,
11952 : ObjectIdGetDatum(InvalidOid));
11953 180 : ScanKeyInit(&skey[2],
11954 : Anum_pg_constraint_conname,
11955 : BTEqualStrategyNumber, F_NAMEEQ,
11956 180 : CStringGetDatum(cmdcon->conname));
11957 180 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11958 : true, NULL, 3, skey);
11959 :
11960 : /* There can be at most one matching row */
11961 180 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11962 6 : ereport(ERROR,
11963 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11964 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11965 : cmdcon->conname, RelationGetRelationName(rel))));
11966 :
11967 174 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11968 174 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
11969 0 : ereport(ERROR,
11970 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11971 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11972 : cmdcon->conname, RelationGetRelationName(rel))));
11973 174 : if (cmdcon->alterInheritability &&
11974 72 : currcon->contype != CONSTRAINT_NOTNULL)
11975 18 : ereport(ERROR,
11976 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
11977 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
11978 : cmdcon->conname, RelationGetRelationName(rel)));
11979 :
11980 : /* Refuse to modify inheritability of inherited constraints */
11981 156 : if (cmdcon->alterInheritability &&
11982 54 : cmdcon->noinherit && currcon->coninhcount > 0)
11983 6 : ereport(ERROR,
11984 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11985 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
11986 : NameStr(currcon->conname),
11987 : RelationGetRelationName(rel)));
11988 :
11989 : /*
11990 : * If it's not the topmost constraint, raise an error.
11991 : *
11992 : * Altering a non-topmost constraint leaves some triggers untouched, since
11993 : * they are not directly connected to this constraint; also, pg_dump would
11994 : * ignore the deferrability status of the individual constraint, since it
11995 : * only dumps topmost constraints. Avoid these problems by refusing this
11996 : * operation and telling the user to alter the parent constraint instead.
11997 : */
11998 150 : if (OidIsValid(currcon->conparentid))
11999 : {
12000 : HeapTuple tp;
12001 12 : Oid parent = currcon->conparentid;
12002 12 : char *ancestorname = NULL;
12003 12 : char *ancestortable = NULL;
12004 :
12005 : /* Loop to find the topmost constraint */
12006 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12007 : {
12008 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12009 :
12010 : /* If no parent, this is the constraint we want */
12011 24 : if (!OidIsValid(contup->conparentid))
12012 : {
12013 12 : ancestorname = pstrdup(NameStr(contup->conname));
12014 12 : ancestortable = get_rel_name(contup->conrelid);
12015 12 : ReleaseSysCache(tp);
12016 12 : break;
12017 : }
12018 :
12019 12 : parent = contup->conparentid;
12020 12 : ReleaseSysCache(tp);
12021 : }
12022 :
12023 12 : ereport(ERROR,
12024 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12025 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12026 : cmdcon->conname, RelationGetRelationName(rel)),
12027 : ancestorname && ancestortable ?
12028 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12029 : cmdcon->conname, ancestorname, ancestortable) : 0,
12030 : errhint("You may alter the constraint it derives from instead.")));
12031 : }
12032 :
12033 138 : address = InvalidObjectAddress;
12034 :
12035 : /*
12036 : * Do the actual catalog work, and recurse if necessary.
12037 : */
12038 138 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12039 : contuple, recurse, &otherrelids, lockmode))
12040 132 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12041 :
12042 : /*
12043 : * ATExecAlterConstraintInternal already invalidated relcache for the
12044 : * relations having the constraint itself; here we also invalidate for
12045 : * relations that have any triggers that are part of the constraint.
12046 : */
12047 384 : foreach_oid(relid, otherrelids)
12048 120 : CacheInvalidateRelcacheByRelid(relid);
12049 :
12050 132 : systable_endscan(scan);
12051 :
12052 132 : table_close(tgrel, RowExclusiveLock);
12053 132 : table_close(conrel, RowExclusiveLock);
12054 :
12055 132 : return address;
12056 : }
12057 :
12058 : /*
12059 : * Recursive subroutine of ATExecAlterConstraint. Returns true if the
12060 : * constraint is altered.
12061 : *
12062 : * *otherrelids is appended OIDs of relations containing affected triggers.
12063 : *
12064 : * Note that we must recurse even when the values are correct, in case
12065 : * indirect descendants have had their constraints altered locally.
12066 : * (This could be avoided if we forbade altering constraints in partitions
12067 : * but existing releases don't do that.)
12068 : */
12069 : static bool
12070 204 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12071 : Relation conrel, Relation tgrel, Relation rel,
12072 : HeapTuple contuple, bool recurse,
12073 : List **otherrelids, LOCKMODE lockmode)
12074 : {
12075 : Form_pg_constraint currcon;
12076 204 : Oid refrelid = InvalidOid;
12077 204 : bool changed = false;
12078 :
12079 : /* since this function recurses, it could be driven to stack overflow */
12080 204 : check_stack_depth();
12081 :
12082 204 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12083 204 : if (currcon->contype == CONSTRAINT_FOREIGN)
12084 156 : refrelid = currcon->confrelid;
12085 :
12086 : /*
12087 : * Update pg_constraint with the flags from cmdcon.
12088 : *
12089 : * If called to modify a constraint that's already in the desired state,
12090 : * silently do nothing.
12091 : */
12092 204 : if (cmdcon->alterDeferrability &&
12093 156 : (currcon->condeferrable != cmdcon->deferrable ||
12094 6 : currcon->condeferred != cmdcon->initdeferred))
12095 : {
12096 : HeapTuple copyTuple;
12097 : Form_pg_constraint copy_con;
12098 :
12099 156 : copyTuple = heap_copytuple(contuple);
12100 156 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12101 156 : copy_con->condeferrable = cmdcon->deferrable;
12102 156 : copy_con->condeferred = cmdcon->initdeferred;
12103 156 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12104 :
12105 156 : InvokeObjectPostAlterHook(ConstraintRelationId, currcon->oid, 0);
12106 :
12107 156 : heap_freetuple(copyTuple);
12108 156 : changed = true;
12109 :
12110 : /* Make new constraint flags visible to others */
12111 156 : CacheInvalidateRelcache(rel);
12112 :
12113 : /*
12114 : * Now we need to update the multiple entries in pg_trigger that
12115 : * implement the constraint.
12116 : */
12117 156 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12118 156 : cmdcon->deferrable,
12119 156 : cmdcon->initdeferred, otherrelids);
12120 : }
12121 :
12122 : /*
12123 : * If the table at either end of the constraint is partitioned, we need to
12124 : * handle every constraint that is a child of this one.
12125 : */
12126 204 : if (recurse && changed &&
12127 156 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12128 132 : (OidIsValid(refrelid) &&
12129 132 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)))
12130 42 : ATExecAlterChildConstr(wqueue, cmdcon, conrel, tgrel, rel, contuple,
12131 : recurse, otherrelids, lockmode);
12132 :
12133 : /*
12134 : * Update the catalog for inheritability. No work if the constraint is
12135 : * already in the requested state.
12136 : */
12137 204 : if (cmdcon->alterInheritability &&
12138 48 : (cmdcon->noinherit != currcon->connoinherit))
12139 : {
12140 : AttrNumber colNum;
12141 : char *colName;
12142 : List *children;
12143 : HeapTuple copyTuple;
12144 : Form_pg_constraint copy_con;
12145 :
12146 : /* The current implementation only works for NOT NULL constraints */
12147 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12148 :
12149 48 : copyTuple = heap_copytuple(contuple);
12150 48 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12151 48 : copy_con->connoinherit = cmdcon->noinherit;
12152 :
12153 48 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12154 48 : CommandCounterIncrement();
12155 48 : heap_freetuple(copyTuple);
12156 48 : changed = true;
12157 :
12158 : /* Fetch the column number and name */
12159 48 : colNum = extractNotNullColumn(contuple);
12160 48 : colName = get_attname(currcon->conrelid, colNum, false);
12161 :
12162 : /*
12163 : * Propagate the change to children. For SET NO INHERIT, we don't
12164 : * recursively affect children, just the immediate level.
12165 : */
12166 48 : children = find_inheritance_children(RelationGetRelid(rel),
12167 : lockmode);
12168 156 : foreach_oid(childoid, children)
12169 : {
12170 : ObjectAddress addr;
12171 :
12172 72 : if (cmdcon->noinherit)
12173 : {
12174 : HeapTuple childtup;
12175 : Form_pg_constraint childcon;
12176 :
12177 24 : childtup = findNotNullConstraint(childoid, colName);
12178 24 : if (!childtup)
12179 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12180 : colName, childoid);
12181 24 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12182 : Assert(childcon->coninhcount > 0);
12183 24 : childcon->coninhcount--;
12184 24 : childcon->conislocal = true;
12185 24 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12186 24 : heap_freetuple(childtup);
12187 : }
12188 : else
12189 : {
12190 48 : Relation childrel = table_open(childoid, NoLock);
12191 :
12192 48 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12193 : colName, true, true, lockmode);
12194 42 : if (OidIsValid(addr.objectId))
12195 42 : CommandCounterIncrement();
12196 42 : table_close(childrel, NoLock);
12197 : }
12198 : }
12199 : }
12200 :
12201 198 : return changed;
12202 : }
12203 :
12204 : /*
12205 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12206 : * trigger's deferrability.
12207 : *
12208 : * The arguments to this function have the same meaning as the arguments to
12209 : * ATExecAlterConstrDeferrability.
12210 : */
12211 : static void
12212 156 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12213 : bool deferrable, bool initdeferred,
12214 : List **otherrelids)
12215 : {
12216 : HeapTuple tgtuple;
12217 : ScanKeyData tgkey;
12218 : SysScanDesc tgscan;
12219 :
12220 156 : ScanKeyInit(&tgkey,
12221 : Anum_pg_trigger_tgconstraint,
12222 : BTEqualStrategyNumber, F_OIDEQ,
12223 : ObjectIdGetDatum(conoid));
12224 156 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12225 : NULL, 1, &tgkey);
12226 648 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12227 : {
12228 492 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12229 : Form_pg_trigger copy_tg;
12230 : HeapTuple tgCopyTuple;
12231 :
12232 : /*
12233 : * Remember OIDs of other relation(s) involved in FK constraint.
12234 : * (Note: it's likely that we could skip forcing a relcache inval for
12235 : * other rels that don't have a trigger whose properties change, but
12236 : * let's be conservative.)
12237 : */
12238 492 : if (tgform->tgrelid != RelationGetRelid(rel))
12239 240 : *otherrelids = list_append_unique_oid(*otherrelids,
12240 : tgform->tgrelid);
12241 :
12242 : /*
12243 : * Update enable status and deferrability of RI_FKey_noaction_del,
12244 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12245 : * triggers, but not others; see createForeignKeyActionTriggers and
12246 : * CreateFKCheckTrigger.
12247 : */
12248 492 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12249 390 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12250 270 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12251 144 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12252 18 : continue;
12253 :
12254 474 : tgCopyTuple = heap_copytuple(tgtuple);
12255 474 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12256 :
12257 474 : copy_tg->tgdeferrable = deferrable;
12258 474 : copy_tg->tginitdeferred = initdeferred;
12259 474 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12260 :
12261 474 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12262 :
12263 474 : heap_freetuple(tgCopyTuple);
12264 : }
12265 :
12266 156 : systable_endscan(tgscan);
12267 156 : }
12268 :
12269 : /*
12270 : * Invokes ATExecAlterConstraintInternal for each constraint that is a child of
12271 : * the specified constraint.
12272 : *
12273 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12274 : * list of child relations and recursing; instead it uses the conparentid
12275 : * relationships. This may need to be reconsidered.
12276 : *
12277 : * The arguments to this function have the same meaning as the arguments to
12278 : * ATExecAlterConstraintInternal.
12279 : */
12280 : static void
12281 42 : ATExecAlterChildConstr(List **wqueue, ATAlterConstraint *cmdcon,
12282 : Relation conrel, Relation tgrel, Relation rel,
12283 : HeapTuple contuple, bool recurse, List **otherrelids,
12284 : LOCKMODE lockmode)
12285 : {
12286 : Form_pg_constraint currcon;
12287 : Oid conoid;
12288 : ScanKeyData pkey;
12289 : SysScanDesc pscan;
12290 : HeapTuple childtup;
12291 :
12292 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12293 42 : conoid = currcon->oid;
12294 :
12295 42 : ScanKeyInit(&pkey,
12296 : Anum_pg_constraint_conparentid,
12297 : BTEqualStrategyNumber, F_OIDEQ,
12298 : ObjectIdGetDatum(conoid));
12299 :
12300 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12301 : true, NULL, 1, &pkey);
12302 :
12303 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12304 : {
12305 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12306 : Relation childrel;
12307 :
12308 66 : childrel = table_open(childcon->conrelid, lockmode);
12309 66 : ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, childrel,
12310 : childtup, recurse, otherrelids, lockmode);
12311 66 : table_close(childrel, NoLock);
12312 : }
12313 :
12314 42 : systable_endscan(pscan);
12315 42 : }
12316 :
12317 : /*
12318 : * ALTER TABLE VALIDATE CONSTRAINT
12319 : *
12320 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12321 : * there's no good way to skip recursing when handling foreign keys: there is
12322 : * no need to lock children in that case, yet we wouldn't be able to avoid
12323 : * doing so at that level.
12324 : *
12325 : * Return value is the address of the validated constraint. If the constraint
12326 : * was already validated, InvalidObjectAddress is returned.
12327 : */
12328 : static ObjectAddress
12329 460 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12330 : bool recurse, bool recursing, LOCKMODE lockmode)
12331 : {
12332 : Relation conrel;
12333 : SysScanDesc scan;
12334 : ScanKeyData skey[3];
12335 : HeapTuple tuple;
12336 : Form_pg_constraint con;
12337 : ObjectAddress address;
12338 :
12339 460 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12340 :
12341 : /*
12342 : * Find and check the target constraint
12343 : */
12344 460 : ScanKeyInit(&skey[0],
12345 : Anum_pg_constraint_conrelid,
12346 : BTEqualStrategyNumber, F_OIDEQ,
12347 : ObjectIdGetDatum(RelationGetRelid(rel)));
12348 460 : ScanKeyInit(&skey[1],
12349 : Anum_pg_constraint_contypid,
12350 : BTEqualStrategyNumber, F_OIDEQ,
12351 : ObjectIdGetDatum(InvalidOid));
12352 460 : ScanKeyInit(&skey[2],
12353 : Anum_pg_constraint_conname,
12354 : BTEqualStrategyNumber, F_NAMEEQ,
12355 : CStringGetDatum(constrName));
12356 460 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12357 : true, NULL, 3, skey);
12358 :
12359 : /* There can be at most one matching row */
12360 460 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12361 0 : ereport(ERROR,
12362 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12363 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12364 : constrName, RelationGetRelationName(rel))));
12365 :
12366 460 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12367 460 : if (con->contype != CONSTRAINT_FOREIGN &&
12368 144 : con->contype != CONSTRAINT_CHECK)
12369 0 : ereport(ERROR,
12370 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12371 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12372 : constrName, RelationGetRelationName(rel))));
12373 :
12374 460 : if (!con->conenforced)
12375 6 : ereport(ERROR,
12376 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12377 : errmsg("cannot validate NOT ENFORCED constraint")));
12378 :
12379 454 : if (!con->convalidated)
12380 : {
12381 436 : if (con->contype == CONSTRAINT_FOREIGN)
12382 : {
12383 310 : QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12384 : }
12385 126 : else if (con->contype == CONSTRAINT_CHECK)
12386 : {
12387 126 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12388 : tuple, recurse, recursing, lockmode);
12389 : }
12390 :
12391 436 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12392 : }
12393 : else
12394 18 : address = InvalidObjectAddress; /* already validated */
12395 :
12396 454 : systable_endscan(scan);
12397 :
12398 454 : table_close(conrel, RowExclusiveLock);
12399 :
12400 454 : return address;
12401 : }
12402 :
12403 : /*
12404 : * QueueFKConstraintValidation
12405 : *
12406 : * Add an entry to the wqueue to validate the given foreign key constraint in
12407 : * Phase 3 and update the convalidated field in the pg_constraint catalog
12408 : * for the specified relation and all its children.
12409 : */
12410 : static void
12411 334 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12412 : HeapTuple contuple, LOCKMODE lockmode)
12413 : {
12414 : Form_pg_constraint con;
12415 : AlteredTableInfo *tab;
12416 : HeapTuple copyTuple;
12417 : Form_pg_constraint copy_con;
12418 :
12419 334 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12420 : Assert(con->contype == CONSTRAINT_FOREIGN);
12421 : Assert(!con->convalidated);
12422 :
12423 334 : if (rel->rd_rel->relkind == RELKIND_RELATION)
12424 : {
12425 : NewConstraint *newcon;
12426 : Constraint *fkconstraint;
12427 :
12428 : /* Queue validation for phase 3 */
12429 322 : fkconstraint = makeNode(Constraint);
12430 : /* for now this is all we need */
12431 322 : fkconstraint->conname = pstrdup(NameStr(con->conname));
12432 :
12433 322 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12434 322 : newcon->name = fkconstraint->conname;
12435 322 : newcon->contype = CONSTR_FOREIGN;
12436 322 : newcon->refrelid = con->confrelid;
12437 322 : newcon->refindid = con->conindid;
12438 322 : newcon->conid = con->oid;
12439 322 : newcon->qual = (Node *) fkconstraint;
12440 :
12441 : /* Find or create work queue entry for this table */
12442 322 : tab = ATGetQueueEntry(wqueue, rel);
12443 322 : tab->constraints = lappend(tab->constraints, newcon);
12444 : }
12445 :
12446 : /*
12447 : * If the table at either end of the constraint is partitioned, we need to
12448 : * recurse and handle every constraint that is a child of this constraint.
12449 : */
12450 656 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12451 322 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
12452 : {
12453 : ScanKeyData pkey;
12454 : SysScanDesc pscan;
12455 : HeapTuple childtup;
12456 :
12457 18 : ScanKeyInit(&pkey,
12458 : Anum_pg_constraint_conparentid,
12459 : BTEqualStrategyNumber, F_OIDEQ,
12460 : ObjectIdGetDatum(con->oid));
12461 :
12462 18 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12463 : true, NULL, 1, &pkey);
12464 :
12465 36 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12466 : {
12467 : Form_pg_constraint childcon;
12468 : Relation childrel;
12469 :
12470 18 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12471 :
12472 : /*
12473 : * If the child constraint has already been validated, no further
12474 : * action is required for it or its descendants, as they are all
12475 : * valid.
12476 : */
12477 18 : if (childcon->convalidated)
12478 12 : continue;
12479 :
12480 6 : childrel = table_open(childcon->conrelid, lockmode);
12481 :
12482 6 : QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
12483 : lockmode);
12484 6 : table_close(childrel, NoLock);
12485 : }
12486 :
12487 18 : systable_endscan(pscan);
12488 : }
12489 :
12490 : /*
12491 : * Now update the catalog, while we have the door open.
12492 : */
12493 334 : copyTuple = heap_copytuple(contuple);
12494 334 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12495 334 : copy_con->convalidated = true;
12496 334 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12497 :
12498 334 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12499 :
12500 334 : heap_freetuple(copyTuple);
12501 334 : }
12502 :
12503 : /*
12504 : * QueueCheckConstraintValidation
12505 : *
12506 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
12507 : * and update the convalidated field in the pg_constraint catalog for the
12508 : * specified relation and all its inheriting children.
12509 : */
12510 : static void
12511 126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12512 : char *constrName, HeapTuple contuple,
12513 : bool recurse, bool recursing, LOCKMODE lockmode)
12514 : {
12515 : Form_pg_constraint con;
12516 : AlteredTableInfo *tab;
12517 : HeapTuple copyTuple;
12518 : Form_pg_constraint copy_con;
12519 :
12520 126 : List *children = NIL;
12521 : ListCell *child;
12522 : NewConstraint *newcon;
12523 : Datum val;
12524 : char *conbin;
12525 :
12526 126 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12527 : Assert(con->contype == CONSTRAINT_CHECK);
12528 :
12529 : /*
12530 : * If we're recursing, the parent has already done this, so skip it. Also,
12531 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
12532 : * for it in the children.
12533 : */
12534 126 : if (!recursing && !con->connoinherit)
12535 72 : children = find_all_inheritors(RelationGetRelid(rel),
12536 : lockmode, NULL);
12537 :
12538 : /*
12539 : * For CHECK constraints, we must ensure that we only mark the constraint
12540 : * as validated on the parent if it's already validated on the children.
12541 : *
12542 : * We recurse before validating on the parent, to reduce risk of
12543 : * deadlocks.
12544 : */
12545 246 : foreach(child, children)
12546 : {
12547 120 : Oid childoid = lfirst_oid(child);
12548 : Relation childrel;
12549 :
12550 120 : if (childoid == RelationGetRelid(rel))
12551 72 : continue;
12552 :
12553 : /*
12554 : * If we are told not to recurse, there had better not be any child
12555 : * tables, because we can't mark the constraint on the parent valid
12556 : * unless it is valid for all child tables.
12557 : */
12558 48 : if (!recurse)
12559 0 : ereport(ERROR,
12560 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12561 : errmsg("constraint must be validated on child tables too")));
12562 :
12563 : /* find_all_inheritors already got lock */
12564 48 : childrel = table_open(childoid, NoLock);
12565 :
12566 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
12567 : true, lockmode);
12568 48 : table_close(childrel, NoLock);
12569 : }
12570 :
12571 : /* Queue validation for phase 3 */
12572 126 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12573 126 : newcon->name = constrName;
12574 126 : newcon->contype = CONSTR_CHECK;
12575 126 : newcon->refrelid = InvalidOid;
12576 126 : newcon->refindid = InvalidOid;
12577 126 : newcon->conid = con->oid;
12578 :
12579 126 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
12580 : Anum_pg_constraint_conbin);
12581 126 : conbin = TextDatumGetCString(val);
12582 126 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
12583 :
12584 : /* Find or create work queue entry for this table */
12585 126 : tab = ATGetQueueEntry(wqueue, rel);
12586 126 : tab->constraints = lappend(tab->constraints, newcon);
12587 :
12588 : /*
12589 : * Invalidate relcache so that others see the new validated constraint.
12590 : */
12591 126 : CacheInvalidateRelcache(rel);
12592 :
12593 : /*
12594 : * Now update the catalog, while we have the door open.
12595 : */
12596 126 : copyTuple = heap_copytuple(contuple);
12597 126 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12598 126 : copy_con->convalidated = true;
12599 126 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12600 :
12601 126 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12602 :
12603 126 : heap_freetuple(copyTuple);
12604 126 : }
12605 :
12606 : /*
12607 : * transformColumnNameList - transform list of column names
12608 : *
12609 : * Lookup each name and return its attnum and, optionally, type and collation
12610 : * OIDs
12611 : *
12612 : * Note: the name of this function suggests that it's general-purpose,
12613 : * but actually it's only used to look up names appearing in foreign-key
12614 : * clauses. The error messages would need work to use it in other cases,
12615 : * and perhaps the validity checks as well.
12616 : */
12617 : static int
12618 6418 : transformColumnNameList(Oid relId, List *colList,
12619 : int16 *attnums, Oid *atttypids, Oid *attcollids)
12620 : {
12621 : ListCell *l;
12622 : int attnum;
12623 :
12624 6418 : attnum = 0;
12625 11704 : foreach(l, colList)
12626 : {
12627 5352 : char *attname = strVal(lfirst(l));
12628 : HeapTuple atttuple;
12629 : Form_pg_attribute attform;
12630 :
12631 5352 : atttuple = SearchSysCacheAttName(relId, attname);
12632 5352 : if (!HeapTupleIsValid(atttuple))
12633 54 : ereport(ERROR,
12634 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12635 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12636 : attname)));
12637 5298 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12638 5298 : if (attform->attnum < 0)
12639 12 : ereport(ERROR,
12640 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12641 : errmsg("system columns cannot be used in foreign keys")));
12642 5286 : if (attnum >= INDEX_MAX_KEYS)
12643 0 : ereport(ERROR,
12644 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
12645 : errmsg("cannot have more than %d keys in a foreign key",
12646 : INDEX_MAX_KEYS)));
12647 5286 : attnums[attnum] = attform->attnum;
12648 5286 : if (atttypids != NULL)
12649 5256 : atttypids[attnum] = attform->atttypid;
12650 5286 : if (attcollids != NULL)
12651 5256 : attcollids[attnum] = attform->attcollation;
12652 5286 : ReleaseSysCache(atttuple);
12653 5286 : attnum++;
12654 : }
12655 :
12656 6352 : return attnum;
12657 : }
12658 :
12659 : /*
12660 : * transformFkeyGetPrimaryKey -
12661 : *
12662 : * Look up the names, attnums, types, and collations of the primary key attributes
12663 : * for the pkrel. Also return the index OID and index opclasses of the
12664 : * index supporting the primary key. Also return whether the index has
12665 : * WITHOUT OVERLAPS.
12666 : *
12667 : * All parameters except pkrel are output parameters. Also, the function
12668 : * return value is the number of attributes in the primary key.
12669 : *
12670 : * Used when the column list in the REFERENCES specification is omitted.
12671 : */
12672 : static int
12673 1166 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12674 : List **attnamelist,
12675 : int16 *attnums, Oid *atttypids, Oid *attcollids,
12676 : Oid *opclasses, bool *pk_has_without_overlaps)
12677 : {
12678 : List *indexoidlist;
12679 : ListCell *indexoidscan;
12680 1166 : HeapTuple indexTuple = NULL;
12681 1166 : Form_pg_index indexStruct = NULL;
12682 : Datum indclassDatum;
12683 : oidvector *indclass;
12684 : int i;
12685 :
12686 : /*
12687 : * Get the list of index OIDs for the table from the relcache, and look up
12688 : * each one in the pg_index syscache until we find one marked primary key
12689 : * (hopefully there isn't more than one such). Insist it's valid, too.
12690 : */
12691 1166 : *indexOid = InvalidOid;
12692 :
12693 1166 : indexoidlist = RelationGetIndexList(pkrel);
12694 :
12695 1172 : foreach(indexoidscan, indexoidlist)
12696 : {
12697 1172 : Oid indexoid = lfirst_oid(indexoidscan);
12698 :
12699 1172 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12700 1172 : if (!HeapTupleIsValid(indexTuple))
12701 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12702 1172 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12703 1172 : if (indexStruct->indisprimary && indexStruct->indisvalid)
12704 : {
12705 : /*
12706 : * Refuse to use a deferrable primary key. This is per SQL spec,
12707 : * and there would be a lot of interesting semantic problems if we
12708 : * tried to allow it.
12709 : */
12710 1166 : if (!indexStruct->indimmediate)
12711 0 : ereport(ERROR,
12712 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12713 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12714 : RelationGetRelationName(pkrel))));
12715 :
12716 1166 : *indexOid = indexoid;
12717 1166 : break;
12718 : }
12719 6 : ReleaseSysCache(indexTuple);
12720 : }
12721 :
12722 1166 : list_free(indexoidlist);
12723 :
12724 : /*
12725 : * Check that we found it
12726 : */
12727 1166 : if (!OidIsValid(*indexOid))
12728 0 : ereport(ERROR,
12729 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12730 : errmsg("there is no primary key for referenced table \"%s\"",
12731 : RelationGetRelationName(pkrel))));
12732 :
12733 : /* Must get indclass the hard way */
12734 1166 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12735 : Anum_pg_index_indclass);
12736 1166 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12737 :
12738 : /*
12739 : * Now build the list of PK attributes from the indkey definition (we
12740 : * assume a primary key cannot have expressional elements)
12741 : */
12742 1166 : *attnamelist = NIL;
12743 2744 : for (i = 0; i < indexStruct->indnkeyatts; i++)
12744 : {
12745 1578 : int pkattno = indexStruct->indkey.values[i];
12746 :
12747 1578 : attnums[i] = pkattno;
12748 1578 : atttypids[i] = attnumTypeId(pkrel, pkattno);
12749 1578 : attcollids[i] = attnumCollationId(pkrel, pkattno);
12750 1578 : opclasses[i] = indclass->values[i];
12751 1578 : *attnamelist = lappend(*attnamelist,
12752 1578 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12753 : }
12754 :
12755 1166 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12756 :
12757 1166 : ReleaseSysCache(indexTuple);
12758 :
12759 1166 : return i;
12760 : }
12761 :
12762 : /*
12763 : * transformFkeyCheckAttrs -
12764 : *
12765 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12766 : * reference as part of a foreign key constraint.
12767 : *
12768 : * Returns the OID of the unique index supporting the constraint and
12769 : * populates the caller-provided 'opclasses' array with the opclasses
12770 : * associated with the index columns. Also sets whether the index
12771 : * uses WITHOUT OVERLAPS.
12772 : *
12773 : * Raises an ERROR on validation failure.
12774 : */
12775 : static Oid
12776 1282 : transformFkeyCheckAttrs(Relation pkrel,
12777 : int numattrs, int16 *attnums,
12778 : bool with_period, Oid *opclasses,
12779 : bool *pk_has_without_overlaps)
12780 : {
12781 1282 : Oid indexoid = InvalidOid;
12782 1282 : bool found = false;
12783 1282 : bool found_deferrable = false;
12784 : List *indexoidlist;
12785 : ListCell *indexoidscan;
12786 : int i,
12787 : j;
12788 :
12789 : /*
12790 : * Reject duplicate appearances of columns in the referenced-columns list.
12791 : * Such a case is forbidden by the SQL standard, and even if we thought it
12792 : * useful to allow it, there would be ambiguity about how to match the
12793 : * list to unique indexes (in particular, it'd be unclear which index
12794 : * opclass goes with which FK column).
12795 : */
12796 2992 : for (i = 0; i < numattrs; i++)
12797 : {
12798 2256 : for (j = i + 1; j < numattrs; j++)
12799 : {
12800 546 : if (attnums[i] == attnums[j])
12801 24 : ereport(ERROR,
12802 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12803 : errmsg("foreign key referenced-columns list must not contain duplicates")));
12804 : }
12805 : }
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, and match unique indexes to the list
12810 : * of attnums we are given.
12811 : */
12812 1258 : indexoidlist = RelationGetIndexList(pkrel);
12813 :
12814 1438 : foreach(indexoidscan, indexoidlist)
12815 : {
12816 : HeapTuple indexTuple;
12817 : Form_pg_index indexStruct;
12818 :
12819 1426 : indexoid = lfirst_oid(indexoidscan);
12820 1426 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12821 1426 : if (!HeapTupleIsValid(indexTuple))
12822 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12823 1426 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12824 :
12825 : /*
12826 : * Must have the right number of columns; must be unique (or if
12827 : * temporal then exclusion instead) and not a partial index; forget it
12828 : * if there are any expressions, too. Invalid indexes are out as well.
12829 : */
12830 2744 : if (indexStruct->indnkeyatts == numattrs &&
12831 1318 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12832 2608 : indexStruct->indisvalid &&
12833 2608 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12834 1304 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12835 : {
12836 : Datum indclassDatum;
12837 : oidvector *indclass;
12838 :
12839 : /* Must get indclass the hard way */
12840 1304 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12841 : Anum_pg_index_indclass);
12842 1304 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12843 :
12844 : /*
12845 : * The given attnum list may match the index columns in any order.
12846 : * Check for a match, and extract the appropriate opclasses while
12847 : * we're at it.
12848 : *
12849 : * We know that attnums[] is duplicate-free per the test at the
12850 : * start of this function, and we checked above that the number of
12851 : * index columns agrees, so if we find a match for each attnums[]
12852 : * entry then we must have a one-to-one match in some order.
12853 : */
12854 3002 : for (i = 0; i < numattrs; i++)
12855 : {
12856 1756 : found = false;
12857 2336 : for (j = 0; j < numattrs; j++)
12858 : {
12859 2278 : if (attnums[i] == indexStruct->indkey.values[j])
12860 : {
12861 1698 : opclasses[i] = indclass->values[j];
12862 1698 : found = true;
12863 1698 : break;
12864 : }
12865 : }
12866 1756 : if (!found)
12867 58 : break;
12868 : }
12869 : /* The last attribute in the index must be the PERIOD FK part */
12870 1304 : if (found && with_period)
12871 : {
12872 122 : int16 periodattnum = attnums[numattrs - 1];
12873 :
12874 122 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12875 : }
12876 :
12877 : /*
12878 : * Refuse to use a deferrable unique/primary key. This is per SQL
12879 : * spec, and there would be a lot of interesting semantic problems
12880 : * if we tried to allow it.
12881 : */
12882 1304 : if (found && !indexStruct->indimmediate)
12883 : {
12884 : /*
12885 : * Remember that we found an otherwise matching index, so that
12886 : * we can generate a more appropriate error message.
12887 : */
12888 0 : found_deferrable = true;
12889 0 : found = false;
12890 : }
12891 :
12892 : /* We need to know whether the index has WITHOUT OVERLAPS */
12893 1304 : if (found)
12894 1246 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12895 : }
12896 1426 : ReleaseSysCache(indexTuple);
12897 1426 : if (found)
12898 1246 : break;
12899 : }
12900 :
12901 1258 : if (!found)
12902 : {
12903 12 : if (found_deferrable)
12904 0 : ereport(ERROR,
12905 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12906 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12907 : RelationGetRelationName(pkrel))));
12908 : else
12909 12 : ereport(ERROR,
12910 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12911 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12912 : RelationGetRelationName(pkrel))));
12913 : }
12914 :
12915 1246 : list_free(indexoidlist);
12916 :
12917 1246 : return indexoid;
12918 : }
12919 :
12920 : /*
12921 : * findFkeyCast -
12922 : *
12923 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12924 : * Caller has equal regard for binary coercibility and for an exact match.
12925 : */
12926 : static CoercionPathType
12927 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12928 : {
12929 : CoercionPathType ret;
12930 :
12931 12 : if (targetTypeId == sourceTypeId)
12932 : {
12933 12 : ret = COERCION_PATH_RELABELTYPE;
12934 12 : *funcid = InvalidOid;
12935 : }
12936 : else
12937 : {
12938 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12939 : COERCION_IMPLICIT, funcid);
12940 0 : if (ret == COERCION_PATH_NONE)
12941 : /* A previously-relied-upon cast is now gone. */
12942 0 : elog(ERROR, "could not find cast from %u to %u",
12943 : sourceTypeId, targetTypeId);
12944 : }
12945 :
12946 12 : return ret;
12947 : }
12948 :
12949 : /*
12950 : * Permissions checks on the referenced table for ADD FOREIGN KEY
12951 : *
12952 : * Note: we have already checked that the user owns the referencing table,
12953 : * else we'd have failed much earlier; no additional checks are needed for it.
12954 : */
12955 : static void
12956 2376 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12957 : {
12958 2376 : Oid roleid = GetUserId();
12959 : AclResult aclresult;
12960 : int i;
12961 :
12962 : /* Okay if we have relation-level REFERENCES permission */
12963 2376 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12964 : ACL_REFERENCES);
12965 2376 : if (aclresult == ACLCHECK_OK)
12966 2376 : return;
12967 : /* Else we must have REFERENCES on each column */
12968 0 : for (i = 0; i < natts; i++)
12969 : {
12970 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12971 : roleid, ACL_REFERENCES);
12972 0 : if (aclresult != ACLCHECK_OK)
12973 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12974 0 : RelationGetRelationName(rel));
12975 : }
12976 : }
12977 :
12978 : /*
12979 : * Scan the existing rows in a table to verify they meet a proposed FK
12980 : * constraint.
12981 : *
12982 : * Caller must have opened and locked both relations appropriately.
12983 : */
12984 : static void
12985 1100 : validateForeignKeyConstraint(char *conname,
12986 : Relation rel,
12987 : Relation pkrel,
12988 : Oid pkindOid,
12989 : Oid constraintOid,
12990 : bool hasperiod)
12991 : {
12992 : TupleTableSlot *slot;
12993 : TableScanDesc scan;
12994 1100 : Trigger trig = {0};
12995 : Snapshot snapshot;
12996 : MemoryContext oldcxt;
12997 : MemoryContext perTupCxt;
12998 :
12999 1100 : ereport(DEBUG1,
13000 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13001 :
13002 : /*
13003 : * Build a trigger call structure; we'll need it either way.
13004 : */
13005 1100 : trig.tgoid = InvalidOid;
13006 1100 : trig.tgname = conname;
13007 1100 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13008 1100 : trig.tgisinternal = true;
13009 1100 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13010 1100 : trig.tgconstrindid = pkindOid;
13011 1100 : trig.tgconstraint = constraintOid;
13012 1100 : trig.tgdeferrable = false;
13013 1100 : trig.tginitdeferred = false;
13014 : /* we needn't fill in remaining fields */
13015 :
13016 : /*
13017 : * See if we can do it with a single LEFT JOIN query. A false result
13018 : * indicates we must proceed with the fire-the-trigger method. We can't do
13019 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13020 : * left joins.
13021 : */
13022 1100 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13023 924 : return;
13024 :
13025 : /*
13026 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13027 : * if that tuple had just been inserted. If any of those fail, it should
13028 : * ereport(ERROR) and that's that.
13029 : */
13030 108 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13031 108 : slot = table_slot_create(rel, NULL);
13032 108 : scan = table_beginscan(rel, snapshot, 0, NULL);
13033 :
13034 108 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13035 : "validateForeignKeyConstraint",
13036 : ALLOCSET_SMALL_SIZES);
13037 108 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13038 :
13039 192 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13040 : {
13041 102 : LOCAL_FCINFO(fcinfo, 0);
13042 102 : TriggerData trigdata = {0};
13043 :
13044 102 : CHECK_FOR_INTERRUPTS();
13045 :
13046 : /*
13047 : * Make a call to the trigger function
13048 : *
13049 : * No parameters are passed, but we do set a context
13050 : */
13051 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13052 :
13053 : /*
13054 : * We assume RI_FKey_check_ins won't look at flinfo...
13055 : */
13056 102 : trigdata.type = T_TriggerData;
13057 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
13058 102 : trigdata.tg_relation = rel;
13059 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13060 102 : trigdata.tg_trigslot = slot;
13061 102 : trigdata.tg_trigger = &trig;
13062 :
13063 102 : fcinfo->context = (Node *) &trigdata;
13064 :
13065 102 : RI_FKey_check_ins(fcinfo);
13066 :
13067 84 : MemoryContextReset(perTupCxt);
13068 : }
13069 :
13070 90 : MemoryContextSwitchTo(oldcxt);
13071 90 : MemoryContextDelete(perTupCxt);
13072 90 : table_endscan(scan);
13073 90 : UnregisterSnapshot(snapshot);
13074 90 : ExecDropSingleTupleTableSlot(slot);
13075 : }
13076 :
13077 : /*
13078 : * CreateFKCheckTrigger
13079 : * Creates the insert (on_insert=true) or update "check" trigger that
13080 : * implements a given foreign key
13081 : *
13082 : * Returns the OID of the so created trigger.
13083 : */
13084 : static Oid
13085 5620 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13086 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13087 : bool on_insert)
13088 : {
13089 : ObjectAddress trigAddress;
13090 : CreateTrigStmt *fk_trigger;
13091 :
13092 : /*
13093 : * Note: for a self-referential FK (referencing and referenced tables are
13094 : * the same), it is important that the ON UPDATE action fires before the
13095 : * CHECK action, since both triggers will fire on the same row during an
13096 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13097 : * state of the row. Triggers fire in name order, so we ensure this by
13098 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13099 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13100 : */
13101 5620 : fk_trigger = makeNode(CreateTrigStmt);
13102 5620 : fk_trigger->replace = false;
13103 5620 : fk_trigger->isconstraint = true;
13104 5620 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
13105 5620 : fk_trigger->relation = NULL;
13106 :
13107 : /* Either ON INSERT or ON UPDATE */
13108 5620 : if (on_insert)
13109 : {
13110 2810 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13111 2810 : fk_trigger->events = TRIGGER_TYPE_INSERT;
13112 : }
13113 : else
13114 : {
13115 2810 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13116 2810 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13117 : }
13118 :
13119 5620 : fk_trigger->args = NIL;
13120 5620 : fk_trigger->row = true;
13121 5620 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13122 5620 : fk_trigger->columns = NIL;
13123 5620 : fk_trigger->whenClause = NULL;
13124 5620 : fk_trigger->transitionRels = NIL;
13125 5620 : fk_trigger->deferrable = fkconstraint->deferrable;
13126 5620 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13127 5620 : fk_trigger->constrrel = NULL;
13128 :
13129 5620 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13130 : constraintOid, indexOid, InvalidOid,
13131 : parentTrigOid, NULL, true, false);
13132 :
13133 : /* Make changes-so-far visible */
13134 5620 : CommandCounterIncrement();
13135 :
13136 5620 : return trigAddress.objectId;
13137 : }
13138 :
13139 : /*
13140 : * createForeignKeyActionTriggers
13141 : * Create the referenced-side "action" triggers that implement a foreign
13142 : * key.
13143 : *
13144 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
13145 : * *updateTrigOid.
13146 : */
13147 : static void
13148 3102 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
13149 : Oid constraintOid, Oid indexOid,
13150 : Oid parentDelTrigger, Oid parentUpdTrigger,
13151 : Oid *deleteTrigOid, Oid *updateTrigOid)
13152 : {
13153 : CreateTrigStmt *fk_trigger;
13154 : ObjectAddress trigAddress;
13155 :
13156 : /*
13157 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13158 : * DELETE action on the referenced table.
13159 : */
13160 3102 : fk_trigger = makeNode(CreateTrigStmt);
13161 3102 : fk_trigger->replace = false;
13162 3102 : fk_trigger->isconstraint = true;
13163 3102 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13164 3102 : fk_trigger->relation = NULL;
13165 3102 : fk_trigger->args = NIL;
13166 3102 : fk_trigger->row = true;
13167 3102 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13168 3102 : fk_trigger->events = TRIGGER_TYPE_DELETE;
13169 3102 : fk_trigger->columns = NIL;
13170 3102 : fk_trigger->whenClause = NULL;
13171 3102 : fk_trigger->transitionRels = NIL;
13172 3102 : fk_trigger->constrrel = NULL;
13173 :
13174 3102 : switch (fkconstraint->fk_del_action)
13175 : {
13176 2486 : case FKCONSTR_ACTION_NOACTION:
13177 2486 : fk_trigger->deferrable = fkconstraint->deferrable;
13178 2486 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13179 2486 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13180 2486 : break;
13181 30 : case FKCONSTR_ACTION_RESTRICT:
13182 30 : fk_trigger->deferrable = false;
13183 30 : fk_trigger->initdeferred = false;
13184 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13185 30 : break;
13186 428 : case FKCONSTR_ACTION_CASCADE:
13187 428 : fk_trigger->deferrable = false;
13188 428 : fk_trigger->initdeferred = false;
13189 428 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13190 428 : break;
13191 98 : case FKCONSTR_ACTION_SETNULL:
13192 98 : fk_trigger->deferrable = false;
13193 98 : fk_trigger->initdeferred = false;
13194 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13195 98 : break;
13196 60 : case FKCONSTR_ACTION_SETDEFAULT:
13197 60 : fk_trigger->deferrable = false;
13198 60 : fk_trigger->initdeferred = false;
13199 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13200 60 : break;
13201 0 : default:
13202 0 : elog(ERROR, "unrecognized FK action type: %d",
13203 : (int) fkconstraint->fk_del_action);
13204 : break;
13205 : }
13206 :
13207 3102 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
13208 : RelationGetRelid(rel),
13209 : constraintOid, indexOid, InvalidOid,
13210 : parentDelTrigger, NULL, true, false);
13211 3102 : if (deleteTrigOid)
13212 3102 : *deleteTrigOid = trigAddress.objectId;
13213 :
13214 : /* Make changes-so-far visible */
13215 3102 : CommandCounterIncrement();
13216 :
13217 : /*
13218 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13219 : * UPDATE action on the referenced table.
13220 : */
13221 3102 : fk_trigger = makeNode(CreateTrigStmt);
13222 3102 : fk_trigger->replace = false;
13223 3102 : fk_trigger->isconstraint = true;
13224 3102 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13225 3102 : fk_trigger->relation = NULL;
13226 3102 : fk_trigger->args = NIL;
13227 3102 : fk_trigger->row = true;
13228 3102 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13229 3102 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13230 3102 : fk_trigger->columns = NIL;
13231 3102 : fk_trigger->whenClause = NULL;
13232 3102 : fk_trigger->transitionRels = NIL;
13233 3102 : fk_trigger->constrrel = NULL;
13234 :
13235 3102 : switch (fkconstraint->fk_upd_action)
13236 : {
13237 2680 : case FKCONSTR_ACTION_NOACTION:
13238 2680 : fk_trigger->deferrable = fkconstraint->deferrable;
13239 2680 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13240 2680 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13241 2680 : break;
13242 36 : case FKCONSTR_ACTION_RESTRICT:
13243 36 : fk_trigger->deferrable = false;
13244 36 : fk_trigger->initdeferred = false;
13245 36 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13246 36 : break;
13247 282 : case FKCONSTR_ACTION_CASCADE:
13248 282 : fk_trigger->deferrable = false;
13249 282 : fk_trigger->initdeferred = false;
13250 282 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13251 282 : break;
13252 62 : case FKCONSTR_ACTION_SETNULL:
13253 62 : fk_trigger->deferrable = false;
13254 62 : fk_trigger->initdeferred = false;
13255 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13256 62 : break;
13257 42 : case FKCONSTR_ACTION_SETDEFAULT:
13258 42 : fk_trigger->deferrable = false;
13259 42 : fk_trigger->initdeferred = false;
13260 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13261 42 : break;
13262 0 : default:
13263 0 : elog(ERROR, "unrecognized FK action type: %d",
13264 : (int) fkconstraint->fk_upd_action);
13265 : break;
13266 : }
13267 :
13268 3102 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
13269 : RelationGetRelid(rel),
13270 : constraintOid, indexOid, InvalidOid,
13271 : parentUpdTrigger, NULL, true, false);
13272 3102 : if (updateTrigOid)
13273 3102 : *updateTrigOid = trigAddress.objectId;
13274 3102 : }
13275 :
13276 : /*
13277 : * createForeignKeyCheckTriggers
13278 : * Create the referencing-side "check" triggers that implement a foreign
13279 : * key.
13280 : *
13281 : * Returns the OIDs of the so created triggers in *insertTrigOid and
13282 : * *updateTrigOid.
13283 : */
13284 : static void
13285 2810 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
13286 : Constraint *fkconstraint, Oid constraintOid,
13287 : Oid indexOid,
13288 : Oid parentInsTrigger, Oid parentUpdTrigger,
13289 : Oid *insertTrigOid, Oid *updateTrigOid)
13290 : {
13291 2810 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13292 : constraintOid, indexOid,
13293 : parentInsTrigger, true);
13294 2810 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13295 : constraintOid, indexOid,
13296 : parentUpdTrigger, false);
13297 2810 : }
13298 :
13299 : /*
13300 : * ALTER TABLE DROP CONSTRAINT
13301 : *
13302 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13303 : */
13304 : static void
13305 774 : ATExecDropConstraint(Relation rel, const char *constrName,
13306 : DropBehavior behavior, bool recurse,
13307 : bool missing_ok, LOCKMODE lockmode)
13308 : {
13309 : Relation conrel;
13310 : SysScanDesc scan;
13311 : ScanKeyData skey[3];
13312 : HeapTuple tuple;
13313 774 : bool found = false;
13314 :
13315 774 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13316 :
13317 : /*
13318 : * Find and drop the target constraint
13319 : */
13320 774 : ScanKeyInit(&skey[0],
13321 : Anum_pg_constraint_conrelid,
13322 : BTEqualStrategyNumber, F_OIDEQ,
13323 : ObjectIdGetDatum(RelationGetRelid(rel)));
13324 774 : ScanKeyInit(&skey[1],
13325 : Anum_pg_constraint_contypid,
13326 : BTEqualStrategyNumber, F_OIDEQ,
13327 : ObjectIdGetDatum(InvalidOid));
13328 774 : ScanKeyInit(&skey[2],
13329 : Anum_pg_constraint_conname,
13330 : BTEqualStrategyNumber, F_NAMEEQ,
13331 : CStringGetDatum(constrName));
13332 774 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13333 : true, NULL, 3, skey);
13334 :
13335 : /* There can be at most one matching row */
13336 774 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13337 : {
13338 738 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
13339 : missing_ok, lockmode);
13340 552 : found = true;
13341 : }
13342 :
13343 588 : systable_endscan(scan);
13344 :
13345 588 : if (!found)
13346 : {
13347 36 : if (!missing_ok)
13348 24 : ereport(ERROR,
13349 : errcode(ERRCODE_UNDEFINED_OBJECT),
13350 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13351 : constrName, RelationGetRelationName(rel)));
13352 : else
13353 12 : ereport(NOTICE,
13354 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
13355 : constrName, RelationGetRelationName(rel)));
13356 : }
13357 :
13358 564 : table_close(conrel, RowExclusiveLock);
13359 564 : }
13360 :
13361 : /*
13362 : * Remove a constraint, using its pg_constraint tuple
13363 : *
13364 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
13365 : * DROP NOT NULL.
13366 : *
13367 : * Returns the address of the constraint being removed.
13368 : */
13369 : static ObjectAddress
13370 1156 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
13371 : bool recurse, bool recursing, bool missing_ok,
13372 : LOCKMODE lockmode)
13373 : {
13374 : Relation conrel;
13375 : Form_pg_constraint con;
13376 : ObjectAddress conobj;
13377 : List *children;
13378 1156 : bool is_no_inherit_constraint = false;
13379 : char *constrName;
13380 1156 : char *colname = NULL;
13381 :
13382 : /* Guard against stack overflow due to overly deep inheritance tree. */
13383 1156 : check_stack_depth();
13384 :
13385 : /* At top level, permission check was done in ATPrepCmd, else do it */
13386 1156 : if (recursing)
13387 210 : ATSimplePermissions(AT_DropConstraint, rel,
13388 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
13389 :
13390 1150 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13391 :
13392 1150 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
13393 1150 : constrName = NameStr(con->conname);
13394 :
13395 : /* Don't allow drop of inherited constraints */
13396 1150 : if (con->coninhcount > 0 && !recursing)
13397 156 : ereport(ERROR,
13398 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13399 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13400 : constrName, RelationGetRelationName(rel))));
13401 :
13402 : /*
13403 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
13404 : *
13405 : * While doing that, we're in a good position to disallow dropping a not-
13406 : * null constraint underneath a primary key, a replica identity index, or
13407 : * a generated identity column.
13408 : */
13409 994 : if (con->contype == CONSTRAINT_NOTNULL)
13410 : {
13411 284 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
13412 284 : AttrNumber attnum = extractNotNullColumn(constraintTup);
13413 : Bitmapset *pkattrs;
13414 : Bitmapset *irattrs;
13415 : HeapTuple atttup;
13416 : Form_pg_attribute attForm;
13417 :
13418 : /* save column name for recursion step */
13419 284 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13420 :
13421 : /*
13422 : * Disallow if it's in the primary key. For partitioned tables we
13423 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
13424 : * return NULL if the primary key is invalid; but we still need to
13425 : * protect not-null constraints under such a constraint, so check the
13426 : * slow way.
13427 : */
13428 284 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
13429 :
13430 284 : if (pkattrs == NULL &&
13431 248 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13432 : {
13433 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
13434 :
13435 18 : if (OidIsValid(pkindex))
13436 : {
13437 0 : Relation pk = relation_open(pkindex, AccessShareLock);
13438 :
13439 0 : pkattrs = NULL;
13440 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
13441 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
13442 :
13443 0 : relation_close(pk, AccessShareLock);
13444 : }
13445 : }
13446 :
13447 320 : if (pkattrs &&
13448 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
13449 24 : ereport(ERROR,
13450 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13451 : errmsg("column \"%s\" is in a primary key",
13452 : get_attname(RelationGetRelid(rel), attnum, false)));
13453 :
13454 : /* Disallow if it's in the replica identity */
13455 260 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
13456 260 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
13457 12 : ereport(ERROR,
13458 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13459 : errmsg("column \"%s\" is in index used as replica identity",
13460 : get_attname(RelationGetRelid(rel), attnum, false)));
13461 :
13462 : /* Disallow if it's a GENERATED AS IDENTITY column */
13463 248 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
13464 248 : if (!HeapTupleIsValid(atttup))
13465 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13466 : attnum, RelationGetRelid(rel));
13467 248 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13468 248 : if (attForm->attidentity != '\0')
13469 0 : ereport(ERROR,
13470 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13471 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
13472 : get_attname(RelationGetRelid(rel), attnum,
13473 : false),
13474 : RelationGetRelationName(rel)));
13475 :
13476 : /* All good -- reset attnotnull if needed */
13477 248 : if (attForm->attnotnull)
13478 : {
13479 248 : attForm->attnotnull = false;
13480 248 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
13481 : }
13482 :
13483 248 : table_close(attrel, RowExclusiveLock);
13484 : }
13485 :
13486 958 : is_no_inherit_constraint = con->connoinherit;
13487 :
13488 : /*
13489 : * If it's a foreign-key constraint, we'd better lock the referenced table
13490 : * and check that that's not in use, just as we've already done for the
13491 : * constrained table (else we might, eg, be dropping a trigger that has
13492 : * unfired events). But we can/must skip that in the self-referential
13493 : * case.
13494 : */
13495 958 : if (con->contype == CONSTRAINT_FOREIGN &&
13496 168 : con->confrelid != RelationGetRelid(rel))
13497 : {
13498 : Relation frel;
13499 :
13500 : /* Must match lock taken by RemoveTriggerById: */
13501 168 : frel = table_open(con->confrelid, AccessExclusiveLock);
13502 168 : CheckAlterTableIsSafe(frel);
13503 162 : table_close(frel, NoLock);
13504 : }
13505 :
13506 : /*
13507 : * Perform the actual constraint deletion
13508 : */
13509 952 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13510 952 : performDeletion(&conobj, behavior, 0);
13511 :
13512 : /*
13513 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13514 : * are dropped via the dependency mechanism, so we're done here.
13515 : */
13516 916 : if (con->contype != CONSTRAINT_CHECK &&
13517 598 : con->contype != CONSTRAINT_NOTNULL &&
13518 350 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13519 : {
13520 78 : table_close(conrel, RowExclusiveLock);
13521 78 : return conobj;
13522 : }
13523 :
13524 : /*
13525 : * Propagate to children as appropriate. Unlike most other ALTER
13526 : * routines, we have to do this one level of recursion at a time; we can't
13527 : * use find_all_inheritors to do it in one pass.
13528 : */
13529 838 : if (!is_no_inherit_constraint)
13530 554 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13531 : else
13532 284 : children = NIL;
13533 :
13534 2042 : foreach_oid(childrelid, children)
13535 : {
13536 : Relation childrel;
13537 : HeapTuple tuple;
13538 : Form_pg_constraint childcon;
13539 :
13540 : /* find_inheritance_children already got lock */
13541 378 : childrel = table_open(childrelid, NoLock);
13542 378 : CheckAlterTableIsSafe(childrel);
13543 :
13544 : /*
13545 : * We search for not-null constraints by column name, and others by
13546 : * constraint name.
13547 : */
13548 378 : if (con->contype == CONSTRAINT_NOTNULL)
13549 : {
13550 142 : tuple = findNotNullConstraint(childrelid, colname);
13551 142 : if (!HeapTupleIsValid(tuple))
13552 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13553 : colname, RelationGetRelid(childrel));
13554 : }
13555 : else
13556 : {
13557 : SysScanDesc scan;
13558 : ScanKeyData skey[3];
13559 :
13560 236 : ScanKeyInit(&skey[0],
13561 : Anum_pg_constraint_conrelid,
13562 : BTEqualStrategyNumber, F_OIDEQ,
13563 : ObjectIdGetDatum(childrelid));
13564 236 : ScanKeyInit(&skey[1],
13565 : Anum_pg_constraint_contypid,
13566 : BTEqualStrategyNumber, F_OIDEQ,
13567 : ObjectIdGetDatum(InvalidOid));
13568 236 : ScanKeyInit(&skey[2],
13569 : Anum_pg_constraint_conname,
13570 : BTEqualStrategyNumber, F_NAMEEQ,
13571 : CStringGetDatum(constrName));
13572 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13573 : true, NULL, 3, skey);
13574 : /* There can only be one, so no need to loop */
13575 236 : tuple = systable_getnext(scan);
13576 236 : if (!HeapTupleIsValid(tuple))
13577 0 : ereport(ERROR,
13578 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13579 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13580 : constrName,
13581 : RelationGetRelationName(childrel))));
13582 236 : tuple = heap_copytuple(tuple);
13583 236 : systable_endscan(scan);
13584 : }
13585 :
13586 378 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13587 :
13588 : /* Right now only CHECK and not-null constraints can be inherited */
13589 378 : if (childcon->contype != CONSTRAINT_CHECK &&
13590 142 : childcon->contype != CONSTRAINT_NOTNULL)
13591 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13592 :
13593 378 : if (childcon->coninhcount <= 0) /* shouldn't happen */
13594 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13595 : childrelid, NameStr(childcon->conname));
13596 :
13597 378 : if (recurse)
13598 : {
13599 : /*
13600 : * If the child constraint has other definition sources, just
13601 : * decrement its inheritance count; if not, recurse to delete it.
13602 : */
13603 276 : if (childcon->coninhcount == 1 && !childcon->conislocal)
13604 : {
13605 : /* Time to delete this child constraint, too */
13606 210 : dropconstraint_internal(childrel, tuple, behavior,
13607 : recurse, true, missing_ok,
13608 : lockmode);
13609 : }
13610 : else
13611 : {
13612 : /* Child constraint must survive my deletion */
13613 66 : childcon->coninhcount--;
13614 66 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13615 :
13616 : /* Make update visible */
13617 66 : CommandCounterIncrement();
13618 : }
13619 : }
13620 : else
13621 : {
13622 : /*
13623 : * If we were told to drop ONLY in this table (no recursion) and
13624 : * there are no further parents for this constraint, we need to
13625 : * mark the inheritors' constraints as locally defined rather than
13626 : * inherited.
13627 : */
13628 102 : childcon->coninhcount--;
13629 102 : if (childcon->coninhcount == 0)
13630 102 : childcon->conislocal = true;
13631 :
13632 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13633 :
13634 : /* Make update visible */
13635 102 : CommandCounterIncrement();
13636 : }
13637 :
13638 372 : heap_freetuple(tuple);
13639 :
13640 372 : table_close(childrel, NoLock);
13641 : }
13642 :
13643 832 : table_close(conrel, RowExclusiveLock);
13644 :
13645 832 : return conobj;
13646 : }
13647 :
13648 : /*
13649 : * ALTER COLUMN TYPE
13650 : *
13651 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13652 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13653 : * transformed (and must be, because we rely on some transformed fields).
13654 : *
13655 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13656 : * table will be done "in parallel" during phase 3, so all the USING
13657 : * expressions should be parsed assuming the original column types. Also,
13658 : * this allows a USING expression to refer to a field that will be dropped.
13659 : *
13660 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13661 : * the first two execution steps in phase 2; they must not see the effects
13662 : * of any other subcommand types, since the USING expressions are parsed
13663 : * against the unmodified table's state.
13664 : */
13665 : static void
13666 1264 : ATPrepAlterColumnType(List **wqueue,
13667 : AlteredTableInfo *tab, Relation rel,
13668 : bool recurse, bool recursing,
13669 : AlterTableCmd *cmd, LOCKMODE lockmode,
13670 : AlterTableUtilityContext *context)
13671 : {
13672 1264 : char *colName = cmd->name;
13673 1264 : ColumnDef *def = (ColumnDef *) cmd->def;
13674 1264 : TypeName *typeName = def->typeName;
13675 1264 : Node *transform = def->cooked_default;
13676 : HeapTuple tuple;
13677 : Form_pg_attribute attTup;
13678 : AttrNumber attnum;
13679 : Oid targettype;
13680 : int32 targettypmod;
13681 : Oid targetcollid;
13682 : NewColumnValue *newval;
13683 1264 : ParseState *pstate = make_parsestate(NULL);
13684 : AclResult aclresult;
13685 : bool is_expr;
13686 :
13687 1264 : pstate->p_sourcetext = context->queryString;
13688 :
13689 1264 : if (rel->rd_rel->reloftype && !recursing)
13690 6 : ereport(ERROR,
13691 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13692 : errmsg("cannot alter column type of typed table"),
13693 : parser_errposition(pstate, def->location)));
13694 :
13695 : /* lookup the attribute so we can check inheritance status */
13696 1258 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13697 1258 : if (!HeapTupleIsValid(tuple))
13698 0 : ereport(ERROR,
13699 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13700 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13701 : colName, RelationGetRelationName(rel)),
13702 : parser_errposition(pstate, def->location)));
13703 1258 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13704 1258 : attnum = attTup->attnum;
13705 :
13706 : /* Can't alter a system attribute */
13707 1258 : if (attnum <= 0)
13708 6 : ereport(ERROR,
13709 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13710 : errmsg("cannot alter system column \"%s\"", colName),
13711 : parser_errposition(pstate, def->location)));
13712 :
13713 : /*
13714 : * Cannot specify USING when altering type of a generated column, because
13715 : * that would violate the generation expression.
13716 : */
13717 1252 : if (attTup->attgenerated && def->cooked_default)
13718 12 : ereport(ERROR,
13719 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
13720 : errmsg("cannot specify USING when altering type of generated column"),
13721 : errdetail("Column \"%s\" is a generated column.", colName),
13722 : parser_errposition(pstate, def->location)));
13723 :
13724 : /*
13725 : * Don't alter inherited columns. At outer level, there had better not be
13726 : * any inherited definition; when recursing, we assume this was checked at
13727 : * the parent level (see below).
13728 : */
13729 1240 : if (attTup->attinhcount > 0 && !recursing)
13730 6 : ereport(ERROR,
13731 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13732 : errmsg("cannot alter inherited column \"%s\"", colName),
13733 : parser_errposition(pstate, def->location)));
13734 :
13735 : /* Don't alter columns used in the partition key */
13736 1234 : if (has_partition_attrs(rel,
13737 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13738 : &is_expr))
13739 18 : ereport(ERROR,
13740 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13741 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13742 : colName, RelationGetRelationName(rel)),
13743 : parser_errposition(pstate, def->location)));
13744 :
13745 : /* Look up the target type */
13746 1216 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
13747 :
13748 1210 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13749 1210 : if (aclresult != ACLCHECK_OK)
13750 12 : aclcheck_error_type(aclresult, targettype);
13751 :
13752 : /* And the collation */
13753 1198 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
13754 :
13755 : /* make sure datatype is legal for a column */
13756 1192 : CheckAttributeType(colName, targettype, targetcollid,
13757 1192 : list_make1_oid(rel->rd_rel->reltype),
13758 : 0);
13759 :
13760 1186 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
13761 : {
13762 : /* do nothing */
13763 : }
13764 1150 : else if (tab->relkind == RELKIND_RELATION ||
13765 202 : tab->relkind == RELKIND_PARTITIONED_TABLE)
13766 : {
13767 : /*
13768 : * Set up an expression to transform the old data value to the new
13769 : * type. If a USING option was given, use the expression as
13770 : * transformed by transformAlterTableStmt, else just take the old
13771 : * value and try to coerce it. We do this first so that type
13772 : * incompatibility can be detected before we waste effort, and because
13773 : * we need the expression to be parsed against the original table row
13774 : * type.
13775 : */
13776 1014 : if (!transform)
13777 : {
13778 792 : transform = (Node *) makeVar(1, attnum,
13779 : attTup->atttypid, attTup->atttypmod,
13780 : attTup->attcollation,
13781 : 0);
13782 : }
13783 :
13784 1014 : transform = coerce_to_target_type(pstate,
13785 : transform, exprType(transform),
13786 : targettype, targettypmod,
13787 : COERCION_ASSIGNMENT,
13788 : COERCE_IMPLICIT_CAST,
13789 : -1);
13790 1014 : if (transform == NULL)
13791 : {
13792 : /* error text depends on whether USING was specified or not */
13793 24 : if (def->cooked_default != NULL)
13794 6 : ereport(ERROR,
13795 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13796 : errmsg("result of USING clause for column \"%s\""
13797 : " cannot be cast automatically to type %s",
13798 : colName, format_type_be(targettype)),
13799 : errhint("You might need to add an explicit cast.")));
13800 : else
13801 18 : ereport(ERROR,
13802 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13803 : errmsg("column \"%s\" cannot be cast automatically to type %s",
13804 : colName, format_type_be(targettype)),
13805 : !attTup->attgenerated ?
13806 : /* translator: USING is SQL, don't translate it */
13807 : errhint("You might need to specify \"USING %s::%s\".",
13808 : quote_identifier(colName),
13809 : format_type_with_typemod(targettype,
13810 : targettypmod)) : 0));
13811 : }
13812 :
13813 : /* Fix collations after all else */
13814 990 : assign_expr_collations(pstate, transform);
13815 :
13816 : /* Plan the expr now so we can accurately assess the need to rewrite. */
13817 990 : transform = (Node *) expression_planner((Expr *) transform);
13818 :
13819 : /*
13820 : * Add a work queue item to make ATRewriteTable update the column
13821 : * contents.
13822 : */
13823 990 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13824 990 : newval->attnum = attnum;
13825 990 : newval->expr = (Expr *) transform;
13826 990 : newval->is_generated = false;
13827 :
13828 990 : tab->newvals = lappend(tab->newvals, newval);
13829 990 : if (ATColumnChangeRequiresRewrite(transform, attnum))
13830 794 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13831 : }
13832 136 : else if (transform)
13833 12 : ereport(ERROR,
13834 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13835 : errmsg("\"%s\" is not a table",
13836 : RelationGetRelationName(rel))));
13837 :
13838 1150 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
13839 : {
13840 : /*
13841 : * For relations or columns without storage, do this check now.
13842 : * Regular tables will check it later when the table is being
13843 : * rewritten.
13844 : */
13845 226 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13846 : }
13847 :
13848 1102 : ReleaseSysCache(tuple);
13849 :
13850 : /*
13851 : * Recurse manually by queueing a new command for each child, if
13852 : * necessary. We cannot apply ATSimpleRecursion here because we need to
13853 : * remap attribute numbers in the USING expression, if any.
13854 : *
13855 : * If we are told not to recurse, there had better not be any child
13856 : * tables; else the alter would put them out of step.
13857 : */
13858 1102 : if (recurse)
13859 : {
13860 844 : Oid relid = RelationGetRelid(rel);
13861 : List *child_oids,
13862 : *child_numparents;
13863 : ListCell *lo,
13864 : *li;
13865 :
13866 844 : child_oids = find_all_inheritors(relid, lockmode,
13867 : &child_numparents);
13868 :
13869 : /*
13870 : * find_all_inheritors does the recursive search of the inheritance
13871 : * hierarchy, so all we have to do is process all of the relids in the
13872 : * list that it returns.
13873 : */
13874 1896 : forboth(lo, child_oids, li, child_numparents)
13875 : {
13876 1076 : Oid childrelid = lfirst_oid(lo);
13877 1076 : int numparents = lfirst_int(li);
13878 : Relation childrel;
13879 : HeapTuple childtuple;
13880 : Form_pg_attribute childattTup;
13881 :
13882 1076 : if (childrelid == relid)
13883 844 : continue;
13884 :
13885 : /* find_all_inheritors already got lock */
13886 232 : childrel = relation_open(childrelid, NoLock);
13887 232 : CheckAlterTableIsSafe(childrel);
13888 :
13889 : /*
13890 : * Verify that the child doesn't have any inherited definitions of
13891 : * this column that came from outside this inheritance hierarchy.
13892 : * (renameatt makes a similar test, though in a different way
13893 : * because of its different recursion mechanism.)
13894 : */
13895 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13896 : colName);
13897 232 : if (!HeapTupleIsValid(childtuple))
13898 0 : ereport(ERROR,
13899 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13900 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13901 : colName, RelationGetRelationName(childrel))));
13902 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13903 :
13904 232 : if (childattTup->attinhcount > numparents)
13905 6 : ereport(ERROR,
13906 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13907 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13908 : colName, RelationGetRelationName(childrel))));
13909 :
13910 226 : ReleaseSysCache(childtuple);
13911 :
13912 : /*
13913 : * Remap the attribute numbers. If no USING expression was
13914 : * specified, there is no need for this step.
13915 : */
13916 226 : if (def->cooked_default)
13917 : {
13918 : AttrMap *attmap;
13919 : bool found_whole_row;
13920 :
13921 : /* create a copy to scribble on */
13922 78 : cmd = copyObject(cmd);
13923 :
13924 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13925 : RelationGetDescr(rel),
13926 : false);
13927 156 : ((ColumnDef *) cmd->def)->cooked_default =
13928 78 : map_variable_attnos(def->cooked_default,
13929 : 1, 0,
13930 : attmap,
13931 : InvalidOid, &found_whole_row);
13932 78 : if (found_whole_row)
13933 6 : ereport(ERROR,
13934 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13935 : errmsg("cannot convert whole-row table reference"),
13936 : errdetail("USING expression contains a whole-row table reference.")));
13937 72 : pfree(attmap);
13938 : }
13939 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13940 208 : relation_close(childrel, NoLock);
13941 : }
13942 : }
13943 308 : else if (!recursing &&
13944 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13945 0 : ereport(ERROR,
13946 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13947 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
13948 : colName)));
13949 :
13950 1078 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13951 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13952 1072 : }
13953 :
13954 : /*
13955 : * When the data type of a column is changed, a rewrite might not be required
13956 : * if the new type is sufficiently identical to the old one, and the USING
13957 : * clause isn't trying to insert some other value. It's safe to skip the
13958 : * rewrite in these cases:
13959 : *
13960 : * - the old type is binary coercible to the new type
13961 : * - the new type is an unconstrained domain over the old type
13962 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13963 : *
13964 : * In the case of a constrained domain, we could get by with scanning the
13965 : * table and checking the constraint rather than actually rewriting it, but we
13966 : * don't currently try to do that.
13967 : */
13968 : static bool
13969 1108 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13970 : {
13971 : Assert(expr != NULL);
13972 :
13973 : for (;;)
13974 : {
13975 : /* only one varno, so no need to check that */
13976 1108 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13977 196 : return false;
13978 912 : else if (IsA(expr, RelabelType))
13979 106 : expr = (Node *) ((RelabelType *) expr)->arg;
13980 806 : else if (IsA(expr, CoerceToDomain))
13981 : {
13982 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
13983 :
13984 0 : if (DomainHasConstraints(d->resulttype))
13985 0 : return true;
13986 0 : expr = (Node *) d->arg;
13987 : }
13988 806 : else if (IsA(expr, FuncExpr))
13989 : {
13990 600 : FuncExpr *f = (FuncExpr *) expr;
13991 :
13992 600 : switch (f->funcid)
13993 : {
13994 18 : case F_TIMESTAMPTZ_TIMESTAMP:
13995 : case F_TIMESTAMP_TIMESTAMPTZ:
13996 18 : if (TimestampTimestampTzRequiresRewrite())
13997 6 : return true;
13998 : else
13999 12 : expr = linitial(f->args);
14000 12 : break;
14001 582 : default:
14002 582 : return true;
14003 : }
14004 : }
14005 : else
14006 206 : return true;
14007 : }
14008 : }
14009 :
14010 : /*
14011 : * ALTER COLUMN .. SET DATA TYPE
14012 : *
14013 : * Return the address of the modified column.
14014 : */
14015 : static ObjectAddress
14016 1036 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14017 : AlterTableCmd *cmd, LOCKMODE lockmode)
14018 : {
14019 1036 : char *colName = cmd->name;
14020 1036 : ColumnDef *def = (ColumnDef *) cmd->def;
14021 1036 : TypeName *typeName = def->typeName;
14022 : HeapTuple heapTup;
14023 : Form_pg_attribute attTup,
14024 : attOldTup;
14025 : AttrNumber attnum;
14026 : HeapTuple typeTuple;
14027 : Form_pg_type tform;
14028 : Oid targettype;
14029 : int32 targettypmod;
14030 : Oid targetcollid;
14031 : Node *defaultexpr;
14032 : Relation attrelation;
14033 : Relation depRel;
14034 : ScanKeyData key[3];
14035 : SysScanDesc scan;
14036 : HeapTuple depTup;
14037 : ObjectAddress address;
14038 :
14039 : /*
14040 : * Clear all the missing values if we're rewriting the table, since this
14041 : * renders them pointless.
14042 : */
14043 1036 : if (tab->rewrite)
14044 : {
14045 : Relation newrel;
14046 :
14047 734 : newrel = table_open(RelationGetRelid(rel), NoLock);
14048 734 : RelationClearMissing(newrel);
14049 734 : relation_close(newrel, NoLock);
14050 : /* make sure we don't conflict with later attribute modifications */
14051 734 : CommandCounterIncrement();
14052 : }
14053 :
14054 1036 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14055 :
14056 : /* Look up the target column */
14057 1036 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14058 1036 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14059 0 : ereport(ERROR,
14060 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14061 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14062 : colName, RelationGetRelationName(rel))));
14063 1036 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14064 1036 : attnum = attTup->attnum;
14065 1036 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14066 :
14067 : /* Check for multiple ALTER TYPE on same column --- can't cope */
14068 1036 : if (attTup->atttypid != attOldTup->atttypid ||
14069 1036 : attTup->atttypmod != attOldTup->atttypmod)
14070 0 : ereport(ERROR,
14071 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14072 : errmsg("cannot alter type of column \"%s\" twice",
14073 : colName)));
14074 :
14075 : /* Look up the target type (should not fail, since prep found it) */
14076 1036 : typeTuple = typenameType(NULL, typeName, &targettypmod);
14077 1036 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
14078 1036 : targettype = tform->oid;
14079 : /* And the collation */
14080 1036 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
14081 :
14082 : /*
14083 : * If there is a default expression for the column, get it and ensure we
14084 : * can coerce it to the new datatype. (We must do this before changing
14085 : * the column type, because build_column_default itself will try to
14086 : * coerce, and will not issue the error message we want if it fails.)
14087 : *
14088 : * We remove any implicit coercion steps at the top level of the old
14089 : * default expression; this has been agreed to satisfy the principle of
14090 : * least surprise. (The conversion to the new column type should act like
14091 : * it started from what the user sees as the stored expression, and the
14092 : * implicit coercions aren't going to be shown.)
14093 : */
14094 1036 : if (attTup->atthasdef)
14095 : {
14096 92 : defaultexpr = build_column_default(rel, attnum);
14097 : Assert(defaultexpr);
14098 92 : defaultexpr = strip_implicit_coercions(defaultexpr);
14099 92 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14100 : defaultexpr, exprType(defaultexpr),
14101 : targettype, targettypmod,
14102 : COERCION_ASSIGNMENT,
14103 : COERCE_IMPLICIT_CAST,
14104 : -1);
14105 92 : if (defaultexpr == NULL)
14106 : {
14107 6 : if (attTup->attgenerated)
14108 0 : ereport(ERROR,
14109 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14110 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14111 : colName, format_type_be(targettype))));
14112 : else
14113 6 : ereport(ERROR,
14114 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14115 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14116 : colName, format_type_be(targettype))));
14117 : }
14118 : }
14119 : else
14120 944 : defaultexpr = NULL;
14121 :
14122 : /*
14123 : * Find everything that depends on the column (constraints, indexes, etc),
14124 : * and record enough information to let us recreate the objects.
14125 : *
14126 : * The actual recreation does not happen here, but only after we have
14127 : * performed all the individual ALTER TYPE operations. We have to save
14128 : * the info before executing ALTER TYPE, though, else the deparser will
14129 : * get confused.
14130 : */
14131 1030 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
14132 :
14133 : /*
14134 : * Now scan for dependencies of this column on other things. The only
14135 : * things we should find are the dependency on the column datatype and
14136 : * possibly a collation dependency. Those can be removed.
14137 : */
14138 994 : depRel = table_open(DependRelationId, RowExclusiveLock);
14139 :
14140 994 : ScanKeyInit(&key[0],
14141 : Anum_pg_depend_classid,
14142 : BTEqualStrategyNumber, F_OIDEQ,
14143 : ObjectIdGetDatum(RelationRelationId));
14144 994 : ScanKeyInit(&key[1],
14145 : Anum_pg_depend_objid,
14146 : BTEqualStrategyNumber, F_OIDEQ,
14147 : ObjectIdGetDatum(RelationGetRelid(rel)));
14148 994 : ScanKeyInit(&key[2],
14149 : Anum_pg_depend_objsubid,
14150 : BTEqualStrategyNumber, F_INT4EQ,
14151 : Int32GetDatum((int32) attnum));
14152 :
14153 994 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
14154 : NULL, 3, key);
14155 :
14156 998 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14157 : {
14158 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14159 : ObjectAddress foundObject;
14160 :
14161 4 : foundObject.classId = foundDep->refclassid;
14162 4 : foundObject.objectId = foundDep->refobjid;
14163 4 : foundObject.objectSubId = foundDep->refobjsubid;
14164 :
14165 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
14166 0 : elog(ERROR, "found unexpected dependency type '%c'",
14167 : foundDep->deptype);
14168 4 : if (!(foundDep->refclassid == TypeRelationId &&
14169 4 : foundDep->refobjid == attTup->atttypid) &&
14170 0 : !(foundDep->refclassid == CollationRelationId &&
14171 0 : foundDep->refobjid == attTup->attcollation))
14172 0 : elog(ERROR, "found unexpected dependency for column: %s",
14173 : getObjectDescription(&foundObject, false));
14174 :
14175 4 : CatalogTupleDelete(depRel, &depTup->t_self);
14176 : }
14177 :
14178 994 : systable_endscan(scan);
14179 :
14180 994 : table_close(depRel, RowExclusiveLock);
14181 :
14182 : /*
14183 : * Here we go --- change the recorded column type and collation. (Note
14184 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14185 : * fix up the missing value if any.
14186 : */
14187 994 : if (attTup->atthasmissing)
14188 : {
14189 : Datum missingval;
14190 : bool missingNull;
14191 :
14192 : /* if rewrite is true the missing value should already be cleared */
14193 : Assert(tab->rewrite == 0);
14194 :
14195 : /* Get the missing value datum */
14196 6 : missingval = heap_getattr(heapTup,
14197 : Anum_pg_attribute_attmissingval,
14198 : attrelation->rd_att,
14199 : &missingNull);
14200 :
14201 : /* if it's a null array there is nothing to do */
14202 :
14203 6 : if (!missingNull)
14204 : {
14205 : /*
14206 : * Get the datum out of the array and repack it in a new array
14207 : * built with the new type data. We assume that since the table
14208 : * doesn't need rewriting, the actual Datum doesn't need to be
14209 : * changed, only the array metadata.
14210 : */
14211 :
14212 6 : int one = 1;
14213 : bool isNull;
14214 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
14215 6 : bool nullsAtt[Natts_pg_attribute] = {0};
14216 6 : bool replacesAtt[Natts_pg_attribute] = {0};
14217 : HeapTuple newTup;
14218 :
14219 12 : missingval = array_get_element(missingval,
14220 : 1,
14221 : &one,
14222 : 0,
14223 6 : attTup->attlen,
14224 6 : attTup->attbyval,
14225 6 : attTup->attalign,
14226 : &isNull);
14227 6 : missingval = PointerGetDatum(construct_array(&missingval,
14228 : 1,
14229 : targettype,
14230 6 : tform->typlen,
14231 6 : tform->typbyval,
14232 6 : tform->typalign));
14233 :
14234 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14235 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14236 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14237 :
14238 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14239 : valuesAtt, nullsAtt, replacesAtt);
14240 6 : heap_freetuple(heapTup);
14241 6 : heapTup = newTup;
14242 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14243 : }
14244 : }
14245 :
14246 994 : attTup->atttypid = targettype;
14247 994 : attTup->atttypmod = targettypmod;
14248 994 : attTup->attcollation = targetcollid;
14249 994 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14250 0 : ereport(ERROR,
14251 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14252 : errmsg("too many array dimensions"));
14253 994 : attTup->attndims = list_length(typeName->arrayBounds);
14254 994 : attTup->attlen = tform->typlen;
14255 994 : attTup->attbyval = tform->typbyval;
14256 994 : attTup->attalign = tform->typalign;
14257 994 : attTup->attstorage = tform->typstorage;
14258 994 : attTup->attcompression = InvalidCompressionMethod;
14259 :
14260 994 : ReleaseSysCache(typeTuple);
14261 :
14262 994 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14263 :
14264 994 : table_close(attrelation, RowExclusiveLock);
14265 :
14266 : /* Install dependencies on new datatype and collation */
14267 994 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
14268 994 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
14269 :
14270 : /*
14271 : * Drop any pg_statistic entry for the column, since it's now wrong type
14272 : */
14273 994 : RemoveStatistics(RelationGetRelid(rel), attnum);
14274 :
14275 994 : InvokeObjectPostAlterHook(RelationRelationId,
14276 : RelationGetRelid(rel), attnum);
14277 :
14278 : /*
14279 : * Update the default, if present, by brute force --- remove and re-add
14280 : * the default. Probably unsafe to take shortcuts, since the new version
14281 : * may well have additional dependencies. (It's okay to do this now,
14282 : * rather than after other ALTER TYPE commands, since the default won't
14283 : * depend on other column types.)
14284 : */
14285 994 : if (defaultexpr)
14286 : {
14287 : /*
14288 : * If it's a GENERATED default, drop its dependency records, in
14289 : * particular its INTERNAL dependency on the column, which would
14290 : * otherwise cause dependency.c to refuse to perform the deletion.
14291 : */
14292 86 : if (attTup->attgenerated)
14293 : {
14294 36 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14295 :
14296 36 : if (!OidIsValid(attrdefoid))
14297 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14298 : RelationGetRelid(rel), attnum);
14299 36 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14300 : }
14301 :
14302 : /*
14303 : * Make updates-so-far visible, particularly the new pg_attribute row
14304 : * which will be updated again.
14305 : */
14306 86 : CommandCounterIncrement();
14307 :
14308 : /*
14309 : * We use RESTRICT here for safety, but at present we do not expect
14310 : * anything to depend on the default.
14311 : */
14312 86 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14313 : true);
14314 :
14315 86 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14316 : }
14317 :
14318 994 : ObjectAddressSubSet(address, RelationRelationId,
14319 : RelationGetRelid(rel), attnum);
14320 :
14321 : /* Cleanup */
14322 994 : heap_freetuple(heapTup);
14323 :
14324 994 : return address;
14325 : }
14326 :
14327 : /*
14328 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14329 : * that depends on the column (constraints, indexes, etc), and record enough
14330 : * information to let us recreate the objects.
14331 : */
14332 : static void
14333 1108 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
14334 : Relation rel, AttrNumber attnum, const char *colName)
14335 : {
14336 : Relation depRel;
14337 : ScanKeyData key[3];
14338 : SysScanDesc scan;
14339 : HeapTuple depTup;
14340 :
14341 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14342 :
14343 1108 : depRel = table_open(DependRelationId, RowExclusiveLock);
14344 :
14345 1108 : ScanKeyInit(&key[0],
14346 : Anum_pg_depend_refclassid,
14347 : BTEqualStrategyNumber, F_OIDEQ,
14348 : ObjectIdGetDatum(RelationRelationId));
14349 1108 : ScanKeyInit(&key[1],
14350 : Anum_pg_depend_refobjid,
14351 : BTEqualStrategyNumber, F_OIDEQ,
14352 : ObjectIdGetDatum(RelationGetRelid(rel)));
14353 1108 : ScanKeyInit(&key[2],
14354 : Anum_pg_depend_refobjsubid,
14355 : BTEqualStrategyNumber, F_INT4EQ,
14356 : Int32GetDatum((int32) attnum));
14357 :
14358 1108 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14359 : NULL, 3, key);
14360 :
14361 2234 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14362 : {
14363 1162 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14364 : ObjectAddress foundObject;
14365 :
14366 1162 : foundObject.classId = foundDep->classid;
14367 1162 : foundObject.objectId = foundDep->objid;
14368 1162 : foundObject.objectSubId = foundDep->objsubid;
14369 :
14370 1162 : switch (foundObject.classId)
14371 : {
14372 274 : case RelationRelationId:
14373 : {
14374 274 : char relKind = get_rel_relkind(foundObject.objectId);
14375 :
14376 274 : if (relKind == RELKIND_INDEX ||
14377 : relKind == RELKIND_PARTITIONED_INDEX)
14378 : {
14379 : Assert(foundObject.objectSubId == 0);
14380 236 : RememberIndexForRebuilding(foundObject.objectId, tab);
14381 : }
14382 38 : else if (relKind == RELKIND_SEQUENCE)
14383 : {
14384 : /*
14385 : * This must be a SERIAL column's sequence. We need
14386 : * not do anything to it.
14387 : */
14388 : Assert(foundObject.objectSubId == 0);
14389 : }
14390 : else
14391 : {
14392 : /* Not expecting any other direct dependencies... */
14393 0 : elog(ERROR, "unexpected object depending on column: %s",
14394 : getObjectDescription(&foundObject, false));
14395 : }
14396 274 : break;
14397 : }
14398 :
14399 674 : case ConstraintRelationId:
14400 : Assert(foundObject.objectSubId == 0);
14401 674 : RememberConstraintForRebuilding(foundObject.objectId, tab);
14402 674 : break;
14403 :
14404 0 : case ProcedureRelationId:
14405 :
14406 : /*
14407 : * A new-style SQL function can depend on a column, if that
14408 : * column is referenced in the parsed function body. Ideally
14409 : * we'd automatically update the function by deparsing and
14410 : * reparsing it, but that's risky and might well fail anyhow.
14411 : * FIXME someday.
14412 : *
14413 : * This is only a problem for AT_AlterColumnType, not
14414 : * AT_SetExpression.
14415 : */
14416 0 : if (subtype == AT_AlterColumnType)
14417 0 : ereport(ERROR,
14418 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14419 : errmsg("cannot alter type of a column used by a function or procedure"),
14420 : errdetail("%s depends on column \"%s\"",
14421 : getObjectDescription(&foundObject, false),
14422 : colName)));
14423 0 : break;
14424 :
14425 12 : case RewriteRelationId:
14426 :
14427 : /*
14428 : * View/rule bodies have pretty much the same issues as
14429 : * function bodies. FIXME someday.
14430 : */
14431 12 : if (subtype == AT_AlterColumnType)
14432 12 : ereport(ERROR,
14433 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14434 : errmsg("cannot alter type of a column used by a view or rule"),
14435 : errdetail("%s depends on column \"%s\"",
14436 : getObjectDescription(&foundObject, false),
14437 : colName)));
14438 0 : break;
14439 :
14440 0 : case TriggerRelationId:
14441 :
14442 : /*
14443 : * A trigger can depend on a column because the column is
14444 : * specified as an update target, or because the column is
14445 : * used in the trigger's WHEN condition. The first case would
14446 : * not require any extra work, but the second case would
14447 : * require updating the WHEN expression, which has the same
14448 : * issues as above. Since we can't easily tell which case
14449 : * applies, we punt for both. FIXME someday.
14450 : */
14451 0 : if (subtype == AT_AlterColumnType)
14452 0 : ereport(ERROR,
14453 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14454 : errmsg("cannot alter type of a column used in a trigger definition"),
14455 : errdetail("%s depends on column \"%s\"",
14456 : getObjectDescription(&foundObject, false),
14457 : colName)));
14458 0 : break;
14459 :
14460 0 : case PolicyRelationId:
14461 :
14462 : /*
14463 : * A policy can depend on a column because the column is
14464 : * specified in the policy's USING or WITH CHECK qual
14465 : * expressions. It might be possible to rewrite and recheck
14466 : * the policy expression, but punt for now. It's certainly
14467 : * easy enough to remove and recreate the policy; still, FIXME
14468 : * someday.
14469 : */
14470 0 : if (subtype == AT_AlterColumnType)
14471 0 : ereport(ERROR,
14472 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14473 : errmsg("cannot alter type of a column used in a policy definition"),
14474 : errdetail("%s depends on column \"%s\"",
14475 : getObjectDescription(&foundObject, false),
14476 : colName)));
14477 0 : break;
14478 :
14479 188 : case AttrDefaultRelationId:
14480 : {
14481 188 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14482 :
14483 188 : if (col.objectId == RelationGetRelid(rel) &&
14484 188 : col.objectSubId == attnum)
14485 : {
14486 : /*
14487 : * Ignore the column's own default expression. The
14488 : * caller deals with it.
14489 : */
14490 : }
14491 : else
14492 : {
14493 : /*
14494 : * This must be a reference from the expression of a
14495 : * generated column elsewhere in the same table.
14496 : * Changing the type/generated expression of a column
14497 : * that is used by a generated column is not allowed
14498 : * by SQL standard, so just punt for now. It might be
14499 : * doable with some thinking and effort.
14500 : */
14501 24 : if (subtype == AT_AlterColumnType)
14502 24 : ereport(ERROR,
14503 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14504 : errmsg("cannot alter type of a column used by a generated column"),
14505 : errdetail("Column \"%s\" is used by generated column \"%s\".",
14506 : colName,
14507 : get_attname(col.objectId,
14508 : col.objectSubId,
14509 : false))));
14510 : }
14511 164 : break;
14512 : }
14513 :
14514 14 : case StatisticExtRelationId:
14515 :
14516 : /*
14517 : * Give the extended-stats machinery a chance to fix anything
14518 : * that this column type change would break.
14519 : */
14520 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
14521 14 : break;
14522 :
14523 0 : case PublicationRelRelationId:
14524 :
14525 : /*
14526 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14527 : * clause. Same issues as above. FIXME someday.
14528 : */
14529 0 : if (subtype == AT_AlterColumnType)
14530 0 : ereport(ERROR,
14531 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14532 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
14533 : errdetail("%s depends on column \"%s\"",
14534 : getObjectDescription(&foundObject, false),
14535 : colName)));
14536 0 : break;
14537 :
14538 0 : default:
14539 :
14540 : /*
14541 : * We don't expect any other sorts of objects to depend on a
14542 : * column.
14543 : */
14544 0 : elog(ERROR, "unexpected object depending on column: %s",
14545 : getObjectDescription(&foundObject, false));
14546 : break;
14547 : }
14548 : }
14549 :
14550 1072 : systable_endscan(scan);
14551 1072 : table_close(depRel, NoLock);
14552 1072 : }
14553 :
14554 : /*
14555 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
14556 : * needs to be reset.
14557 : */
14558 : static void
14559 444 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14560 : {
14561 444 : if (!get_index_isreplident(indoid))
14562 426 : return;
14563 :
14564 18 : if (tab->replicaIdentityIndex)
14565 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14566 :
14567 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
14568 : }
14569 :
14570 : /*
14571 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
14572 : */
14573 : static void
14574 444 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14575 : {
14576 444 : if (!get_index_isclustered(indoid))
14577 426 : return;
14578 :
14579 18 : if (tab->clusterOnIndex)
14580 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14581 :
14582 18 : tab->clusterOnIndex = get_rel_name(indoid);
14583 : }
14584 :
14585 : /*
14586 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14587 : * to be rebuilt (which we might already know).
14588 : */
14589 : static void
14590 686 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14591 : {
14592 : /*
14593 : * This de-duplication check is critical for two independent reasons: we
14594 : * mustn't try to recreate the same constraint twice, and if a constraint
14595 : * depends on more than one column whose type is to be altered, we must
14596 : * capture its definition string before applying any of the column type
14597 : * changes. ruleutils.c will get confused if we ask again later.
14598 : */
14599 686 : if (!list_member_oid(tab->changedConstraintOids, conoid))
14600 : {
14601 : /* OK, capture the constraint's existing definition string */
14602 596 : char *defstring = pg_get_constraintdef_command(conoid);
14603 : Oid indoid;
14604 :
14605 : /*
14606 : * It is critical to create not-null constraints ahead of primary key
14607 : * indexes; otherwise, the not-null constraint would be created by the
14608 : * primary key, and the constraint name would be wrong.
14609 : */
14610 596 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
14611 : {
14612 198 : tab->changedConstraintOids = lcons_oid(conoid,
14613 : tab->changedConstraintOids);
14614 198 : tab->changedConstraintDefs = lcons(defstring,
14615 : tab->changedConstraintDefs);
14616 : }
14617 : else
14618 : {
14619 :
14620 398 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14621 : conoid);
14622 398 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14623 : defstring);
14624 : }
14625 :
14626 : /*
14627 : * For the index of a constraint, if any, remember if it is used for
14628 : * the table's replica identity or if it is a clustered index, so that
14629 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14630 : * those properties.
14631 : */
14632 596 : indoid = get_constraint_index(conoid);
14633 596 : if (OidIsValid(indoid))
14634 : {
14635 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
14636 228 : RememberClusterOnForRebuilding(indoid, tab);
14637 : }
14638 : }
14639 686 : }
14640 :
14641 : /*
14642 : * Subroutine for ATExecAlterColumnType: remember that an index needs
14643 : * to be rebuilt (which we might already know).
14644 : */
14645 : static void
14646 236 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14647 : {
14648 : /*
14649 : * This de-duplication check is critical for two independent reasons: we
14650 : * mustn't try to recreate the same index twice, and if an index depends
14651 : * on more than one column whose type is to be altered, we must capture
14652 : * its definition string before applying any of the column type changes.
14653 : * ruleutils.c will get confused if we ask again later.
14654 : */
14655 236 : if (!list_member_oid(tab->changedIndexOids, indoid))
14656 : {
14657 : /*
14658 : * Before adding it as an index-to-rebuild, we'd better see if it
14659 : * belongs to a constraint, and if so rebuild the constraint instead.
14660 : * Typically this check fails, because constraint indexes normally
14661 : * have only dependencies on their constraint. But it's possible for
14662 : * such an index to also have direct dependencies on table columns,
14663 : * for example with a partial exclusion constraint.
14664 : */
14665 228 : Oid conoid = get_index_constraint(indoid);
14666 :
14667 228 : if (OidIsValid(conoid))
14668 : {
14669 12 : RememberConstraintForRebuilding(conoid, tab);
14670 : }
14671 : else
14672 : {
14673 : /* OK, capture the index's existing definition string */
14674 216 : char *defstring = pg_get_indexdef_string(indoid);
14675 :
14676 216 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14677 : indoid);
14678 216 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14679 : defstring);
14680 :
14681 : /*
14682 : * Remember if this index is used for the table's replica identity
14683 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14684 : * can queue up commands necessary to restore those properties.
14685 : */
14686 216 : RememberReplicaIdentityForRebuilding(indoid, tab);
14687 216 : RememberClusterOnForRebuilding(indoid, tab);
14688 : }
14689 : }
14690 236 : }
14691 :
14692 : /*
14693 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
14694 : * needs to be rebuilt (which we might already know).
14695 : */
14696 : static void
14697 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14698 : {
14699 : /*
14700 : * This de-duplication check is critical for two independent reasons: we
14701 : * mustn't try to recreate the same statistics object twice, and if the
14702 : * statistics object depends on more than one column whose type is to be
14703 : * altered, we must capture its definition string before applying any of
14704 : * the type changes. ruleutils.c will get confused if we ask again later.
14705 : */
14706 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14707 : {
14708 : /* OK, capture the statistics object's existing definition string */
14709 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
14710 :
14711 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14712 : stxoid);
14713 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14714 : defstring);
14715 : }
14716 14 : }
14717 :
14718 : /*
14719 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14720 : * operations for a particular relation. We have to drop and recreate all the
14721 : * indexes and constraints that depend on the altered columns. We do the
14722 : * actual dropping here, but re-creation is managed by adding work queue
14723 : * entries to do those steps later.
14724 : */
14725 : static void
14726 1084 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14727 : {
14728 : ObjectAddress obj;
14729 : ObjectAddresses *objects;
14730 : ListCell *def_item;
14731 : ListCell *oid_item;
14732 :
14733 : /*
14734 : * Collect all the constraints and indexes to drop so we can process them
14735 : * in a single call. That way we don't have to worry about dependencies
14736 : * among them.
14737 : */
14738 1084 : objects = new_object_addresses();
14739 :
14740 : /*
14741 : * Re-parse the index and constraint definitions, and attach them to the
14742 : * appropriate work queue entries. We do this before dropping because in
14743 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14744 : * lock on the table the constraint is attached to, and we need to get
14745 : * that before reparsing/dropping.
14746 : *
14747 : * We can't rely on the output of deparsing to tell us which relation to
14748 : * operate on, because concurrent activity might have made the name
14749 : * resolve differently. Instead, we've got to use the OID of the
14750 : * constraint or index we're processing to figure out which relation to
14751 : * operate on.
14752 : */
14753 1680 : forboth(oid_item, tab->changedConstraintOids,
14754 : def_item, tab->changedConstraintDefs)
14755 : {
14756 596 : Oid oldId = lfirst_oid(oid_item);
14757 : HeapTuple tup;
14758 : Form_pg_constraint con;
14759 : Oid relid;
14760 : Oid confrelid;
14761 : char contype;
14762 : bool conislocal;
14763 :
14764 596 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14765 596 : if (!HeapTupleIsValid(tup)) /* should not happen */
14766 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14767 596 : con = (Form_pg_constraint) GETSTRUCT(tup);
14768 596 : if (OidIsValid(con->conrelid))
14769 582 : relid = con->conrelid;
14770 : else
14771 : {
14772 : /* must be a domain constraint */
14773 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
14774 14 : if (!OidIsValid(relid))
14775 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14776 : }
14777 596 : confrelid = con->confrelid;
14778 596 : contype = con->contype;
14779 596 : conislocal = con->conislocal;
14780 596 : ReleaseSysCache(tup);
14781 :
14782 596 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
14783 596 : add_exact_object_address(&obj, objects);
14784 :
14785 : /*
14786 : * If the constraint is inherited (only), we don't want to inject a
14787 : * new definition here; it'll get recreated when
14788 : * ATAddCheckNNConstraint recurses from adding the parent table's
14789 : * constraint. But we had to carry the info this far so that we can
14790 : * drop the constraint below.
14791 : */
14792 596 : if (!conislocal)
14793 28 : continue;
14794 :
14795 : /*
14796 : * When rebuilding an FK constraint that references the table we're
14797 : * modifying, we might not yet have any lock on the FK's table, so get
14798 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14799 : * step, so there's no value in asking for anything weaker.
14800 : */
14801 568 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14802 36 : LockRelationOid(relid, AccessExclusiveLock);
14803 :
14804 568 : ATPostAlterTypeParse(oldId, relid, confrelid,
14805 568 : (char *) lfirst(def_item),
14806 568 : wqueue, lockmode, tab->rewrite);
14807 : }
14808 1300 : forboth(oid_item, tab->changedIndexOids,
14809 : def_item, tab->changedIndexDefs)
14810 : {
14811 216 : Oid oldId = lfirst_oid(oid_item);
14812 : Oid relid;
14813 :
14814 216 : relid = IndexGetRelation(oldId, false);
14815 216 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14816 216 : (char *) lfirst(def_item),
14817 216 : wqueue, lockmode, tab->rewrite);
14818 :
14819 216 : ObjectAddressSet(obj, RelationRelationId, oldId);
14820 216 : add_exact_object_address(&obj, objects);
14821 : }
14822 :
14823 : /* add dependencies for new statistics */
14824 1098 : forboth(oid_item, tab->changedStatisticsOids,
14825 : def_item, tab->changedStatisticsDefs)
14826 : {
14827 14 : Oid oldId = lfirst_oid(oid_item);
14828 : Oid relid;
14829 :
14830 14 : relid = StatisticsGetRelation(oldId, false);
14831 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14832 14 : (char *) lfirst(def_item),
14833 14 : wqueue, lockmode, tab->rewrite);
14834 :
14835 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14836 14 : add_exact_object_address(&obj, objects);
14837 : }
14838 :
14839 : /*
14840 : * Queue up command to restore replica identity index marking
14841 : */
14842 1084 : if (tab->replicaIdentityIndex)
14843 : {
14844 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14845 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14846 :
14847 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14848 18 : subcmd->name = tab->replicaIdentityIndex;
14849 18 : cmd->subtype = AT_ReplicaIdentity;
14850 18 : cmd->def = (Node *) subcmd;
14851 :
14852 : /* do it after indexes and constraints */
14853 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14854 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14855 : }
14856 :
14857 : /*
14858 : * Queue up command to restore marking of index used for cluster.
14859 : */
14860 1084 : if (tab->clusterOnIndex)
14861 : {
14862 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14863 :
14864 18 : cmd->subtype = AT_ClusterOn;
14865 18 : cmd->name = tab->clusterOnIndex;
14866 :
14867 : /* do it after indexes and constraints */
14868 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14869 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14870 : }
14871 :
14872 : /*
14873 : * It should be okay to use DROP_RESTRICT here, since nothing else should
14874 : * be depending on these objects.
14875 : */
14876 1084 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14877 :
14878 1084 : free_object_addresses(objects);
14879 :
14880 : /*
14881 : * The objects will get recreated during subsequent passes over the work
14882 : * queue.
14883 : */
14884 1084 : }
14885 :
14886 : /*
14887 : * Parse the previously-saved definition string for a constraint, index or
14888 : * statistics object against the newly-established column data type(s), and
14889 : * queue up the resulting command parsetrees for execution.
14890 : *
14891 : * This might fail if, for example, you have a WHERE clause that uses an
14892 : * operator that's not available for the new column type.
14893 : */
14894 : static void
14895 798 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14896 : List **wqueue, LOCKMODE lockmode, bool rewrite)
14897 : {
14898 : List *raw_parsetree_list;
14899 : List *querytree_list;
14900 : ListCell *list_item;
14901 : Relation rel;
14902 :
14903 : /*
14904 : * We expect that we will get only ALTER TABLE and CREATE INDEX
14905 : * statements. Hence, there is no need to pass them through
14906 : * parse_analyze_*() or the rewriter, but instead we need to pass them
14907 : * through parse_utilcmd.c to make them ready for execution.
14908 : */
14909 798 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14910 798 : querytree_list = NIL;
14911 1596 : foreach(list_item, raw_parsetree_list)
14912 : {
14913 798 : RawStmt *rs = lfirst_node(RawStmt, list_item);
14914 798 : Node *stmt = rs->stmt;
14915 :
14916 798 : if (IsA(stmt, IndexStmt))
14917 216 : querytree_list = lappend(querytree_list,
14918 216 : transformIndexStmt(oldRelId,
14919 : (IndexStmt *) stmt,
14920 : cmd));
14921 582 : else if (IsA(stmt, AlterTableStmt))
14922 : {
14923 : List *beforeStmts;
14924 : List *afterStmts;
14925 :
14926 554 : stmt = (Node *) transformAlterTableStmt(oldRelId,
14927 : (AlterTableStmt *) stmt,
14928 : cmd,
14929 : &beforeStmts,
14930 : &afterStmts);
14931 554 : querytree_list = list_concat(querytree_list, beforeStmts);
14932 554 : querytree_list = lappend(querytree_list, stmt);
14933 554 : querytree_list = list_concat(querytree_list, afterStmts);
14934 : }
14935 28 : else if (IsA(stmt, CreateStatsStmt))
14936 14 : querytree_list = lappend(querytree_list,
14937 14 : transformStatsStmt(oldRelId,
14938 : (CreateStatsStmt *) stmt,
14939 : cmd));
14940 : else
14941 14 : querytree_list = lappend(querytree_list, stmt);
14942 : }
14943 :
14944 : /* Caller should already have acquired whatever lock we need. */
14945 798 : rel = relation_open(oldRelId, NoLock);
14946 :
14947 : /*
14948 : * Attach each generated command to the proper place in the work queue.
14949 : * Note this could result in creation of entirely new work-queue entries.
14950 : *
14951 : * Also note that we have to tweak the command subtypes, because it turns
14952 : * out that re-creation of indexes and constraints has to act a bit
14953 : * differently from initial creation.
14954 : */
14955 1596 : foreach(list_item, querytree_list)
14956 : {
14957 798 : Node *stm = (Node *) lfirst(list_item);
14958 : AlteredTableInfo *tab;
14959 :
14960 798 : tab = ATGetQueueEntry(wqueue, rel);
14961 :
14962 798 : if (IsA(stm, IndexStmt))
14963 : {
14964 216 : IndexStmt *stmt = (IndexStmt *) stm;
14965 : AlterTableCmd *newcmd;
14966 :
14967 216 : if (!rewrite)
14968 56 : TryReuseIndex(oldId, stmt);
14969 216 : stmt->reset_default_tblspc = true;
14970 : /* keep the index's comment */
14971 216 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14972 :
14973 216 : newcmd = makeNode(AlterTableCmd);
14974 216 : newcmd->subtype = AT_ReAddIndex;
14975 216 : newcmd->def = (Node *) stmt;
14976 216 : tab->subcmds[AT_PASS_OLD_INDEX] =
14977 216 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14978 : }
14979 582 : else if (IsA(stm, AlterTableStmt))
14980 : {
14981 554 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
14982 : ListCell *lcmd;
14983 :
14984 1108 : foreach(lcmd, stmt->cmds)
14985 : {
14986 554 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14987 :
14988 554 : if (cmd->subtype == AT_AddIndex)
14989 : {
14990 : IndexStmt *indstmt;
14991 : Oid indoid;
14992 :
14993 228 : indstmt = castNode(IndexStmt, cmd->def);
14994 228 : indoid = get_constraint_index(oldId);
14995 :
14996 228 : if (!rewrite)
14997 48 : TryReuseIndex(indoid, indstmt);
14998 : /* keep any comment on the index */
14999 228 : indstmt->idxcomment = GetComment(indoid,
15000 : RelationRelationId, 0);
15001 228 : indstmt->reset_default_tblspc = true;
15002 :
15003 228 : cmd->subtype = AT_ReAddIndex;
15004 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15005 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15006 :
15007 : /* recreate any comment on the constraint */
15008 228 : RebuildConstraintComment(tab,
15009 : AT_PASS_OLD_INDEX,
15010 : oldId,
15011 : rel,
15012 : NIL,
15013 228 : indstmt->idxname);
15014 : }
15015 326 : else if (cmd->subtype == AT_AddConstraint)
15016 : {
15017 326 : Constraint *con = castNode(Constraint, cmd->def);
15018 :
15019 326 : con->old_pktable_oid = refRelId;
15020 : /* rewriting neither side of a FK */
15021 326 : if (con->contype == CONSTR_FOREIGN &&
15022 72 : !rewrite && tab->rewrite == 0)
15023 6 : TryReuseForeignKey(oldId, con);
15024 326 : con->reset_default_tblspc = true;
15025 326 : cmd->subtype = AT_ReAddConstraint;
15026 326 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15027 326 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15028 :
15029 : /*
15030 : * Recreate any comment on the constraint. If we have
15031 : * recreated a primary key, then transformTableConstraint
15032 : * has added an unnamed not-null constraint here; skip
15033 : * this in that case.
15034 : */
15035 326 : if (con->conname)
15036 326 : RebuildConstraintComment(tab,
15037 : AT_PASS_OLD_CONSTR,
15038 : oldId,
15039 : rel,
15040 : NIL,
15041 326 : con->conname);
15042 : else
15043 : Assert(con->contype == CONSTR_NOTNULL);
15044 : }
15045 : else
15046 0 : elog(ERROR, "unexpected statement subtype: %d",
15047 : (int) cmd->subtype);
15048 : }
15049 : }
15050 28 : else if (IsA(stm, AlterDomainStmt))
15051 : {
15052 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
15053 :
15054 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15055 : {
15056 14 : Constraint *con = castNode(Constraint, stmt->def);
15057 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15058 :
15059 14 : cmd->subtype = AT_ReAddDomainConstraint;
15060 14 : cmd->def = (Node *) stmt;
15061 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15062 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15063 :
15064 : /* recreate any comment on the constraint */
15065 14 : RebuildConstraintComment(tab,
15066 : AT_PASS_OLD_CONSTR,
15067 : oldId,
15068 : NULL,
15069 : stmt->typeName,
15070 14 : con->conname);
15071 : }
15072 : else
15073 0 : elog(ERROR, "unexpected statement subtype: %d",
15074 : (int) stmt->subtype);
15075 : }
15076 14 : else if (IsA(stm, CreateStatsStmt))
15077 : {
15078 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
15079 : AlterTableCmd *newcmd;
15080 :
15081 : /* keep the statistics object's comment */
15082 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15083 :
15084 14 : newcmd = makeNode(AlterTableCmd);
15085 14 : newcmd->subtype = AT_ReAddStatistics;
15086 14 : newcmd->def = (Node *) stmt;
15087 14 : tab->subcmds[AT_PASS_MISC] =
15088 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15089 : }
15090 : else
15091 0 : elog(ERROR, "unexpected statement type: %d",
15092 : (int) nodeTag(stm));
15093 : }
15094 :
15095 798 : relation_close(rel, NoLock);
15096 798 : }
15097 :
15098 : /*
15099 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15100 : * for a table or domain constraint that is being rebuilt.
15101 : *
15102 : * objid is the OID of the constraint.
15103 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15104 : * as a string list) for a domain constraint.
15105 : * (We could dig that info, as well as the conname, out of the pg_constraint
15106 : * entry; but callers already have them so might as well pass them.)
15107 : */
15108 : static void
15109 568 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
15110 : Relation rel, List *domname,
15111 : const char *conname)
15112 : {
15113 : CommentStmt *cmd;
15114 : char *comment_str;
15115 : AlterTableCmd *newcmd;
15116 :
15117 : /* Look for comment for object wanted, and leave if none */
15118 568 : comment_str = GetComment(objid, ConstraintRelationId, 0);
15119 568 : if (comment_str == NULL)
15120 478 : return;
15121 :
15122 : /* Build CommentStmt node, copying all input data for safety */
15123 90 : cmd = makeNode(CommentStmt);
15124 90 : if (rel)
15125 : {
15126 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
15127 78 : cmd->object = (Node *)
15128 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
15129 : makeString(pstrdup(RelationGetRelationName(rel))),
15130 : makeString(pstrdup(conname)));
15131 : }
15132 : else
15133 : {
15134 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
15135 12 : cmd->object = (Node *)
15136 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
15137 : makeString(pstrdup(conname)));
15138 : }
15139 90 : cmd->comment = comment_str;
15140 :
15141 : /* Append it to list of commands */
15142 90 : newcmd = makeNode(AlterTableCmd);
15143 90 : newcmd->subtype = AT_ReAddComment;
15144 90 : newcmd->def = (Node *) cmd;
15145 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15146 : }
15147 :
15148 : /*
15149 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15150 : * for the real analysis, then mutates the IndexStmt based on that verdict.
15151 : */
15152 : static void
15153 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
15154 : {
15155 104 : if (CheckIndexCompatible(oldId,
15156 104 : stmt->accessMethod,
15157 104 : stmt->indexParams,
15158 104 : stmt->excludeOpNames,
15159 104 : stmt->iswithoutoverlaps))
15160 : {
15161 104 : Relation irel = index_open(oldId, NoLock);
15162 :
15163 : /* If it's a partitioned index, there is no storage to share. */
15164 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15165 : {
15166 74 : stmt->oldNumber = irel->rd_locator.relNumber;
15167 74 : stmt->oldCreateSubid = irel->rd_createSubid;
15168 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15169 : }
15170 104 : index_close(irel, NoLock);
15171 : }
15172 104 : }
15173 :
15174 : /*
15175 : * Subroutine for ATPostAlterTypeParse().
15176 : *
15177 : * Stash the old P-F equality operator into the Constraint node, for possible
15178 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15179 : * this constraint can be skipped.
15180 : */
15181 : static void
15182 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
15183 : {
15184 : HeapTuple tup;
15185 : Datum adatum;
15186 : ArrayType *arr;
15187 : Oid *rawarr;
15188 : int numkeys;
15189 : int i;
15190 :
15191 : Assert(con->contype == CONSTR_FOREIGN);
15192 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15193 :
15194 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15195 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
15196 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15197 :
15198 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15199 : Anum_pg_constraint_conpfeqop);
15200 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15201 6 : numkeys = ARR_DIMS(arr)[0];
15202 : /* test follows the one in ri_FetchConstraintInfo() */
15203 6 : if (ARR_NDIM(arr) != 1 ||
15204 6 : ARR_HASNULL(arr) ||
15205 6 : ARR_ELEMTYPE(arr) != OIDOID)
15206 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
15207 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
15208 :
15209 : /* stash a List of the operator Oids in our Constraint node */
15210 12 : for (i = 0; i < numkeys; i++)
15211 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15212 :
15213 6 : ReleaseSysCache(tup);
15214 6 : }
15215 :
15216 : /*
15217 : * ALTER COLUMN .. OPTIONS ( ... )
15218 : *
15219 : * Returns the address of the modified column
15220 : */
15221 : static ObjectAddress
15222 172 : ATExecAlterColumnGenericOptions(Relation rel,
15223 : const char *colName,
15224 : List *options,
15225 : LOCKMODE lockmode)
15226 : {
15227 : Relation ftrel;
15228 : Relation attrel;
15229 : ForeignServer *server;
15230 : ForeignDataWrapper *fdw;
15231 : HeapTuple tuple;
15232 : HeapTuple newtuple;
15233 : bool isnull;
15234 : Datum repl_val[Natts_pg_attribute];
15235 : bool repl_null[Natts_pg_attribute];
15236 : bool repl_repl[Natts_pg_attribute];
15237 : Datum datum;
15238 : Form_pg_foreign_table fttableform;
15239 : Form_pg_attribute atttableform;
15240 : AttrNumber attnum;
15241 : ObjectAddress address;
15242 :
15243 172 : if (options == NIL)
15244 0 : return InvalidObjectAddress;
15245 :
15246 : /* First, determine FDW validator associated to the foreign table. */
15247 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15248 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15249 172 : if (!HeapTupleIsValid(tuple))
15250 0 : ereport(ERROR,
15251 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15252 : errmsg("foreign table \"%s\" does not exist",
15253 : RelationGetRelationName(rel))));
15254 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15255 172 : server = GetForeignServer(fttableform->ftserver);
15256 172 : fdw = GetForeignDataWrapper(server->fdwid);
15257 :
15258 172 : table_close(ftrel, AccessShareLock);
15259 172 : ReleaseSysCache(tuple);
15260 :
15261 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
15262 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15263 172 : if (!HeapTupleIsValid(tuple))
15264 0 : ereport(ERROR,
15265 : (errcode(ERRCODE_UNDEFINED_COLUMN),
15266 : errmsg("column \"%s\" of relation \"%s\" does not exist",
15267 : colName, RelationGetRelationName(rel))));
15268 :
15269 : /* Prevent them from altering a system attribute */
15270 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15271 172 : attnum = atttableform->attnum;
15272 172 : if (attnum <= 0)
15273 6 : ereport(ERROR,
15274 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15275 : errmsg("cannot alter system column \"%s\"", colName)));
15276 :
15277 :
15278 : /* Initialize buffers for new tuple values */
15279 166 : memset(repl_val, 0, sizeof(repl_val));
15280 166 : memset(repl_null, false, sizeof(repl_null));
15281 166 : memset(repl_repl, false, sizeof(repl_repl));
15282 :
15283 : /* Extract the current options */
15284 166 : datum = SysCacheGetAttr(ATTNAME,
15285 : tuple,
15286 : Anum_pg_attribute_attfdwoptions,
15287 : &isnull);
15288 166 : if (isnull)
15289 156 : datum = PointerGetDatum(NULL);
15290 :
15291 : /* Transform the options */
15292 166 : datum = transformGenericOptions(AttributeRelationId,
15293 : datum,
15294 : options,
15295 : fdw->fdwvalidator);
15296 :
15297 166 : if (PointerIsValid(DatumGetPointer(datum)))
15298 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15299 : else
15300 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15301 :
15302 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15303 :
15304 : /* Everything looks good - update the tuple */
15305 :
15306 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15307 : repl_val, repl_null, repl_repl);
15308 :
15309 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15310 :
15311 166 : InvokeObjectPostAlterHook(RelationRelationId,
15312 : RelationGetRelid(rel),
15313 : atttableform->attnum);
15314 166 : ObjectAddressSubSet(address, RelationRelationId,
15315 : RelationGetRelid(rel), attnum);
15316 :
15317 166 : ReleaseSysCache(tuple);
15318 :
15319 166 : table_close(attrel, RowExclusiveLock);
15320 :
15321 166 : heap_freetuple(newtuple);
15322 :
15323 166 : return address;
15324 : }
15325 :
15326 : /*
15327 : * ALTER TABLE OWNER
15328 : *
15329 : * recursing is true if we are recursing from a table to its indexes,
15330 : * sequences, or toast table. We don't allow the ownership of those things to
15331 : * be changed separately from the parent table. Also, we can skip permission
15332 : * checks (this is necessary not just an optimization, else we'd fail to
15333 : * handle toast tables properly).
15334 : *
15335 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15336 : * free-standing composite type.
15337 : */
15338 : void
15339 2140 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
15340 : {
15341 : Relation target_rel;
15342 : Relation class_rel;
15343 : HeapTuple tuple;
15344 : Form_pg_class tuple_class;
15345 :
15346 : /*
15347 : * Get exclusive lock till end of transaction on the target table. Use
15348 : * relation_open so that we can work on indexes and sequences.
15349 : */
15350 2140 : target_rel = relation_open(relationOid, lockmode);
15351 :
15352 : /* Get its pg_class tuple, too */
15353 2140 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
15354 :
15355 2140 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
15356 2140 : if (!HeapTupleIsValid(tuple))
15357 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
15358 2140 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15359 :
15360 : /* Can we change the ownership of this tuple? */
15361 2140 : switch (tuple_class->relkind)
15362 : {
15363 1858 : case RELKIND_RELATION:
15364 : case RELKIND_VIEW:
15365 : case RELKIND_MATVIEW:
15366 : case RELKIND_FOREIGN_TABLE:
15367 : case RELKIND_PARTITIONED_TABLE:
15368 : /* ok to change owner */
15369 1858 : break;
15370 96 : case RELKIND_INDEX:
15371 96 : if (!recursing)
15372 : {
15373 : /*
15374 : * Because ALTER INDEX OWNER used to be allowed, and in fact
15375 : * is generated by old versions of pg_dump, we give a warning
15376 : * and do nothing rather than erroring out. Also, to avoid
15377 : * unnecessary chatter while restoring those old dumps, say
15378 : * nothing at all if the command would be a no-op anyway.
15379 : */
15380 0 : if (tuple_class->relowner != newOwnerId)
15381 0 : ereport(WARNING,
15382 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15383 : errmsg("cannot change owner of index \"%s\"",
15384 : NameStr(tuple_class->relname)),
15385 : errhint("Change the ownership of the index's table instead.")));
15386 : /* quick hack to exit via the no-op path */
15387 0 : newOwnerId = tuple_class->relowner;
15388 : }
15389 96 : break;
15390 20 : case RELKIND_PARTITIONED_INDEX:
15391 20 : if (recursing)
15392 20 : break;
15393 0 : ereport(ERROR,
15394 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15395 : errmsg("cannot change owner of index \"%s\"",
15396 : NameStr(tuple_class->relname)),
15397 : errhint("Change the ownership of the index's table instead.")));
15398 : break;
15399 116 : case RELKIND_SEQUENCE:
15400 116 : if (!recursing &&
15401 68 : tuple_class->relowner != newOwnerId)
15402 : {
15403 : /* if it's an owned sequence, disallow changing it by itself */
15404 : Oid tableId;
15405 : int32 colId;
15406 :
15407 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15408 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15409 0 : ereport(ERROR,
15410 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15411 : errmsg("cannot change owner of sequence \"%s\"",
15412 : NameStr(tuple_class->relname)),
15413 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
15414 : NameStr(tuple_class->relname),
15415 : get_rel_name(tableId))));
15416 : }
15417 116 : break;
15418 8 : case RELKIND_COMPOSITE_TYPE:
15419 8 : if (recursing)
15420 8 : break;
15421 0 : ereport(ERROR,
15422 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15423 : errmsg("\"%s\" is a composite type",
15424 : NameStr(tuple_class->relname)),
15425 : /* translator: %s is an SQL ALTER command */
15426 : errhint("Use %s instead.",
15427 : "ALTER TYPE")));
15428 : break;
15429 42 : case RELKIND_TOASTVALUE:
15430 42 : if (recursing)
15431 42 : break;
15432 : /* FALL THRU */
15433 : default:
15434 0 : ereport(ERROR,
15435 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15436 : errmsg("cannot change owner of relation \"%s\"",
15437 : NameStr(tuple_class->relname)),
15438 : errdetail_relkind_not_supported(tuple_class->relkind)));
15439 : }
15440 :
15441 : /*
15442 : * If the new owner is the same as the existing owner, consider the
15443 : * command to have succeeded. This is for dump restoration purposes.
15444 : */
15445 2140 : if (tuple_class->relowner != newOwnerId)
15446 : {
15447 : Datum repl_val[Natts_pg_class];
15448 : bool repl_null[Natts_pg_class];
15449 : bool repl_repl[Natts_pg_class];
15450 : Acl *newAcl;
15451 : Datum aclDatum;
15452 : bool isNull;
15453 : HeapTuple newtuple;
15454 :
15455 : /* skip permission checks when recursing to index or toast table */
15456 498 : if (!recursing)
15457 : {
15458 : /* Superusers can always do it */
15459 280 : if (!superuser())
15460 : {
15461 42 : Oid namespaceOid = tuple_class->relnamespace;
15462 : AclResult aclresult;
15463 :
15464 : /* Otherwise, must be owner of the existing object */
15465 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15466 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15467 0 : RelationGetRelationName(target_rel));
15468 :
15469 : /* Must be able to become new owner */
15470 42 : check_can_set_role(GetUserId(), newOwnerId);
15471 :
15472 : /* New owner must have CREATE privilege on namespace */
15473 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15474 : ACL_CREATE);
15475 30 : if (aclresult != ACLCHECK_OK)
15476 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
15477 0 : get_namespace_name(namespaceOid));
15478 : }
15479 : }
15480 :
15481 486 : memset(repl_null, false, sizeof(repl_null));
15482 486 : memset(repl_repl, false, sizeof(repl_repl));
15483 :
15484 486 : repl_repl[Anum_pg_class_relowner - 1] = true;
15485 486 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15486 :
15487 : /*
15488 : * Determine the modified ACL for the new owner. This is only
15489 : * necessary when the ACL is non-null.
15490 : */
15491 486 : aclDatum = SysCacheGetAttr(RELOID, tuple,
15492 : Anum_pg_class_relacl,
15493 : &isNull);
15494 486 : if (!isNull)
15495 : {
15496 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15497 : tuple_class->relowner, newOwnerId);
15498 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
15499 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15500 : }
15501 :
15502 486 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15503 :
15504 486 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15505 :
15506 486 : heap_freetuple(newtuple);
15507 :
15508 : /*
15509 : * We must similarly update any per-column ACLs to reflect the new
15510 : * owner; for neatness reasons that's split out as a subroutine.
15511 : */
15512 486 : change_owner_fix_column_acls(relationOid,
15513 : tuple_class->relowner,
15514 : newOwnerId);
15515 :
15516 : /*
15517 : * Update owner dependency reference, if any. A composite type has
15518 : * none, because it's tracked for the pg_type entry instead of here;
15519 : * indexes and TOAST tables don't have their own entries either.
15520 : */
15521 486 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15522 478 : tuple_class->relkind != RELKIND_INDEX &&
15523 382 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15524 362 : tuple_class->relkind != RELKIND_TOASTVALUE)
15525 320 : changeDependencyOnOwner(RelationRelationId, relationOid,
15526 : newOwnerId);
15527 :
15528 : /*
15529 : * Also change the ownership of the table's row type, if it has one
15530 : */
15531 486 : if (OidIsValid(tuple_class->reltype))
15532 294 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15533 :
15534 : /*
15535 : * If we are operating on a table or materialized view, also change
15536 : * the ownership of any indexes and sequences that belong to the
15537 : * relation, as well as its toast table (if it has one).
15538 : */
15539 486 : if (tuple_class->relkind == RELKIND_RELATION ||
15540 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15541 224 : tuple_class->relkind == RELKIND_MATVIEW ||
15542 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
15543 : {
15544 : List *index_oid_list;
15545 : ListCell *i;
15546 :
15547 : /* Find all the indexes belonging to this relation */
15548 304 : index_oid_list = RelationGetIndexList(target_rel);
15549 :
15550 : /* For each index, recursively change its ownership */
15551 420 : foreach(i, index_oid_list)
15552 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15553 :
15554 304 : list_free(index_oid_list);
15555 : }
15556 :
15557 : /* If it has a toast table, recurse to change its ownership */
15558 486 : if (tuple_class->reltoastrelid != InvalidOid)
15559 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15560 : true, lockmode);
15561 :
15562 : /* If it has dependent sequences, recurse to change them too */
15563 486 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15564 : }
15565 :
15566 2128 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15567 :
15568 2128 : ReleaseSysCache(tuple);
15569 2128 : table_close(class_rel, RowExclusiveLock);
15570 2128 : relation_close(target_rel, NoLock);
15571 2128 : }
15572 :
15573 : /*
15574 : * change_owner_fix_column_acls
15575 : *
15576 : * Helper function for ATExecChangeOwner. Scan the columns of the table
15577 : * and fix any non-null column ACLs to reflect the new owner.
15578 : */
15579 : static void
15580 486 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15581 : {
15582 : Relation attRelation;
15583 : SysScanDesc scan;
15584 : ScanKeyData key[1];
15585 : HeapTuple attributeTuple;
15586 :
15587 486 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15588 486 : ScanKeyInit(&key[0],
15589 : Anum_pg_attribute_attrelid,
15590 : BTEqualStrategyNumber, F_OIDEQ,
15591 : ObjectIdGetDatum(relationOid));
15592 486 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15593 : true, NULL, 1, key);
15594 3372 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15595 : {
15596 2886 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15597 : Datum repl_val[Natts_pg_attribute];
15598 : bool repl_null[Natts_pg_attribute];
15599 : bool repl_repl[Natts_pg_attribute];
15600 : Acl *newAcl;
15601 : Datum aclDatum;
15602 : bool isNull;
15603 : HeapTuple newtuple;
15604 :
15605 : /* Ignore dropped columns */
15606 2886 : if (att->attisdropped)
15607 2884 : continue;
15608 :
15609 2886 : aclDatum = heap_getattr(attributeTuple,
15610 : Anum_pg_attribute_attacl,
15611 : RelationGetDescr(attRelation),
15612 : &isNull);
15613 : /* Null ACLs do not require changes */
15614 2886 : if (isNull)
15615 2884 : continue;
15616 :
15617 2 : memset(repl_null, false, sizeof(repl_null));
15618 2 : memset(repl_repl, false, sizeof(repl_repl));
15619 :
15620 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15621 : oldOwnerId, newOwnerId);
15622 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
15623 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15624 :
15625 2 : newtuple = heap_modify_tuple(attributeTuple,
15626 : RelationGetDescr(attRelation),
15627 : repl_val, repl_null, repl_repl);
15628 :
15629 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15630 :
15631 2 : heap_freetuple(newtuple);
15632 : }
15633 486 : systable_endscan(scan);
15634 486 : table_close(attRelation, RowExclusiveLock);
15635 486 : }
15636 :
15637 : /*
15638 : * change_owner_recurse_to_sequences
15639 : *
15640 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
15641 : * for sequences that are dependent on serial columns, and changes their
15642 : * ownership.
15643 : */
15644 : static void
15645 486 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15646 : {
15647 : Relation depRel;
15648 : SysScanDesc scan;
15649 : ScanKeyData key[2];
15650 : HeapTuple tup;
15651 :
15652 : /*
15653 : * SERIAL sequences are those having an auto dependency on one of the
15654 : * table's columns (we don't care *which* column, exactly).
15655 : */
15656 486 : depRel = table_open(DependRelationId, AccessShareLock);
15657 :
15658 486 : ScanKeyInit(&key[0],
15659 : Anum_pg_depend_refclassid,
15660 : BTEqualStrategyNumber, F_OIDEQ,
15661 : ObjectIdGetDatum(RelationRelationId));
15662 486 : ScanKeyInit(&key[1],
15663 : Anum_pg_depend_refobjid,
15664 : BTEqualStrategyNumber, F_OIDEQ,
15665 : ObjectIdGetDatum(relationOid));
15666 : /* we leave refobjsubid unspecified */
15667 :
15668 486 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15669 : NULL, 2, key);
15670 :
15671 1374 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
15672 : {
15673 888 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15674 : Relation seqRel;
15675 :
15676 : /* skip dependencies other than auto dependencies on columns */
15677 888 : if (depForm->refobjsubid == 0 ||
15678 352 : depForm->classid != RelationRelationId ||
15679 142 : depForm->objsubid != 0 ||
15680 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15681 746 : continue;
15682 :
15683 : /* Use relation_open just in case it's an index */
15684 142 : seqRel = relation_open(depForm->objid, lockmode);
15685 :
15686 : /* skip non-sequence relations */
15687 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15688 : {
15689 : /* No need to keep the lock */
15690 116 : relation_close(seqRel, lockmode);
15691 116 : continue;
15692 : }
15693 :
15694 : /* We don't need to close the sequence while we alter it. */
15695 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15696 :
15697 : /* Now we can close it. Keep the lock till end of transaction. */
15698 26 : relation_close(seqRel, NoLock);
15699 : }
15700 :
15701 486 : systable_endscan(scan);
15702 :
15703 486 : relation_close(depRel, AccessShareLock);
15704 486 : }
15705 :
15706 : /*
15707 : * ALTER TABLE CLUSTER ON
15708 : *
15709 : * The only thing we have to do is to change the indisclustered bits.
15710 : *
15711 : * Return the address of the new clustering index.
15712 : */
15713 : static ObjectAddress
15714 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15715 : {
15716 : Oid indexOid;
15717 : ObjectAddress address;
15718 :
15719 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15720 :
15721 64 : if (!OidIsValid(indexOid))
15722 0 : ereport(ERROR,
15723 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15724 : errmsg("index \"%s\" for table \"%s\" does not exist",
15725 : indexName, RelationGetRelationName(rel))));
15726 :
15727 : /* Check index is valid to cluster on */
15728 64 : check_index_is_clusterable(rel, indexOid, lockmode);
15729 :
15730 : /* And do the work */
15731 64 : mark_index_clustered(rel, indexOid, false);
15732 :
15733 58 : ObjectAddressSet(address,
15734 : RelationRelationId, indexOid);
15735 :
15736 58 : return address;
15737 : }
15738 :
15739 : /*
15740 : * ALTER TABLE SET WITHOUT CLUSTER
15741 : *
15742 : * We have to find any indexes on the table that have indisclustered bit
15743 : * set and turn it off.
15744 : */
15745 : static void
15746 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15747 : {
15748 18 : mark_index_clustered(rel, InvalidOid, false);
15749 12 : }
15750 :
15751 : /*
15752 : * Preparation phase for SET ACCESS METHOD
15753 : *
15754 : * Check that the access method exists and determine whether a change is
15755 : * actually needed.
15756 : */
15757 : static void
15758 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15759 : {
15760 : Oid amoid;
15761 :
15762 : /*
15763 : * Look up the access method name and check that it differs from the
15764 : * table's current AM. If DEFAULT was specified for a partitioned table
15765 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15766 : */
15767 110 : if (amname != NULL)
15768 74 : amoid = get_table_am_oid(amname, false);
15769 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15770 18 : amoid = InvalidOid;
15771 : else
15772 18 : amoid = get_table_am_oid(default_table_access_method, false);
15773 :
15774 : /* if it's a match, phase 3 doesn't need to do anything */
15775 110 : if (rel->rd_rel->relam == amoid)
15776 12 : return;
15777 :
15778 : /* Save info for Phase 3 to do the real work */
15779 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15780 98 : tab->newAccessMethod = amoid;
15781 98 : tab->chgAccessMethod = true;
15782 : }
15783 :
15784 : /*
15785 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15786 : * storage that have an interest in preserving AM.
15787 : *
15788 : * Since these have no storage, setting the access method is a catalog only
15789 : * operation.
15790 : */
15791 : static void
15792 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15793 : {
15794 : Relation pg_class;
15795 : Oid oldAccessMethodId;
15796 : HeapTuple tuple;
15797 : Form_pg_class rd_rel;
15798 44 : Oid reloid = RelationGetRelid(rel);
15799 :
15800 : /*
15801 : * Shouldn't be called on relations having storage; these are processed in
15802 : * phase 3.
15803 : */
15804 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15805 :
15806 : /* Get a modifiable copy of the relation's pg_class row. */
15807 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
15808 :
15809 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15810 44 : if (!HeapTupleIsValid(tuple))
15811 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
15812 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15813 :
15814 : /* Update the pg_class row. */
15815 44 : oldAccessMethodId = rd_rel->relam;
15816 44 : rd_rel->relam = newAccessMethodId;
15817 :
15818 : /* Leave if no update required */
15819 44 : if (rd_rel->relam == oldAccessMethodId)
15820 : {
15821 0 : heap_freetuple(tuple);
15822 0 : table_close(pg_class, RowExclusiveLock);
15823 0 : return;
15824 : }
15825 :
15826 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15827 :
15828 : /*
15829 : * Update the dependency on the new access method. No dependency is added
15830 : * if the new access method is InvalidOid (default case). Be very careful
15831 : * that this has to compare the previous value stored in pg_class with the
15832 : * new one.
15833 : */
15834 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15835 20 : {
15836 : ObjectAddress relobj,
15837 : referenced;
15838 :
15839 : /*
15840 : * New access method is defined and there was no dependency
15841 : * previously, so record a new one.
15842 : */
15843 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
15844 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15845 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15846 : }
15847 24 : else if (OidIsValid(oldAccessMethodId) &&
15848 24 : !OidIsValid(rd_rel->relam))
15849 : {
15850 : /*
15851 : * There was an access method defined, and no new one, so just remove
15852 : * the existing dependency.
15853 : */
15854 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
15855 : AccessMethodRelationId,
15856 : DEPENDENCY_NORMAL);
15857 : }
15858 : else
15859 : {
15860 : Assert(OidIsValid(oldAccessMethodId) &&
15861 : OidIsValid(rd_rel->relam));
15862 :
15863 : /* Both are valid, so update the dependency */
15864 12 : changeDependencyFor(RelationRelationId, reloid,
15865 : AccessMethodRelationId,
15866 : oldAccessMethodId, rd_rel->relam);
15867 : }
15868 :
15869 : /* make the relam and dependency changes visible */
15870 44 : CommandCounterIncrement();
15871 :
15872 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15873 :
15874 44 : heap_freetuple(tuple);
15875 44 : table_close(pg_class, RowExclusiveLock);
15876 : }
15877 :
15878 : /*
15879 : * ALTER TABLE SET TABLESPACE
15880 : */
15881 : static void
15882 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15883 : {
15884 : Oid tablespaceId;
15885 :
15886 : /* Check that the tablespace exists */
15887 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
15888 :
15889 : /* Check permissions except when moving to database's default */
15890 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15891 : {
15892 : AclResult aclresult;
15893 :
15894 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15895 66 : if (aclresult != ACLCHECK_OK)
15896 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15897 : }
15898 :
15899 : /* Save info for Phase 3 to do the real work */
15900 158 : if (OidIsValid(tab->newTableSpace))
15901 0 : ereport(ERROR,
15902 : (errcode(ERRCODE_SYNTAX_ERROR),
15903 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
15904 :
15905 158 : tab->newTableSpace = tablespaceId;
15906 158 : }
15907 :
15908 : /*
15909 : * Set, reset, or replace reloptions.
15910 : */
15911 : static void
15912 952 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15913 : LOCKMODE lockmode)
15914 : {
15915 : Oid relid;
15916 : Relation pgclass;
15917 : HeapTuple tuple;
15918 : HeapTuple newtuple;
15919 : Datum datum;
15920 : Datum newOptions;
15921 : Datum repl_val[Natts_pg_class];
15922 : bool repl_null[Natts_pg_class];
15923 : bool repl_repl[Natts_pg_class];
15924 952 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
15925 :
15926 952 : if (defList == NIL && operation != AT_ReplaceRelOptions)
15927 0 : return; /* nothing to do */
15928 :
15929 952 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
15930 :
15931 : /* Fetch heap tuple */
15932 952 : relid = RelationGetRelid(rel);
15933 952 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
15934 952 : if (!HeapTupleIsValid(tuple))
15935 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
15936 :
15937 952 : if (operation == AT_ReplaceRelOptions)
15938 : {
15939 : /*
15940 : * If we're supposed to replace the reloptions list, we just pretend
15941 : * there were none before.
15942 : */
15943 194 : datum = (Datum) 0;
15944 : }
15945 : else
15946 : {
15947 : bool isnull;
15948 :
15949 : /* Get the old reloptions */
15950 758 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15951 : &isnull);
15952 758 : if (isnull)
15953 470 : datum = (Datum) 0;
15954 : }
15955 :
15956 : /* Generate new proposed reloptions (text array) */
15957 952 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
15958 : operation == AT_ResetRelOptions);
15959 :
15960 : /* Validate */
15961 946 : switch (rel->rd_rel->relkind)
15962 : {
15963 530 : case RELKIND_RELATION:
15964 : case RELKIND_MATVIEW:
15965 530 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15966 530 : break;
15967 6 : case RELKIND_PARTITIONED_TABLE:
15968 6 : (void) partitioned_table_reloptions(newOptions, true);
15969 0 : break;
15970 296 : case RELKIND_VIEW:
15971 296 : (void) view_reloptions(newOptions, true);
15972 278 : break;
15973 114 : case RELKIND_INDEX:
15974 : case RELKIND_PARTITIONED_INDEX:
15975 114 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15976 92 : break;
15977 0 : case RELKIND_TOASTVALUE:
15978 : /* fall through to error -- shouldn't ever get here */
15979 : default:
15980 0 : ereport(ERROR,
15981 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15982 : errmsg("cannot set options for relation \"%s\"",
15983 : RelationGetRelationName(rel)),
15984 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15985 : break;
15986 : }
15987 :
15988 : /* Special-case validation of view options */
15989 900 : if (rel->rd_rel->relkind == RELKIND_VIEW)
15990 : {
15991 278 : Query *view_query = get_view_query(rel);
15992 278 : List *view_options = untransformRelOptions(newOptions);
15993 : ListCell *cell;
15994 278 : bool check_option = false;
15995 :
15996 380 : foreach(cell, view_options)
15997 : {
15998 102 : DefElem *defel = (DefElem *) lfirst(cell);
15999 :
16000 102 : if (strcmp(defel->defname, "check_option") == 0)
16001 24 : check_option = true;
16002 : }
16003 :
16004 : /*
16005 : * If the check option is specified, look to see if the view is
16006 : * actually auto-updatable or not.
16007 : */
16008 278 : if (check_option)
16009 : {
16010 : const char *view_updatable_error =
16011 24 : view_query_is_auto_updatable(view_query, true);
16012 :
16013 24 : if (view_updatable_error)
16014 0 : ereport(ERROR,
16015 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16016 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16017 : errhint("%s", _(view_updatable_error))));
16018 : }
16019 : }
16020 :
16021 : /*
16022 : * All we need do here is update the pg_class row; the new options will be
16023 : * propagated into relcaches during post-commit cache inval.
16024 : */
16025 900 : memset(repl_val, 0, sizeof(repl_val));
16026 900 : memset(repl_null, false, sizeof(repl_null));
16027 900 : memset(repl_repl, false, sizeof(repl_repl));
16028 :
16029 900 : if (newOptions != (Datum) 0)
16030 606 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16031 : else
16032 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
16033 :
16034 900 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16035 :
16036 900 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16037 : repl_val, repl_null, repl_repl);
16038 :
16039 900 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16040 900 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16041 :
16042 900 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16043 :
16044 900 : heap_freetuple(newtuple);
16045 :
16046 900 : ReleaseSysCache(tuple);
16047 :
16048 : /* repeat the whole exercise for the toast table, if there's one */
16049 900 : if (OidIsValid(rel->rd_rel->reltoastrelid))
16050 : {
16051 : Relation toastrel;
16052 262 : Oid toastid = rel->rd_rel->reltoastrelid;
16053 :
16054 262 : toastrel = table_open(toastid, lockmode);
16055 :
16056 : /* Fetch heap tuple */
16057 262 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16058 262 : if (!HeapTupleIsValid(tuple))
16059 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
16060 :
16061 262 : if (operation == AT_ReplaceRelOptions)
16062 : {
16063 : /*
16064 : * If we're supposed to replace the reloptions list, we just
16065 : * pretend there were none before.
16066 : */
16067 0 : datum = (Datum) 0;
16068 : }
16069 : else
16070 : {
16071 : bool isnull;
16072 :
16073 : /* Get the old reloptions */
16074 262 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16075 : &isnull);
16076 262 : if (isnull)
16077 226 : datum = (Datum) 0;
16078 : }
16079 :
16080 262 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16081 : false, operation == AT_ResetRelOptions);
16082 :
16083 262 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16084 :
16085 262 : memset(repl_val, 0, sizeof(repl_val));
16086 262 : memset(repl_null, false, sizeof(repl_null));
16087 262 : memset(repl_repl, false, sizeof(repl_repl));
16088 :
16089 262 : if (newOptions != (Datum) 0)
16090 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16091 : else
16092 220 : repl_null[Anum_pg_class_reloptions - 1] = true;
16093 :
16094 262 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16095 :
16096 262 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16097 : repl_val, repl_null, repl_repl);
16098 :
16099 262 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16100 :
16101 262 : InvokeObjectPostAlterHookArg(RelationRelationId,
16102 : RelationGetRelid(toastrel), 0,
16103 : InvalidOid, true);
16104 :
16105 262 : heap_freetuple(newtuple);
16106 :
16107 262 : ReleaseSysCache(tuple);
16108 :
16109 262 : table_close(toastrel, NoLock);
16110 : }
16111 :
16112 900 : table_close(pgclass, RowExclusiveLock);
16113 : }
16114 :
16115 : /*
16116 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16117 : * rewriting to be done, so we just want to copy the data as fast as possible.
16118 : */
16119 : static void
16120 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16121 : {
16122 : Relation rel;
16123 : Oid reltoastrelid;
16124 : RelFileNumber newrelfilenumber;
16125 : RelFileLocator newrlocator;
16126 162 : List *reltoastidxids = NIL;
16127 : ListCell *lc;
16128 :
16129 : /*
16130 : * Need lock here in case we are recursing to toast table or index
16131 : */
16132 162 : rel = relation_open(tableOid, lockmode);
16133 :
16134 : /* Check first if relation can be moved to new tablespace */
16135 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16136 : {
16137 2 : InvokeObjectPostAlterHook(RelationRelationId,
16138 : RelationGetRelid(rel), 0);
16139 2 : relation_close(rel, NoLock);
16140 2 : return;
16141 : }
16142 :
16143 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
16144 : /* Fetch the list of indexes on toast relation if necessary */
16145 160 : if (OidIsValid(reltoastrelid))
16146 : {
16147 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
16148 :
16149 20 : reltoastidxids = RelationGetIndexList(toastRel);
16150 20 : relation_close(toastRel, lockmode);
16151 : }
16152 :
16153 : /*
16154 : * Relfilenumbers are not unique in databases across tablespaces, so we
16155 : * need to allocate a new one in the new tablespace.
16156 : */
16157 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16158 160 : rel->rd_rel->relpersistence);
16159 :
16160 : /* Open old and new relation */
16161 160 : newrlocator = rel->rd_locator;
16162 160 : newrlocator.relNumber = newrelfilenumber;
16163 160 : newrlocator.spcOid = newTableSpace;
16164 :
16165 : /* hand off to AM to actually create new rel storage and copy the data */
16166 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
16167 : {
16168 62 : index_copy_data(rel, newrlocator);
16169 : }
16170 : else
16171 : {
16172 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16173 98 : table_relation_copy_data(rel, &newrlocator);
16174 : }
16175 :
16176 : /*
16177 : * Update the pg_class row.
16178 : *
16179 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16180 : * executed on pg_class or its indexes (the above copy wouldn't contain
16181 : * the updated pg_class entry), but that's forbidden with
16182 : * CheckRelationTableSpaceMove().
16183 : */
16184 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16185 :
16186 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16187 :
16188 160 : RelationAssumeNewRelfilelocator(rel);
16189 :
16190 160 : relation_close(rel, NoLock);
16191 :
16192 : /* Make sure the reltablespace change is visible */
16193 160 : CommandCounterIncrement();
16194 :
16195 : /* Move associated toast relation and/or indexes, too */
16196 160 : if (OidIsValid(reltoastrelid))
16197 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16198 180 : foreach(lc, reltoastidxids)
16199 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16200 :
16201 : /* Clean up */
16202 160 : list_free(reltoastidxids);
16203 : }
16204 :
16205 : /*
16206 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16207 : * storage that have an interest in preserving tablespace.
16208 : *
16209 : * Since these have no storage the tablespace can be updated with a simple
16210 : * metadata only operation to update the tablespace.
16211 : */
16212 : static void
16213 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
16214 : {
16215 : /*
16216 : * Shouldn't be called on relations having storage; these are processed in
16217 : * phase 3.
16218 : */
16219 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16220 :
16221 : /* check if relation can be moved to its new tablespace */
16222 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16223 : {
16224 0 : InvokeObjectPostAlterHook(RelationRelationId,
16225 : RelationGetRelid(rel),
16226 : 0);
16227 0 : return;
16228 : }
16229 :
16230 : /* Update can be done, so change reltablespace */
16231 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16232 :
16233 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16234 :
16235 : /* Make sure the reltablespace change is visible */
16236 30 : CommandCounterIncrement();
16237 : }
16238 :
16239 : /*
16240 : * Alter Table ALL ... SET TABLESPACE
16241 : *
16242 : * Allows a user to move all objects of some type in a given tablespace in the
16243 : * current database to another tablespace. Objects can be chosen based on the
16244 : * owner of the object also, to allow users to move only their objects.
16245 : * The user must have CREATE rights on the new tablespace, as usual. The main
16246 : * permissions handling is done by the lower-level table move function.
16247 : *
16248 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
16249 : * lock can't be acquired then we ereport(ERROR).
16250 : */
16251 : Oid
16252 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
16253 : {
16254 30 : List *relations = NIL;
16255 : ListCell *l;
16256 : ScanKeyData key[1];
16257 : Relation rel;
16258 : TableScanDesc scan;
16259 : HeapTuple tuple;
16260 : Oid orig_tablespaceoid;
16261 : Oid new_tablespaceoid;
16262 30 : List *role_oids = roleSpecsToIds(stmt->roles);
16263 :
16264 : /* Ensure we were not asked to move something we can't */
16265 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16266 12 : stmt->objtype != OBJECT_MATVIEW)
16267 0 : ereport(ERROR,
16268 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16269 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16270 :
16271 : /* Get the orig and new tablespace OIDs */
16272 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16273 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16274 :
16275 : /* Can't move shared relations in to or out of pg_global */
16276 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16277 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16278 : new_tablespaceoid == GLOBALTABLESPACE_OID)
16279 0 : ereport(ERROR,
16280 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16281 : errmsg("cannot move relations in to or out of pg_global tablespace")));
16282 :
16283 : /*
16284 : * Must have CREATE rights on the new tablespace, unless it is the
16285 : * database default tablespace (which all users implicitly have CREATE
16286 : * rights on).
16287 : */
16288 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16289 : {
16290 : AclResult aclresult;
16291 :
16292 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16293 : ACL_CREATE);
16294 0 : if (aclresult != ACLCHECK_OK)
16295 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
16296 0 : get_tablespace_name(new_tablespaceoid));
16297 : }
16298 :
16299 : /*
16300 : * Now that the checks are done, check if we should set either to
16301 : * InvalidOid because it is our database's default tablespace.
16302 : */
16303 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
16304 0 : orig_tablespaceoid = InvalidOid;
16305 :
16306 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
16307 30 : new_tablespaceoid = InvalidOid;
16308 :
16309 : /* no-op */
16310 30 : if (orig_tablespaceoid == new_tablespaceoid)
16311 0 : return new_tablespaceoid;
16312 :
16313 : /*
16314 : * Walk the list of objects in the tablespace and move them. This will
16315 : * only find objects in our database, of course.
16316 : */
16317 30 : ScanKeyInit(&key[0],
16318 : Anum_pg_class_reltablespace,
16319 : BTEqualStrategyNumber, F_OIDEQ,
16320 : ObjectIdGetDatum(orig_tablespaceoid));
16321 :
16322 30 : rel = table_open(RelationRelationId, AccessShareLock);
16323 30 : scan = table_beginscan_catalog(rel, 1, key);
16324 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16325 : {
16326 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16327 102 : Oid relOid = relForm->oid;
16328 :
16329 : /*
16330 : * Do not move objects in pg_catalog as part of this, if an admin
16331 : * really wishes to do so, they can issue the individual ALTER
16332 : * commands directly.
16333 : *
16334 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
16335 : * (TOAST will be moved with the main table).
16336 : */
16337 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
16338 204 : relForm->relisshared ||
16339 204 : isAnyTempNamespace(relForm->relnamespace) ||
16340 102 : IsToastNamespace(relForm->relnamespace))
16341 0 : continue;
16342 :
16343 : /* Only move the object type requested */
16344 102 : if ((stmt->objtype == OBJECT_TABLE &&
16345 60 : relForm->relkind != RELKIND_RELATION &&
16346 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16347 66 : (stmt->objtype == OBJECT_INDEX &&
16348 36 : relForm->relkind != RELKIND_INDEX &&
16349 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16350 60 : (stmt->objtype == OBJECT_MATVIEW &&
16351 6 : relForm->relkind != RELKIND_MATVIEW))
16352 42 : continue;
16353 :
16354 : /* Check if we are only moving objects owned by certain roles */
16355 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16356 0 : continue;
16357 :
16358 : /*
16359 : * Handle permissions-checking here since we are locking the tables
16360 : * and also to avoid doing a bunch of work only to fail part-way. Note
16361 : * that permissions will also be checked by AlterTableInternal().
16362 : *
16363 : * Caller must be considered an owner on the table to move it.
16364 : */
16365 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16366 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
16367 0 : NameStr(relForm->relname));
16368 :
16369 60 : if (stmt->nowait &&
16370 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
16371 0 : ereport(ERROR,
16372 : (errcode(ERRCODE_OBJECT_IN_USE),
16373 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
16374 : get_namespace_name(relForm->relnamespace),
16375 : NameStr(relForm->relname))));
16376 : else
16377 60 : LockRelationOid(relOid, AccessExclusiveLock);
16378 :
16379 : /* Add to our list of objects to move */
16380 60 : relations = lappend_oid(relations, relOid);
16381 : }
16382 :
16383 30 : table_endscan(scan);
16384 30 : table_close(rel, AccessShareLock);
16385 :
16386 30 : if (relations == NIL)
16387 12 : ereport(NOTICE,
16388 : (errcode(ERRCODE_NO_DATA_FOUND),
16389 : errmsg("no matching relations in tablespace \"%s\" found",
16390 : orig_tablespaceoid == InvalidOid ? "(database default)" :
16391 : get_tablespace_name(orig_tablespaceoid))));
16392 :
16393 : /* Everything is locked, loop through and move all of the relations. */
16394 90 : foreach(l, relations)
16395 : {
16396 60 : List *cmds = NIL;
16397 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
16398 :
16399 60 : cmd->subtype = AT_SetTableSpace;
16400 60 : cmd->name = stmt->new_tablespacename;
16401 :
16402 60 : cmds = lappend(cmds, cmd);
16403 :
16404 60 : EventTriggerAlterTableStart((Node *) stmt);
16405 : /* OID is set by AlterTableInternal */
16406 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
16407 60 : EventTriggerAlterTableEnd();
16408 : }
16409 :
16410 30 : return new_tablespaceoid;
16411 : }
16412 :
16413 : static void
16414 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
16415 : {
16416 : SMgrRelation dstrel;
16417 :
16418 : /*
16419 : * Since we copy the file directly without looking at the shared buffers,
16420 : * we'd better first flush out any pages of the source relation that are
16421 : * in shared buffers. We assume no new changes will be made while we are
16422 : * holding exclusive lock on the rel.
16423 : */
16424 62 : FlushRelationBuffers(rel);
16425 :
16426 : /*
16427 : * Create and copy all forks of the relation, and schedule unlinking of
16428 : * old physical files.
16429 : *
16430 : * NOTE: any conflict in relfilenumber value will be caught in
16431 : * RelationCreateStorage().
16432 : */
16433 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16434 :
16435 : /* copy main fork */
16436 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
16437 62 : rel->rd_rel->relpersistence);
16438 :
16439 : /* copy those extra forks that exist */
16440 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16441 186 : forkNum <= MAX_FORKNUM; forkNum++)
16442 : {
16443 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
16444 : {
16445 0 : smgrcreate(dstrel, forkNum, false);
16446 :
16447 : /*
16448 : * WAL log creation if the relation is persistent, or this is the
16449 : * init fork of an unlogged relation.
16450 : */
16451 0 : if (RelationIsPermanent(rel) ||
16452 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16453 : forkNum == INIT_FORKNUM))
16454 0 : log_smgrcreate(&newrlocator, forkNum);
16455 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16456 0 : rel->rd_rel->relpersistence);
16457 : }
16458 : }
16459 :
16460 : /* drop old relation, and close new one */
16461 62 : RelationDropStorage(rel);
16462 62 : smgrclose(dstrel);
16463 62 : }
16464 :
16465 : /*
16466 : * ALTER TABLE ENABLE/DISABLE TRIGGER
16467 : *
16468 : * We just pass this off to trigger.c.
16469 : */
16470 : static void
16471 340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16472 : char fires_when, bool skip_system, bool recurse,
16473 : LOCKMODE lockmode)
16474 : {
16475 340 : EnableDisableTrigger(rel, trigname, InvalidOid,
16476 : fires_when, skip_system, recurse,
16477 : lockmode);
16478 :
16479 340 : InvokeObjectPostAlterHook(RelationRelationId,
16480 : RelationGetRelid(rel), 0);
16481 340 : }
16482 :
16483 : /*
16484 : * ALTER TABLE ENABLE/DISABLE RULE
16485 : *
16486 : * We just pass this off to rewriteDefine.c.
16487 : */
16488 : static void
16489 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
16490 : char fires_when, LOCKMODE lockmode)
16491 : {
16492 46 : EnableDisableRule(rel, rulename, fires_when);
16493 :
16494 46 : InvokeObjectPostAlterHook(RelationRelationId,
16495 : RelationGetRelid(rel), 0);
16496 46 : }
16497 :
16498 : /*
16499 : * ALTER TABLE INHERIT
16500 : *
16501 : * Add a parent to the child's parents. This verifies that all the columns and
16502 : * check constraints of the parent appear in the child and that they have the
16503 : * same data types and expressions.
16504 : */
16505 : static void
16506 410 : ATPrepAddInherit(Relation child_rel)
16507 : {
16508 410 : if (child_rel->rd_rel->reloftype)
16509 6 : ereport(ERROR,
16510 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16511 : errmsg("cannot change inheritance of typed table")));
16512 :
16513 404 : if (child_rel->rd_rel->relispartition)
16514 6 : ereport(ERROR,
16515 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16516 : errmsg("cannot change inheritance of a partition")));
16517 :
16518 398 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16519 6 : ereport(ERROR,
16520 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16521 : errmsg("cannot change inheritance of partitioned table")));
16522 392 : }
16523 :
16524 : /*
16525 : * Return the address of the new parent relation.
16526 : */
16527 : static ObjectAddress
16528 392 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16529 : {
16530 : Relation parent_rel;
16531 : List *children;
16532 : ObjectAddress address;
16533 : const char *trigger_name;
16534 :
16535 : /*
16536 : * A self-exclusive lock is needed here. See the similar case in
16537 : * MergeAttributes() for a full explanation.
16538 : */
16539 392 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16540 :
16541 : /*
16542 : * Must be owner of both parent and child -- child was checked by
16543 : * ATSimplePermissions call in ATPrepCmd
16544 : */
16545 392 : ATSimplePermissions(AT_AddInherit, parent_rel,
16546 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
16547 :
16548 : /* Permanent rels cannot inherit from temporary ones */
16549 392 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16550 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16551 0 : ereport(ERROR,
16552 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16553 : errmsg("cannot inherit from temporary relation \"%s\"",
16554 : RelationGetRelationName(parent_rel))));
16555 :
16556 : /* If parent rel is temp, it must belong to this session */
16557 392 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16558 6 : !parent_rel->rd_islocaltemp)
16559 0 : ereport(ERROR,
16560 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16561 : errmsg("cannot inherit from temporary relation of another session")));
16562 :
16563 : /* Ditto for the child */
16564 392 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16565 6 : !child_rel->rd_islocaltemp)
16566 0 : ereport(ERROR,
16567 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16568 : errmsg("cannot inherit to temporary relation of another session")));
16569 :
16570 : /* Prevent partitioned tables from becoming inheritance parents */
16571 392 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16572 6 : ereport(ERROR,
16573 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16574 : errmsg("cannot inherit from partitioned table \"%s\"",
16575 : parent->relname)));
16576 :
16577 : /* Likewise for partitions */
16578 386 : if (parent_rel->rd_rel->relispartition)
16579 6 : ereport(ERROR,
16580 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16581 : errmsg("cannot inherit from a partition")));
16582 :
16583 : /*
16584 : * Prevent circularity by seeing if proposed parent inherits from child.
16585 : * (In particular, this disallows making a rel inherit from itself.)
16586 : *
16587 : * This is not completely bulletproof because of race conditions: in
16588 : * multi-level inheritance trees, someone else could concurrently be
16589 : * making another inheritance link that closes the loop but does not join
16590 : * either of the rels we have locked. Preventing that seems to require
16591 : * exclusive locks on the entire inheritance tree, which is a cure worse
16592 : * than the disease. find_all_inheritors() will cope with circularity
16593 : * anyway, so don't sweat it too much.
16594 : *
16595 : * We use weakest lock we can on child's children, namely AccessShareLock.
16596 : */
16597 380 : children = find_all_inheritors(RelationGetRelid(child_rel),
16598 : AccessShareLock, NULL);
16599 :
16600 380 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
16601 12 : ereport(ERROR,
16602 : (errcode(ERRCODE_DUPLICATE_TABLE),
16603 : errmsg("circular inheritance not allowed"),
16604 : errdetail("\"%s\" is already a child of \"%s\".",
16605 : parent->relname,
16606 : RelationGetRelationName(child_rel))));
16607 :
16608 : /*
16609 : * If child_rel has row-level triggers with transition tables, we
16610 : * currently don't allow it to become an inheritance child. See also
16611 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
16612 : */
16613 368 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16614 368 : if (trigger_name != NULL)
16615 6 : ereport(ERROR,
16616 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16617 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16618 : trigger_name, RelationGetRelationName(child_rel)),
16619 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16620 :
16621 : /* OK to create inheritance */
16622 362 : CreateInheritance(child_rel, parent_rel, false);
16623 :
16624 284 : ObjectAddressSet(address, RelationRelationId,
16625 : RelationGetRelid(parent_rel));
16626 :
16627 : /* keep our lock on the parent relation until commit */
16628 284 : table_close(parent_rel, NoLock);
16629 :
16630 284 : return address;
16631 : }
16632 :
16633 : /*
16634 : * CreateInheritance
16635 : * Catalog manipulation portion of creating inheritance between a child
16636 : * table and a parent table.
16637 : *
16638 : * Common to ATExecAddInherit() and ATExecAttachPartition().
16639 : */
16640 : static void
16641 2508 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16642 : {
16643 : Relation catalogRelation;
16644 : SysScanDesc scan;
16645 : ScanKeyData key;
16646 : HeapTuple inheritsTuple;
16647 : int32 inhseqno;
16648 :
16649 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16650 2508 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16651 :
16652 : /*
16653 : * Check for duplicates in the list of parents, and determine the highest
16654 : * inhseqno already present; we'll use the next one for the new parent.
16655 : * Also, if proposed child is a partition, it cannot already be
16656 : * inheriting.
16657 : *
16658 : * Note: we do not reject the case where the child already inherits from
16659 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16660 : */
16661 2508 : ScanKeyInit(&key,
16662 : Anum_pg_inherits_inhrelid,
16663 : BTEqualStrategyNumber, F_OIDEQ,
16664 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16665 2508 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16666 : true, NULL, 1, &key);
16667 :
16668 : /* inhseqno sequences start at 1 */
16669 2508 : inhseqno = 0;
16670 2574 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16671 : {
16672 72 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16673 :
16674 72 : if (inh->inhparent == RelationGetRelid(parent_rel))
16675 6 : ereport(ERROR,
16676 : (errcode(ERRCODE_DUPLICATE_TABLE),
16677 : errmsg("relation \"%s\" would be inherited from more than once",
16678 : RelationGetRelationName(parent_rel))));
16679 :
16680 66 : if (inh->inhseqno > inhseqno)
16681 66 : inhseqno = inh->inhseqno;
16682 : }
16683 2502 : systable_endscan(scan);
16684 :
16685 : /* Match up the columns and bump attinhcount as needed */
16686 2502 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16687 :
16688 : /* Match up the constraints and bump coninhcount as needed */
16689 2376 : MergeConstraintsIntoExisting(child_rel, parent_rel);
16690 :
16691 : /*
16692 : * OK, it looks valid. Make the catalog entries that show inheritance.
16693 : */
16694 2328 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
16695 : RelationGetRelid(parent_rel),
16696 : inhseqno + 1,
16697 : catalogRelation,
16698 2328 : parent_rel->rd_rel->relkind ==
16699 : RELKIND_PARTITIONED_TABLE);
16700 :
16701 : /* Now we're done with pg_inherits */
16702 2328 : table_close(catalogRelation, RowExclusiveLock);
16703 2328 : }
16704 :
16705 : /*
16706 : * Obtain the source-text form of the constraint expression for a check
16707 : * constraint, given its pg_constraint tuple
16708 : */
16709 : static char *
16710 184 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16711 : {
16712 : Form_pg_constraint con;
16713 : bool isnull;
16714 : Datum attr;
16715 : Datum expr;
16716 :
16717 184 : con = (Form_pg_constraint) GETSTRUCT(contup);
16718 184 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16719 184 : if (isnull)
16720 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
16721 :
16722 184 : expr = DirectFunctionCall2(pg_get_expr, attr,
16723 : ObjectIdGetDatum(con->conrelid));
16724 184 : return TextDatumGetCString(expr);
16725 : }
16726 :
16727 : /*
16728 : * Determine whether two check constraints are functionally equivalent
16729 : *
16730 : * The test we apply is to see whether they reverse-compile to the same
16731 : * source string. This insulates us from issues like whether attributes
16732 : * have the same physical column numbers in parent and child relations.
16733 : *
16734 : * Note that we ignore enforceability as there are cases where constraints
16735 : * with differing enforceability are allowed.
16736 : */
16737 : static bool
16738 92 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16739 : {
16740 92 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16741 92 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16742 :
16743 92 : if (acon->condeferrable != bcon->condeferrable ||
16744 92 : acon->condeferred != bcon->condeferred ||
16745 92 : strcmp(decompile_conbin(a, tupleDesc),
16746 92 : decompile_conbin(b, tupleDesc)) != 0)
16747 6 : return false;
16748 : else
16749 86 : return true;
16750 : }
16751 :
16752 : /*
16753 : * Check columns in child table match up with columns in parent, and increment
16754 : * their attinhcount.
16755 : *
16756 : * Called by CreateInheritance
16757 : *
16758 : * Currently all parent columns must be found in child. Missing columns are an
16759 : * error. One day we might consider creating new columns like CREATE TABLE
16760 : * does. However, that is widely unpopular --- in the common use case of
16761 : * partitioned tables it's a foot-gun.
16762 : *
16763 : * The data type must match exactly. If the parent column is NOT NULL then
16764 : * the child must be as well. Defaults are not compared, however.
16765 : */
16766 : static void
16767 2502 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16768 : {
16769 : Relation attrrel;
16770 : TupleDesc parent_desc;
16771 :
16772 2502 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16773 2502 : parent_desc = RelationGetDescr(parent_rel);
16774 :
16775 8044 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16776 : {
16777 5668 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16778 5668 : char *parent_attname = NameStr(parent_att->attname);
16779 : HeapTuple tuple;
16780 :
16781 : /* Ignore dropped columns in the parent. */
16782 5668 : if (parent_att->attisdropped)
16783 296 : continue;
16784 :
16785 : /* Find same column in child (matching on column name). */
16786 5372 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16787 5372 : if (HeapTupleIsValid(tuple))
16788 : {
16789 5360 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16790 :
16791 5360 : if (parent_att->atttypid != child_att->atttypid ||
16792 5354 : parent_att->atttypmod != child_att->atttypmod)
16793 12 : ereport(ERROR,
16794 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16795 : errmsg("child table \"%s\" has different type for column \"%s\"",
16796 : RelationGetRelationName(child_rel), parent_attname)));
16797 :
16798 5348 : if (parent_att->attcollation != child_att->attcollation)
16799 6 : ereport(ERROR,
16800 : (errcode(ERRCODE_COLLATION_MISMATCH),
16801 : errmsg("child table \"%s\" has different collation for column \"%s\"",
16802 : RelationGetRelationName(child_rel), parent_attname)));
16803 :
16804 : /*
16805 : * If the parent has a not-null constraint that's not NO INHERIT,
16806 : * make sure the child has one too.
16807 : *
16808 : * Other constraints are checked elsewhere.
16809 : */
16810 5342 : if (parent_att->attnotnull && !child_att->attnotnull)
16811 : {
16812 : HeapTuple contup;
16813 :
16814 42 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16815 42 : parent_att->attnum);
16816 42 : if (HeapTupleIsValid(contup) &&
16817 42 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16818 24 : ereport(ERROR,
16819 : errcode(ERRCODE_DATATYPE_MISMATCH),
16820 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16821 : parent_attname, RelationGetRelationName(child_rel)));
16822 : }
16823 :
16824 : /*
16825 : * Child column must be generated if and only if parent column is.
16826 : */
16827 5318 : if (parent_att->attgenerated && !child_att->attgenerated)
16828 36 : ereport(ERROR,
16829 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16830 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16831 5282 : if (child_att->attgenerated && !parent_att->attgenerated)
16832 24 : ereport(ERROR,
16833 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16834 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16835 :
16836 5258 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
16837 12 : ereport(ERROR,
16838 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16839 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
16840 : errdetail("Parent column is %s, child column is %s.",
16841 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
16842 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
16843 :
16844 : /*
16845 : * Regular inheritance children are independent enough not to
16846 : * inherit identity columns. But partitions are integral part of
16847 : * a partitioned table and inherit identity column.
16848 : */
16849 5246 : if (ispartition)
16850 4586 : child_att->attidentity = parent_att->attidentity;
16851 :
16852 : /*
16853 : * OK, bump the child column's inheritance count. (If we fail
16854 : * later on, this change will just roll back.)
16855 : */
16856 5246 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
16857 : &child_att->attinhcount))
16858 0 : ereport(ERROR,
16859 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16860 : errmsg("too many inheritance parents"));
16861 :
16862 : /*
16863 : * In case of partitions, we must enforce that value of attislocal
16864 : * is same in all partitions. (Note: there are only inherited
16865 : * attributes in partitions)
16866 : */
16867 5246 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16868 : {
16869 : Assert(child_att->attinhcount == 1);
16870 4586 : child_att->attislocal = false;
16871 : }
16872 :
16873 5246 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16874 5246 : heap_freetuple(tuple);
16875 : }
16876 : else
16877 : {
16878 12 : ereport(ERROR,
16879 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16880 : errmsg("child table is missing column \"%s\"", parent_attname)));
16881 : }
16882 : }
16883 :
16884 2376 : table_close(attrrel, RowExclusiveLock);
16885 2376 : }
16886 :
16887 : /*
16888 : * Check constraints in child table match up with constraints in parent,
16889 : * and increment their coninhcount.
16890 : *
16891 : * Constraints that are marked ONLY in the parent are ignored.
16892 : *
16893 : * Called by CreateInheritance
16894 : *
16895 : * Currently all constraints in parent must be present in the child. One day we
16896 : * may consider adding new constraints like CREATE TABLE does.
16897 : *
16898 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
16899 : * constraints. As long as tables have more like 10 constraints it shouldn't be
16900 : * a problem though. Even 100 constraints ought not be the end of the world.
16901 : *
16902 : * XXX See MergeWithExistingConstraint too if you change this code.
16903 : */
16904 : static void
16905 2376 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16906 : {
16907 : Relation constraintrel;
16908 : SysScanDesc parent_scan;
16909 : ScanKeyData parent_key;
16910 : HeapTuple parent_tuple;
16911 2376 : Oid parent_relid = RelationGetRelid(parent_rel);
16912 : AttrMap *attmap;
16913 :
16914 2376 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16915 :
16916 : /* Outer loop scans through the parent's constraint definitions */
16917 2376 : ScanKeyInit(&parent_key,
16918 : Anum_pg_constraint_conrelid,
16919 : BTEqualStrategyNumber, F_OIDEQ,
16920 : ObjectIdGetDatum(parent_relid));
16921 2376 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16922 : true, NULL, 1, &parent_key);
16923 :
16924 2376 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
16925 : RelationGetDescr(child_rel),
16926 : true);
16927 :
16928 4142 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16929 : {
16930 1814 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16931 : SysScanDesc child_scan;
16932 : ScanKeyData child_key;
16933 : HeapTuple child_tuple;
16934 : AttrNumber parent_attno;
16935 1814 : bool found = false;
16936 :
16937 1814 : if (parent_con->contype != CONSTRAINT_CHECK &&
16938 1678 : parent_con->contype != CONSTRAINT_NOTNULL)
16939 880 : continue;
16940 :
16941 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16942 978 : if (parent_con->connoinherit)
16943 44 : continue;
16944 :
16945 934 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16946 818 : parent_attno = extractNotNullColumn(parent_tuple);
16947 : else
16948 116 : parent_attno = InvalidAttrNumber;
16949 :
16950 : /* Search for a child constraint matching this one */
16951 934 : ScanKeyInit(&child_key,
16952 : Anum_pg_constraint_conrelid,
16953 : BTEqualStrategyNumber, F_OIDEQ,
16954 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16955 934 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16956 : true, NULL, 1, &child_key);
16957 :
16958 1580 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16959 : {
16960 1556 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16961 : HeapTuple child_copy;
16962 :
16963 1556 : if (child_con->contype != parent_con->contype)
16964 352 : continue;
16965 :
16966 : /*
16967 : * CHECK constraint are matched by constraint name, NOT NULL ones
16968 : * by attribute number.
16969 : */
16970 1204 : if (child_con->contype == CONSTRAINT_CHECK)
16971 : {
16972 122 : if (strcmp(NameStr(parent_con->conname),
16973 122 : NameStr(child_con->conname)) != 0)
16974 30 : continue;
16975 : }
16976 1082 : else if (child_con->contype == CONSTRAINT_NOTNULL)
16977 : {
16978 : Form_pg_attribute parent_attr;
16979 : Form_pg_attribute child_attr;
16980 : AttrNumber child_attno;
16981 :
16982 1082 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
16983 1082 : child_attno = extractNotNullColumn(child_tuple);
16984 1082 : if (parent_attno != attmap->attnums[child_attno - 1])
16985 264 : continue;
16986 :
16987 818 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
16988 : /* there shouldn't be constraints on dropped columns */
16989 818 : if (parent_attr->attisdropped || child_attr->attisdropped)
16990 0 : elog(ERROR, "found not-null constraint on dropped columns");
16991 : }
16992 :
16993 910 : if (child_con->contype == CONSTRAINT_CHECK &&
16994 92 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16995 6 : ereport(ERROR,
16996 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16997 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16998 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16999 :
17000 : /*
17001 : * If the child constraint is "no inherit" then cannot merge
17002 : */
17003 904 : if (child_con->connoinherit)
17004 12 : ereport(ERROR,
17005 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17006 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17007 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17008 :
17009 : /*
17010 : * If the child constraint is "not valid" then cannot merge with a
17011 : * valid parent constraint
17012 : */
17013 892 : if (parent_con->convalidated && child_con->conenforced &&
17014 880 : !child_con->convalidated)
17015 0 : ereport(ERROR,
17016 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17017 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17018 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17019 :
17020 : /*
17021 : * A non-enforced child constraint cannot be merged with an
17022 : * enforced parent constraint. However, the reverse is allowed,
17023 : * where the child constraint is enforced.
17024 : */
17025 892 : if (parent_con->conenforced && !child_con->conenforced)
17026 6 : ereport(ERROR,
17027 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17028 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17029 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17030 :
17031 : /*
17032 : * OK, bump the child constraint's inheritance count. (If we fail
17033 : * later on, this change will just roll back.)
17034 : */
17035 886 : child_copy = heap_copytuple(child_tuple);
17036 886 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17037 :
17038 886 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
17039 : &child_con->coninhcount))
17040 0 : ereport(ERROR,
17041 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17042 : errmsg("too many inheritance parents"));
17043 :
17044 : /*
17045 : * In case of partitions, an inherited constraint must be
17046 : * inherited only once since it cannot have multiple parents and
17047 : * it is never considered local.
17048 : */
17049 886 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17050 : {
17051 : Assert(child_con->coninhcount == 1);
17052 752 : child_con->conislocal = false;
17053 : }
17054 :
17055 886 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17056 886 : heap_freetuple(child_copy);
17057 :
17058 886 : found = true;
17059 886 : break;
17060 : }
17061 :
17062 910 : systable_endscan(child_scan);
17063 :
17064 910 : if (!found)
17065 : {
17066 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17067 0 : ereport(ERROR,
17068 : errcode(ERRCODE_DATATYPE_MISMATCH),
17069 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17070 : get_attname(parent_relid,
17071 : extractNotNullColumn(parent_tuple),
17072 : false),
17073 : RelationGetRelationName(child_rel)));
17074 :
17075 24 : ereport(ERROR,
17076 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17077 : errmsg("child table is missing constraint \"%s\"",
17078 : NameStr(parent_con->conname))));
17079 : }
17080 : }
17081 :
17082 2328 : systable_endscan(parent_scan);
17083 2328 : table_close(constraintrel, RowExclusiveLock);
17084 2328 : }
17085 :
17086 : /*
17087 : * ALTER TABLE NO INHERIT
17088 : *
17089 : * Return value is the address of the relation that is no longer parent.
17090 : */
17091 : static ObjectAddress
17092 86 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
17093 : {
17094 : ObjectAddress address;
17095 : Relation parent_rel;
17096 :
17097 86 : if (rel->rd_rel->relispartition)
17098 0 : ereport(ERROR,
17099 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17100 : errmsg("cannot change inheritance of a partition")));
17101 :
17102 : /*
17103 : * AccessShareLock on the parent is probably enough, seeing that DROP
17104 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
17105 : * be inspecting the parent's schema.
17106 : */
17107 86 : parent_rel = table_openrv(parent, AccessShareLock);
17108 :
17109 : /*
17110 : * We don't bother to check ownership of the parent table --- ownership of
17111 : * the child is presumed enough rights.
17112 : */
17113 :
17114 : /* Off to RemoveInheritance() where most of the work happens */
17115 86 : RemoveInheritance(rel, parent_rel, false);
17116 :
17117 80 : ObjectAddressSet(address, RelationRelationId,
17118 : RelationGetRelid(parent_rel));
17119 :
17120 : /* keep our lock on the parent relation until commit */
17121 80 : table_close(parent_rel, NoLock);
17122 :
17123 80 : return address;
17124 : }
17125 :
17126 : /*
17127 : * MarkInheritDetached
17128 : *
17129 : * Set inhdetachpending for a partition, for ATExecDetachPartition
17130 : * in concurrent mode. While at it, verify that no other partition is
17131 : * already pending detach.
17132 : */
17133 : static void
17134 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
17135 : {
17136 : Relation catalogRelation;
17137 : SysScanDesc scan;
17138 : ScanKeyData key;
17139 : HeapTuple inheritsTuple;
17140 146 : bool found = false;
17141 :
17142 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17143 :
17144 : /*
17145 : * Find pg_inherits entries by inhparent. (We need to scan them all in
17146 : * order to verify that no other partition is pending detach.)
17147 : */
17148 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17149 146 : ScanKeyInit(&key,
17150 : Anum_pg_inherits_inhparent,
17151 : BTEqualStrategyNumber, F_OIDEQ,
17152 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17153 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17154 : true, NULL, 1, &key);
17155 :
17156 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17157 : {
17158 : Form_pg_inherits inhForm;
17159 :
17160 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17161 286 : if (inhForm->inhdetachpending)
17162 2 : ereport(ERROR,
17163 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17164 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17165 : get_rel_name(inhForm->inhrelid),
17166 : get_namespace_name(parent_rel->rd_rel->relnamespace),
17167 : RelationGetRelationName(parent_rel)),
17168 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17169 :
17170 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
17171 : {
17172 : HeapTuple newtup;
17173 :
17174 144 : newtup = heap_copytuple(inheritsTuple);
17175 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17176 :
17177 144 : CatalogTupleUpdate(catalogRelation,
17178 : &inheritsTuple->t_self,
17179 : newtup);
17180 144 : found = true;
17181 144 : heap_freetuple(newtup);
17182 : /* keep looking, to ensure we catch others pending detach */
17183 : }
17184 : }
17185 :
17186 : /* Done */
17187 144 : systable_endscan(scan);
17188 144 : table_close(catalogRelation, RowExclusiveLock);
17189 :
17190 144 : if (!found)
17191 0 : ereport(ERROR,
17192 : (errcode(ERRCODE_UNDEFINED_TABLE),
17193 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17194 : RelationGetRelationName(child_rel),
17195 : RelationGetRelationName(parent_rel))));
17196 144 : }
17197 :
17198 : /*
17199 : * RemoveInheritance
17200 : *
17201 : * Drop a parent from the child's parents. This just adjusts the attinhcount
17202 : * and attislocal of the columns and removes the pg_inherit and pg_depend
17203 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17204 : *
17205 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17206 : * up attislocal stays true, which means if a child is ever removed from a
17207 : * parent then its columns will never be automatically dropped which may
17208 : * surprise. But at least we'll never surprise by dropping columns someone
17209 : * isn't expecting to be dropped which would actually mean data loss.
17210 : *
17211 : * coninhcount and conislocal for inherited constraints are adjusted in
17212 : * exactly the same way.
17213 : *
17214 : * Common to ATExecDropInherit() and ATExecDetachPartition().
17215 : */
17216 : static void
17217 578 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17218 : {
17219 : Relation catalogRelation;
17220 : SysScanDesc scan;
17221 : ScanKeyData key[3];
17222 : HeapTuple attributeTuple,
17223 : constraintTuple;
17224 : AttrMap *attmap;
17225 : List *connames;
17226 : List *nncolumns;
17227 : bool found;
17228 : bool is_partitioning;
17229 :
17230 578 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17231 :
17232 578 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17233 : RelationGetRelid(parent_rel),
17234 : expect_detached,
17235 578 : RelationGetRelationName(child_rel));
17236 578 : if (!found)
17237 : {
17238 24 : if (is_partitioning)
17239 18 : ereport(ERROR,
17240 : (errcode(ERRCODE_UNDEFINED_TABLE),
17241 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17242 : RelationGetRelationName(child_rel),
17243 : RelationGetRelationName(parent_rel))));
17244 : else
17245 6 : ereport(ERROR,
17246 : (errcode(ERRCODE_UNDEFINED_TABLE),
17247 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17248 : RelationGetRelationName(parent_rel),
17249 : RelationGetRelationName(child_rel))));
17250 : }
17251 :
17252 : /*
17253 : * Search through child columns looking for ones matching parent rel
17254 : */
17255 554 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17256 554 : ScanKeyInit(&key[0],
17257 : Anum_pg_attribute_attrelid,
17258 : BTEqualStrategyNumber, F_OIDEQ,
17259 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17260 554 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17261 : true, NULL, 1, key);
17262 4946 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17263 : {
17264 4392 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17265 :
17266 : /* Ignore if dropped or not inherited */
17267 4392 : if (att->attisdropped)
17268 6 : continue;
17269 4386 : if (att->attinhcount <= 0)
17270 3354 : continue;
17271 :
17272 1032 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
17273 1032 : NameStr(att->attname)))
17274 : {
17275 : /* Decrement inhcount and possibly set islocal to true */
17276 978 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
17277 978 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17278 :
17279 978 : copy_att->attinhcount--;
17280 978 : if (copy_att->attinhcount == 0)
17281 948 : copy_att->attislocal = true;
17282 :
17283 978 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17284 978 : heap_freetuple(copyTuple);
17285 : }
17286 : }
17287 554 : systable_endscan(scan);
17288 554 : table_close(catalogRelation, RowExclusiveLock);
17289 :
17290 : /*
17291 : * Likewise, find inherited check and not-null constraints and disinherit
17292 : * them. To do this, we first need a list of the names of the parent's
17293 : * check constraints. (We cheat a bit by only checking for name matches,
17294 : * assuming that the expressions will match.)
17295 : *
17296 : * For NOT NULL columns, we store column numbers to match, mapping them in
17297 : * to the child rel's attribute numbers.
17298 : */
17299 554 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17300 : RelationGetDescr(parent_rel),
17301 : false);
17302 :
17303 554 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17304 554 : ScanKeyInit(&key[0],
17305 : Anum_pg_constraint_conrelid,
17306 : BTEqualStrategyNumber, F_OIDEQ,
17307 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17308 554 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17309 : true, NULL, 1, key);
17310 :
17311 554 : connames = NIL;
17312 554 : nncolumns = NIL;
17313 :
17314 1082 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17315 : {
17316 528 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17317 :
17318 528 : if (con->connoinherit)
17319 98 : continue;
17320 :
17321 430 : if (con->contype == CONSTRAINT_CHECK)
17322 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
17323 430 : if (con->contype == CONSTRAINT_NOTNULL)
17324 : {
17325 196 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17326 :
17327 196 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
17328 : }
17329 : }
17330 :
17331 554 : systable_endscan(scan);
17332 :
17333 : /* Now scan the child's constraints to find matches */
17334 554 : ScanKeyInit(&key[0],
17335 : Anum_pg_constraint_conrelid,
17336 : BTEqualStrategyNumber, F_OIDEQ,
17337 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17338 554 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17339 : true, NULL, 1, key);
17340 :
17341 1272 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17342 : {
17343 718 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17344 718 : bool match = false;
17345 :
17346 : /*
17347 : * Match CHECK constraints by name, not-null constraints by column
17348 : * number, and ignore all others.
17349 : */
17350 718 : if (con->contype == CONSTRAINT_CHECK)
17351 : {
17352 350 : foreach_ptr(char, chkname, connames)
17353 : {
17354 18 : if (con->contype == CONSTRAINT_CHECK &&
17355 18 : strcmp(NameStr(con->conname), chkname) == 0)
17356 : {
17357 12 : match = true;
17358 12 : connames = foreach_delete_current(connames, chkname);
17359 12 : break;
17360 : }
17361 : }
17362 : }
17363 546 : else if (con->contype == CONSTRAINT_NOTNULL)
17364 : {
17365 256 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
17366 :
17367 518 : foreach_int(prevattno, nncolumns)
17368 : {
17369 202 : if (prevattno == child_attno)
17370 : {
17371 196 : match = true;
17372 196 : nncolumns = foreach_delete_current(nncolumns, prevattno);
17373 196 : break;
17374 : }
17375 : }
17376 : }
17377 : else
17378 290 : continue;
17379 :
17380 428 : if (match)
17381 : {
17382 : /* Decrement inhcount and possibly set islocal to true */
17383 208 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
17384 208 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17385 :
17386 208 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
17387 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17388 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
17389 :
17390 208 : copy_con->coninhcount--;
17391 208 : if (copy_con->coninhcount == 0)
17392 190 : copy_con->conislocal = true;
17393 :
17394 208 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17395 208 : heap_freetuple(copyTuple);
17396 : }
17397 : }
17398 :
17399 : /* We should have matched all constraints */
17400 554 : if (connames != NIL || nncolumns != NIL)
17401 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
17402 : list_length(connames) + list_length(nncolumns),
17403 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
17404 :
17405 554 : systable_endscan(scan);
17406 554 : table_close(catalogRelation, RowExclusiveLock);
17407 :
17408 554 : drop_parent_dependency(RelationGetRelid(child_rel),
17409 : RelationRelationId,
17410 : RelationGetRelid(parent_rel),
17411 : child_dependency_type(is_partitioning));
17412 :
17413 : /*
17414 : * Post alter hook of this inherits. Since object_access_hook doesn't take
17415 : * multiple object identifiers, we relay oid of parent relation using
17416 : * auxiliary_id argument.
17417 : */
17418 554 : InvokeObjectPostAlterHookArg(InheritsRelationId,
17419 : RelationGetRelid(child_rel), 0,
17420 : RelationGetRelid(parent_rel), false);
17421 554 : }
17422 :
17423 : /*
17424 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17425 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17426 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17427 : * be TypeRelationId). There's no convenient way to do this, so go trawling
17428 : * through pg_depend.
17429 : */
17430 : static void
17431 566 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
17432 : DependencyType deptype)
17433 : {
17434 : Relation catalogRelation;
17435 : SysScanDesc scan;
17436 : ScanKeyData key[3];
17437 : HeapTuple depTuple;
17438 :
17439 566 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17440 :
17441 566 : ScanKeyInit(&key[0],
17442 : Anum_pg_depend_classid,
17443 : BTEqualStrategyNumber, F_OIDEQ,
17444 : ObjectIdGetDatum(RelationRelationId));
17445 566 : ScanKeyInit(&key[1],
17446 : Anum_pg_depend_objid,
17447 : BTEqualStrategyNumber, F_OIDEQ,
17448 : ObjectIdGetDatum(relid));
17449 566 : ScanKeyInit(&key[2],
17450 : Anum_pg_depend_objsubid,
17451 : BTEqualStrategyNumber, F_INT4EQ,
17452 : Int32GetDatum(0));
17453 :
17454 566 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17455 : NULL, 3, key);
17456 :
17457 1758 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17458 : {
17459 1192 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17460 :
17461 1192 : if (dep->refclassid == refclassid &&
17462 608 : dep->refobjid == refobjid &&
17463 566 : dep->refobjsubid == 0 &&
17464 566 : dep->deptype == deptype)
17465 566 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17466 : }
17467 :
17468 566 : systable_endscan(scan);
17469 566 : table_close(catalogRelation, RowExclusiveLock);
17470 566 : }
17471 :
17472 : /*
17473 : * ALTER TABLE OF
17474 : *
17475 : * Attach a table to a composite type, as though it had been created with CREATE
17476 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17477 : * subject table must not have inheritance parents. These restrictions ensure
17478 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17479 : *
17480 : * The address of the type is returned.
17481 : */
17482 : static ObjectAddress
17483 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17484 : {
17485 66 : Oid relid = RelationGetRelid(rel);
17486 : Type typetuple;
17487 : Form_pg_type typeform;
17488 : Oid typeid;
17489 : Relation inheritsRelation,
17490 : relationRelation;
17491 : SysScanDesc scan;
17492 : ScanKeyData key;
17493 : AttrNumber table_attno,
17494 : type_attno;
17495 : TupleDesc typeTupleDesc,
17496 : tableTupleDesc;
17497 : ObjectAddress tableobj,
17498 : typeobj;
17499 : HeapTuple classtuple;
17500 :
17501 : /* Validate the type. */
17502 66 : typetuple = typenameType(NULL, ofTypename, NULL);
17503 66 : check_of_type(typetuple);
17504 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
17505 66 : typeid = typeform->oid;
17506 :
17507 : /* Fail if the table has any inheritance parents. */
17508 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17509 66 : ScanKeyInit(&key,
17510 : Anum_pg_inherits_inhrelid,
17511 : BTEqualStrategyNumber, F_OIDEQ,
17512 : ObjectIdGetDatum(relid));
17513 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17514 : true, NULL, 1, &key);
17515 66 : if (HeapTupleIsValid(systable_getnext(scan)))
17516 6 : ereport(ERROR,
17517 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17518 : errmsg("typed tables cannot inherit")));
17519 60 : systable_endscan(scan);
17520 60 : table_close(inheritsRelation, AccessShareLock);
17521 :
17522 : /*
17523 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
17524 : * require that the order also match. However, attnotnull need not match.
17525 : */
17526 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17527 60 : tableTupleDesc = RelationGetDescr(rel);
17528 60 : table_attno = 1;
17529 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17530 : {
17531 : Form_pg_attribute type_attr,
17532 : table_attr;
17533 : const char *type_attname,
17534 : *table_attname;
17535 :
17536 : /* Get the next non-dropped type attribute. */
17537 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17538 154 : if (type_attr->attisdropped)
17539 44 : continue;
17540 110 : type_attname = NameStr(type_attr->attname);
17541 :
17542 : /* Get the next non-dropped table attribute. */
17543 : do
17544 : {
17545 122 : if (table_attno > tableTupleDesc->natts)
17546 6 : ereport(ERROR,
17547 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17548 : errmsg("table is missing column \"%s\"",
17549 : type_attname)));
17550 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17551 116 : table_attno++;
17552 116 : } while (table_attr->attisdropped);
17553 104 : table_attname = NameStr(table_attr->attname);
17554 :
17555 : /* Compare name. */
17556 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17557 6 : ereport(ERROR,
17558 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17559 : errmsg("table has column \"%s\" where type requires \"%s\"",
17560 : table_attname, type_attname)));
17561 :
17562 : /* Compare type. */
17563 98 : if (table_attr->atttypid != type_attr->atttypid ||
17564 92 : table_attr->atttypmod != type_attr->atttypmod ||
17565 86 : table_attr->attcollation != type_attr->attcollation)
17566 12 : ereport(ERROR,
17567 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17568 : errmsg("table \"%s\" has different type for column \"%s\"",
17569 : RelationGetRelationName(rel), type_attname)));
17570 : }
17571 36 : ReleaseTupleDesc(typeTupleDesc);
17572 :
17573 : /* Any remaining columns at the end of the table had better be dropped. */
17574 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
17575 : {
17576 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17577 : table_attno - 1);
17578 :
17579 6 : if (!table_attr->attisdropped)
17580 6 : ereport(ERROR,
17581 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17582 : errmsg("table has extra column \"%s\"",
17583 : NameStr(table_attr->attname))));
17584 : }
17585 :
17586 : /* If the table was already typed, drop the existing dependency. */
17587 30 : if (rel->rd_rel->reloftype)
17588 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17589 : DEPENDENCY_NORMAL);
17590 :
17591 : /* Record a dependency on the new type. */
17592 30 : tableobj.classId = RelationRelationId;
17593 30 : tableobj.objectId = relid;
17594 30 : tableobj.objectSubId = 0;
17595 30 : typeobj.classId = TypeRelationId;
17596 30 : typeobj.objectId = typeid;
17597 30 : typeobj.objectSubId = 0;
17598 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17599 :
17600 : /* Update pg_class.reloftype */
17601 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17602 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17603 30 : if (!HeapTupleIsValid(classtuple))
17604 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17605 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17606 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17607 :
17608 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17609 :
17610 30 : heap_freetuple(classtuple);
17611 30 : table_close(relationRelation, RowExclusiveLock);
17612 :
17613 30 : ReleaseSysCache(typetuple);
17614 :
17615 30 : return typeobj;
17616 : }
17617 :
17618 : /*
17619 : * ALTER TABLE NOT OF
17620 : *
17621 : * Detach a typed table from its originating type. Just clear reloftype and
17622 : * remove the dependency.
17623 : */
17624 : static void
17625 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
17626 : {
17627 6 : Oid relid = RelationGetRelid(rel);
17628 : Relation relationRelation;
17629 : HeapTuple tuple;
17630 :
17631 6 : if (!OidIsValid(rel->rd_rel->reloftype))
17632 0 : ereport(ERROR,
17633 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17634 : errmsg("\"%s\" is not a typed table",
17635 : RelationGetRelationName(rel))));
17636 :
17637 : /*
17638 : * We don't bother to check ownership of the type --- ownership of the
17639 : * table is presumed enough rights. No lock required on the type, either.
17640 : */
17641 :
17642 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17643 : DEPENDENCY_NORMAL);
17644 :
17645 : /* Clear pg_class.reloftype */
17646 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17647 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17648 6 : if (!HeapTupleIsValid(tuple))
17649 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17650 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17651 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17652 :
17653 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17654 :
17655 6 : heap_freetuple(tuple);
17656 6 : table_close(relationRelation, RowExclusiveLock);
17657 6 : }
17658 :
17659 : /*
17660 : * relation_mark_replica_identity: Update a table's replica identity
17661 : *
17662 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17663 : * index. Otherwise, it must be InvalidOid.
17664 : *
17665 : * Caller had better hold an exclusive lock on the relation, as the results
17666 : * of running two of these concurrently wouldn't be pretty.
17667 : */
17668 : static void
17669 460 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17670 : bool is_internal)
17671 : {
17672 : Relation pg_index;
17673 : Relation pg_class;
17674 : HeapTuple pg_class_tuple;
17675 : HeapTuple pg_index_tuple;
17676 : Form_pg_class pg_class_form;
17677 : Form_pg_index pg_index_form;
17678 : ListCell *index;
17679 :
17680 : /*
17681 : * Check whether relreplident has changed, and update it if so.
17682 : */
17683 460 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17684 460 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
17685 : ObjectIdGetDatum(RelationGetRelid(rel)));
17686 460 : if (!HeapTupleIsValid(pg_class_tuple))
17687 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
17688 : RelationGetRelationName(rel));
17689 460 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17690 460 : if (pg_class_form->relreplident != ri_type)
17691 : {
17692 410 : pg_class_form->relreplident = ri_type;
17693 410 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17694 : }
17695 460 : table_close(pg_class, RowExclusiveLock);
17696 460 : heap_freetuple(pg_class_tuple);
17697 :
17698 : /*
17699 : * Update the per-index indisreplident flags correctly.
17700 : */
17701 460 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
17702 1180 : foreach(index, RelationGetIndexList(rel))
17703 : {
17704 720 : Oid thisIndexOid = lfirst_oid(index);
17705 720 : bool dirty = false;
17706 :
17707 720 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17708 : ObjectIdGetDatum(thisIndexOid));
17709 720 : if (!HeapTupleIsValid(pg_index_tuple))
17710 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17711 720 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17712 :
17713 720 : if (thisIndexOid == indexOid)
17714 : {
17715 : /* Set the bit if not already set. */
17716 240 : if (!pg_index_form->indisreplident)
17717 : {
17718 222 : dirty = true;
17719 222 : pg_index_form->indisreplident = true;
17720 : }
17721 : }
17722 : else
17723 : {
17724 : /* Unset the bit if set. */
17725 480 : if (pg_index_form->indisreplident)
17726 : {
17727 52 : dirty = true;
17728 52 : pg_index_form->indisreplident = false;
17729 : }
17730 : }
17731 :
17732 720 : if (dirty)
17733 : {
17734 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17735 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17736 : InvalidOid, is_internal);
17737 :
17738 : /*
17739 : * Invalidate the relcache for the table, so that after we commit
17740 : * all sessions will refresh the table's replica identity index
17741 : * before attempting any UPDATE or DELETE on the table. (If we
17742 : * changed the table's pg_class row above, then a relcache inval
17743 : * is already queued due to that; but we might not have.)
17744 : */
17745 274 : CacheInvalidateRelcache(rel);
17746 : }
17747 720 : heap_freetuple(pg_index_tuple);
17748 : }
17749 :
17750 460 : table_close(pg_index, RowExclusiveLock);
17751 460 : }
17752 :
17753 : /*
17754 : * ALTER TABLE <name> REPLICA IDENTITY ...
17755 : */
17756 : static void
17757 508 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17758 : {
17759 : Oid indexOid;
17760 : Relation indexRel;
17761 : int key;
17762 :
17763 508 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17764 : {
17765 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17766 6 : return;
17767 : }
17768 502 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17769 : {
17770 166 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17771 166 : return;
17772 : }
17773 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17774 : {
17775 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17776 48 : return;
17777 : }
17778 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17779 : {
17780 : /* fallthrough */ ;
17781 : }
17782 : else
17783 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17784 :
17785 : /* Check that the index exists */
17786 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17787 288 : if (!OidIsValid(indexOid))
17788 0 : ereport(ERROR,
17789 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17790 : errmsg("index \"%s\" for table \"%s\" does not exist",
17791 : stmt->name, RelationGetRelationName(rel))));
17792 :
17793 288 : indexRel = index_open(indexOid, ShareLock);
17794 :
17795 : /* Check that the index is on the relation we're altering. */
17796 288 : if (indexRel->rd_index == NULL ||
17797 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
17798 6 : ereport(ERROR,
17799 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17800 : errmsg("\"%s\" is not an index for table \"%s\"",
17801 : RelationGetRelationName(indexRel),
17802 : RelationGetRelationName(rel))));
17803 :
17804 : /*
17805 : * The AM must support uniqueness, and the index must in fact be unique.
17806 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
17807 : * exclusion), we can use that too.
17808 : */
17809 282 : if ((!indexRel->rd_indam->amcanunique ||
17810 262 : !indexRel->rd_index->indisunique) &&
17811 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
17812 12 : ereport(ERROR,
17813 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17814 : errmsg("cannot use non-unique index \"%s\" as replica identity",
17815 : RelationGetRelationName(indexRel))));
17816 : /* Deferred indexes are not guaranteed to be always unique. */
17817 270 : if (!indexRel->rd_index->indimmediate)
17818 12 : ereport(ERROR,
17819 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17820 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
17821 : RelationGetRelationName(indexRel))));
17822 : /* Expression indexes aren't supported. */
17823 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
17824 6 : ereport(ERROR,
17825 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17826 : errmsg("cannot use expression index \"%s\" as replica identity",
17827 : RelationGetRelationName(indexRel))));
17828 : /* Predicate indexes aren't supported. */
17829 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
17830 6 : ereport(ERROR,
17831 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17832 : errmsg("cannot use partial index \"%s\" as replica identity",
17833 : RelationGetRelationName(indexRel))));
17834 :
17835 : /* Check index for nullable columns. */
17836 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17837 : {
17838 312 : int16 attno = indexRel->rd_index->indkey.values[key];
17839 : Form_pg_attribute attr;
17840 :
17841 : /*
17842 : * Reject any other system columns. (Going forward, we'll disallow
17843 : * indexes containing such columns in the first place, but they might
17844 : * exist in older branches.)
17845 : */
17846 312 : if (attno <= 0)
17847 0 : ereport(ERROR,
17848 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17849 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17850 : RelationGetRelationName(indexRel), attno)));
17851 :
17852 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
17853 312 : if (!attr->attnotnull)
17854 6 : ereport(ERROR,
17855 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17856 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17857 : RelationGetRelationName(indexRel),
17858 : NameStr(attr->attname))));
17859 : }
17860 :
17861 : /* This index is suitable for use as a replica identity. Mark it. */
17862 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17863 :
17864 240 : index_close(indexRel, NoLock);
17865 : }
17866 :
17867 : /*
17868 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17869 : */
17870 : static void
17871 300 : ATExecSetRowSecurity(Relation rel, bool rls)
17872 : {
17873 : Relation pg_class;
17874 : Oid relid;
17875 : HeapTuple tuple;
17876 :
17877 300 : relid = RelationGetRelid(rel);
17878 :
17879 : /* Pull the record for this relation and update it */
17880 300 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17881 :
17882 300 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17883 :
17884 300 : if (!HeapTupleIsValid(tuple))
17885 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17886 :
17887 300 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17888 300 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17889 :
17890 300 : InvokeObjectPostAlterHook(RelationRelationId,
17891 : RelationGetRelid(rel), 0);
17892 :
17893 300 : table_close(pg_class, RowExclusiveLock);
17894 300 : heap_freetuple(tuple);
17895 300 : }
17896 :
17897 : /*
17898 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17899 : */
17900 : static void
17901 126 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17902 : {
17903 : Relation pg_class;
17904 : Oid relid;
17905 : HeapTuple tuple;
17906 :
17907 126 : relid = RelationGetRelid(rel);
17908 :
17909 126 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17910 :
17911 126 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17912 :
17913 126 : if (!HeapTupleIsValid(tuple))
17914 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17915 :
17916 126 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17917 126 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17918 :
17919 126 : InvokeObjectPostAlterHook(RelationRelationId,
17920 : RelationGetRelid(rel), 0);
17921 :
17922 126 : table_close(pg_class, RowExclusiveLock);
17923 126 : heap_freetuple(tuple);
17924 126 : }
17925 :
17926 : /*
17927 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
17928 : */
17929 : static void
17930 58 : ATExecGenericOptions(Relation rel, List *options)
17931 : {
17932 : Relation ftrel;
17933 : ForeignServer *server;
17934 : ForeignDataWrapper *fdw;
17935 : HeapTuple tuple;
17936 : bool isnull;
17937 : Datum repl_val[Natts_pg_foreign_table];
17938 : bool repl_null[Natts_pg_foreign_table];
17939 : bool repl_repl[Natts_pg_foreign_table];
17940 : Datum datum;
17941 : Form_pg_foreign_table tableform;
17942 :
17943 58 : if (options == NIL)
17944 0 : return;
17945 :
17946 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17947 :
17948 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17949 : ObjectIdGetDatum(rel->rd_id));
17950 58 : if (!HeapTupleIsValid(tuple))
17951 0 : ereport(ERROR,
17952 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17953 : errmsg("foreign table \"%s\" does not exist",
17954 : RelationGetRelationName(rel))));
17955 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17956 58 : server = GetForeignServer(tableform->ftserver);
17957 58 : fdw = GetForeignDataWrapper(server->fdwid);
17958 :
17959 58 : memset(repl_val, 0, sizeof(repl_val));
17960 58 : memset(repl_null, false, sizeof(repl_null));
17961 58 : memset(repl_repl, false, sizeof(repl_repl));
17962 :
17963 : /* Extract the current options */
17964 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
17965 : tuple,
17966 : Anum_pg_foreign_table_ftoptions,
17967 : &isnull);
17968 58 : if (isnull)
17969 4 : datum = PointerGetDatum(NULL);
17970 :
17971 : /* Transform the options */
17972 58 : datum = transformGenericOptions(ForeignTableRelationId,
17973 : datum,
17974 : options,
17975 : fdw->fdwvalidator);
17976 :
17977 56 : if (PointerIsValid(DatumGetPointer(datum)))
17978 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17979 : else
17980 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17981 :
17982 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17983 :
17984 : /* Everything looks good - update the tuple */
17985 :
17986 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17987 : repl_val, repl_null, repl_repl);
17988 :
17989 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17990 :
17991 : /*
17992 : * Invalidate relcache so that all sessions will refresh any cached plans
17993 : * that might depend on the old options.
17994 : */
17995 56 : CacheInvalidateRelcache(rel);
17996 :
17997 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
17998 : RelationGetRelid(rel), 0);
17999 :
18000 56 : table_close(ftrel, RowExclusiveLock);
18001 :
18002 56 : heap_freetuple(tuple);
18003 : }
18004 :
18005 : /*
18006 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18007 : *
18008 : * Return value is the address of the modified column
18009 : */
18010 : static ObjectAddress
18011 68 : ATExecSetCompression(Relation rel,
18012 : const char *column,
18013 : Node *newValue,
18014 : LOCKMODE lockmode)
18015 : {
18016 : Relation attrel;
18017 : HeapTuple tuple;
18018 : Form_pg_attribute atttableform;
18019 : AttrNumber attnum;
18020 : char *compression;
18021 : char cmethod;
18022 : ObjectAddress address;
18023 :
18024 68 : compression = strVal(newValue);
18025 :
18026 68 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18027 :
18028 : /* copy the cache entry so we can scribble on it below */
18029 68 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18030 68 : if (!HeapTupleIsValid(tuple))
18031 0 : ereport(ERROR,
18032 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18033 : errmsg("column \"%s\" of relation \"%s\" does not exist",
18034 : column, RelationGetRelationName(rel))));
18035 :
18036 : /* prevent them from altering a system attribute */
18037 68 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18038 68 : attnum = atttableform->attnum;
18039 68 : if (attnum <= 0)
18040 0 : ereport(ERROR,
18041 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18042 : errmsg("cannot alter system column \"%s\"", column)));
18043 :
18044 : /*
18045 : * Check that column type is compressible, then get the attribute
18046 : * compression method code
18047 : */
18048 68 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18049 :
18050 : /* update pg_attribute entry */
18051 62 : atttableform->attcompression = cmethod;
18052 62 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18053 :
18054 62 : InvokeObjectPostAlterHook(RelationRelationId,
18055 : RelationGetRelid(rel),
18056 : attnum);
18057 :
18058 : /*
18059 : * Apply the change to indexes as well (only for simple index columns,
18060 : * matching behavior of index.c ConstructTupleDescriptor()).
18061 : */
18062 62 : SetIndexStorageProperties(rel, attrel, attnum,
18063 : false, 0,
18064 : true, cmethod,
18065 : lockmode);
18066 :
18067 62 : heap_freetuple(tuple);
18068 :
18069 62 : table_close(attrel, RowExclusiveLock);
18070 :
18071 : /* make changes visible */
18072 62 : CommandCounterIncrement();
18073 :
18074 62 : ObjectAddressSubSet(address, RelationRelationId,
18075 : RelationGetRelid(rel), attnum);
18076 62 : return address;
18077 : }
18078 :
18079 :
18080 : /*
18081 : * Preparation phase for SET LOGGED/UNLOGGED
18082 : *
18083 : * This verifies that we're not trying to change a temp table. Also,
18084 : * existing foreign key constraints are checked to avoid ending up with
18085 : * permanent tables referencing unlogged tables.
18086 : */
18087 : static void
18088 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
18089 : {
18090 : Relation pg_constraint;
18091 : HeapTuple tuple;
18092 : SysScanDesc scan;
18093 : ScanKeyData skey[1];
18094 :
18095 : /*
18096 : * Disallow changing status for a temp table. Also verify whether we can
18097 : * get away with doing nothing; in such cases we don't need to run the
18098 : * checks below, either.
18099 : */
18100 100 : switch (rel->rd_rel->relpersistence)
18101 : {
18102 0 : case RELPERSISTENCE_TEMP:
18103 0 : ereport(ERROR,
18104 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18105 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
18106 : RelationGetRelationName(rel)),
18107 : errtable(rel)));
18108 : break;
18109 56 : case RELPERSISTENCE_PERMANENT:
18110 56 : if (toLogged)
18111 : /* nothing to do */
18112 12 : return;
18113 50 : break;
18114 44 : case RELPERSISTENCE_UNLOGGED:
18115 44 : if (!toLogged)
18116 : /* nothing to do */
18117 6 : return;
18118 38 : break;
18119 : }
18120 :
18121 : /*
18122 : * Check that the table is not part of any publication when changing to
18123 : * UNLOGGED, as UNLOGGED tables can't be published.
18124 : */
18125 138 : if (!toLogged &&
18126 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
18127 0 : ereport(ERROR,
18128 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18129 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18130 : RelationGetRelationName(rel)),
18131 : errdetail("Unlogged relations cannot be replicated.")));
18132 :
18133 : /*
18134 : * Check existing foreign key constraints to preserve the invariant that
18135 : * permanent tables cannot reference unlogged ones. Self-referencing
18136 : * foreign keys can safely be ignored.
18137 : */
18138 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18139 :
18140 : /*
18141 : * Scan conrelid if changing to permanent, else confrelid. This also
18142 : * determines whether a useful index exists.
18143 : */
18144 88 : ScanKeyInit(&skey[0],
18145 : toLogged ? Anum_pg_constraint_conrelid :
18146 : Anum_pg_constraint_confrelid,
18147 : BTEqualStrategyNumber, F_OIDEQ,
18148 : ObjectIdGetDatum(RelationGetRelid(rel)));
18149 88 : scan = systable_beginscan(pg_constraint,
18150 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18151 : true, NULL, 1, skey);
18152 :
18153 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18154 : {
18155 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
18156 :
18157 66 : if (con->contype == CONSTRAINT_FOREIGN)
18158 : {
18159 : Oid foreignrelid;
18160 : Relation foreignrel;
18161 :
18162 : /* the opposite end of what we used as scankey */
18163 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
18164 :
18165 : /* ignore if self-referencing */
18166 30 : if (RelationGetRelid(rel) == foreignrelid)
18167 12 : continue;
18168 :
18169 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
18170 :
18171 18 : if (toLogged)
18172 : {
18173 6 : if (!RelationIsPermanent(foreignrel))
18174 6 : ereport(ERROR,
18175 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18176 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18177 : RelationGetRelationName(rel),
18178 : RelationGetRelationName(foreignrel)),
18179 : errtableconstraint(rel, NameStr(con->conname))));
18180 : }
18181 : else
18182 : {
18183 12 : if (RelationIsPermanent(foreignrel))
18184 6 : ereport(ERROR,
18185 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18186 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18187 : RelationGetRelationName(rel),
18188 : RelationGetRelationName(foreignrel)),
18189 : errtableconstraint(rel, NameStr(con->conname))));
18190 : }
18191 :
18192 6 : relation_close(foreignrel, AccessShareLock);
18193 : }
18194 : }
18195 :
18196 76 : systable_endscan(scan);
18197 :
18198 76 : table_close(pg_constraint, AccessShareLock);
18199 :
18200 : /* force rewrite if necessary; see comment in ATRewriteTables */
18201 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
18202 76 : if (toLogged)
18203 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18204 : else
18205 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18206 76 : tab->chgPersistence = true;
18207 : }
18208 :
18209 : /*
18210 : * Execute ALTER TABLE SET SCHEMA
18211 : */
18212 : ObjectAddress
18213 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
18214 : {
18215 : Relation rel;
18216 : Oid relid;
18217 : Oid oldNspOid;
18218 : Oid nspOid;
18219 : RangeVar *newrv;
18220 : ObjectAddresses *objsMoved;
18221 : ObjectAddress myself;
18222 :
18223 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18224 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18225 : RangeVarCallbackForAlterRelation,
18226 : stmt);
18227 :
18228 102 : if (!OidIsValid(relid))
18229 : {
18230 12 : ereport(NOTICE,
18231 : (errmsg("relation \"%s\" does not exist, skipping",
18232 : stmt->relation->relname)));
18233 12 : return InvalidObjectAddress;
18234 : }
18235 :
18236 90 : rel = relation_open(relid, NoLock);
18237 :
18238 90 : oldNspOid = RelationGetNamespace(rel);
18239 :
18240 : /* If it's an owned sequence, disallow moving it by itself. */
18241 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18242 : {
18243 : Oid tableId;
18244 : int32 colId;
18245 :
18246 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18247 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18248 6 : ereport(ERROR,
18249 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18250 : errmsg("cannot move an owned sequence into another schema"),
18251 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
18252 : RelationGetRelationName(rel),
18253 : get_rel_name(tableId))));
18254 : }
18255 :
18256 : /* Get and lock schema OID and check its permissions. */
18257 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18258 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18259 :
18260 : /* common checks on switching namespaces */
18261 84 : CheckSetNamespace(oldNspOid, nspOid);
18262 :
18263 84 : objsMoved = new_object_addresses();
18264 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18265 84 : free_object_addresses(objsMoved);
18266 :
18267 84 : ObjectAddressSet(myself, RelationRelationId, relid);
18268 :
18269 84 : if (oldschema)
18270 84 : *oldschema = oldNspOid;
18271 :
18272 : /* close rel, but keep lock until commit */
18273 84 : relation_close(rel, NoLock);
18274 :
18275 84 : return myself;
18276 : }
18277 :
18278 : /*
18279 : * The guts of relocating a table or materialized view to another namespace:
18280 : * besides moving the relation itself, its dependent objects are relocated to
18281 : * the new schema.
18282 : */
18283 : void
18284 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
18285 : ObjectAddresses *objsMoved)
18286 : {
18287 : Relation classRel;
18288 :
18289 : Assert(objsMoved != NULL);
18290 :
18291 : /* OK, modify the pg_class row and pg_depend entry */
18292 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
18293 :
18294 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18295 : nspOid, true, objsMoved);
18296 :
18297 : /* Fix the table's row type too, if it has one */
18298 86 : if (OidIsValid(rel->rd_rel->reltype))
18299 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18300 : false, /* isImplicitArray */
18301 : false, /* ignoreDependent */
18302 : false, /* errorOnTableType */
18303 : objsMoved);
18304 :
18305 : /* Fix other dependent stuff */
18306 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18307 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18308 : objsMoved, AccessExclusiveLock);
18309 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18310 : false, objsMoved);
18311 :
18312 86 : table_close(classRel, RowExclusiveLock);
18313 86 : }
18314 :
18315 : /*
18316 : * The guts of relocating a relation to another namespace: fix the pg_class
18317 : * entry, and the pg_depend entry if any. Caller must already have
18318 : * opened and write-locked pg_class.
18319 : */
18320 : void
18321 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
18322 : Oid oldNspOid, Oid newNspOid,
18323 : bool hasDependEntry,
18324 : ObjectAddresses *objsMoved)
18325 : {
18326 : HeapTuple classTup;
18327 : Form_pg_class classForm;
18328 : ObjectAddress thisobj;
18329 188 : bool already_done = false;
18330 :
18331 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18332 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18333 188 : if (!HeapTupleIsValid(classTup))
18334 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
18335 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
18336 :
18337 : Assert(classForm->relnamespace == oldNspOid);
18338 :
18339 188 : thisobj.classId = RelationRelationId;
18340 188 : thisobj.objectId = relOid;
18341 188 : thisobj.objectSubId = 0;
18342 :
18343 : /*
18344 : * If the object has already been moved, don't move it again. If it's
18345 : * already in the right place, don't move it, but still fire the object
18346 : * access hook.
18347 : */
18348 188 : already_done = object_address_present(&thisobj, objsMoved);
18349 188 : if (!already_done && oldNspOid != newNspOid)
18350 146 : {
18351 146 : ItemPointerData otid = classTup->t_self;
18352 :
18353 : /* check for duplicate name (more friendly than unique-index failure) */
18354 146 : if (get_relname_relid(NameStr(classForm->relname),
18355 : newNspOid) != InvalidOid)
18356 0 : ereport(ERROR,
18357 : (errcode(ERRCODE_DUPLICATE_TABLE),
18358 : errmsg("relation \"%s\" already exists in schema \"%s\"",
18359 : NameStr(classForm->relname),
18360 : get_namespace_name(newNspOid))));
18361 :
18362 : /* classTup is a copy, so OK to scribble on */
18363 146 : classForm->relnamespace = newNspOid;
18364 :
18365 146 : CatalogTupleUpdate(classRel, &otid, classTup);
18366 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
18367 :
18368 :
18369 : /* Update dependency on schema if caller said so */
18370 250 : if (hasDependEntry &&
18371 104 : changeDependencyFor(RelationRelationId,
18372 : relOid,
18373 : NamespaceRelationId,
18374 : oldNspOid,
18375 : newNspOid) != 1)
18376 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
18377 : NameStr(classForm->relname));
18378 : }
18379 : else
18380 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
18381 188 : if (!already_done)
18382 : {
18383 188 : add_exact_object_address(&thisobj, objsMoved);
18384 :
18385 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18386 : }
18387 :
18388 188 : heap_freetuple(classTup);
18389 188 : }
18390 :
18391 : /*
18392 : * Move all indexes for the specified relation to another namespace.
18393 : *
18394 : * Note: we assume adequate permission checking was done by the caller,
18395 : * and that the caller has a suitable lock on the owning relation.
18396 : */
18397 : static void
18398 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
18399 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
18400 : {
18401 : List *indexList;
18402 : ListCell *l;
18403 :
18404 86 : indexList = RelationGetIndexList(rel);
18405 :
18406 132 : foreach(l, indexList)
18407 : {
18408 46 : Oid indexOid = lfirst_oid(l);
18409 : ObjectAddress thisobj;
18410 :
18411 46 : thisobj.classId = RelationRelationId;
18412 46 : thisobj.objectId = indexOid;
18413 46 : thisobj.objectSubId = 0;
18414 :
18415 : /*
18416 : * Note: currently, the index will not have its own dependency on the
18417 : * namespace, so we don't need to do changeDependencyFor(). There's no
18418 : * row type in pg_type, either.
18419 : *
18420 : * XXX this objsMoved test may be pointless -- surely we have a single
18421 : * dependency link from a relation to each index?
18422 : */
18423 46 : if (!object_address_present(&thisobj, objsMoved))
18424 : {
18425 46 : AlterRelationNamespaceInternal(classRel, indexOid,
18426 : oldNspOid, newNspOid,
18427 : false, objsMoved);
18428 46 : add_exact_object_address(&thisobj, objsMoved);
18429 : }
18430 : }
18431 :
18432 86 : list_free(indexList);
18433 86 : }
18434 :
18435 : /*
18436 : * Move all identity and SERIAL-column sequences of the specified relation to another
18437 : * namespace.
18438 : *
18439 : * Note: we assume adequate permission checking was done by the caller,
18440 : * and that the caller has a suitable lock on the owning relation.
18441 : */
18442 : static void
18443 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
18444 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
18445 : LOCKMODE lockmode)
18446 : {
18447 : Relation depRel;
18448 : SysScanDesc scan;
18449 : ScanKeyData key[2];
18450 : HeapTuple tup;
18451 :
18452 : /*
18453 : * SERIAL sequences are those having an auto dependency on one of the
18454 : * table's columns (we don't care *which* column, exactly).
18455 : */
18456 86 : depRel = table_open(DependRelationId, AccessShareLock);
18457 :
18458 86 : ScanKeyInit(&key[0],
18459 : Anum_pg_depend_refclassid,
18460 : BTEqualStrategyNumber, F_OIDEQ,
18461 : ObjectIdGetDatum(RelationRelationId));
18462 86 : ScanKeyInit(&key[1],
18463 : Anum_pg_depend_refobjid,
18464 : BTEqualStrategyNumber, F_OIDEQ,
18465 : ObjectIdGetDatum(RelationGetRelid(rel)));
18466 : /* we leave refobjsubid unspecified */
18467 :
18468 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18469 : NULL, 2, key);
18470 :
18471 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
18472 : {
18473 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18474 : Relation seqRel;
18475 :
18476 : /* skip dependencies other than auto dependencies on columns */
18477 530 : if (depForm->refobjsubid == 0 ||
18478 382 : depForm->classid != RelationRelationId ||
18479 42 : depForm->objsubid != 0 ||
18480 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18481 488 : continue;
18482 :
18483 : /* Use relation_open just in case it's an index */
18484 42 : seqRel = relation_open(depForm->objid, lockmode);
18485 :
18486 : /* skip non-sequence relations */
18487 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18488 : {
18489 : /* No need to keep the lock */
18490 0 : relation_close(seqRel, lockmode);
18491 0 : continue;
18492 : }
18493 :
18494 : /* Fix the pg_class and pg_depend entries */
18495 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
18496 : oldNspOid, newNspOid,
18497 : true, objsMoved);
18498 :
18499 : /*
18500 : * Sequences used to have entries in pg_type, but no longer do. If we
18501 : * ever re-instate that, we'll need to move the pg_type entry to the
18502 : * new namespace, too (using AlterTypeNamespaceInternal).
18503 : */
18504 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18505 :
18506 : /* Now we can close it. Keep the lock till end of transaction. */
18507 42 : relation_close(seqRel, NoLock);
18508 : }
18509 :
18510 86 : systable_endscan(scan);
18511 :
18512 86 : relation_close(depRel, AccessShareLock);
18513 86 : }
18514 :
18515 :
18516 : /*
18517 : * This code supports
18518 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18519 : *
18520 : * Because we only support this for TEMP tables, it's sufficient to remember
18521 : * the state in a backend-local data structure.
18522 : */
18523 :
18524 : /*
18525 : * Register a newly-created relation's ON COMMIT action.
18526 : */
18527 : void
18528 166 : register_on_commit_action(Oid relid, OnCommitAction action)
18529 : {
18530 : OnCommitItem *oc;
18531 : MemoryContext oldcxt;
18532 :
18533 : /*
18534 : * We needn't bother registering the relation unless there is an ON COMMIT
18535 : * action we need to take.
18536 : */
18537 166 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18538 24 : return;
18539 :
18540 142 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18541 :
18542 142 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18543 142 : oc->relid = relid;
18544 142 : oc->oncommit = action;
18545 142 : oc->creating_subid = GetCurrentSubTransactionId();
18546 142 : oc->deleting_subid = InvalidSubTransactionId;
18547 :
18548 : /*
18549 : * We use lcons() here so that ON COMMIT actions are processed in reverse
18550 : * order of registration. That might not be essential but it seems
18551 : * reasonable.
18552 : */
18553 142 : on_commits = lcons(oc, on_commits);
18554 :
18555 142 : MemoryContextSwitchTo(oldcxt);
18556 : }
18557 :
18558 : /*
18559 : * Unregister any ON COMMIT action when a relation is deleted.
18560 : *
18561 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18562 : */
18563 : void
18564 47440 : remove_on_commit_action(Oid relid)
18565 : {
18566 : ListCell *l;
18567 :
18568 47574 : foreach(l, on_commits)
18569 : {
18570 264 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18571 :
18572 264 : if (oc->relid == relid)
18573 : {
18574 130 : oc->deleting_subid = GetCurrentSubTransactionId();
18575 130 : break;
18576 : }
18577 : }
18578 47440 : }
18579 :
18580 : /*
18581 : * Perform ON COMMIT actions.
18582 : *
18583 : * This is invoked just before actually committing, since it's possible
18584 : * to encounter errors.
18585 : */
18586 : void
18587 766002 : PreCommit_on_commit_actions(void)
18588 : {
18589 : ListCell *l;
18590 766002 : List *oids_to_truncate = NIL;
18591 766002 : List *oids_to_drop = NIL;
18592 :
18593 766718 : foreach(l, on_commits)
18594 : {
18595 716 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18596 :
18597 : /* Ignore entry if already dropped in this xact */
18598 716 : if (oc->deleting_subid != InvalidSubTransactionId)
18599 68 : continue;
18600 :
18601 648 : switch (oc->oncommit)
18602 : {
18603 0 : case ONCOMMIT_NOOP:
18604 : case ONCOMMIT_PRESERVE_ROWS:
18605 : /* Do nothing (there shouldn't be such entries, actually) */
18606 0 : break;
18607 598 : case ONCOMMIT_DELETE_ROWS:
18608 :
18609 : /*
18610 : * If this transaction hasn't accessed any temporary
18611 : * relations, we can skip truncating ON COMMIT DELETE ROWS
18612 : * tables, as they must still be empty.
18613 : */
18614 598 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18615 400 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18616 598 : break;
18617 50 : case ONCOMMIT_DROP:
18618 50 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18619 50 : break;
18620 : }
18621 716 : }
18622 :
18623 : /*
18624 : * Truncate relations before dropping so that all dependencies between
18625 : * relations are removed after they are worked on. Doing it like this
18626 : * might be a waste as it is possible that a relation being truncated will
18627 : * be dropped anyway due to its parent being dropped, but this makes the
18628 : * code more robust because of not having to re-check that the relation
18629 : * exists at truncation time.
18630 : */
18631 766002 : if (oids_to_truncate != NIL)
18632 334 : heap_truncate(oids_to_truncate);
18633 :
18634 765996 : if (oids_to_drop != NIL)
18635 : {
18636 44 : ObjectAddresses *targetObjects = new_object_addresses();
18637 :
18638 94 : foreach(l, oids_to_drop)
18639 : {
18640 : ObjectAddress object;
18641 :
18642 50 : object.classId = RelationRelationId;
18643 50 : object.objectId = lfirst_oid(l);
18644 50 : object.objectSubId = 0;
18645 :
18646 : Assert(!object_address_present(&object, targetObjects));
18647 :
18648 50 : add_exact_object_address(&object, targetObjects);
18649 : }
18650 :
18651 : /*
18652 : * Object deletion might involve toast table access (to clean up
18653 : * toasted catalog entries), so ensure we have a valid snapshot.
18654 : */
18655 44 : PushActiveSnapshot(GetTransactionSnapshot());
18656 :
18657 : /*
18658 : * Since this is an automatic drop, rather than one directly initiated
18659 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18660 : */
18661 44 : performMultipleDeletions(targetObjects, DROP_CASCADE,
18662 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18663 :
18664 44 : PopActiveSnapshot();
18665 :
18666 : #ifdef USE_ASSERT_CHECKING
18667 :
18668 : /*
18669 : * Note that table deletion will call remove_on_commit_action, so the
18670 : * entry should get marked as deleted.
18671 : */
18672 : foreach(l, on_commits)
18673 : {
18674 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18675 :
18676 : if (oc->oncommit != ONCOMMIT_DROP)
18677 : continue;
18678 :
18679 : Assert(oc->deleting_subid != InvalidSubTransactionId);
18680 : }
18681 : #endif
18682 : }
18683 765996 : }
18684 :
18685 : /*
18686 : * Post-commit or post-abort cleanup for ON COMMIT management.
18687 : *
18688 : * All we do here is remove no-longer-needed OnCommitItem entries.
18689 : *
18690 : * During commit, remove entries that were deleted during this transaction;
18691 : * during abort, remove those created during this transaction.
18692 : */
18693 : void
18694 813774 : AtEOXact_on_commit_actions(bool isCommit)
18695 : {
18696 : ListCell *cur_item;
18697 :
18698 814520 : foreach(cur_item, on_commits)
18699 : {
18700 746 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18701 :
18702 848 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18703 102 : oc->creating_subid != InvalidSubTransactionId)
18704 : {
18705 : /* cur_item must be removed */
18706 142 : on_commits = foreach_delete_current(on_commits, cur_item);
18707 142 : pfree(oc);
18708 : }
18709 : else
18710 : {
18711 : /* cur_item must be preserved */
18712 604 : oc->creating_subid = InvalidSubTransactionId;
18713 604 : oc->deleting_subid = InvalidSubTransactionId;
18714 : }
18715 : }
18716 813774 : }
18717 :
18718 : /*
18719 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18720 : *
18721 : * During subabort, we can immediately remove entries created during this
18722 : * subtransaction. During subcommit, just relabel entries marked during
18723 : * this subtransaction as being the parent's responsibility.
18724 : */
18725 : void
18726 20042 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18727 : SubTransactionId parentSubid)
18728 : {
18729 : ListCell *cur_item;
18730 :
18731 20042 : foreach(cur_item, on_commits)
18732 : {
18733 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18734 :
18735 0 : if (!isCommit && oc->creating_subid == mySubid)
18736 : {
18737 : /* cur_item must be removed */
18738 0 : on_commits = foreach_delete_current(on_commits, cur_item);
18739 0 : pfree(oc);
18740 : }
18741 : else
18742 : {
18743 : /* cur_item must be preserved */
18744 0 : if (oc->creating_subid == mySubid)
18745 0 : oc->creating_subid = parentSubid;
18746 0 : if (oc->deleting_subid == mySubid)
18747 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18748 : }
18749 : }
18750 20042 : }
18751 :
18752 : /*
18753 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18754 : * the relation to be locked only if (1) it's a plain or partitioned table,
18755 : * materialized view, or TOAST table and (2) the current user is the owner (or
18756 : * the superuser) or has been granted MAINTAIN. This meets the
18757 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18758 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18759 : */
18760 : void
18761 1014 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
18762 : Oid relId, Oid oldRelId, void *arg)
18763 : {
18764 : char relkind;
18765 : AclResult aclresult;
18766 :
18767 : /* Nothing to do if the relation was not found. */
18768 1014 : if (!OidIsValid(relId))
18769 6 : return;
18770 :
18771 : /*
18772 : * If the relation does exist, check whether it's an index. But note that
18773 : * the relation might have been dropped between the time we did the name
18774 : * lookup and now. In that case, there's nothing to do.
18775 : */
18776 1008 : relkind = get_rel_relkind(relId);
18777 1008 : if (!relkind)
18778 0 : return;
18779 1008 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18780 140 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18781 28 : ereport(ERROR,
18782 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18783 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18784 :
18785 : /* Check permissions */
18786 980 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18787 980 : if (aclresult != ACLCHECK_OK)
18788 30 : aclcheck_error(aclresult,
18789 30 : get_relkind_objtype(get_rel_relkind(relId)),
18790 30 : relation->relname);
18791 : }
18792 :
18793 : /*
18794 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18795 : */
18796 : static void
18797 2014 : RangeVarCallbackForTruncate(const RangeVar *relation,
18798 : Oid relId, Oid oldRelId, void *arg)
18799 : {
18800 : HeapTuple tuple;
18801 :
18802 : /* Nothing to do if the relation was not found. */
18803 2014 : if (!OidIsValid(relId))
18804 0 : return;
18805 :
18806 2014 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18807 2014 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18808 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18809 :
18810 2014 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18811 2008 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18812 :
18813 1976 : ReleaseSysCache(tuple);
18814 : }
18815 :
18816 : /*
18817 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18818 : * the owner of the relation, or superuser.
18819 : */
18820 : void
18821 15518 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
18822 : Oid relId, Oid oldRelId, void *arg)
18823 : {
18824 : HeapTuple tuple;
18825 :
18826 : /* Nothing to do if the relation was not found. */
18827 15518 : if (!OidIsValid(relId))
18828 12 : return;
18829 :
18830 15506 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18831 15506 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18832 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18833 :
18834 15506 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18835 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18836 6 : relation->relname);
18837 :
18838 30880 : if (!allowSystemTableMods &&
18839 15380 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18840 2 : ereport(ERROR,
18841 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18842 : errmsg("permission denied: \"%s\" is a system catalog",
18843 : relation->relname)));
18844 :
18845 15498 : ReleaseSysCache(tuple);
18846 : }
18847 :
18848 : /*
18849 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
18850 : * processing.
18851 : */
18852 : static void
18853 31484 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18854 : void *arg)
18855 : {
18856 31484 : Node *stmt = (Node *) arg;
18857 : ObjectType reltype;
18858 : HeapTuple tuple;
18859 : Form_pg_class classform;
18860 : AclResult aclresult;
18861 : char relkind;
18862 :
18863 31484 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18864 31484 : if (!HeapTupleIsValid(tuple))
18865 214 : return; /* concurrently dropped */
18866 31270 : classform = (Form_pg_class) GETSTRUCT(tuple);
18867 31270 : relkind = classform->relkind;
18868 :
18869 : /* Must own relation. */
18870 31270 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18871 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18872 :
18873 : /* No system table modifications unless explicitly allowed. */
18874 31210 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
18875 30 : ereport(ERROR,
18876 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18877 : errmsg("permission denied: \"%s\" is a system catalog",
18878 : rv->relname)));
18879 :
18880 : /*
18881 : * Extract the specified relation type from the statement parse tree.
18882 : *
18883 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
18884 : * have CREATE rights on the containing namespace.
18885 : */
18886 31180 : if (IsA(stmt, RenameStmt))
18887 : {
18888 482 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18889 : GetUserId(), ACL_CREATE);
18890 482 : if (aclresult != ACLCHECK_OK)
18891 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
18892 0 : get_namespace_name(classform->relnamespace));
18893 482 : reltype = ((RenameStmt *) stmt)->renameType;
18894 : }
18895 30698 : else if (IsA(stmt, AlterObjectSchemaStmt))
18896 90 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18897 :
18898 30608 : else if (IsA(stmt, AlterTableStmt))
18899 30608 : reltype = ((AlterTableStmt *) stmt)->objtype;
18900 : else
18901 : {
18902 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18903 : reltype = OBJECT_TABLE; /* placate compiler */
18904 : }
18905 :
18906 : /*
18907 : * For compatibility with prior releases, we allow ALTER TABLE to be used
18908 : * with most other types of relations (but not composite types). We allow
18909 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
18910 : * otherwise. Otherwise, the user must select the correct form of the
18911 : * command for the relation at issue.
18912 : */
18913 31180 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18914 0 : ereport(ERROR,
18915 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18916 : errmsg("\"%s\" is not a sequence", rv->relname)));
18917 :
18918 31180 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18919 0 : ereport(ERROR,
18920 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18921 : errmsg("\"%s\" is not a view", rv->relname)));
18922 :
18923 31180 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18924 0 : ereport(ERROR,
18925 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18926 : errmsg("\"%s\" is not a materialized view", rv->relname)));
18927 :
18928 31180 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18929 0 : ereport(ERROR,
18930 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18931 : errmsg("\"%s\" is not a foreign table", rv->relname)));
18932 :
18933 31180 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18934 0 : ereport(ERROR,
18935 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18936 : errmsg("\"%s\" is not a composite type", rv->relname)));
18937 :
18938 31180 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18939 : relkind != RELKIND_PARTITIONED_INDEX
18940 32 : && !IsA(stmt, RenameStmt))
18941 6 : ereport(ERROR,
18942 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18943 : errmsg("\"%s\" is not an index", rv->relname)));
18944 :
18945 : /*
18946 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18947 : * TYPE for that.
18948 : */
18949 31174 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18950 0 : ereport(ERROR,
18951 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18952 : errmsg("\"%s\" is a composite type", rv->relname),
18953 : /* translator: %s is an SQL ALTER command */
18954 : errhint("Use %s instead.",
18955 : "ALTER TYPE")));
18956 :
18957 : /*
18958 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18959 : * to a different schema, such as indexes and TOAST tables.
18960 : */
18961 31174 : if (IsA(stmt, AlterObjectSchemaStmt))
18962 : {
18963 90 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18964 0 : ereport(ERROR,
18965 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18966 : errmsg("cannot change schema of index \"%s\"",
18967 : rv->relname),
18968 : errhint("Change the schema of the table instead.")));
18969 90 : else if (relkind == RELKIND_COMPOSITE_TYPE)
18970 0 : ereport(ERROR,
18971 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18972 : errmsg("cannot change schema of composite type \"%s\"",
18973 : rv->relname),
18974 : /* translator: %s is an SQL ALTER command */
18975 : errhint("Use %s instead.",
18976 : "ALTER TYPE")));
18977 90 : else if (relkind == RELKIND_TOASTVALUE)
18978 0 : ereport(ERROR,
18979 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18980 : errmsg("cannot change schema of TOAST table \"%s\"",
18981 : rv->relname),
18982 : errhint("Change the schema of the table instead.")));
18983 : }
18984 :
18985 31174 : ReleaseSysCache(tuple);
18986 : }
18987 :
18988 : /*
18989 : * Transform any expressions present in the partition key
18990 : *
18991 : * Returns a transformed PartitionSpec.
18992 : */
18993 : static PartitionSpec *
18994 4864 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18995 : {
18996 : PartitionSpec *newspec;
18997 : ParseState *pstate;
18998 : ParseNamespaceItem *nsitem;
18999 : ListCell *l;
19000 :
19001 4864 : newspec = makeNode(PartitionSpec);
19002 :
19003 4864 : newspec->strategy = partspec->strategy;
19004 4864 : newspec->partParams = NIL;
19005 4864 : newspec->location = partspec->location;
19006 :
19007 : /* Check valid number of columns for strategy */
19008 7326 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19009 2462 : list_length(partspec->partParams) != 1)
19010 6 : ereport(ERROR,
19011 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19012 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19013 :
19014 : /*
19015 : * Create a dummy ParseState and insert the target relation as its sole
19016 : * rangetable entry. We need a ParseState for transformExpr.
19017 : */
19018 4858 : pstate = make_parsestate(NULL);
19019 4858 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19020 : NULL, false, true);
19021 4858 : addNSItemToQuery(pstate, nsitem, true, true, true);
19022 :
19023 : /* take care of any partition expressions */
19024 10142 : foreach(l, partspec->partParams)
19025 : {
19026 5308 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
19027 :
19028 5308 : if (pelem->expr)
19029 : {
19030 : /* Copy, to avoid scribbling on the input */
19031 304 : pelem = copyObject(pelem);
19032 :
19033 : /* Now do parse transformation of the expression */
19034 304 : pelem->expr = transformExpr(pstate, pelem->expr,
19035 : EXPR_KIND_PARTITION_EXPRESSION);
19036 :
19037 : /* we have to fix its collations too */
19038 280 : assign_expr_collations(pstate, pelem->expr);
19039 : }
19040 :
19041 5284 : newspec->partParams = lappend(newspec->partParams, pelem);
19042 : }
19043 :
19044 4834 : return newspec;
19045 : }
19046 :
19047 : /*
19048 : * Compute per-partition-column information from a list of PartitionElems.
19049 : * Expressions in the PartitionElems must be parse-analyzed already.
19050 : */
19051 : static void
19052 4834 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19053 : List **partexprs, Oid *partopclass, Oid *partcollation,
19054 : PartitionStrategy strategy)
19055 : {
19056 : int attn;
19057 : ListCell *lc;
19058 : Oid am_oid;
19059 :
19060 4834 : attn = 0;
19061 10022 : foreach(lc, partParams)
19062 : {
19063 5284 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
19064 : Oid atttype;
19065 : Oid attcollation;
19066 :
19067 5284 : if (pelem->name != NULL)
19068 : {
19069 : /* Simple attribute reference */
19070 : HeapTuple atttuple;
19071 : Form_pg_attribute attform;
19072 :
19073 5004 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
19074 5004 : pelem->name);
19075 5004 : if (!HeapTupleIsValid(atttuple))
19076 12 : ereport(ERROR,
19077 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19078 : errmsg("column \"%s\" named in partition key does not exist",
19079 : pelem->name),
19080 : parser_errposition(pstate, pelem->location)));
19081 4992 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19082 :
19083 4992 : if (attform->attnum <= 0)
19084 6 : ereport(ERROR,
19085 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19086 : errmsg("cannot use system column \"%s\" in partition key",
19087 : pelem->name),
19088 : parser_errposition(pstate, pelem->location)));
19089 :
19090 : /*
19091 : * Stored generated columns cannot work: They are computed after
19092 : * BEFORE triggers, but partition routing is done before all
19093 : * triggers. Maybe virtual generated columns could be made to
19094 : * work, but then they would need to be handled as an expression
19095 : * below.
19096 : */
19097 4986 : if (attform->attgenerated)
19098 12 : ereport(ERROR,
19099 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19100 : errmsg("cannot use generated column in partition key"),
19101 : errdetail("Column \"%s\" is a generated column.",
19102 : pelem->name),
19103 : parser_errposition(pstate, pelem->location)));
19104 :
19105 4974 : partattrs[attn] = attform->attnum;
19106 4974 : atttype = attform->atttypid;
19107 4974 : attcollation = attform->attcollation;
19108 4974 : ReleaseSysCache(atttuple);
19109 : }
19110 : else
19111 : {
19112 : /* Expression */
19113 280 : Node *expr = pelem->expr;
19114 : char partattname[16];
19115 :
19116 : Assert(expr != NULL);
19117 280 : atttype = exprType(expr);
19118 280 : attcollation = exprCollation(expr);
19119 :
19120 : /*
19121 : * The expression must be of a storable type (e.g., not RECORD).
19122 : * The test is the same as for whether a table column is of a safe
19123 : * type (which is why we needn't check for the non-expression
19124 : * case).
19125 : */
19126 280 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19127 280 : CheckAttributeType(partattname,
19128 : atttype, attcollation,
19129 : NIL, CHKATYPE_IS_PARTKEY);
19130 :
19131 : /*
19132 : * Strip any top-level COLLATE clause. This ensures that we treat
19133 : * "x COLLATE y" and "(x COLLATE y)" alike.
19134 : */
19135 268 : while (IsA(expr, CollateExpr))
19136 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
19137 :
19138 268 : if (IsA(expr, Var) &&
19139 12 : ((Var *) expr)->varattno > 0)
19140 : {
19141 : /*
19142 : * User wrote "(column)" or "(column COLLATE something)".
19143 : * Treat it like simple attribute anyway.
19144 : */
19145 6 : partattrs[attn] = ((Var *) expr)->varattno;
19146 : }
19147 : else
19148 : {
19149 262 : Bitmapset *expr_attrs = NULL;
19150 : int i;
19151 :
19152 262 : partattrs[attn] = 0; /* marks the column as expression */
19153 262 : *partexprs = lappend(*partexprs, expr);
19154 :
19155 : /*
19156 : * transformPartitionSpec() should have already rejected
19157 : * subqueries, aggregates, window functions, and SRFs, based
19158 : * on the EXPR_KIND_ for partition expressions.
19159 : */
19160 :
19161 : /*
19162 : * Cannot allow system column references, since that would
19163 : * make partition routing impossible: their values won't be
19164 : * known yet when we need to do that.
19165 : */
19166 262 : pull_varattnos(expr, 1, &expr_attrs);
19167 2096 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19168 : {
19169 1834 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
19170 : expr_attrs))
19171 0 : ereport(ERROR,
19172 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19173 : errmsg("partition key expressions cannot contain system column references")));
19174 : }
19175 :
19176 : /*
19177 : * Stored generated columns cannot work: They are computed
19178 : * after BEFORE triggers, but partition routing is done before
19179 : * all triggers. Virtual generated columns could probably
19180 : * work, but it would require more work elsewhere (for example
19181 : * SET EXPRESSION would need to check whether the column is
19182 : * used in partition keys). Seems safer to prohibit for now.
19183 : */
19184 262 : i = -1;
19185 570 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
19186 : {
19187 320 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
19188 :
19189 320 : if (attno > 0 &&
19190 314 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19191 12 : ereport(ERROR,
19192 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19193 : errmsg("cannot use generated column in partition key"),
19194 : errdetail("Column \"%s\" is a generated column.",
19195 : get_attname(RelationGetRelid(rel), attno, false)),
19196 : parser_errposition(pstate, pelem->location)));
19197 : }
19198 :
19199 : /*
19200 : * Preprocess the expression before checking for mutability.
19201 : * This is essential for the reasons described in
19202 : * contain_mutable_functions_after_planning. However, we call
19203 : * expression_planner for ourselves rather than using that
19204 : * function, because if constant-folding reduces the
19205 : * expression to a constant, we'd like to know that so we can
19206 : * complain below.
19207 : *
19208 : * Like contain_mutable_functions_after_planning, assume that
19209 : * expression_planner won't scribble on its input, so this
19210 : * won't affect the partexprs entry we saved above.
19211 : */
19212 250 : expr = (Node *) expression_planner((Expr *) expr);
19213 :
19214 : /*
19215 : * Partition expressions cannot contain mutable functions,
19216 : * because a given row must always map to the same partition
19217 : * as long as there is no change in the partition boundary
19218 : * structure.
19219 : */
19220 250 : if (contain_mutable_functions(expr))
19221 6 : ereport(ERROR,
19222 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19223 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19224 :
19225 : /*
19226 : * While it is not exactly *wrong* for a partition expression
19227 : * to be a constant, it seems better to reject such keys.
19228 : */
19229 244 : if (IsA(expr, Const))
19230 12 : ereport(ERROR,
19231 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19232 : errmsg("cannot use constant expression as partition key")));
19233 : }
19234 : }
19235 :
19236 : /*
19237 : * Apply collation override if any
19238 : */
19239 5212 : if (pelem->collation)
19240 54 : attcollation = get_collation_oid(pelem->collation, false);
19241 :
19242 : /*
19243 : * Check we have a collation iff it's a collatable type. The only
19244 : * expected failures here are (1) COLLATE applied to a noncollatable
19245 : * type, or (2) partition expression had an unresolved collation. But
19246 : * we might as well code this to be a complete consistency check.
19247 : */
19248 5212 : if (type_is_collatable(atttype))
19249 : {
19250 602 : if (!OidIsValid(attcollation))
19251 0 : ereport(ERROR,
19252 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
19253 : errmsg("could not determine which collation to use for partition expression"),
19254 : errhint("Use the COLLATE clause to set the collation explicitly.")));
19255 : }
19256 : else
19257 : {
19258 4610 : if (OidIsValid(attcollation))
19259 0 : ereport(ERROR,
19260 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19261 : errmsg("collations are not supported by type %s",
19262 : format_type_be(atttype))));
19263 : }
19264 :
19265 5212 : partcollation[attn] = attcollation;
19266 :
19267 : /*
19268 : * Identify the appropriate operator class. For list and range
19269 : * partitioning, we use a btree operator class; hash partitioning uses
19270 : * a hash operator class.
19271 : */
19272 5212 : if (strategy == PARTITION_STRATEGY_HASH)
19273 294 : am_oid = HASH_AM_OID;
19274 : else
19275 4918 : am_oid = BTREE_AM_OID;
19276 :
19277 5212 : if (!pelem->opclass)
19278 : {
19279 5080 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19280 :
19281 5080 : if (!OidIsValid(partopclass[attn]))
19282 : {
19283 12 : if (strategy == PARTITION_STRATEGY_HASH)
19284 0 : ereport(ERROR,
19285 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19286 : errmsg("data type %s has no default operator class for access method \"%s\"",
19287 : format_type_be(atttype), "hash"),
19288 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19289 : else
19290 12 : ereport(ERROR,
19291 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19292 : errmsg("data type %s has no default operator class for access method \"%s\"",
19293 : format_type_be(atttype), "btree"),
19294 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19295 : }
19296 : }
19297 : else
19298 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
19299 : atttype,
19300 : am_oid == HASH_AM_OID ? "hash" : "btree",
19301 : am_oid);
19302 :
19303 5188 : attn++;
19304 : }
19305 4738 : }
19306 :
19307 : /*
19308 : * PartConstraintImpliedByRelConstraint
19309 : * Do scanrel's existing constraints imply the partition constraint?
19310 : *
19311 : * "Existing constraints" include its check constraints and column-level
19312 : * not-null constraints. partConstraint describes the partition constraint,
19313 : * in implicit-AND form.
19314 : */
19315 : bool
19316 3102 : PartConstraintImpliedByRelConstraint(Relation scanrel,
19317 : List *partConstraint)
19318 : {
19319 3102 : List *existConstraint = NIL;
19320 3102 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19321 : int i;
19322 :
19323 3102 : if (constr && constr->has_not_null)
19324 : {
19325 766 : int natts = scanrel->rd_att->natts;
19326 :
19327 2488 : for (i = 1; i <= natts; i++)
19328 : {
19329 1722 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
19330 :
19331 1722 : if (att->attnotnull && !att->attisdropped)
19332 : {
19333 1070 : NullTest *ntest = makeNode(NullTest);
19334 :
19335 1070 : ntest->arg = (Expr *) makeVar(1,
19336 : i,
19337 : att->atttypid,
19338 : att->atttypmod,
19339 : att->attcollation,
19340 : 0);
19341 1070 : ntest->nulltesttype = IS_NOT_NULL;
19342 :
19343 : /*
19344 : * argisrow=false is correct even for a composite column,
19345 : * because attnotnull does not represent a SQL-spec IS NOT
19346 : * NULL test in such a case, just IS DISTINCT FROM NULL.
19347 : */
19348 1070 : ntest->argisrow = false;
19349 1070 : ntest->location = -1;
19350 1070 : existConstraint = lappend(existConstraint, ntest);
19351 : }
19352 : }
19353 : }
19354 :
19355 3102 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19356 : }
19357 :
19358 : /*
19359 : * ConstraintImpliedByRelConstraint
19360 : * Do scanrel's existing constraints imply the given constraint?
19361 : *
19362 : * testConstraint is the constraint to validate. provenConstraint is a
19363 : * caller-provided list of conditions which this function may assume
19364 : * to be true. Both provenConstraint and testConstraint must be in
19365 : * implicit-AND form, must only contain immutable clauses, and must
19366 : * contain only Vars with varno = 1.
19367 : */
19368 : bool
19369 4304 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
19370 : {
19371 4304 : List *existConstraint = list_copy(provenConstraint);
19372 4304 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19373 : int num_check,
19374 : i;
19375 :
19376 4304 : num_check = (constr != NULL) ? constr->num_check : 0;
19377 4820 : for (i = 0; i < num_check; i++)
19378 : {
19379 : Node *cexpr;
19380 :
19381 : /*
19382 : * If this constraint hasn't been fully validated yet, we must ignore
19383 : * it here.
19384 : */
19385 516 : if (!constr->check[i].ccvalid)
19386 6 : continue;
19387 :
19388 : /*
19389 : * NOT ENFORCED constraints are always marked as invalid, which should
19390 : * have been ignored.
19391 : */
19392 : Assert(constr->check[i].ccenforced);
19393 :
19394 510 : cexpr = stringToNode(constr->check[i].ccbin);
19395 :
19396 : /*
19397 : * Run each expression through const-simplification and
19398 : * canonicalization. It is necessary, because we will be comparing it
19399 : * to similarly-processed partition constraint expressions, and may
19400 : * fail to detect valid matches without this.
19401 : */
19402 510 : cexpr = eval_const_expressions(NULL, cexpr);
19403 510 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19404 :
19405 510 : existConstraint = list_concat(existConstraint,
19406 510 : make_ands_implicit((Expr *) cexpr));
19407 : }
19408 :
19409 : /*
19410 : * Try to make the proof. Since we are comparing CHECK constraints, we
19411 : * need to use weak implication, i.e., we assume existConstraint is
19412 : * not-false and try to prove the same for testConstraint.
19413 : *
19414 : * Note that predicate_implied_by assumes its first argument is known
19415 : * immutable. That should always be true for both NOT NULL and partition
19416 : * constraints, so we don't test it here.
19417 : */
19418 4304 : return predicate_implied_by(testConstraint, existConstraint, true);
19419 : }
19420 :
19421 : /*
19422 : * QueuePartitionConstraintValidation
19423 : *
19424 : * Add an entry to wqueue to have the given partition constraint validated by
19425 : * Phase 3, for the given relation, and all its children.
19426 : *
19427 : * We first verify whether the given constraint is implied by pre-existing
19428 : * relation constraints; if it is, there's no need to scan the table to
19429 : * validate, so don't queue in that case.
19430 : */
19431 : static void
19432 2472 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
19433 : List *partConstraint,
19434 : bool validate_default)
19435 : {
19436 : /*
19437 : * Based on the table's existing constraints, determine whether or not we
19438 : * may skip scanning the table.
19439 : */
19440 2472 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19441 : {
19442 90 : if (!validate_default)
19443 68 : ereport(DEBUG1,
19444 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19445 : RelationGetRelationName(scanrel))));
19446 : else
19447 22 : ereport(DEBUG1,
19448 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19449 : RelationGetRelationName(scanrel))));
19450 90 : return;
19451 : }
19452 :
19453 : /*
19454 : * Constraints proved insufficient. For plain relations, queue a
19455 : * validation item now; for partitioned tables, recurse to process each
19456 : * partition.
19457 : */
19458 2382 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19459 : {
19460 : AlteredTableInfo *tab;
19461 :
19462 : /* Grab a work queue entry. */
19463 1986 : tab = ATGetQueueEntry(wqueue, scanrel);
19464 : Assert(tab->partition_constraint == NULL);
19465 1986 : tab->partition_constraint = (Expr *) linitial(partConstraint);
19466 1986 : tab->validate_default = validate_default;
19467 : }
19468 396 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19469 : {
19470 348 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19471 : int i;
19472 :
19473 726 : for (i = 0; i < partdesc->nparts; i++)
19474 : {
19475 : Relation part_rel;
19476 : List *thisPartConstraint;
19477 :
19478 : /*
19479 : * This is the minimum lock we need to prevent deadlocks.
19480 : */
19481 378 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19482 :
19483 : /*
19484 : * Adjust the constraint for scanrel so that it matches this
19485 : * partition's attribute numbers.
19486 : */
19487 : thisPartConstraint =
19488 378 : map_partition_varattnos(partConstraint, 1,
19489 : part_rel, scanrel);
19490 :
19491 378 : QueuePartitionConstraintValidation(wqueue, part_rel,
19492 : thisPartConstraint,
19493 : validate_default);
19494 378 : table_close(part_rel, NoLock); /* keep lock till commit */
19495 : }
19496 : }
19497 : }
19498 :
19499 : /*
19500 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19501 : *
19502 : * Return the address of the newly attached partition.
19503 : */
19504 : static ObjectAddress
19505 2302 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19506 : AlterTableUtilityContext *context)
19507 : {
19508 : Relation attachrel,
19509 : catalog;
19510 : List *attachrel_children;
19511 : List *partConstraint;
19512 : SysScanDesc scan;
19513 : ScanKeyData skey;
19514 : AttrNumber attno;
19515 : int natts;
19516 : TupleDesc tupleDesc;
19517 : ObjectAddress address;
19518 : const char *trigger_name;
19519 : Oid defaultPartOid;
19520 : List *partBoundConstraint;
19521 2302 : ParseState *pstate = make_parsestate(NULL);
19522 :
19523 2302 : pstate->p_sourcetext = context->queryString;
19524 :
19525 : /*
19526 : * We must lock the default partition if one exists, because attaching a
19527 : * new partition will change its partition constraint.
19528 : */
19529 : defaultPartOid =
19530 2302 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19531 2302 : if (OidIsValid(defaultPartOid))
19532 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19533 :
19534 2302 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19535 :
19536 : /*
19537 : * XXX I think it'd be a good idea to grab locks on all tables referenced
19538 : * by FKs at this point also.
19539 : */
19540 :
19541 : /*
19542 : * Must be owner of both parent and source table -- parent was checked by
19543 : * ATSimplePermissions call in ATPrepCmd
19544 : */
19545 2296 : ATSimplePermissions(AT_AttachPartition, attachrel,
19546 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
19547 :
19548 : /* A partition can only have one parent */
19549 2290 : if (attachrel->rd_rel->relispartition)
19550 6 : ereport(ERROR,
19551 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19552 : errmsg("\"%s\" is already a partition",
19553 : RelationGetRelationName(attachrel))));
19554 :
19555 2284 : if (OidIsValid(attachrel->rd_rel->reloftype))
19556 6 : ereport(ERROR,
19557 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19558 : errmsg("cannot attach a typed table as partition")));
19559 :
19560 : /*
19561 : * Table being attached should not already be part of inheritance; either
19562 : * as a child table...
19563 : */
19564 2278 : catalog = table_open(InheritsRelationId, AccessShareLock);
19565 2278 : ScanKeyInit(&skey,
19566 : Anum_pg_inherits_inhrelid,
19567 : BTEqualStrategyNumber, F_OIDEQ,
19568 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19569 2278 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19570 : NULL, 1, &skey);
19571 2278 : if (HeapTupleIsValid(systable_getnext(scan)))
19572 6 : ereport(ERROR,
19573 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19574 : errmsg("cannot attach inheritance child as partition")));
19575 2272 : systable_endscan(scan);
19576 :
19577 : /* ...or as a parent table (except the case when it is partitioned) */
19578 2272 : ScanKeyInit(&skey,
19579 : Anum_pg_inherits_inhparent,
19580 : BTEqualStrategyNumber, F_OIDEQ,
19581 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19582 2272 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19583 : 1, &skey);
19584 2272 : if (HeapTupleIsValid(systable_getnext(scan)) &&
19585 256 : attachrel->rd_rel->relkind == RELKIND_RELATION)
19586 6 : ereport(ERROR,
19587 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19588 : errmsg("cannot attach inheritance parent as partition")));
19589 2266 : systable_endscan(scan);
19590 2266 : table_close(catalog, AccessShareLock);
19591 :
19592 : /*
19593 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
19594 : * particular, this disallows making a rel a partition of itself.)
19595 : *
19596 : * We do that by checking if rel is a member of the list of attachrel's
19597 : * partitions provided the latter is partitioned at all. We want to avoid
19598 : * having to construct this list again, so we request the strongest lock
19599 : * on all partitions. We need the strongest lock, because we may decide
19600 : * to scan them if we find out that the table being attached (or its leaf
19601 : * partitions) may contain rows that violate the partition constraint. If
19602 : * the table has a constraint that would prevent such rows, which by
19603 : * definition is present in all the partitions, we need not scan the
19604 : * table, nor its partitions. But we cannot risk a deadlock by taking a
19605 : * weaker lock now and the stronger one only when needed.
19606 : */
19607 2266 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19608 : AccessExclusiveLock, NULL);
19609 2266 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19610 12 : ereport(ERROR,
19611 : (errcode(ERRCODE_DUPLICATE_TABLE),
19612 : errmsg("circular inheritance not allowed"),
19613 : errdetail("\"%s\" is already a child of \"%s\".",
19614 : RelationGetRelationName(rel),
19615 : RelationGetRelationName(attachrel))));
19616 :
19617 : /* If the parent is permanent, so must be all of its partitions. */
19618 2254 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19619 2212 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19620 6 : ereport(ERROR,
19621 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19622 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19623 : RelationGetRelationName(rel))));
19624 :
19625 : /* Temp parent cannot have a partition that is itself not a temp */
19626 2248 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19627 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19628 18 : ereport(ERROR,
19629 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19630 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19631 : RelationGetRelationName(rel))));
19632 :
19633 : /* If the parent is temp, it must belong to this session */
19634 2230 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19635 24 : !rel->rd_islocaltemp)
19636 0 : ereport(ERROR,
19637 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19638 : errmsg("cannot attach as partition of temporary relation of another session")));
19639 :
19640 : /* Ditto for the partition */
19641 2230 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19642 24 : !attachrel->rd_islocaltemp)
19643 0 : ereport(ERROR,
19644 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19645 : errmsg("cannot attach temporary relation of another session as partition")));
19646 :
19647 : /*
19648 : * Check if attachrel has any identity columns or any columns that aren't
19649 : * in the parent.
19650 : */
19651 2230 : tupleDesc = RelationGetDescr(attachrel);
19652 2230 : natts = tupleDesc->natts;
19653 7642 : for (attno = 1; attno <= natts; attno++)
19654 : {
19655 5454 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19656 5454 : char *attributeName = NameStr(attribute->attname);
19657 :
19658 : /* Ignore dropped */
19659 5454 : if (attribute->attisdropped)
19660 580 : continue;
19661 :
19662 4874 : if (attribute->attidentity)
19663 24 : ereport(ERROR,
19664 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19665 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19666 : RelationGetRelationName(attachrel), attributeName),
19667 : errdetail("The new partition may not contain an identity column."));
19668 :
19669 : /* Try to find the column in parent (matching on column name) */
19670 4850 : if (!SearchSysCacheExists2(ATTNAME,
19671 : ObjectIdGetDatum(RelationGetRelid(rel)),
19672 : CStringGetDatum(attributeName)))
19673 18 : ereport(ERROR,
19674 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19675 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19676 : RelationGetRelationName(attachrel), attributeName,
19677 : RelationGetRelationName(rel)),
19678 : errdetail("The new partition may contain only the columns present in parent.")));
19679 : }
19680 :
19681 : /*
19682 : * If child_rel has row-level triggers with transition tables, we
19683 : * currently don't allow it to become a partition. See also prohibitions
19684 : * in ATExecAddInherit() and CreateTrigger().
19685 : */
19686 2188 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19687 2188 : if (trigger_name != NULL)
19688 6 : ereport(ERROR,
19689 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19690 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19691 : trigger_name, RelationGetRelationName(attachrel)),
19692 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
19693 :
19694 : /*
19695 : * Check that the new partition's bound is valid and does not overlap any
19696 : * of existing partitions of the parent - note that it does not return on
19697 : * error.
19698 : */
19699 2182 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19700 : cmd->bound, pstate);
19701 :
19702 : /* OK to create inheritance. Rest of the checks performed there */
19703 2146 : CreateInheritance(attachrel, rel, true);
19704 :
19705 : /* Update the pg_class entry. */
19706 2044 : StorePartitionBound(attachrel, rel, cmd->bound);
19707 :
19708 : /* Ensure there exists a correct set of indexes in the partition. */
19709 2044 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19710 :
19711 : /* and triggers */
19712 2014 : CloneRowTriggersToPartition(rel, attachrel);
19713 :
19714 : /*
19715 : * Clone foreign key constraints. Callee is responsible for setting up
19716 : * for phase 3 constraint verification.
19717 : */
19718 2008 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
19719 :
19720 : /*
19721 : * Generate partition constraint from the partition bound specification.
19722 : * If the parent itself is a partition, make sure to include its
19723 : * constraint as well.
19724 : */
19725 1996 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19726 :
19727 : /*
19728 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
19729 : * since it's needed later to construct the constraint expression for
19730 : * validating against the default partition, if any.
19731 : */
19732 1996 : partConstraint = list_concat_copy(partBoundConstraint,
19733 1996 : RelationGetPartitionQual(rel));
19734 :
19735 : /* Skip validation if there are no constraints to validate. */
19736 1996 : if (partConstraint)
19737 : {
19738 : /*
19739 : * Run the partition quals through const-simplification similar to
19740 : * check constraints. We skip canonicalize_qual, though, because
19741 : * partition quals should be in canonical form already.
19742 : */
19743 : partConstraint =
19744 1948 : (List *) eval_const_expressions(NULL,
19745 : (Node *) partConstraint);
19746 :
19747 : /* XXX this sure looks wrong */
19748 1948 : partConstraint = list_make1(make_ands_explicit(partConstraint));
19749 :
19750 : /*
19751 : * Adjust the generated constraint to match this partition's attribute
19752 : * numbers.
19753 : */
19754 1948 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19755 : rel);
19756 :
19757 : /* Validate partition constraints against the table being attached. */
19758 1948 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19759 : false);
19760 : }
19761 :
19762 : /*
19763 : * If we're attaching a partition other than the default partition and a
19764 : * default one exists, then that partition's partition constraint changes,
19765 : * so add an entry to the work queue to validate it, too. (We must not do
19766 : * this when the partition being attached is the default one; we already
19767 : * did it above!)
19768 : */
19769 1996 : if (OidIsValid(defaultPartOid))
19770 : {
19771 : Relation defaultrel;
19772 : List *defPartConstraint;
19773 :
19774 : Assert(!cmd->bound->is_default);
19775 :
19776 : /* we already hold a lock on the default partition */
19777 146 : defaultrel = table_open(defaultPartOid, NoLock);
19778 : defPartConstraint =
19779 146 : get_proposed_default_constraint(partBoundConstraint);
19780 :
19781 : /*
19782 : * Map the Vars in the constraint expression from rel's attnos to
19783 : * defaultrel's.
19784 : */
19785 : defPartConstraint =
19786 146 : map_partition_varattnos(defPartConstraint,
19787 : 1, defaultrel, rel);
19788 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
19789 : defPartConstraint, true);
19790 :
19791 : /* keep our lock until commit. */
19792 146 : table_close(defaultrel, NoLock);
19793 : }
19794 :
19795 1996 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19796 :
19797 : /*
19798 : * If the partition we just attached is partitioned itself, invalidate
19799 : * relcache for all descendent partitions too to ensure that their
19800 : * rd_partcheck expression trees are rebuilt; partitions already locked at
19801 : * the beginning of this function.
19802 : */
19803 1996 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19804 : {
19805 : ListCell *l;
19806 :
19807 984 : foreach(l, attachrel_children)
19808 : {
19809 660 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19810 : }
19811 : }
19812 :
19813 : /* keep our lock until commit */
19814 1996 : table_close(attachrel, NoLock);
19815 :
19816 1996 : return address;
19817 : }
19818 :
19819 : /*
19820 : * AttachPartitionEnsureIndexes
19821 : * subroutine for ATExecAttachPartition to create/match indexes
19822 : *
19823 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19824 : * PARTITION: every partition must have an index attached to each index on the
19825 : * partitioned table.
19826 : */
19827 : static void
19828 2044 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19829 : {
19830 : List *idxes;
19831 : List *attachRelIdxs;
19832 : Relation *attachrelIdxRels;
19833 : IndexInfo **attachInfos;
19834 : ListCell *cell;
19835 : MemoryContext cxt;
19836 : MemoryContext oldcxt;
19837 :
19838 2044 : cxt = AllocSetContextCreate(CurrentMemoryContext,
19839 : "AttachPartitionEnsureIndexes",
19840 : ALLOCSET_DEFAULT_SIZES);
19841 2044 : oldcxt = MemoryContextSwitchTo(cxt);
19842 :
19843 2044 : idxes = RelationGetIndexList(rel);
19844 2044 : attachRelIdxs = RelationGetIndexList(attachrel);
19845 2044 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19846 2044 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19847 :
19848 : /* Build arrays of all existing indexes and their IndexInfos */
19849 4452 : foreach_oid(cldIdxId, attachRelIdxs)
19850 : {
19851 364 : int i = foreach_current_index(cldIdxId);
19852 :
19853 364 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19854 364 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19855 : }
19856 :
19857 : /*
19858 : * If we're attaching a foreign table, we must fail if any of the indexes
19859 : * is a constraint index; otherwise, there's nothing to do here. Do this
19860 : * before starting work, to avoid wasting the effort of building a few
19861 : * non-unique indexes before coming across a unique one.
19862 : */
19863 2044 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19864 : {
19865 86 : foreach(cell, idxes)
19866 : {
19867 36 : Oid idx = lfirst_oid(cell);
19868 36 : Relation idxRel = index_open(idx, AccessShareLock);
19869 :
19870 36 : if (idxRel->rd_index->indisunique ||
19871 24 : idxRel->rd_index->indisprimary)
19872 12 : ereport(ERROR,
19873 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19874 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19875 : RelationGetRelationName(attachrel),
19876 : RelationGetRelationName(rel)),
19877 : errdetail("Partitioned table \"%s\" contains unique indexes.",
19878 : RelationGetRelationName(rel))));
19879 24 : index_close(idxRel, AccessShareLock);
19880 : }
19881 :
19882 50 : goto out;
19883 : }
19884 :
19885 : /*
19886 : * For each index on the partitioned table, find a matching one in the
19887 : * partition-to-be; if one is not found, create one.
19888 : */
19889 2402 : foreach(cell, idxes)
19890 : {
19891 438 : Oid idx = lfirst_oid(cell);
19892 438 : Relation idxRel = index_open(idx, AccessShareLock);
19893 : IndexInfo *info;
19894 : AttrMap *attmap;
19895 438 : bool found = false;
19896 : Oid constraintOid;
19897 :
19898 : /*
19899 : * Ignore indexes in the partitioned table other than partitioned
19900 : * indexes.
19901 : */
19902 438 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19903 : {
19904 0 : index_close(idxRel, AccessShareLock);
19905 0 : continue;
19906 : }
19907 :
19908 : /* construct an indexinfo to compare existing indexes against */
19909 438 : info = BuildIndexInfo(idxRel);
19910 438 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19911 : RelationGetDescr(rel),
19912 : false);
19913 438 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19914 :
19915 : /*
19916 : * Scan the list of existing indexes in the partition-to-be, and mark
19917 : * the first matching, valid, unattached one we find, if any, as
19918 : * partition of the parent index. If we find one, we're done.
19919 : */
19920 498 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19921 : {
19922 262 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19923 262 : Oid cldConstrOid = InvalidOid;
19924 :
19925 : /* does this index have a parent? if so, can't use it */
19926 262 : if (attachrelIdxRels[i]->rd_rel->relispartition)
19927 12 : continue;
19928 :
19929 : /* If this index is invalid, can't use it */
19930 250 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
19931 6 : continue;
19932 :
19933 244 : if (CompareIndexInfo(attachInfos[i], info,
19934 244 : attachrelIdxRels[i]->rd_indcollation,
19935 244 : idxRel->rd_indcollation,
19936 244 : attachrelIdxRels[i]->rd_opfamily,
19937 244 : idxRel->rd_opfamily,
19938 : attmap))
19939 : {
19940 : /*
19941 : * If this index is being created in the parent because of a
19942 : * constraint, then the child needs to have a constraint also,
19943 : * so look for one. If there is no such constraint, this
19944 : * index is no good, so keep looking.
19945 : */
19946 208 : if (OidIsValid(constraintOid))
19947 : {
19948 : cldConstrOid =
19949 110 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19950 : cldIdxId);
19951 : /* no dice */
19952 110 : if (!OidIsValid(cldConstrOid))
19953 6 : continue;
19954 :
19955 : /* Ensure they're both the same type of constraint */
19956 208 : if (get_constraint_type(constraintOid) !=
19957 104 : get_constraint_type(cldConstrOid))
19958 0 : continue;
19959 : }
19960 :
19961 : /* bingo. */
19962 202 : IndexSetParentIndex(attachrelIdxRels[i], idx);
19963 202 : if (OidIsValid(constraintOid))
19964 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19965 : RelationGetRelid(attachrel));
19966 202 : found = true;
19967 :
19968 202 : CommandCounterIncrement();
19969 202 : break;
19970 : }
19971 : }
19972 :
19973 : /*
19974 : * If no suitable index was found in the partition-to-be, create one
19975 : * now. Note that if this is a PK, not-null constraints must already
19976 : * exist.
19977 : */
19978 438 : if (!found)
19979 : {
19980 : IndexStmt *stmt;
19981 : Oid conOid;
19982 :
19983 236 : stmt = generateClonedIndexStmt(NULL,
19984 : idxRel, attmap,
19985 : &conOid);
19986 236 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19987 : RelationGetRelid(idxRel),
19988 : conOid,
19989 : -1,
19990 : true, false, false, false, false);
19991 : }
19992 :
19993 420 : index_close(idxRel, AccessShareLock);
19994 : }
19995 :
19996 2014 : out:
19997 : /* Clean up. */
19998 2366 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19999 352 : index_close(attachrelIdxRels[i], AccessShareLock);
20000 2014 : MemoryContextSwitchTo(oldcxt);
20001 2014 : MemoryContextDelete(cxt);
20002 2014 : }
20003 :
20004 : /*
20005 : * CloneRowTriggersToPartition
20006 : * subroutine for ATExecAttachPartition/DefineRelation to create row
20007 : * triggers on partitions
20008 : */
20009 : static void
20010 2434 : CloneRowTriggersToPartition(Relation parent, Relation partition)
20011 : {
20012 : Relation pg_trigger;
20013 : ScanKeyData key;
20014 : SysScanDesc scan;
20015 : HeapTuple tuple;
20016 : MemoryContext perTupCxt;
20017 :
20018 2434 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20019 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20020 2434 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20021 2434 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20022 : true, NULL, 1, &key);
20023 :
20024 2434 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
20025 : "clone trig", ALLOCSET_SMALL_SIZES);
20026 :
20027 4062 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20028 : {
20029 1634 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20030 : CreateTrigStmt *trigStmt;
20031 1634 : Node *qual = NULL;
20032 : Datum value;
20033 : bool isnull;
20034 1634 : List *cols = NIL;
20035 1634 : List *trigargs = NIL;
20036 : MemoryContext oldcxt;
20037 :
20038 : /*
20039 : * Ignore statement-level triggers; those are not cloned.
20040 : */
20041 1634 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20042 1478 : continue;
20043 :
20044 : /*
20045 : * Don't clone internal triggers, because the constraint cloning code
20046 : * will.
20047 : */
20048 1634 : if (trigForm->tgisinternal)
20049 1478 : continue;
20050 :
20051 : /*
20052 : * Complain if we find an unexpected trigger type.
20053 : */
20054 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20055 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
20056 0 : elog(ERROR, "unexpected trigger \"%s\" found",
20057 : NameStr(trigForm->tgname));
20058 :
20059 : /* Use short-lived context for CREATE TRIGGER */
20060 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
20061 :
20062 : /*
20063 : * If there is a WHEN clause, generate a 'cooked' version of it that's
20064 : * appropriate for the partition.
20065 : */
20066 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20067 : RelationGetDescr(pg_trigger), &isnull);
20068 156 : if (!isnull)
20069 : {
20070 6 : qual = stringToNode(TextDatumGetCString(value));
20071 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20072 : partition, parent);
20073 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20074 : partition, parent);
20075 : }
20076 :
20077 : /*
20078 : * If there is a column list, transform it to a list of column names.
20079 : * Note we don't need to map this list in any way ...
20080 : */
20081 156 : if (trigForm->tgattr.dim1 > 0)
20082 : {
20083 : int i;
20084 :
20085 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
20086 : {
20087 : Form_pg_attribute col;
20088 :
20089 6 : col = TupleDescAttr(parent->rd_att,
20090 6 : trigForm->tgattr.values[i] - 1);
20091 6 : cols = lappend(cols,
20092 6 : makeString(pstrdup(NameStr(col->attname))));
20093 : }
20094 : }
20095 :
20096 : /* Reconstruct trigger arguments list. */
20097 156 : if (trigForm->tgnargs > 0)
20098 : {
20099 : char *p;
20100 :
20101 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20102 : RelationGetDescr(pg_trigger), &isnull);
20103 12 : if (isnull)
20104 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20105 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
20106 :
20107 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20108 :
20109 36 : for (int i = 0; i < trigForm->tgnargs; i++)
20110 : {
20111 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
20112 24 : p += strlen(p) + 1;
20113 : }
20114 : }
20115 :
20116 156 : trigStmt = makeNode(CreateTrigStmt);
20117 156 : trigStmt->replace = false;
20118 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20119 156 : trigStmt->trigname = NameStr(trigForm->tgname);
20120 156 : trigStmt->relation = NULL;
20121 156 : trigStmt->funcname = NULL; /* passed separately */
20122 156 : trigStmt->args = trigargs;
20123 156 : trigStmt->row = true;
20124 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20125 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20126 156 : trigStmt->columns = cols;
20127 156 : trigStmt->whenClause = NULL; /* passed separately */
20128 156 : trigStmt->transitionRels = NIL; /* not supported at present */
20129 156 : trigStmt->deferrable = trigForm->tgdeferrable;
20130 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
20131 156 : trigStmt->constrrel = NULL; /* passed separately */
20132 :
20133 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20134 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20135 : trigForm->tgfoid, trigForm->oid, qual,
20136 156 : false, true, trigForm->tgenabled);
20137 :
20138 150 : MemoryContextSwitchTo(oldcxt);
20139 150 : MemoryContextReset(perTupCxt);
20140 : }
20141 :
20142 2428 : MemoryContextDelete(perTupCxt);
20143 :
20144 2428 : systable_endscan(scan);
20145 2428 : table_close(pg_trigger, RowExclusiveLock);
20146 2428 : }
20147 :
20148 : /*
20149 : * ALTER TABLE DETACH PARTITION
20150 : *
20151 : * Return the address of the relation that is no longer a partition of rel.
20152 : *
20153 : * If concurrent mode is requested, we run in two transactions. A side-
20154 : * effect is that this command cannot run in a multi-part ALTER TABLE.
20155 : * Currently, that's enforced by the grammar.
20156 : *
20157 : * The strategy for concurrency is to first modify the partition's
20158 : * pg_inherit catalog row to make it visible to everyone that the
20159 : * partition is detached, lock the partition against writes, and commit
20160 : * the transaction; anyone who requests the partition descriptor from
20161 : * that point onwards has to ignore such a partition. In a second
20162 : * transaction, we wait until all transactions that could have seen the
20163 : * partition as attached are gone, then we remove the rest of partition
20164 : * metadata (pg_inherits and pg_class.relpartbounds).
20165 : */
20166 : static ObjectAddress
20167 558 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20168 : RangeVar *name, bool concurrent)
20169 : {
20170 : Relation partRel;
20171 : ObjectAddress address;
20172 : Oid defaultPartOid;
20173 :
20174 : /*
20175 : * We must lock the default partition, because detaching this partition
20176 : * will change its partition constraint.
20177 : */
20178 : defaultPartOid =
20179 558 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20180 558 : if (OidIsValid(defaultPartOid))
20181 : {
20182 : /*
20183 : * Concurrent detaching when a default partition exists is not
20184 : * supported. The main problem is that the default partition
20185 : * constraint would change. And there's a definitional problem: what
20186 : * should happen to the tuples that are being inserted that belong to
20187 : * the partition being detached? Putting them on the partition being
20188 : * detached would be wrong, since they'd become "lost" after the
20189 : * detaching completes but we cannot put them in the default partition
20190 : * either until we alter its partition constraint.
20191 : *
20192 : * I think we could solve this problem if we effected the constraint
20193 : * change before committing the first transaction. But the lock would
20194 : * have to remain AEL and it would cause concurrent query planning to
20195 : * be blocked, so changing it that way would be even worse.
20196 : */
20197 106 : if (concurrent)
20198 12 : ereport(ERROR,
20199 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20200 : errmsg("cannot detach partitions concurrently when a default partition exists")));
20201 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20202 : }
20203 :
20204 : /*
20205 : * In concurrent mode, the partition is locked with share-update-exclusive
20206 : * in the first transaction. This allows concurrent transactions to be
20207 : * doing DML to the partition.
20208 : */
20209 546 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20210 : AccessExclusiveLock);
20211 :
20212 : /*
20213 : * Check inheritance conditions and either delete the pg_inherits row (in
20214 : * non-concurrent mode) or just set the inhdetachpending flag.
20215 : */
20216 534 : if (!concurrent)
20217 388 : RemoveInheritance(partRel, rel, false);
20218 : else
20219 146 : MarkInheritDetached(partRel, rel);
20220 :
20221 : /*
20222 : * Ensure that foreign keys still hold after this detach. This keeps
20223 : * locks on the referencing tables, which prevents concurrent transactions
20224 : * from adding rows that we wouldn't see. For this to work in concurrent
20225 : * mode, it is critical that the partition appears as no longer attached
20226 : * for the RI queries as soon as the first transaction commits.
20227 : */
20228 514 : ATDetachCheckNoForeignKeyRefs(partRel);
20229 :
20230 : /*
20231 : * Concurrent mode has to work harder; first we add a new constraint to
20232 : * the partition that matches the partition constraint. Then we close our
20233 : * existing transaction, and in a new one wait for all processes to catch
20234 : * up on the catalog updates we've done so far; at that point we can
20235 : * complete the operation.
20236 : */
20237 480 : if (concurrent)
20238 : {
20239 : Oid partrelid,
20240 : parentrelid;
20241 : LOCKTAG tag;
20242 : char *parentrelname;
20243 : char *partrelname;
20244 :
20245 : /*
20246 : * Add a new constraint to the partition being detached, which
20247 : * supplants the partition constraint (unless there is one already).
20248 : */
20249 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
20250 :
20251 : /*
20252 : * We're almost done now; the only traces that remain are the
20253 : * pg_inherits tuple and the partition's relpartbounds. Before we can
20254 : * remove those, we need to wait until all transactions that know that
20255 : * this is a partition are gone.
20256 : */
20257 :
20258 : /*
20259 : * Remember relation OIDs to re-acquire them later; and relation names
20260 : * too, for error messages if something is dropped in between.
20261 : */
20262 140 : partrelid = RelationGetRelid(partRel);
20263 140 : parentrelid = RelationGetRelid(rel);
20264 140 : parentrelname = MemoryContextStrdup(PortalContext,
20265 140 : RelationGetRelationName(rel));
20266 140 : partrelname = MemoryContextStrdup(PortalContext,
20267 140 : RelationGetRelationName(partRel));
20268 :
20269 : /* Invalidate relcache entries for the parent -- must be before close */
20270 140 : CacheInvalidateRelcache(rel);
20271 :
20272 140 : table_close(partRel, NoLock);
20273 140 : table_close(rel, NoLock);
20274 140 : tab->rel = NULL;
20275 :
20276 : /* Make updated catalog entry visible */
20277 140 : PopActiveSnapshot();
20278 140 : CommitTransactionCommand();
20279 :
20280 140 : StartTransactionCommand();
20281 :
20282 : /*
20283 : * Now wait. This ensures that all queries that were planned
20284 : * including the partition are finished before we remove the rest of
20285 : * catalog entries. We don't need or indeed want to acquire this
20286 : * lock, though -- that would block later queries.
20287 : *
20288 : * We don't need to concern ourselves with waiting for a lock on the
20289 : * partition itself, since we will acquire AccessExclusiveLock below.
20290 : */
20291 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20292 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
20293 :
20294 : /*
20295 : * Now acquire locks in both relations again. Note they may have been
20296 : * removed in the meantime, so care is required.
20297 : */
20298 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20299 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
20300 :
20301 : /* If the relations aren't there, something bad happened; bail out */
20302 90 : if (rel == NULL)
20303 : {
20304 0 : if (partRel != NULL) /* shouldn't happen */
20305 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20306 : partrelname);
20307 0 : ereport(ERROR,
20308 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20309 : errmsg("partitioned table \"%s\" was removed concurrently",
20310 : parentrelname)));
20311 : }
20312 90 : if (partRel == NULL)
20313 0 : ereport(ERROR,
20314 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20315 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
20316 :
20317 90 : tab->rel = rel;
20318 : }
20319 :
20320 : /* Do the final part of detaching */
20321 430 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20322 :
20323 428 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20324 :
20325 : /* keep our lock until commit */
20326 428 : table_close(partRel, NoLock);
20327 :
20328 428 : return address;
20329 : }
20330 :
20331 : /*
20332 : * Second part of ALTER TABLE .. DETACH.
20333 : *
20334 : * This is separate so that it can be run independently when the second
20335 : * transaction of the concurrent algorithm fails (crash or abort).
20336 : */
20337 : static void
20338 444 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20339 : Oid defaultPartOid)
20340 : {
20341 : Relation classRel;
20342 : List *fks;
20343 : ListCell *cell;
20344 : List *indexes;
20345 : Datum new_val[Natts_pg_class];
20346 : bool new_null[Natts_pg_class],
20347 : new_repl[Natts_pg_class];
20348 : HeapTuple tuple,
20349 : newtuple;
20350 444 : Relation trigrel = NULL;
20351 444 : List *fkoids = NIL;
20352 :
20353 444 : if (concurrent)
20354 : {
20355 : /*
20356 : * We can remove the pg_inherits row now. (In the non-concurrent case,
20357 : * this was already done).
20358 : */
20359 104 : RemoveInheritance(partRel, rel, true);
20360 : }
20361 :
20362 : /* Drop any triggers that were cloned on creation/attach. */
20363 444 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
20364 :
20365 : /*
20366 : * Detach any foreign keys that are inherited. This includes creating
20367 : * additional action triggers.
20368 : */
20369 444 : fks = copyObject(RelationGetFKeyList(partRel));
20370 444 : if (fks != NIL)
20371 72 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20372 :
20373 : /*
20374 : * It's possible that the partition being detached has a foreign key that
20375 : * references a partitioned table. When that happens, there are multiple
20376 : * pg_constraint rows for the partition: one points to the partitioned
20377 : * table itself, while the others point to each of its partitions. Only
20378 : * the topmost one is to be considered here; the child constraints must be
20379 : * left alone, because conceptually those aren't coming from our parent
20380 : * partitioned table, but from this partition itself.
20381 : *
20382 : * We implement this by collecting all the constraint OIDs in a first scan
20383 : * of the FK array, and skipping in the loop below those constraints whose
20384 : * parents are listed here.
20385 : */
20386 1044 : foreach_node(ForeignKeyCacheInfo, fk, fks)
20387 156 : fkoids = lappend_oid(fkoids, fk->conoid);
20388 :
20389 600 : foreach(cell, fks)
20390 : {
20391 156 : ForeignKeyCacheInfo *fk = lfirst(cell);
20392 : HeapTuple contup;
20393 : Form_pg_constraint conform;
20394 : Oid insertTriggerOid,
20395 : updateTriggerOid;
20396 :
20397 156 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20398 156 : if (!HeapTupleIsValid(contup))
20399 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20400 156 : conform = (Form_pg_constraint) GETSTRUCT(contup);
20401 :
20402 : /*
20403 : * Consider only inherited foreign keys, and only if their parents
20404 : * aren't in the list.
20405 : */
20406 156 : if (conform->contype != CONSTRAINT_FOREIGN ||
20407 288 : !OidIsValid(conform->conparentid) ||
20408 132 : list_member_oid(fkoids, conform->conparentid))
20409 : {
20410 66 : ReleaseSysCache(contup);
20411 66 : continue;
20412 : }
20413 :
20414 : /*
20415 : * The constraint on this table must be marked no longer a child of
20416 : * the parent's constraint, as do its check triggers.
20417 : */
20418 90 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
20419 :
20420 : /*
20421 : * Also, look up the partition's "check" triggers corresponding to the
20422 : * constraint being detached and detach them from the parent triggers.
20423 : */
20424 90 : GetForeignKeyCheckTriggers(trigrel,
20425 : fk->conoid, fk->confrelid, fk->conrelid,
20426 : &insertTriggerOid, &updateTriggerOid);
20427 : Assert(OidIsValid(insertTriggerOid));
20428 90 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20429 : RelationGetRelid(partRel));
20430 : Assert(OidIsValid(updateTriggerOid));
20431 90 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20432 : RelationGetRelid(partRel));
20433 :
20434 : /*
20435 : * Lastly, create the action triggers on the referenced table, using
20436 : * addFkRecurseReferenced, which requires some elaborate setup (so put
20437 : * it in a separate block). While at it, if the table is partitioned,
20438 : * that function will recurse to create the pg_constraint rows and
20439 : * action triggers for each partition.
20440 : *
20441 : * Note there's no need to do addFkConstraint() here, because the
20442 : * pg_constraint row already exists.
20443 : */
20444 : {
20445 : Constraint *fkconstraint;
20446 : int numfks;
20447 : AttrNumber conkey[INDEX_MAX_KEYS];
20448 : AttrNumber confkey[INDEX_MAX_KEYS];
20449 : Oid conpfeqop[INDEX_MAX_KEYS];
20450 : Oid conppeqop[INDEX_MAX_KEYS];
20451 : Oid conffeqop[INDEX_MAX_KEYS];
20452 : int numfkdelsetcols;
20453 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
20454 : Relation refdRel;
20455 :
20456 90 : DeconstructFkConstraintRow(contup,
20457 : &numfks,
20458 : conkey,
20459 : confkey,
20460 : conpfeqop,
20461 : conppeqop,
20462 : conffeqop,
20463 : &numfkdelsetcols,
20464 : confdelsetcols);
20465 :
20466 : /* Create a synthetic node we'll use throughout */
20467 90 : fkconstraint = makeNode(Constraint);
20468 90 : fkconstraint->contype = CONSTRAINT_FOREIGN;
20469 90 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
20470 90 : fkconstraint->deferrable = conform->condeferrable;
20471 90 : fkconstraint->initdeferred = conform->condeferred;
20472 90 : fkconstraint->skip_validation = true;
20473 90 : fkconstraint->initially_valid = true;
20474 : /* a few irrelevant fields omitted here */
20475 90 : fkconstraint->pktable = NULL;
20476 90 : fkconstraint->fk_attrs = NIL;
20477 90 : fkconstraint->pk_attrs = NIL;
20478 90 : fkconstraint->fk_matchtype = conform->confmatchtype;
20479 90 : fkconstraint->fk_upd_action = conform->confupdtype;
20480 90 : fkconstraint->fk_del_action = conform->confdeltype;
20481 90 : fkconstraint->fk_del_set_cols = NIL;
20482 90 : fkconstraint->old_conpfeqop = NIL;
20483 90 : fkconstraint->old_pktable_oid = InvalidOid;
20484 90 : fkconstraint->location = -1;
20485 :
20486 : /* set up colnames, used to generate the constraint name */
20487 228 : for (int i = 0; i < numfks; i++)
20488 : {
20489 : Form_pg_attribute att;
20490 :
20491 138 : att = TupleDescAttr(RelationGetDescr(partRel),
20492 138 : conkey[i] - 1);
20493 :
20494 138 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
20495 138 : makeString(NameStr(att->attname)));
20496 : }
20497 :
20498 90 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
20499 :
20500 90 : addFkRecurseReferenced(fkconstraint, partRel,
20501 : refdRel,
20502 : conform->conindid,
20503 : fk->conoid,
20504 : numfks,
20505 : confkey,
20506 : conkey,
20507 : conpfeqop,
20508 : conppeqop,
20509 : conffeqop,
20510 : numfkdelsetcols,
20511 : confdelsetcols,
20512 : true,
20513 : InvalidOid, InvalidOid,
20514 90 : conform->conperiod);
20515 90 : table_close(refdRel, NoLock); /* keep lock till end of xact */
20516 : }
20517 :
20518 90 : ReleaseSysCache(contup);
20519 : }
20520 444 : list_free_deep(fks);
20521 444 : if (trigrel)
20522 72 : table_close(trigrel, RowExclusiveLock);
20523 :
20524 : /*
20525 : * Any sub-constraints that are in the referenced-side of a larger
20526 : * constraint have to be removed. This partition is no longer part of the
20527 : * key space of the constraint.
20528 : */
20529 480 : foreach(cell, GetParentedForeignKeyRefs(partRel))
20530 : {
20531 38 : Oid constrOid = lfirst_oid(cell);
20532 : ObjectAddress constraint;
20533 :
20534 38 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20535 38 : deleteDependencyRecordsForClass(ConstraintRelationId,
20536 : constrOid,
20537 : ConstraintRelationId,
20538 : DEPENDENCY_INTERNAL);
20539 38 : CommandCounterIncrement();
20540 :
20541 38 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20542 38 : performDeletion(&constraint, DROP_RESTRICT, 0);
20543 : }
20544 :
20545 : /* Now we can detach indexes */
20546 442 : indexes = RelationGetIndexList(partRel);
20547 624 : foreach(cell, indexes)
20548 : {
20549 182 : Oid idxid = lfirst_oid(cell);
20550 : Oid parentidx;
20551 : Relation idx;
20552 : Oid constrOid;
20553 : Oid parentConstrOid;
20554 :
20555 182 : if (!has_superclass(idxid))
20556 12 : continue;
20557 :
20558 170 : parentidx = get_partition_parent(idxid, false);
20559 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
20560 :
20561 170 : idx = index_open(idxid, AccessExclusiveLock);
20562 170 : IndexSetParentIndex(idx, InvalidOid);
20563 :
20564 : /*
20565 : * If there's a constraint associated with the index, detach it too.
20566 : * Careful: it is possible for a constraint index in a partition to be
20567 : * the child of a non-constraint index, so verify whether the parent
20568 : * index does actually have a constraint.
20569 : */
20570 170 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20571 : idxid);
20572 170 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
20573 : parentidx);
20574 170 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
20575 72 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20576 :
20577 170 : index_close(idx, NoLock);
20578 : }
20579 :
20580 : /* Update pg_class tuple */
20581 442 : classRel = table_open(RelationRelationId, RowExclusiveLock);
20582 442 : tuple = SearchSysCacheCopy1(RELOID,
20583 : ObjectIdGetDatum(RelationGetRelid(partRel)));
20584 442 : if (!HeapTupleIsValid(tuple))
20585 0 : elog(ERROR, "cache lookup failed for relation %u",
20586 : RelationGetRelid(partRel));
20587 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20588 :
20589 : /* Clear relpartbound and reset relispartition */
20590 442 : memset(new_val, 0, sizeof(new_val));
20591 442 : memset(new_null, false, sizeof(new_null));
20592 442 : memset(new_repl, false, sizeof(new_repl));
20593 442 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20594 442 : new_null[Anum_pg_class_relpartbound - 1] = true;
20595 442 : new_repl[Anum_pg_class_relpartbound - 1] = true;
20596 442 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20597 : new_val, new_null, new_repl);
20598 :
20599 442 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20600 442 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20601 442 : heap_freetuple(newtuple);
20602 442 : table_close(classRel, RowExclusiveLock);
20603 :
20604 : /*
20605 : * Drop identity property from all identity columns of partition.
20606 : */
20607 1258 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20608 : {
20609 816 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20610 :
20611 816 : if (!attr->attisdropped && attr->attidentity)
20612 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20613 : AccessExclusiveLock, true, true);
20614 : }
20615 :
20616 442 : if (OidIsValid(defaultPartOid))
20617 : {
20618 : /*
20619 : * If the relation being detached is the default partition itself,
20620 : * remove it from the parent's pg_partitioned_table entry.
20621 : *
20622 : * If not, we must invalidate default partition's relcache entry, as
20623 : * in StorePartitionBound: its partition constraint depends on every
20624 : * other partition's partition constraint.
20625 : */
20626 46 : if (RelationGetRelid(partRel) == defaultPartOid)
20627 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20628 : else
20629 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
20630 : }
20631 :
20632 : /*
20633 : * Invalidate the parent's relcache so that the partition is no longer
20634 : * included in its partition descriptor.
20635 : */
20636 442 : CacheInvalidateRelcache(rel);
20637 :
20638 : /*
20639 : * If the partition we just detached is partitioned itself, invalidate
20640 : * relcache for all descendent partitions too to ensure that their
20641 : * rd_partcheck expression trees are rebuilt; must lock partitions before
20642 : * doing so, using the same lockmode as what partRel has been locked with
20643 : * by the caller.
20644 : */
20645 442 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20646 : {
20647 : List *children;
20648 :
20649 56 : children = find_all_inheritors(RelationGetRelid(partRel),
20650 : AccessExclusiveLock, NULL);
20651 180 : foreach(cell, children)
20652 : {
20653 124 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20654 : }
20655 : }
20656 442 : }
20657 :
20658 : /*
20659 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20660 : *
20661 : * To use when a DETACH PARTITION command previously did not run to
20662 : * completion; this completes the detaching process.
20663 : */
20664 : static ObjectAddress
20665 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20666 : {
20667 : Relation partRel;
20668 : ObjectAddress address;
20669 14 : Snapshot snap = GetActiveSnapshot();
20670 :
20671 14 : partRel = table_openrv(name, AccessExclusiveLock);
20672 :
20673 : /*
20674 : * Wait until existing snapshots are gone. This is important if the
20675 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20676 : * user could immediately run DETACH FINALIZE without actually waiting for
20677 : * existing transactions. We must not complete the detach action until
20678 : * all such queries are complete (otherwise we would present them with an
20679 : * inconsistent view of catalogs).
20680 : */
20681 14 : WaitForOlderSnapshots(snap->xmin, false);
20682 :
20683 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20684 :
20685 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20686 :
20687 14 : table_close(partRel, NoLock);
20688 :
20689 14 : return address;
20690 : }
20691 :
20692 : /*
20693 : * DetachAddConstraintIfNeeded
20694 : * Subroutine for ATExecDetachPartition. Create a constraint that
20695 : * takes the place of the partition constraint, but avoid creating
20696 : * a dupe if a constraint already exists which implies the needed
20697 : * constraint.
20698 : */
20699 : static void
20700 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20701 : {
20702 : List *constraintExpr;
20703 :
20704 140 : constraintExpr = RelationGetPartitionQual(partRel);
20705 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20706 :
20707 : /*
20708 : * Avoid adding a new constraint if the needed constraint is implied by an
20709 : * existing constraint
20710 : */
20711 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20712 : {
20713 : AlteredTableInfo *tab;
20714 : Constraint *n;
20715 :
20716 134 : tab = ATGetQueueEntry(wqueue, partRel);
20717 :
20718 : /* Add constraint on partition, equivalent to the partition constraint */
20719 134 : n = makeNode(Constraint);
20720 134 : n->contype = CONSTR_CHECK;
20721 134 : n->conname = NULL;
20722 134 : n->location = -1;
20723 134 : n->is_no_inherit = false;
20724 134 : n->raw_expr = NULL;
20725 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20726 134 : n->is_enforced = true;
20727 134 : n->initially_valid = true;
20728 134 : n->skip_validation = true;
20729 : /* It's a re-add, since it nominally already exists */
20730 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20731 : true, false, true, ShareUpdateExclusiveLock);
20732 : }
20733 140 : }
20734 :
20735 : /*
20736 : * DropClonedTriggersFromPartition
20737 : * subroutine for ATExecDetachPartition to remove any triggers that were
20738 : * cloned to the partition when it was created-as-partition or attached.
20739 : * This undoes what CloneRowTriggersToPartition did.
20740 : */
20741 : static void
20742 444 : DropClonedTriggersFromPartition(Oid partitionId)
20743 : {
20744 : ScanKeyData skey;
20745 : SysScanDesc scan;
20746 : HeapTuple trigtup;
20747 : Relation tgrel;
20748 : ObjectAddresses *objects;
20749 :
20750 444 : objects = new_object_addresses();
20751 :
20752 : /*
20753 : * Scan pg_trigger to search for all triggers on this rel.
20754 : */
20755 444 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20756 : F_OIDEQ, ObjectIdGetDatum(partitionId));
20757 444 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20758 444 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20759 : true, NULL, 1, &skey);
20760 766 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20761 : {
20762 322 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20763 : ObjectAddress trig;
20764 :
20765 : /* Ignore triggers that weren't cloned */
20766 322 : if (!OidIsValid(pg_trigger->tgparentid))
20767 304 : continue;
20768 :
20769 : /*
20770 : * Ignore internal triggers that are implementation objects of foreign
20771 : * keys, because these will be detached when the foreign keys
20772 : * themselves are.
20773 : */
20774 274 : if (OidIsValid(pg_trigger->tgconstrrelid))
20775 256 : continue;
20776 :
20777 : /*
20778 : * This is ugly, but necessary: remove the dependency markings on the
20779 : * trigger so that it can be removed.
20780 : */
20781 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20782 : TriggerRelationId,
20783 : DEPENDENCY_PARTITION_PRI);
20784 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20785 : RelationRelationId,
20786 : DEPENDENCY_PARTITION_SEC);
20787 :
20788 : /* remember this trigger to remove it below */
20789 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20790 18 : add_exact_object_address(&trig, objects);
20791 : }
20792 :
20793 : /* make the dependency removal visible to the deletion below */
20794 444 : CommandCounterIncrement();
20795 444 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20796 :
20797 : /* done */
20798 444 : free_object_addresses(objects);
20799 444 : systable_endscan(scan);
20800 444 : table_close(tgrel, RowExclusiveLock);
20801 444 : }
20802 :
20803 : /*
20804 : * Before acquiring lock on an index, acquire the same lock on the owning
20805 : * table.
20806 : */
20807 : struct AttachIndexCallbackState
20808 : {
20809 : Oid partitionOid;
20810 : Oid parentTblOid;
20811 : bool lockedParentTbl;
20812 : };
20813 :
20814 : static void
20815 398 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20816 : void *arg)
20817 : {
20818 : struct AttachIndexCallbackState *state;
20819 : Form_pg_class classform;
20820 : HeapTuple tuple;
20821 :
20822 398 : state = (struct AttachIndexCallbackState *) arg;
20823 :
20824 398 : if (!state->lockedParentTbl)
20825 : {
20826 392 : LockRelationOid(state->parentTblOid, AccessShareLock);
20827 392 : state->lockedParentTbl = true;
20828 : }
20829 :
20830 : /*
20831 : * If we previously locked some other heap, and the name we're looking up
20832 : * no longer refers to an index on that relation, release the now-useless
20833 : * lock. XXX maybe we should do *after* we verify whether the index does
20834 : * not actually belong to the same relation ...
20835 : */
20836 398 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20837 : {
20838 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
20839 0 : state->partitionOid = InvalidOid;
20840 : }
20841 :
20842 : /* Didn't find a relation, so no need for locking or permission checks. */
20843 398 : if (!OidIsValid(relOid))
20844 6 : return;
20845 :
20846 392 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20847 392 : if (!HeapTupleIsValid(tuple))
20848 0 : return; /* concurrently dropped, so nothing to do */
20849 392 : classform = (Form_pg_class) GETSTRUCT(tuple);
20850 392 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20851 302 : classform->relkind != RELKIND_INDEX)
20852 6 : ereport(ERROR,
20853 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20854 : errmsg("\"%s\" is not an index", rv->relname)));
20855 386 : ReleaseSysCache(tuple);
20856 :
20857 : /*
20858 : * Since we need only examine the heap's tupledesc, an access share lock
20859 : * on it (preventing any DDL) is sufficient.
20860 : */
20861 386 : state->partitionOid = IndexGetRelation(relOid, false);
20862 386 : LockRelationOid(state->partitionOid, AccessShareLock);
20863 : }
20864 :
20865 : /*
20866 : * ALTER INDEX i1 ATTACH PARTITION i2
20867 : */
20868 : static ObjectAddress
20869 392 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20870 : {
20871 : Relation partIdx;
20872 : Relation partTbl;
20873 : Relation parentTbl;
20874 : ObjectAddress address;
20875 : Oid partIdxId;
20876 : Oid currParent;
20877 : struct AttachIndexCallbackState state;
20878 :
20879 : /*
20880 : * We need to obtain lock on the index 'name' to modify it, but we also
20881 : * need to read its owning table's tuple descriptor -- so we need to lock
20882 : * both. To avoid deadlocks, obtain lock on the table before doing so on
20883 : * the index. Furthermore, we need to examine the parent table of the
20884 : * partition, so lock that one too.
20885 : */
20886 392 : state.partitionOid = InvalidOid;
20887 392 : state.parentTblOid = parentIdx->rd_index->indrelid;
20888 392 : state.lockedParentTbl = false;
20889 : partIdxId =
20890 392 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20891 : RangeVarCallbackForAttachIndex,
20892 : &state);
20893 : /* Not there? */
20894 380 : if (!OidIsValid(partIdxId))
20895 0 : ereport(ERROR,
20896 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20897 : errmsg("index \"%s\" does not exist", name->relname)));
20898 :
20899 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20900 380 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
20901 :
20902 : /* we already hold locks on both tables, so this is safe: */
20903 380 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20904 380 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20905 :
20906 380 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20907 :
20908 : /* Silently do nothing if already in the right state */
20909 760 : currParent = partIdx->rd_rel->relispartition ?
20910 380 : get_partition_parent(partIdxId, false) : InvalidOid;
20911 380 : if (currParent != RelationGetRelid(parentIdx))
20912 : {
20913 : IndexInfo *childInfo;
20914 : IndexInfo *parentInfo;
20915 : AttrMap *attmap;
20916 : bool found;
20917 : int i;
20918 : PartitionDesc partDesc;
20919 : Oid constraintOid,
20920 356 : cldConstrId = InvalidOid;
20921 :
20922 : /*
20923 : * If this partition already has an index attached, refuse the
20924 : * operation.
20925 : */
20926 356 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20927 :
20928 350 : if (OidIsValid(currParent))
20929 0 : ereport(ERROR,
20930 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20931 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20932 : RelationGetRelationName(partIdx),
20933 : RelationGetRelationName(parentIdx)),
20934 : errdetail("Index \"%s\" is already attached to another index.",
20935 : RelationGetRelationName(partIdx))));
20936 :
20937 : /* Make sure it indexes a partition of the other index's table */
20938 350 : partDesc = RelationGetPartitionDesc(parentTbl, true);
20939 350 : found = false;
20940 552 : for (i = 0; i < partDesc->nparts; i++)
20941 : {
20942 546 : if (partDesc->oids[i] == state.partitionOid)
20943 : {
20944 344 : found = true;
20945 344 : break;
20946 : }
20947 : }
20948 350 : if (!found)
20949 6 : ereport(ERROR,
20950 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20951 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20952 : RelationGetRelationName(partIdx),
20953 : RelationGetRelationName(parentIdx)),
20954 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20955 : RelationGetRelationName(partIdx),
20956 : RelationGetRelationName(parentTbl))));
20957 :
20958 : /* Ensure the indexes are compatible */
20959 344 : childInfo = BuildIndexInfo(partIdx);
20960 344 : parentInfo = BuildIndexInfo(parentIdx);
20961 344 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20962 : RelationGetDescr(parentTbl),
20963 : false);
20964 344 : if (!CompareIndexInfo(childInfo, parentInfo,
20965 344 : partIdx->rd_indcollation,
20966 344 : parentIdx->rd_indcollation,
20967 344 : partIdx->rd_opfamily,
20968 344 : parentIdx->rd_opfamily,
20969 : attmap))
20970 42 : ereport(ERROR,
20971 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20972 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20973 : RelationGetRelationName(partIdx),
20974 : RelationGetRelationName(parentIdx)),
20975 : errdetail("The index definitions do not match.")));
20976 :
20977 : /*
20978 : * If there is a constraint in the parent, make sure there is one in
20979 : * the child too.
20980 : */
20981 302 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20982 : RelationGetRelid(parentIdx));
20983 :
20984 302 : if (OidIsValid(constraintOid))
20985 : {
20986 122 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20987 : partIdxId);
20988 122 : if (!OidIsValid(cldConstrId))
20989 6 : ereport(ERROR,
20990 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20991 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20992 : RelationGetRelationName(partIdx),
20993 : RelationGetRelationName(parentIdx)),
20994 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20995 : RelationGetRelationName(parentIdx),
20996 : RelationGetRelationName(parentTbl),
20997 : RelationGetRelationName(partIdx))));
20998 : }
20999 :
21000 : /*
21001 : * If it's a primary key, make sure the columns in the partition are
21002 : * NOT NULL.
21003 : */
21004 296 : if (parentIdx->rd_index->indisprimary)
21005 92 : verifyPartitionIndexNotNull(childInfo, partTbl);
21006 :
21007 : /* All good -- do it */
21008 296 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21009 296 : if (OidIsValid(constraintOid))
21010 116 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
21011 : RelationGetRelid(partTbl));
21012 :
21013 296 : free_attrmap(attmap);
21014 :
21015 296 : validatePartitionedIndex(parentIdx, parentTbl);
21016 : }
21017 :
21018 320 : relation_close(parentTbl, AccessShareLock);
21019 : /* keep these locks till commit */
21020 320 : relation_close(partTbl, NoLock);
21021 320 : relation_close(partIdx, NoLock);
21022 :
21023 320 : return address;
21024 : }
21025 :
21026 : /*
21027 : * Verify whether the given partition already contains an index attached
21028 : * to the given partitioned index. If so, raise an error.
21029 : */
21030 : static void
21031 356 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21032 : {
21033 : Oid existingIdx;
21034 :
21035 356 : existingIdx = index_get_partition(partitionTbl,
21036 : RelationGetRelid(parentIdx));
21037 356 : if (OidIsValid(existingIdx))
21038 6 : ereport(ERROR,
21039 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21040 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21041 : RelationGetRelationName(partIdx),
21042 : RelationGetRelationName(parentIdx)),
21043 : errdetail("Another index is already attached for partition \"%s\".",
21044 : RelationGetRelationName(partitionTbl))));
21045 350 : }
21046 :
21047 : /*
21048 : * Verify whether the set of attached partition indexes to a parent index on
21049 : * a partitioned table is complete. If it is, mark the parent index valid.
21050 : *
21051 : * This should be called each time a partition index is attached.
21052 : */
21053 : static void
21054 338 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
21055 : {
21056 : Relation inheritsRel;
21057 : SysScanDesc scan;
21058 : ScanKeyData key;
21059 338 : int tuples = 0;
21060 : HeapTuple inhTup;
21061 338 : bool updated = false;
21062 :
21063 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21064 :
21065 : /*
21066 : * Scan pg_inherits for this parent index. Count each valid index we find
21067 : * (verifying the pg_index entry for each), and if we reach the total
21068 : * amount we expect, we can mark this parent index as valid.
21069 : */
21070 338 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21071 338 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21072 : BTEqualStrategyNumber, F_OIDEQ,
21073 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21074 338 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21075 : NULL, 1, &key);
21076 880 : while ((inhTup = systable_getnext(scan)) != NULL)
21077 : {
21078 542 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21079 : HeapTuple indTup;
21080 : Form_pg_index indexForm;
21081 :
21082 542 : indTup = SearchSysCache1(INDEXRELID,
21083 : ObjectIdGetDatum(inhForm->inhrelid));
21084 542 : if (!HeapTupleIsValid(indTup))
21085 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21086 542 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21087 542 : if (indexForm->indisvalid)
21088 484 : tuples += 1;
21089 542 : ReleaseSysCache(indTup);
21090 : }
21091 :
21092 : /* Done with pg_inherits */
21093 338 : systable_endscan(scan);
21094 338 : table_close(inheritsRel, AccessShareLock);
21095 :
21096 : /*
21097 : * If we found as many inherited indexes as the partitioned table has
21098 : * partitions, we're good; update pg_index to set indisvalid.
21099 : */
21100 338 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21101 : {
21102 : Relation idxRel;
21103 : HeapTuple indTup;
21104 : Form_pg_index indexForm;
21105 :
21106 168 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
21107 168 : indTup = SearchSysCacheCopy1(INDEXRELID,
21108 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21109 168 : if (!HeapTupleIsValid(indTup))
21110 0 : elog(ERROR, "cache lookup failed for index %u",
21111 : RelationGetRelid(partedIdx));
21112 168 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21113 :
21114 168 : indexForm->indisvalid = true;
21115 168 : updated = true;
21116 :
21117 168 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21118 :
21119 168 : table_close(idxRel, RowExclusiveLock);
21120 168 : heap_freetuple(indTup);
21121 : }
21122 :
21123 : /*
21124 : * If this index is in turn a partition of a larger index, validating it
21125 : * might cause the parent to become valid also. Try that.
21126 : */
21127 338 : if (updated && partedIdx->rd_rel->relispartition)
21128 : {
21129 : Oid parentIdxId,
21130 : parentTblId;
21131 : Relation parentIdx,
21132 : parentTbl;
21133 :
21134 : /* make sure we see the validation we just did */
21135 42 : CommandCounterIncrement();
21136 :
21137 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21138 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21139 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21140 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21141 : Assert(!parentIdx->rd_index->indisvalid);
21142 :
21143 42 : validatePartitionedIndex(parentIdx, parentTbl);
21144 :
21145 42 : relation_close(parentIdx, AccessExclusiveLock);
21146 42 : relation_close(parentTbl, AccessExclusiveLock);
21147 : }
21148 338 : }
21149 :
21150 : /*
21151 : * When attaching an index as a partition of a partitioned index which is a
21152 : * primary key, verify that all the columns in the partition are marked NOT
21153 : * NULL.
21154 : */
21155 : static void
21156 92 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
21157 : {
21158 186 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21159 : {
21160 94 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
21161 94 : iinfo->ii_IndexAttrNumbers[i] - 1);
21162 :
21163 94 : if (!att->attnotnull)
21164 0 : ereport(ERROR,
21165 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21166 : errmsg("invalid primary key definition"),
21167 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21168 : NameStr(att->attname),
21169 : RelationGetRelationName(partition)));
21170 : }
21171 92 : }
21172 :
21173 : /*
21174 : * Return an OID list of constraints that reference the given relation
21175 : * that are marked as having a parent constraints.
21176 : */
21177 : static List *
21178 958 : GetParentedForeignKeyRefs(Relation partition)
21179 : {
21180 : Relation pg_constraint;
21181 : HeapTuple tuple;
21182 : SysScanDesc scan;
21183 : ScanKeyData key[2];
21184 958 : List *constraints = NIL;
21185 :
21186 : /*
21187 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
21188 : * scan.
21189 : */
21190 1362 : if (RelationGetIndexList(partition) == NIL ||
21191 404 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
21192 : INDEX_ATTR_BITMAP_KEY)))
21193 710 : return NIL;
21194 :
21195 : /* Search for constraints referencing this table */
21196 248 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21197 248 : ScanKeyInit(&key[0],
21198 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21199 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21200 248 : ScanKeyInit(&key[1],
21201 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
21202 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21203 :
21204 : /* XXX This is a seqscan, as we don't have a usable index */
21205 248 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21206 372 : while ((tuple = systable_getnext(scan)) != NULL)
21207 : {
21208 124 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21209 :
21210 : /*
21211 : * We only need to process constraints that are part of larger ones.
21212 : */
21213 124 : if (!OidIsValid(constrForm->conparentid))
21214 0 : continue;
21215 :
21216 124 : constraints = lappend_oid(constraints, constrForm->oid);
21217 : }
21218 :
21219 248 : systable_endscan(scan);
21220 248 : table_close(pg_constraint, AccessShareLock);
21221 :
21222 248 : return constraints;
21223 : }
21224 :
21225 : /*
21226 : * During DETACH PARTITION, verify that any foreign keys pointing to the
21227 : * partitioned table would not become invalid. An error is raised if any
21228 : * referenced values exist.
21229 : */
21230 : static void
21231 514 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21232 : {
21233 : List *constraints;
21234 : ListCell *cell;
21235 :
21236 514 : constraints = GetParentedForeignKeyRefs(partition);
21237 :
21238 566 : foreach(cell, constraints)
21239 : {
21240 86 : Oid constrOid = lfirst_oid(cell);
21241 : HeapTuple tuple;
21242 : Form_pg_constraint constrForm;
21243 : Relation rel;
21244 86 : Trigger trig = {0};
21245 :
21246 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21247 86 : if (!HeapTupleIsValid(tuple))
21248 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21249 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21250 :
21251 : Assert(OidIsValid(constrForm->conparentid));
21252 : Assert(constrForm->confrelid == RelationGetRelid(partition));
21253 :
21254 : /* prevent data changes into the referencing table until commit */
21255 86 : rel = table_open(constrForm->conrelid, ShareLock);
21256 :
21257 86 : trig.tgoid = InvalidOid;
21258 86 : trig.tgname = NameStr(constrForm->conname);
21259 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
21260 86 : trig.tgisinternal = true;
21261 86 : trig.tgconstrrelid = RelationGetRelid(partition);
21262 86 : trig.tgconstrindid = constrForm->conindid;
21263 86 : trig.tgconstraint = constrForm->oid;
21264 86 : trig.tgdeferrable = false;
21265 86 : trig.tginitdeferred = false;
21266 : /* we needn't fill in remaining fields */
21267 :
21268 86 : RI_PartitionRemove_Check(&trig, rel, partition);
21269 :
21270 52 : ReleaseSysCache(tuple);
21271 :
21272 52 : table_close(rel, NoLock);
21273 : }
21274 480 : }
21275 :
21276 : /*
21277 : * resolve column compression specification to compression method.
21278 : */
21279 : static char
21280 236282 : GetAttributeCompression(Oid atttypid, const char *compression)
21281 : {
21282 : char cmethod;
21283 :
21284 236282 : if (compression == NULL || strcmp(compression, "default") == 0)
21285 236126 : return InvalidCompressionMethod;
21286 :
21287 : /*
21288 : * To specify a nondefault method, the column data type must be toastable.
21289 : * Note this says nothing about whether the column's attstorage setting
21290 : * permits compression; we intentionally allow attstorage and
21291 : * attcompression to be independent. But with a non-toastable type,
21292 : * attstorage could not be set to a value that would permit compression.
21293 : *
21294 : * We don't actually need to enforce this, since nothing bad would happen
21295 : * if attcompression were non-default; it would never be consulted. But
21296 : * it seems more user-friendly to complain about a certainly-useless
21297 : * attempt to set the property.
21298 : */
21299 156 : if (!TypeIsToastable(atttypid))
21300 6 : ereport(ERROR,
21301 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21302 : errmsg("column data type %s does not support compression",
21303 : format_type_be(atttypid))));
21304 :
21305 150 : cmethod = CompressionNameToMethod(compression);
21306 150 : if (!CompressionMethodIsValid(cmethod))
21307 12 : ereport(ERROR,
21308 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21309 : errmsg("invalid compression method \"%s\"", compression)));
21310 :
21311 138 : return cmethod;
21312 : }
21313 :
21314 : /*
21315 : * resolve column storage specification
21316 : */
21317 : static char
21318 248 : GetAttributeStorage(Oid atttypid, const char *storagemode)
21319 : {
21320 248 : char cstorage = 0;
21321 :
21322 248 : if (pg_strcasecmp(storagemode, "plain") == 0)
21323 50 : cstorage = TYPSTORAGE_PLAIN;
21324 198 : else if (pg_strcasecmp(storagemode, "external") == 0)
21325 156 : cstorage = TYPSTORAGE_EXTERNAL;
21326 42 : else if (pg_strcasecmp(storagemode, "extended") == 0)
21327 16 : cstorage = TYPSTORAGE_EXTENDED;
21328 26 : else if (pg_strcasecmp(storagemode, "main") == 0)
21329 20 : cstorage = TYPSTORAGE_MAIN;
21330 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
21331 6 : cstorage = get_typstorage(atttypid);
21332 : else
21333 0 : ereport(ERROR,
21334 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21335 : errmsg("invalid storage type \"%s\"",
21336 : storagemode)));
21337 :
21338 : /*
21339 : * safety check: do not allow toasted storage modes unless column datatype
21340 : * is TOAST-aware.
21341 : */
21342 248 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
21343 6 : ereport(ERROR,
21344 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21345 : errmsg("column data type %s can only have storage PLAIN",
21346 : format_type_be(atttypid))));
21347 :
21348 242 : return cstorage;
21349 : }
|