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-2024, 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/heapam.h"
20 : #include "access/heapam_xlog.h"
21 : #include "access/multixact.h"
22 : #include "access/reloptions.h"
23 : #include "access/relscan.h"
24 : #include "access/sysattr.h"
25 : #include "access/tableam.h"
26 : #include "access/toast_compression.h"
27 : #include "access/xact.h"
28 : #include "access/xlog.h"
29 : #include "access/xloginsert.h"
30 : #include "catalog/catalog.h"
31 : #include "catalog/heap.h"
32 : #include "catalog/index.h"
33 : #include "catalog/namespace.h"
34 : #include "catalog/objectaccess.h"
35 : #include "catalog/partition.h"
36 : #include "catalog/pg_am.h"
37 : #include "catalog/pg_attrdef.h"
38 : #include "catalog/pg_collation.h"
39 : #include "catalog/pg_constraint.h"
40 : #include "catalog/pg_depend.h"
41 : #include "catalog/pg_foreign_table.h"
42 : #include "catalog/pg_inherits.h"
43 : #include "catalog/pg_largeobject.h"
44 : #include "catalog/pg_namespace.h"
45 : #include "catalog/pg_opclass.h"
46 : #include "catalog/pg_policy.h"
47 : #include "catalog/pg_proc.h"
48 : #include "catalog/pg_publication_rel.h"
49 : #include "catalog/pg_rewrite.h"
50 : #include "catalog/pg_statistic_ext.h"
51 : #include "catalog/pg_tablespace.h"
52 : #include "catalog/pg_trigger.h"
53 : #include "catalog/pg_type.h"
54 : #include "catalog/storage.h"
55 : #include "catalog/storage_xlog.h"
56 : #include "catalog/toasting.h"
57 : #include "commands/cluster.h"
58 : #include "commands/comment.h"
59 : #include "commands/defrem.h"
60 : #include "commands/event_trigger.h"
61 : #include "commands/sequence.h"
62 : #include "commands/tablecmds.h"
63 : #include "commands/tablespace.h"
64 : #include "commands/trigger.h"
65 : #include "commands/typecmds.h"
66 : #include "commands/user.h"
67 : #include "commands/vacuum.h"
68 : #include "executor/executor.h"
69 : #include "foreign/fdwapi.h"
70 : #include "foreign/foreign.h"
71 : #include "miscadmin.h"
72 : #include "nodes/makefuncs.h"
73 : #include "nodes/nodeFuncs.h"
74 : #include "nodes/parsenodes.h"
75 : #include "optimizer/optimizer.h"
76 : #include "parser/parse_coerce.h"
77 : #include "parser/parse_collate.h"
78 : #include "parser/parse_expr.h"
79 : #include "parser/parse_relation.h"
80 : #include "parser/parse_type.h"
81 : #include "parser/parse_utilcmd.h"
82 : #include "parser/parser.h"
83 : #include "partitioning/partbounds.h"
84 : #include "partitioning/partdesc.h"
85 : #include "pgstat.h"
86 : #include "rewrite/rewriteDefine.h"
87 : #include "rewrite/rewriteHandler.h"
88 : #include "rewrite/rewriteManip.h"
89 : #include "storage/bufmgr.h"
90 : #include "storage/lmgr.h"
91 : #include "storage/lock.h"
92 : #include "storage/predicate.h"
93 : #include "storage/smgr.h"
94 : #include "tcop/utility.h"
95 : #include "utils/acl.h"
96 : #include "utils/builtins.h"
97 : #include "utils/fmgroids.h"
98 : #include "utils/inval.h"
99 : #include "utils/lsyscache.h"
100 : #include "utils/memutils.h"
101 : #include "utils/partcache.h"
102 : #include "utils/relcache.h"
103 : #include "utils/ruleutils.h"
104 : #include "utils/snapmgr.h"
105 : #include "utils/syscache.h"
106 : #include "utils/timestamp.h"
107 : #include "utils/typcache.h"
108 : #include "utils/usercontext.h"
109 :
110 : /*
111 : * ON COMMIT action list
112 : */
113 : typedef struct OnCommitItem
114 : {
115 : Oid relid; /* relid of relation */
116 : OnCommitAction oncommit; /* what to do at end of xact */
117 :
118 : /*
119 : * If this entry was created during the current transaction,
120 : * creating_subid is the ID of the creating subxact; if created in a prior
121 : * transaction, creating_subid is zero. If deleted during the current
122 : * transaction, deleting_subid is the ID of the deleting subxact; if no
123 : * deletion request is pending, deleting_subid is zero.
124 : */
125 : SubTransactionId creating_subid;
126 : SubTransactionId deleting_subid;
127 : } OnCommitItem;
128 :
129 : static List *on_commits = NIL;
130 :
131 :
132 : /*
133 : * State information for ALTER TABLE
134 : *
135 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
136 : * structs, one for each table modified by the operation (the named table
137 : * plus any child tables that are affected). We save lists of subcommands
138 : * to apply to this table (possibly modified by parse transformation steps);
139 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
140 : * necessary information is stored in the constraints and newvals lists.
141 : *
142 : * Phase 2 is divided into multiple passes; subcommands are executed in
143 : * a pass determined by subcommand type.
144 : */
145 :
146 : typedef enum AlterTablePass
147 : {
148 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
149 : AT_PASS_DROP, /* DROP (all flavors) */
150 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
151 : AT_PASS_ADD_COL, /* ADD COLUMN */
152 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
153 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
154 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
155 : /* We could support a RENAME COLUMN pass here, but not currently used */
156 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
157 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
158 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
159 : AT_PASS_ADD_INDEX, /* ADD indexes */
160 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
161 : AT_PASS_MISC, /* other stuff */
162 : } AlterTablePass;
163 :
164 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
165 :
166 : typedef struct AlteredTableInfo
167 : {
168 : /* Information saved before any work commences: */
169 : Oid relid; /* Relation to work on */
170 : char relkind; /* Its relkind */
171 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
172 :
173 : /*
174 : * Transiently set during Phase 2, normally set to NULL.
175 : *
176 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
177 : * returns control. This can be exploited by ATExecCmd subroutines to
178 : * close/reopen across transaction boundaries.
179 : */
180 : Relation rel;
181 :
182 : /* Information saved by Phase 1 for Phase 2: */
183 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
184 : /* Information saved by Phases 1/2 for Phase 3: */
185 : List *constraints; /* List of NewConstraint */
186 : List *newvals; /* List of NewColumnValue */
187 : List *afterStmts; /* List of utility command parsetrees */
188 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
189 : int rewrite; /* Reason for forced rewrite, if any */
190 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
191 : Oid newAccessMethod; /* new access method; 0 means no change,
192 : * if above is true */
193 : Oid newTableSpace; /* new tablespace; 0 means no change */
194 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
195 : char newrelpersistence; /* if above is true */
196 : Expr *partition_constraint; /* for attach partition validation */
197 : /* true, if validating default due to some other attach/detach */
198 : bool validate_default;
199 : /* Objects to rebuild after completing ALTER TYPE operations */
200 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
201 : List *changedConstraintDefs; /* string definitions of same */
202 : List *changedIndexOids; /* OIDs of indexes to rebuild */
203 : List *changedIndexDefs; /* string definitions of same */
204 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
205 : char *clusterOnIndex; /* index to use for CLUSTER */
206 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
207 : List *changedStatisticsDefs; /* string definitions of same */
208 : } AlteredTableInfo;
209 :
210 : /* Struct describing one new constraint to check in Phase 3 scan */
211 : /* Note: new not-null constraints are handled elsewhere */
212 : typedef struct NewConstraint
213 : {
214 : char *name; /* Constraint name, or NULL if none */
215 : ConstrType contype; /* CHECK or FOREIGN */
216 : Oid refrelid; /* PK rel, if FOREIGN */
217 : Oid refindid; /* OID of PK's index, if FOREIGN */
218 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
219 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
220 : ExprState *qualstate; /* Execution state for CHECK expr */
221 : } NewConstraint;
222 :
223 : /*
224 : * Struct describing one new column value that needs to be computed during
225 : * Phase 3 copy (this could be either a new column with a non-null default, or
226 : * a column that we're changing the type of). Columns without such an entry
227 : * are just copied from the old table during ATRewriteTable. Note that the
228 : * expr is an expression over *old* table values, except when is_generated
229 : * is true; then it is an expression over columns of the *new* tuple.
230 : */
231 : typedef struct NewColumnValue
232 : {
233 : AttrNumber attnum; /* which column */
234 : Expr *expr; /* expression to compute */
235 : ExprState *exprstate; /* execution state */
236 : bool is_generated; /* is it a GENERATED expression? */
237 : } NewColumnValue;
238 :
239 : /*
240 : * Error-reporting support for RemoveRelations
241 : */
242 : struct dropmsgstrings
243 : {
244 : char kind;
245 : int nonexistent_code;
246 : const char *nonexistent_msg;
247 : const char *skipping_msg;
248 : const char *nota_msg;
249 : const char *drophint_msg;
250 : };
251 :
252 : static const struct dropmsgstrings dropmsgstringarray[] = {
253 : {RELKIND_RELATION,
254 : ERRCODE_UNDEFINED_TABLE,
255 : gettext_noop("table \"%s\" does not exist"),
256 : gettext_noop("table \"%s\" does not exist, skipping"),
257 : gettext_noop("\"%s\" is not a table"),
258 : gettext_noop("Use DROP TABLE to remove a table.")},
259 : {RELKIND_SEQUENCE,
260 : ERRCODE_UNDEFINED_TABLE,
261 : gettext_noop("sequence \"%s\" does not exist"),
262 : gettext_noop("sequence \"%s\" does not exist, skipping"),
263 : gettext_noop("\"%s\" is not a sequence"),
264 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
265 : {RELKIND_VIEW,
266 : ERRCODE_UNDEFINED_TABLE,
267 : gettext_noop("view \"%s\" does not exist"),
268 : gettext_noop("view \"%s\" does not exist, skipping"),
269 : gettext_noop("\"%s\" is not a view"),
270 : gettext_noop("Use DROP VIEW to remove a view.")},
271 : {RELKIND_MATVIEW,
272 : ERRCODE_UNDEFINED_TABLE,
273 : gettext_noop("materialized view \"%s\" does not exist"),
274 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
275 : gettext_noop("\"%s\" is not a materialized view"),
276 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
277 : {RELKIND_INDEX,
278 : ERRCODE_UNDEFINED_OBJECT,
279 : gettext_noop("index \"%s\" does not exist"),
280 : gettext_noop("index \"%s\" does not exist, skipping"),
281 : gettext_noop("\"%s\" is not an index"),
282 : gettext_noop("Use DROP INDEX to remove an index.")},
283 : {RELKIND_COMPOSITE_TYPE,
284 : ERRCODE_UNDEFINED_OBJECT,
285 : gettext_noop("type \"%s\" does not exist"),
286 : gettext_noop("type \"%s\" does not exist, skipping"),
287 : gettext_noop("\"%s\" is not a type"),
288 : gettext_noop("Use DROP TYPE to remove a type.")},
289 : {RELKIND_FOREIGN_TABLE,
290 : ERRCODE_UNDEFINED_OBJECT,
291 : gettext_noop("foreign table \"%s\" does not exist"),
292 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
293 : gettext_noop("\"%s\" is not a foreign table"),
294 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
295 : {RELKIND_PARTITIONED_TABLE,
296 : ERRCODE_UNDEFINED_TABLE,
297 : gettext_noop("table \"%s\" does not exist"),
298 : gettext_noop("table \"%s\" does not exist, skipping"),
299 : gettext_noop("\"%s\" is not a table"),
300 : gettext_noop("Use DROP TABLE to remove a table.")},
301 : {RELKIND_PARTITIONED_INDEX,
302 : ERRCODE_UNDEFINED_OBJECT,
303 : gettext_noop("index \"%s\" does not exist"),
304 : gettext_noop("index \"%s\" does not exist, skipping"),
305 : gettext_noop("\"%s\" is not an index"),
306 : gettext_noop("Use DROP INDEX to remove an index.")},
307 : {'\0', 0, NULL, NULL, NULL, NULL}
308 : };
309 :
310 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
311 : struct DropRelationCallbackState
312 : {
313 : /* These fields are set by RemoveRelations: */
314 : char expected_relkind;
315 : LOCKMODE heap_lockmode;
316 : /* These fields are state to track which subsidiary locks are held: */
317 : Oid heapOid;
318 : Oid partParentOid;
319 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
320 : char actual_relkind;
321 : char actual_relpersistence;
322 : };
323 :
324 : /* Alter table target-type flags for ATSimplePermissions */
325 : #define ATT_TABLE 0x0001
326 : #define ATT_VIEW 0x0002
327 : #define ATT_MATVIEW 0x0004
328 : #define ATT_INDEX 0x0008
329 : #define ATT_COMPOSITE_TYPE 0x0010
330 : #define ATT_FOREIGN_TABLE 0x0020
331 : #define ATT_PARTITIONED_INDEX 0x0040
332 : #define ATT_SEQUENCE 0x0080
333 :
334 : /*
335 : * ForeignTruncateInfo
336 : *
337 : * Information related to truncation of foreign tables. This is used for
338 : * the elements in a hash table. It uses the server OID as lookup key,
339 : * and includes a per-server list of all foreign tables involved in the
340 : * truncation.
341 : */
342 : typedef struct ForeignTruncateInfo
343 : {
344 : Oid serverid;
345 : List *rels;
346 : } ForeignTruncateInfo;
347 :
348 : /*
349 : * Partition tables are expected to be dropped when the parent partitioned
350 : * table gets dropped. Hence for partitioning we use AUTO dependency.
351 : * Otherwise, for regular inheritance use NORMAL dependency.
352 : */
353 : #define child_dependency_type(child_is_partition) \
354 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
355 :
356 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
357 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
358 : static void truncate_check_activity(Relation rel);
359 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
360 : Oid relId, Oid oldRelId, void *arg);
361 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
362 : bool is_partition, List **supconstr);
363 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
364 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
365 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
366 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
367 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
368 : static void StoreCatalogInheritance(Oid relationId, List *supers,
369 : bool child_is_partition);
370 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
371 : int32 seqNumber, Relation inhRelation,
372 : bool child_is_partition);
373 : static int findAttrByName(const char *attributeName, const List *columns);
374 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
375 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
376 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
377 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
378 : LOCKMODE lockmode);
379 : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
380 : bool recurse, bool recursing, LOCKMODE lockmode);
381 : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
382 : Relation rel, HeapTuple contuple, List **otherrelids,
383 : LOCKMODE lockmode);
384 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
385 : Relation rel, char *constrName,
386 : bool recurse, bool recursing, LOCKMODE lockmode);
387 : static int transformColumnNameList(Oid relId, List *colList,
388 : int16 *attnums, Oid *atttypids);
389 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
390 : List **attnamelist,
391 : int16 *attnums, Oid *atttypids,
392 : Oid *opclasses);
393 : static Oid transformFkeyCheckAttrs(Relation pkrel,
394 : int numattrs, int16 *attnums,
395 : Oid *opclasses);
396 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
397 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
398 : Oid *funcid);
399 : static void validateForeignKeyConstraint(char *conname,
400 : Relation rel, Relation pkrel,
401 : Oid pkindOid, Oid constraintOid);
402 : static void CheckAlterTableIsSafe(Relation rel);
403 : static void ATController(AlterTableStmt *parsetree,
404 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
405 : AlterTableUtilityContext *context);
406 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
407 : bool recurse, bool recursing, LOCKMODE lockmode,
408 : AlterTableUtilityContext *context);
409 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
410 : AlterTableUtilityContext *context);
411 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
412 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
413 : AlterTableUtilityContext *context);
414 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
415 : Relation rel, AlterTableCmd *cmd,
416 : bool recurse, LOCKMODE lockmode,
417 : AlterTablePass cur_pass,
418 : AlterTableUtilityContext *context);
419 : static void ATRewriteTables(AlterTableStmt *parsetree,
420 : List **wqueue, LOCKMODE lockmode,
421 : AlterTableUtilityContext *context);
422 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
423 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
424 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
425 : static void ATSimpleRecursion(List **wqueue, Relation rel,
426 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
427 : AlterTableUtilityContext *context);
428 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
429 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
430 : LOCKMODE lockmode,
431 : AlterTableUtilityContext *context);
432 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
433 : DropBehavior behavior);
434 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
435 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
436 : AlterTableUtilityContext *context);
437 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
438 : Relation rel, AlterTableCmd **cmd,
439 : bool recurse, bool recursing,
440 : LOCKMODE lockmode, AlterTablePass cur_pass,
441 : AlterTableUtilityContext *context);
442 : static bool check_for_column_name_collision(Relation rel, const char *colname,
443 : bool if_not_exists);
444 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
445 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
446 : static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
447 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
448 : static void ATPrepSetNotNull(List **wqueue, Relation rel,
449 : AlterTableCmd *cmd, bool recurse, bool recursing,
450 : LOCKMODE lockmode,
451 : AlterTableUtilityContext *context);
452 : static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
453 : const char *colName, LOCKMODE lockmode);
454 : static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
455 : const char *colName, LOCKMODE lockmode);
456 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
457 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
458 : List *testConstraint, List *provenConstraint);
459 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
460 : Node *newDefault, LOCKMODE lockmode);
461 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
462 : Node *newDefault);
463 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
464 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
465 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
466 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
467 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
468 : bool recurse, bool recursing);
469 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
470 : Node *newExpr, LOCKMODE lockmode);
471 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
472 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
473 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
474 : Node *newValue, LOCKMODE lockmode);
475 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
476 : Node *options, bool isReset, LOCKMODE lockmode);
477 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
478 : Node *newValue, LOCKMODE lockmode);
479 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
480 : AlterTableCmd *cmd, LOCKMODE lockmode,
481 : AlterTableUtilityContext *context);
482 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
483 : DropBehavior behavior,
484 : bool recurse, bool recursing,
485 : bool missing_ok, LOCKMODE lockmode,
486 : ObjectAddresses *addrs);
487 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
488 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
489 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
490 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
491 : static ObjectAddress ATExecAddConstraint(List **wqueue,
492 : AlteredTableInfo *tab, Relation rel,
493 : Constraint *newConstraint, bool recurse, bool is_readd,
494 : LOCKMODE lockmode);
495 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
496 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
497 : IndexStmt *stmt, LOCKMODE lockmode);
498 : static ObjectAddress ATAddCheckConstraint(List **wqueue,
499 : AlteredTableInfo *tab, Relation rel,
500 : Constraint *constr,
501 : bool recurse, bool recursing, bool is_readd,
502 : LOCKMODE lockmode);
503 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
504 : Relation rel, Constraint *fkconstraint,
505 : bool recurse, bool recursing,
506 : LOCKMODE lockmode);
507 : static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
508 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
509 : int numfks, int16 *pkattnum, int16 *fkattnum,
510 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
511 : int numfkdelsetcols, int16 *fkdelsetcols,
512 : bool old_check_ok,
513 : Oid parentDelTrigger, Oid parentUpdTrigger);
514 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
515 : int numfksetcols, const int16 *fksetcolsattnums,
516 : List *fksetcols);
517 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
518 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
519 : int numfks, int16 *pkattnum, int16 *fkattnum,
520 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
521 : int numfkdelsetcols, int16 *fkdelsetcols,
522 : bool old_check_ok, LOCKMODE lockmode,
523 : Oid parentInsTrigger, Oid parentUpdTrigger);
524 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
525 : Relation partitionRel);
526 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
527 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
528 : Relation partRel);
529 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
530 : Constraint *fkconstraint, Oid constraintOid,
531 : Oid indexOid,
532 : Oid parentInsTrigger, Oid parentUpdTrigger,
533 : Oid *insertTrigOid, Oid *updateTrigOid);
534 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
535 : Constraint *fkconstraint, Oid constraintOid,
536 : Oid indexOid,
537 : Oid parentDelTrigger, Oid parentUpdTrigger,
538 : Oid *deleteTrigOid, Oid *updateTrigOid);
539 : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
540 : Oid partRelid,
541 : Oid parentConstrOid, int numfks,
542 : AttrNumber *mapped_conkey, AttrNumber *confkey,
543 : Oid *conpfeqop,
544 : Oid parentInsTrigger,
545 : Oid parentUpdTrigger,
546 : Relation trigrel);
547 : static void GetForeignKeyActionTriggers(Relation trigrel,
548 : Oid conoid, Oid confrelid, Oid conrelid,
549 : Oid *deleteTriggerOid,
550 : Oid *updateTriggerOid);
551 : static void GetForeignKeyCheckTriggers(Relation trigrel,
552 : Oid conoid, Oid confrelid, Oid conrelid,
553 : Oid *insertTriggerOid,
554 : Oid *updateTriggerOid);
555 : static void ATExecDropConstraint(Relation rel, const char *constrName,
556 : DropBehavior behavior,
557 : bool recurse, bool recursing,
558 : bool missing_ok, LOCKMODE lockmode);
559 : static void ATPrepAlterColumnType(List **wqueue,
560 : AlteredTableInfo *tab, Relation rel,
561 : bool recurse, bool recursing,
562 : AlterTableCmd *cmd, LOCKMODE lockmode,
563 : AlterTableUtilityContext *context);
564 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
565 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
566 : AlterTableCmd *cmd, LOCKMODE lockmode);
567 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
568 : Relation rel, AttrNumber attnum, const char *colName);
569 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
570 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
571 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
572 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
573 : LOCKMODE lockmode);
574 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
575 : char *cmd, List **wqueue, LOCKMODE lockmode,
576 : bool rewrite);
577 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
578 : Oid objid, Relation rel, List *domname,
579 : const char *conname);
580 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
581 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
582 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
583 : List *options, LOCKMODE lockmode);
584 : static void change_owner_fix_column_acls(Oid relationOid,
585 : Oid oldOwnerId, Oid newOwnerId);
586 : static void change_owner_recurse_to_sequences(Oid relationOid,
587 : Oid newOwnerId, LOCKMODE lockmode);
588 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
589 : LOCKMODE lockmode);
590 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
591 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
592 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
593 : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
594 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
595 : const char *tablespacename, LOCKMODE lockmode);
596 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
597 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
598 : static void ATExecSetRelOptions(Relation rel, List *defList,
599 : AlterTableType operation,
600 : LOCKMODE lockmode);
601 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
602 : char fires_when, bool skip_system, bool recurse,
603 : LOCKMODE lockmode);
604 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
605 : char fires_when, LOCKMODE lockmode);
606 : static void ATPrepAddInherit(Relation child_rel);
607 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
608 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
609 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
610 : DependencyType deptype);
611 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
612 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
613 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
614 : static void ATExecGenericOptions(Relation rel, List *options);
615 : static void ATExecSetRowSecurity(Relation rel, bool rls);
616 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
617 : static ObjectAddress ATExecSetCompression(Relation rel,
618 : const char *column, Node *newValue, LOCKMODE lockmode);
619 :
620 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
621 : static const char *storage_name(char c);
622 :
623 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
624 : Oid oldRelOid, void *arg);
625 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
626 : Oid oldrelid, void *arg);
627 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
628 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
629 : List **partexprs, Oid *partopclass, Oid *partcollation,
630 : PartitionStrategy strategy);
631 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
632 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
633 : bool expect_detached);
634 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
635 : PartitionCmd *cmd,
636 : AlterTableUtilityContext *context);
637 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
638 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
639 : List *partConstraint,
640 : bool validate_default);
641 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
642 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
643 : static void DropClonedTriggersFromPartition(Oid partitionId);
644 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
645 : Relation rel, RangeVar *name,
646 : bool concurrent);
647 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
648 : bool concurrent, Oid defaultPartOid);
649 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
650 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
651 : RangeVar *name);
652 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
653 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
654 : Relation partitionTbl);
655 : static List *GetParentedForeignKeyRefs(Relation partition);
656 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
657 : static char GetAttributeCompression(Oid atttypid, const char *compression);
658 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
659 :
660 : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
661 : Relation rel, PartitionCmd *cmd,
662 : AlterTableUtilityContext *context);
663 : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
664 : PartitionCmd *cmd, AlterTableUtilityContext *context);
665 :
666 : /* ----------------------------------------------------------------
667 : * DefineRelation
668 : * Creates a new relation.
669 : *
670 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
671 : * The other arguments are used to extend the behavior for other cases:
672 : * relkind: relkind to assign to the new relation
673 : * ownerId: if not InvalidOid, use this as the new relation's owner.
674 : * typaddress: if not null, it's set to the pg_type entry's address.
675 : * queryString: for error reporting
676 : *
677 : * Note that permissions checks are done against current user regardless of
678 : * ownerId. A nonzero ownerId is used when someone is creating a relation
679 : * "on behalf of" someone else, so we still want to see that the current user
680 : * has permissions to do it.
681 : *
682 : * If successful, returns the address of the new relation.
683 : * ----------------------------------------------------------------
684 : */
685 : ObjectAddress
686 52762 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
687 : ObjectAddress *typaddress, const char *queryString)
688 : {
689 : char relname[NAMEDATALEN];
690 : Oid namespaceId;
691 : Oid relationId;
692 : Oid tablespaceId;
693 : Relation rel;
694 : TupleDesc descriptor;
695 : List *inheritOids;
696 : List *old_constraints;
697 : List *rawDefaults;
698 : List *cookedDefaults;
699 : Datum reloptions;
700 : ListCell *listptr;
701 : AttrNumber attnum;
702 : bool partitioned;
703 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
704 : Oid ofTypeId;
705 : ObjectAddress address;
706 : LOCKMODE parentLockmode;
707 52762 : Oid accessMethodId = InvalidOid;
708 :
709 : /*
710 : * Truncate relname to appropriate length (probably a waste of time, as
711 : * parser should have done this already).
712 : */
713 52762 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
714 :
715 : /*
716 : * Check consistency of arguments
717 : */
718 52762 : if (stmt->oncommit != ONCOMMIT_NOOP
719 178 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
720 12 : ereport(ERROR,
721 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
722 : errmsg("ON COMMIT can only be used on temporary tables")));
723 :
724 52750 : if (stmt->partspec != NULL)
725 : {
726 4948 : if (relkind != RELKIND_RELATION)
727 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
728 :
729 4948 : relkind = RELKIND_PARTITIONED_TABLE;
730 4948 : partitioned = true;
731 : }
732 : else
733 47802 : partitioned = false;
734 :
735 : /*
736 : * Look up the namespace in which we are supposed to create the relation,
737 : * check we have permission to create there, lock it against concurrent
738 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
739 : * namespace is selected.
740 : */
741 : namespaceId =
742 52750 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
743 :
744 : /*
745 : * Security check: disallow creating temp tables from security-restricted
746 : * code. This is needed because calling code might not expect untrusted
747 : * tables to appear in pg_temp at the front of its search path.
748 : */
749 52750 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
750 3118 : && InSecurityRestrictedOperation())
751 0 : ereport(ERROR,
752 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
753 : errmsg("cannot create temporary table within security-restricted operation")));
754 :
755 : /*
756 : * Determine the lockmode to use when scanning parents. A self-exclusive
757 : * lock is needed here.
758 : *
759 : * For regular inheritance, if two backends attempt to add children to the
760 : * same parent simultaneously, and that parent has no pre-existing
761 : * children, then both will attempt to update the parent's relhassubclass
762 : * field, leading to a "tuple concurrently updated" error. Also, this
763 : * interlocks against a concurrent ANALYZE on the parent table, which
764 : * might otherwise be attempting to clear the parent's relhassubclass
765 : * field, if its previous children were recently dropped.
766 : *
767 : * If the child table is a partition, then we instead grab an exclusive
768 : * lock on the parent because its partition descriptor will be changed by
769 : * addition of the new partition.
770 : */
771 52750 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
772 : ShareUpdateExclusiveLock);
773 :
774 : /* Determine the list of OIDs of the parents. */
775 52750 : inheritOids = NIL;
776 62938 : foreach(listptr, stmt->inhRelations)
777 : {
778 10188 : RangeVar *rv = (RangeVar *) lfirst(listptr);
779 : Oid parentOid;
780 :
781 10188 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
782 :
783 : /*
784 : * Reject duplications in the list of parents.
785 : */
786 10188 : if (list_member_oid(inheritOids, parentOid))
787 0 : ereport(ERROR,
788 : (errcode(ERRCODE_DUPLICATE_TABLE),
789 : errmsg("relation \"%s\" would be inherited from more than once",
790 : get_rel_name(parentOid))));
791 :
792 10188 : inheritOids = lappend_oid(inheritOids, parentOid);
793 : }
794 :
795 : /*
796 : * Select tablespace to use: an explicitly indicated one, or (in the case
797 : * of a partitioned table) the parent's, if it has one.
798 : */
799 52750 : if (stmt->tablespacename)
800 : {
801 146 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
802 :
803 140 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
804 6 : ereport(ERROR,
805 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
806 : errmsg("cannot specify default tablespace for partitioned relations")));
807 : }
808 52604 : else if (stmt->partbound)
809 : {
810 : Assert(list_length(inheritOids) == 1);
811 8356 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
812 : }
813 : else
814 44248 : tablespaceId = InvalidOid;
815 :
816 : /* still nothing? use the default */
817 52738 : if (!OidIsValid(tablespaceId))
818 52564 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
819 : partitioned);
820 :
821 : /* Check permissions except when using database's default */
822 52732 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
823 : {
824 : AclResult aclresult;
825 :
826 192 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
827 : ACL_CREATE);
828 192 : if (aclresult != ACLCHECK_OK)
829 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
830 6 : get_tablespace_name(tablespaceId));
831 : }
832 :
833 : /* In all cases disallow placing user relations in pg_global */
834 52726 : if (tablespaceId == GLOBALTABLESPACE_OID)
835 18 : ereport(ERROR,
836 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
837 : errmsg("only shared relations can be placed in pg_global tablespace")));
838 :
839 : /* Identify user ID that will own the table */
840 52708 : if (!OidIsValid(ownerId))
841 52472 : ownerId = GetUserId();
842 :
843 : /*
844 : * Parse and validate reloptions, if any.
845 : */
846 52708 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
847 : true, false);
848 :
849 52690 : switch (relkind)
850 : {
851 13384 : case RELKIND_VIEW:
852 13384 : (void) view_reloptions(reloptions, true);
853 13366 : break;
854 4930 : case RELKIND_PARTITIONED_TABLE:
855 4930 : (void) partitioned_table_reloptions(reloptions, true);
856 4924 : break;
857 34376 : default:
858 34376 : (void) heap_reloptions(relkind, reloptions, true);
859 : }
860 :
861 52570 : if (stmt->ofTypename)
862 : {
863 : AclResult aclresult;
864 :
865 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
866 :
867 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
868 86 : if (aclresult != ACLCHECK_OK)
869 6 : aclcheck_error_type(aclresult, ofTypeId);
870 : }
871 : else
872 52484 : ofTypeId = InvalidOid;
873 :
874 : /*
875 : * Look up inheritance ancestors and generate relation schema, including
876 : * inherited attributes. (Note that stmt->tableElts is destructively
877 : * modified by MergeAttributes.)
878 : */
879 52396 : stmt->tableElts =
880 52564 : MergeAttributes(stmt->tableElts, inheritOids,
881 52564 : stmt->relation->relpersistence,
882 52564 : stmt->partbound != NULL,
883 : &old_constraints);
884 :
885 : /*
886 : * Create a tuple descriptor from the relation schema. Note that this
887 : * deals with column names, types, and not-null constraints, but not
888 : * default values or CHECK constraints; we handle those below.
889 : */
890 52396 : descriptor = BuildDescForRelation(stmt->tableElts);
891 :
892 : /*
893 : * Find columns with default values and prepare for insertion of the
894 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
895 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
896 : * while raw defaults go into a list of RawColumnDefault structs that will
897 : * be processed by AddRelationNewConstraints. (We can't deal with raw
898 : * expressions until we can do transformExpr.)
899 : *
900 : * We can set the atthasdef flags now in the tuple descriptor; this just
901 : * saves StoreAttrDefault from having to do an immediate update of the
902 : * pg_attribute rows.
903 : */
904 52348 : rawDefaults = NIL;
905 52348 : cookedDefaults = NIL;
906 52348 : attnum = 0;
907 :
908 266010 : foreach(listptr, stmt->tableElts)
909 : {
910 213662 : ColumnDef *colDef = lfirst(listptr);
911 : Form_pg_attribute attr;
912 :
913 213662 : attnum++;
914 213662 : attr = TupleDescAttr(descriptor, attnum - 1);
915 :
916 213662 : if (colDef->raw_default != NULL)
917 : {
918 : RawColumnDefault *rawEnt;
919 :
920 : Assert(colDef->cooked_default == NULL);
921 :
922 2388 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
923 2388 : rawEnt->attnum = attnum;
924 2388 : rawEnt->raw_default = colDef->raw_default;
925 2388 : rawEnt->missingMode = false;
926 2388 : rawEnt->generated = colDef->generated;
927 2388 : rawDefaults = lappend(rawDefaults, rawEnt);
928 2388 : attr->atthasdef = true;
929 : }
930 211274 : else if (colDef->cooked_default != NULL)
931 : {
932 : CookedConstraint *cooked;
933 :
934 446 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
935 446 : cooked->contype = CONSTR_DEFAULT;
936 446 : cooked->conoid = InvalidOid; /* until created */
937 446 : cooked->name = NULL;
938 446 : cooked->attnum = attnum;
939 446 : cooked->expr = colDef->cooked_default;
940 446 : cooked->skip_validation = false;
941 446 : cooked->is_local = true; /* not used for defaults */
942 446 : cooked->inhcount = 0; /* ditto */
943 446 : cooked->is_no_inherit = false;
944 446 : cookedDefaults = lappend(cookedDefaults, cooked);
945 446 : attr->atthasdef = true;
946 : }
947 : }
948 :
949 : /*
950 : * For relations with table AM and partitioned tables, select access
951 : * method to use: an explicitly indicated one, or (in the case of a
952 : * partitioned table) the parent's, if it has one.
953 : */
954 52348 : if (stmt->accessMethod != NULL)
955 : {
956 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
957 146 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
958 : }
959 52202 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
960 : {
961 36062 : if (stmt->partbound)
962 : {
963 : Assert(list_length(inheritOids) == 1);
964 8198 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
965 : }
966 :
967 36062 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
968 31112 : accessMethodId = get_table_am_oid(default_table_access_method, false);
969 : }
970 :
971 : /*
972 : * Create the relation. Inherited defaults and constraints are passed in
973 : * for immediate handling --- since they don't need parsing, they can be
974 : * stored immediately.
975 : */
976 52330 : relationId = heap_create_with_catalog(relname,
977 : namespaceId,
978 : tablespaceId,
979 : InvalidOid,
980 : InvalidOid,
981 : ofTypeId,
982 : ownerId,
983 : accessMethodId,
984 : descriptor,
985 : list_concat(cookedDefaults,
986 : old_constraints),
987 : relkind,
988 52330 : stmt->relation->relpersistence,
989 : false,
990 : false,
991 : stmt->oncommit,
992 : reloptions,
993 : true,
994 : allowSystemTableMods,
995 : false,
996 : InvalidOid,
997 : typaddress);
998 :
999 : /*
1000 : * We must bump the command counter to make the newly-created relation
1001 : * tuple visible for opening.
1002 : */
1003 52306 : CommandCounterIncrement();
1004 :
1005 : /*
1006 : * Open the new relation and acquire exclusive lock on it. This isn't
1007 : * really necessary for locking out other backends (since they can't see
1008 : * the new rel anyway until we commit), but it keeps the lock manager from
1009 : * complaining about deadlock risks.
1010 : */
1011 52306 : rel = relation_open(relationId, AccessExclusiveLock);
1012 :
1013 : /*
1014 : * Now add any newly specified column default and generation expressions
1015 : * to the new relation. These are passed to us in the form of raw
1016 : * parsetrees; we need to transform them to executable expression trees
1017 : * before they can be added. The most convenient way to do that is to
1018 : * apply the parser's transformExpr routine, but transformExpr doesn't
1019 : * work unless we have a pre-existing relation. So, the transformation has
1020 : * to be postponed to this final step of CREATE TABLE.
1021 : *
1022 : * This needs to be before processing the partitioning clauses because
1023 : * those could refer to generated columns.
1024 : */
1025 52306 : if (rawDefaults)
1026 2024 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1027 : true, true, false, queryString);
1028 :
1029 : /*
1030 : * Make column generation expressions visible for use by partitioning.
1031 : */
1032 52204 : CommandCounterIncrement();
1033 :
1034 : /* Process and store partition bound, if any. */
1035 52204 : if (stmt->partbound)
1036 : {
1037 : PartitionBoundSpec *bound;
1038 : ParseState *pstate;
1039 8302 : Oid parentId = linitial_oid(inheritOids),
1040 : defaultPartOid;
1041 : Relation parent,
1042 8302 : defaultRel = NULL;
1043 : ParseNamespaceItem *nsitem;
1044 :
1045 : /* Already have strong enough lock on the parent */
1046 8302 : parent = table_open(parentId, NoLock);
1047 :
1048 : /*
1049 : * We are going to try to validate the partition bound specification
1050 : * against the partition key of parentRel, so it better have one.
1051 : */
1052 8302 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1053 18 : ereport(ERROR,
1054 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1055 : errmsg("\"%s\" is not partitioned",
1056 : RelationGetRelationName(parent))));
1057 :
1058 : /*
1059 : * The partition constraint of the default partition depends on the
1060 : * partition bounds of every other partition. It is possible that
1061 : * another backend might be about to execute a query on the default
1062 : * partition table, and that the query relies on previously cached
1063 : * default partition constraints. We must therefore take a table lock
1064 : * strong enough to prevent all queries on the default partition from
1065 : * proceeding until we commit and send out a shared-cache-inval notice
1066 : * that will make them update their index lists.
1067 : *
1068 : * Order of locking: The relation being added won't be visible to
1069 : * other backends until it is committed, hence here in
1070 : * DefineRelation() the order of locking the default partition and the
1071 : * relation being added does not matter. But at all other places we
1072 : * need to lock the default relation before we lock the relation being
1073 : * added or removed i.e. we should take the lock in same order at all
1074 : * the places such that lock parent, lock default partition and then
1075 : * lock the partition so as to avoid a deadlock.
1076 : */
1077 : defaultPartOid =
1078 8284 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1079 : true));
1080 8284 : if (OidIsValid(defaultPartOid))
1081 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1082 :
1083 : /* Transform the bound values */
1084 8284 : pstate = make_parsestate(NULL);
1085 8284 : pstate->p_sourcetext = queryString;
1086 :
1087 : /*
1088 : * Add an nsitem containing this relation, so that transformExpr
1089 : * called on partition bound expressions is able to report errors
1090 : * using a proper context.
1091 : */
1092 8284 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1093 : NULL, false, false);
1094 8284 : addNSItemToQuery(pstate, nsitem, false, true, true);
1095 :
1096 8284 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1097 :
1098 : /*
1099 : * Check first that the new partition's bound is valid and does not
1100 : * overlap with any of existing partitions of the parent.
1101 : */
1102 8080 : check_new_partition_bound(relname, parent, bound, pstate);
1103 :
1104 : /*
1105 : * If the default partition exists, its partition constraints will
1106 : * change after the addition of this new partition such that it won't
1107 : * allow any row that qualifies for this new partition. So, check that
1108 : * the existing data in the default partition satisfies the constraint
1109 : * as it will exist after adding this partition.
1110 : */
1111 7966 : if (OidIsValid(defaultPartOid))
1112 : {
1113 348 : check_default_partition_contents(parent, defaultRel, bound);
1114 : /* Keep the lock until commit. */
1115 330 : table_close(defaultRel, NoLock);
1116 : }
1117 :
1118 : /* Update the pg_class entry. */
1119 7948 : StorePartitionBound(rel, parent, bound);
1120 :
1121 7948 : table_close(parent, NoLock);
1122 : }
1123 :
1124 : /* Store inheritance information for new rel. */
1125 51850 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1126 :
1127 : /*
1128 : * Process the partitioning specification (if any) and store the partition
1129 : * key information into the catalog.
1130 : */
1131 51850 : if (partitioned)
1132 : {
1133 : ParseState *pstate;
1134 : int partnatts;
1135 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1136 : Oid partopclass[PARTITION_MAX_KEYS];
1137 : Oid partcollation[PARTITION_MAX_KEYS];
1138 4924 : List *partexprs = NIL;
1139 :
1140 4924 : pstate = make_parsestate(NULL);
1141 4924 : pstate->p_sourcetext = queryString;
1142 :
1143 4924 : partnatts = list_length(stmt->partspec->partParams);
1144 :
1145 : /* Protect fixed-size arrays here and in executor */
1146 4924 : if (partnatts > PARTITION_MAX_KEYS)
1147 0 : ereport(ERROR,
1148 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1149 : errmsg("cannot partition using more than %d columns",
1150 : PARTITION_MAX_KEYS)));
1151 :
1152 : /*
1153 : * We need to transform the raw parsetrees corresponding to partition
1154 : * expressions into executable expression trees. Like column defaults
1155 : * and CHECK constraints, we could not have done the transformation
1156 : * earlier.
1157 : */
1158 4924 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1159 :
1160 4894 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1161 : partattrs, &partexprs, partopclass,
1162 4894 : partcollation, stmt->partspec->strategy);
1163 :
1164 4810 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1165 : partexprs,
1166 : partopclass, partcollation);
1167 :
1168 : /* make it all visible */
1169 4810 : CommandCounterIncrement();
1170 : }
1171 :
1172 : /*
1173 : * If we're creating a partition, create now all the indexes, triggers,
1174 : * FKs defined in the parent.
1175 : *
1176 : * We can't do it earlier, because DefineIndex wants to know the partition
1177 : * key which we just stored.
1178 : */
1179 51736 : if (stmt->partbound)
1180 : {
1181 7942 : Oid parentId = linitial_oid(inheritOids);
1182 : Relation parent;
1183 : List *idxlist;
1184 : ListCell *cell;
1185 :
1186 : /* Already have strong enough lock on the parent */
1187 7942 : parent = table_open(parentId, NoLock);
1188 7942 : idxlist = RelationGetIndexList(parent);
1189 :
1190 : /*
1191 : * For each index in the parent table, create one in the partition
1192 : */
1193 9346 : foreach(cell, idxlist)
1194 : {
1195 1422 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1196 : AttrMap *attmap;
1197 : IndexStmt *idxstmt;
1198 : Oid constraintOid;
1199 :
1200 1422 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1201 : {
1202 36 : if (idxRel->rd_index->indisunique)
1203 12 : ereport(ERROR,
1204 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1205 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1206 : RelationGetRelationName(parent)),
1207 : errdetail("Table \"%s\" contains indexes that are unique.",
1208 : RelationGetRelationName(parent))));
1209 : else
1210 : {
1211 24 : index_close(idxRel, AccessShareLock);
1212 24 : continue;
1213 : }
1214 : }
1215 :
1216 1386 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1217 : RelationGetDescr(parent),
1218 : false);
1219 : idxstmt =
1220 1386 : generateClonedIndexStmt(NULL, idxRel,
1221 : attmap, &constraintOid);
1222 1386 : DefineIndex(RelationGetRelid(rel),
1223 : idxstmt,
1224 : InvalidOid,
1225 : RelationGetRelid(idxRel),
1226 : constraintOid,
1227 : -1,
1228 : false, false, false, false, false);
1229 :
1230 1380 : index_close(idxRel, AccessShareLock);
1231 : }
1232 :
1233 7924 : list_free(idxlist);
1234 :
1235 : /*
1236 : * If there are any row-level triggers, clone them to the new
1237 : * partition.
1238 : */
1239 7924 : if (parent->trigdesc != NULL)
1240 414 : CloneRowTriggersToPartition(parent, rel);
1241 :
1242 : /*
1243 : * And foreign keys too. Note that because we're freshly creating the
1244 : * table, there is no need to verify these new constraints.
1245 : */
1246 7924 : CloneForeignKeyConstraints(NULL, parent, rel);
1247 :
1248 7924 : table_close(parent, NoLock);
1249 : }
1250 :
1251 : /*
1252 : * Now add any newly specified CHECK constraints to the new relation. Same
1253 : * as for defaults above, but these need to come after partitioning is set
1254 : * up.
1255 : */
1256 51718 : if (stmt->constraints)
1257 634 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1258 : true, true, false, queryString);
1259 :
1260 51700 : ObjectAddressSet(address, RelationRelationId, relationId);
1261 :
1262 : /*
1263 : * Clean up. We keep lock on new relation (although it shouldn't be
1264 : * visible to anyone else anyway, until commit).
1265 : */
1266 51700 : relation_close(rel, NoLock);
1267 :
1268 51700 : return address;
1269 : }
1270 :
1271 : /*
1272 : * BuildDescForRelation
1273 : *
1274 : * Given a list of ColumnDef nodes, build a TupleDesc.
1275 : *
1276 : * Note: This is only for the limited purpose of table and view creation. Not
1277 : * everything is filled in. A real tuple descriptor should be obtained from
1278 : * the relcache.
1279 : */
1280 : TupleDesc
1281 55038 : BuildDescForRelation(const List *columns)
1282 : {
1283 : int natts;
1284 : AttrNumber attnum;
1285 : ListCell *l;
1286 : TupleDesc desc;
1287 : char *attname;
1288 : Oid atttypid;
1289 : int32 atttypmod;
1290 : Oid attcollation;
1291 : int attdim;
1292 :
1293 : /*
1294 : * allocate a new tuple descriptor
1295 : */
1296 55038 : natts = list_length(columns);
1297 55038 : desc = CreateTemplateTupleDesc(natts);
1298 :
1299 55038 : attnum = 0;
1300 :
1301 271606 : foreach(l, columns)
1302 : {
1303 216628 : ColumnDef *entry = lfirst(l);
1304 : AclResult aclresult;
1305 : Form_pg_attribute att;
1306 :
1307 : /*
1308 : * for each entry in the list, get the name and type information from
1309 : * the list and have TupleDescInitEntry fill in the attribute
1310 : * information we need.
1311 : */
1312 216628 : attnum++;
1313 :
1314 216628 : attname = entry->colname;
1315 216628 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1316 :
1317 216628 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1318 216628 : if (aclresult != ACLCHECK_OK)
1319 42 : aclcheck_error_type(aclresult, atttypid);
1320 :
1321 216586 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1322 216586 : attdim = list_length(entry->typeName->arrayBounds);
1323 216586 : if (attdim > PG_INT16_MAX)
1324 0 : ereport(ERROR,
1325 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1326 : errmsg("too many array dimensions"));
1327 :
1328 216586 : if (entry->typeName->setof)
1329 0 : ereport(ERROR,
1330 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1331 : errmsg("column \"%s\" cannot be declared SETOF",
1332 : attname)));
1333 :
1334 216586 : TupleDescInitEntry(desc, attnum, attname,
1335 : atttypid, atttypmod, attdim);
1336 216586 : att = TupleDescAttr(desc, attnum - 1);
1337 :
1338 : /* Override TupleDescInitEntry's settings as requested */
1339 216586 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1340 :
1341 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1342 216586 : att->attnotnull = entry->is_not_null;
1343 216586 : att->attislocal = entry->is_local;
1344 216586 : att->attinhcount = entry->inhcount;
1345 216586 : att->attidentity = entry->identity;
1346 216586 : att->attgenerated = entry->generated;
1347 216586 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1348 216574 : if (entry->storage)
1349 22588 : att->attstorage = entry->storage;
1350 193986 : else if (entry->storage_name)
1351 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1352 : }
1353 :
1354 54978 : return desc;
1355 : }
1356 :
1357 : /*
1358 : * Emit the right error or warning message for a "DROP" command issued on a
1359 : * non-existent relation
1360 : */
1361 : static void
1362 1084 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1363 : {
1364 : const struct dropmsgstrings *rentry;
1365 :
1366 1204 : if (rel->schemaname != NULL &&
1367 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1368 : {
1369 42 : if (!missing_ok)
1370 : {
1371 0 : ereport(ERROR,
1372 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1373 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1374 : }
1375 : else
1376 : {
1377 42 : ereport(NOTICE,
1378 : (errmsg("schema \"%s\" does not exist, skipping",
1379 : rel->schemaname)));
1380 : }
1381 42 : return;
1382 : }
1383 :
1384 1362 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1385 : {
1386 1362 : if (rentry->kind == rightkind)
1387 : {
1388 1042 : if (!missing_ok)
1389 : {
1390 132 : ereport(ERROR,
1391 : (errcode(rentry->nonexistent_code),
1392 : errmsg(rentry->nonexistent_msg, rel->relname)));
1393 : }
1394 : else
1395 : {
1396 910 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1397 910 : break;
1398 : }
1399 : }
1400 : }
1401 :
1402 : Assert(rentry->kind != '\0'); /* Should be impossible */
1403 : }
1404 :
1405 : /*
1406 : * Emit the right error message for a "DROP" command issued on a
1407 : * relation of the wrong type
1408 : */
1409 : static void
1410 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1411 : {
1412 : const struct dropmsgstrings *rentry;
1413 : const struct dropmsgstrings *wentry;
1414 :
1415 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1416 0 : if (rentry->kind == rightkind)
1417 0 : break;
1418 : Assert(rentry->kind != '\0');
1419 :
1420 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1421 0 : if (wentry->kind == wrongkind)
1422 0 : break;
1423 : /* wrongkind could be something we don't have in our table... */
1424 :
1425 0 : ereport(ERROR,
1426 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1427 : errmsg(rentry->nota_msg, relname),
1428 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1429 : }
1430 :
1431 : /*
1432 : * RemoveRelations
1433 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1434 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1435 : */
1436 : void
1437 16074 : RemoveRelations(DropStmt *drop)
1438 : {
1439 : ObjectAddresses *objects;
1440 : char relkind;
1441 : ListCell *cell;
1442 16074 : int flags = 0;
1443 16074 : LOCKMODE lockmode = AccessExclusiveLock;
1444 :
1445 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1446 16074 : if (drop->concurrent)
1447 : {
1448 : /*
1449 : * Note that for temporary relations this lock may get upgraded later
1450 : * on, but as no other session can access a temporary relation, this
1451 : * is actually fine.
1452 : */
1453 130 : lockmode = ShareUpdateExclusiveLock;
1454 : Assert(drop->removeType == OBJECT_INDEX);
1455 130 : if (list_length(drop->objects) != 1)
1456 6 : ereport(ERROR,
1457 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1458 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1459 124 : if (drop->behavior == DROP_CASCADE)
1460 0 : ereport(ERROR,
1461 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1462 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1463 : }
1464 :
1465 : /*
1466 : * First we identify all the relations, then we delete them in a single
1467 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1468 : * RESTRICT errors if one of the relations depends on another.
1469 : */
1470 :
1471 : /* Determine required relkind */
1472 16068 : switch (drop->removeType)
1473 : {
1474 13970 : case OBJECT_TABLE:
1475 13970 : relkind = RELKIND_RELATION;
1476 13970 : break;
1477 :
1478 770 : case OBJECT_INDEX:
1479 770 : relkind = RELKIND_INDEX;
1480 770 : break;
1481 :
1482 172 : case OBJECT_SEQUENCE:
1483 172 : relkind = RELKIND_SEQUENCE;
1484 172 : break;
1485 :
1486 886 : case OBJECT_VIEW:
1487 886 : relkind = RELKIND_VIEW;
1488 886 : break;
1489 :
1490 120 : case OBJECT_MATVIEW:
1491 120 : relkind = RELKIND_MATVIEW;
1492 120 : break;
1493 :
1494 150 : case OBJECT_FOREIGN_TABLE:
1495 150 : relkind = RELKIND_FOREIGN_TABLE;
1496 150 : break;
1497 :
1498 0 : default:
1499 0 : elog(ERROR, "unrecognized drop object type: %d",
1500 : (int) drop->removeType);
1501 : relkind = 0; /* keep compiler quiet */
1502 : break;
1503 : }
1504 :
1505 : /* Lock and validate each relation; build a list of object addresses */
1506 16068 : objects = new_object_addresses();
1507 :
1508 35524 : foreach(cell, drop->objects)
1509 : {
1510 19614 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1511 : Oid relOid;
1512 : ObjectAddress obj;
1513 : struct DropRelationCallbackState state;
1514 :
1515 : /*
1516 : * These next few steps are a great deal like relation_openrv, but we
1517 : * don't bother building a relcache entry since we don't need it.
1518 : *
1519 : * Check for shared-cache-inval messages before trying to access the
1520 : * relation. This is needed to cover the case where the name
1521 : * identifies a rel that has been dropped and recreated since the
1522 : * start of our transaction: if we don't flush the old syscache entry,
1523 : * then we'll latch onto that entry and suffer an error later.
1524 : */
1525 19614 : AcceptInvalidationMessages();
1526 :
1527 : /* Look up the appropriate relation using namespace search. */
1528 19614 : state.expected_relkind = relkind;
1529 39228 : state.heap_lockmode = drop->concurrent ?
1530 19614 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1531 : /* We must initialize these fields to show that no locks are held: */
1532 19614 : state.heapOid = InvalidOid;
1533 19614 : state.partParentOid = InvalidOid;
1534 :
1535 19614 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1536 : RangeVarCallbackForDropRelation,
1537 : (void *) &state);
1538 :
1539 : /* Not there? */
1540 19594 : if (!OidIsValid(relOid))
1541 : {
1542 1084 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1543 952 : continue;
1544 : }
1545 :
1546 : /*
1547 : * Decide if concurrent mode needs to be used here or not. The
1548 : * callback retrieved the rel's persistence for us.
1549 : */
1550 18510 : if (drop->concurrent &&
1551 118 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1552 : {
1553 : Assert(list_length(drop->objects) == 1 &&
1554 : drop->removeType == OBJECT_INDEX);
1555 100 : flags |= PERFORM_DELETION_CONCURRENTLY;
1556 : }
1557 :
1558 : /*
1559 : * Concurrent index drop cannot be used with partitioned indexes,
1560 : * either.
1561 : */
1562 18510 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1563 100 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1564 6 : ereport(ERROR,
1565 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1566 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1567 : rel->relname)));
1568 :
1569 : /*
1570 : * If we're told to drop a partitioned index, we must acquire lock on
1571 : * all the children of its parent partitioned table before proceeding.
1572 : * Otherwise we'd try to lock the child index partitions before their
1573 : * tables, leading to potential deadlock against other sessions that
1574 : * will lock those objects in the other order.
1575 : */
1576 18504 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1577 70 : (void) find_all_inheritors(state.heapOid,
1578 : state.heap_lockmode,
1579 : NULL);
1580 :
1581 : /* OK, we're ready to delete this one */
1582 18504 : obj.classId = RelationRelationId;
1583 18504 : obj.objectId = relOid;
1584 18504 : obj.objectSubId = 0;
1585 :
1586 18504 : add_exact_object_address(&obj, objects);
1587 : }
1588 :
1589 15910 : performMultipleDeletions(objects, drop->behavior, flags);
1590 :
1591 15774 : free_object_addresses(objects);
1592 15774 : }
1593 :
1594 : /*
1595 : * Before acquiring a table lock, check whether we have sufficient rights.
1596 : * In the case of DROP INDEX, also try to lock the table before the index.
1597 : * Also, if the table to be dropped is a partition, we try to lock the parent
1598 : * first.
1599 : */
1600 : static void
1601 19774 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1602 : void *arg)
1603 : {
1604 : HeapTuple tuple;
1605 : struct DropRelationCallbackState *state;
1606 : char expected_relkind;
1607 : bool is_partition;
1608 : Form_pg_class classform;
1609 : LOCKMODE heap_lockmode;
1610 19774 : bool invalid_system_index = false;
1611 :
1612 19774 : state = (struct DropRelationCallbackState *) arg;
1613 19774 : heap_lockmode = state->heap_lockmode;
1614 :
1615 : /*
1616 : * If we previously locked some other index's heap, and the name we're
1617 : * looking up no longer refers to that relation, release the now-useless
1618 : * lock.
1619 : */
1620 19774 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1621 : {
1622 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1623 0 : state->heapOid = InvalidOid;
1624 : }
1625 :
1626 : /*
1627 : * Similarly, if we previously locked some other partition's heap, and the
1628 : * name we're looking up no longer refers to that relation, release the
1629 : * now-useless lock.
1630 : */
1631 19774 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1632 : {
1633 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1634 0 : state->partParentOid = InvalidOid;
1635 : }
1636 :
1637 : /* Didn't find a relation, so no need for locking or permission checks. */
1638 19774 : if (!OidIsValid(relOid))
1639 1088 : return;
1640 :
1641 18686 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1642 18686 : if (!HeapTupleIsValid(tuple))
1643 0 : return; /* concurrently dropped, so nothing to do */
1644 18686 : classform = (Form_pg_class) GETSTRUCT(tuple);
1645 18686 : is_partition = classform->relispartition;
1646 :
1647 : /* Pass back some data to save lookups in RemoveRelations */
1648 18686 : state->actual_relkind = classform->relkind;
1649 18686 : state->actual_relpersistence = classform->relpersistence;
1650 :
1651 : /*
1652 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1653 : * but RemoveRelations() can only pass one relkind for a given relation.
1654 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1655 : * That means we must be careful before giving the wrong type error when
1656 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1657 : * exists with indexes.
1658 : */
1659 18686 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1660 3006 : expected_relkind = RELKIND_RELATION;
1661 15680 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1662 76 : expected_relkind = RELKIND_INDEX;
1663 : else
1664 15604 : expected_relkind = classform->relkind;
1665 :
1666 18686 : if (state->expected_relkind != expected_relkind)
1667 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1668 0 : state->expected_relkind);
1669 :
1670 : /* Allow DROP to either table owner or schema owner */
1671 18686 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1672 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1673 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1674 18 : get_relkind_objtype(classform->relkind),
1675 18 : rel->relname);
1676 :
1677 : /*
1678 : * Check the case of a system index that might have been invalidated by a
1679 : * failed concurrent process and allow its drop. For the time being, this
1680 : * only concerns indexes of toast relations that became invalid during a
1681 : * REINDEX CONCURRENTLY process.
1682 : */
1683 18668 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1684 : {
1685 : HeapTuple locTuple;
1686 : Form_pg_index indexform;
1687 : bool indisvalid;
1688 :
1689 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1690 0 : if (!HeapTupleIsValid(locTuple))
1691 : {
1692 0 : ReleaseSysCache(tuple);
1693 0 : return;
1694 : }
1695 :
1696 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1697 0 : indisvalid = indexform->indisvalid;
1698 0 : ReleaseSysCache(locTuple);
1699 :
1700 : /* Mark object as being an invalid index of system catalogs */
1701 0 : if (!indisvalid)
1702 0 : invalid_system_index = true;
1703 : }
1704 :
1705 : /* In the case of an invalid index, it is fine to bypass this check */
1706 18668 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1707 2 : ereport(ERROR,
1708 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1709 : errmsg("permission denied: \"%s\" is a system catalog",
1710 : rel->relname)));
1711 :
1712 18666 : ReleaseSysCache(tuple);
1713 :
1714 : /*
1715 : * In DROP INDEX, attempt to acquire lock on the parent table before
1716 : * locking the index. index_drop() will need this anyway, and since
1717 : * regular queries lock tables before their indexes, we risk deadlock if
1718 : * we do it the other way around. No error if we don't find a pg_index
1719 : * entry, though --- the relation may have been dropped. Note that this
1720 : * code will execute for either plain or partitioned indexes.
1721 : */
1722 18666 : if (expected_relkind == RELKIND_INDEX &&
1723 : relOid != oldRelOid)
1724 : {
1725 746 : state->heapOid = IndexGetRelation(relOid, true);
1726 746 : if (OidIsValid(state->heapOid))
1727 746 : LockRelationOid(state->heapOid, heap_lockmode);
1728 : }
1729 :
1730 : /*
1731 : * Similarly, if the relation is a partition, we must acquire lock on its
1732 : * parent before locking the partition. That's because queries lock the
1733 : * parent before its partitions, so we risk deadlock if we do it the other
1734 : * way around.
1735 : */
1736 18666 : if (is_partition && relOid != oldRelOid)
1737 : {
1738 600 : state->partParentOid = get_partition_parent(relOid, true);
1739 600 : if (OidIsValid(state->partParentOid))
1740 600 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1741 : }
1742 : }
1743 :
1744 : /*
1745 : * ExecuteTruncate
1746 : * Executes a TRUNCATE command.
1747 : *
1748 : * This is a multi-relation truncate. We first open and grab exclusive
1749 : * lock on all relations involved, checking permissions and otherwise
1750 : * verifying that the relation is OK for truncation. Note that if relations
1751 : * are foreign tables, at this stage, we have not yet checked that their
1752 : * foreign data in external data sources are OK for truncation. These are
1753 : * checked when foreign data are actually truncated later. In CASCADE mode,
1754 : * relations having FK references to the targeted relations are automatically
1755 : * added to the group; in RESTRICT mode, we check that all FK references are
1756 : * internal to the group that's being truncated. Finally all the relations
1757 : * are truncated and reindexed.
1758 : */
1759 : void
1760 1368 : ExecuteTruncate(TruncateStmt *stmt)
1761 : {
1762 1368 : List *rels = NIL;
1763 1368 : List *relids = NIL;
1764 1368 : List *relids_logged = NIL;
1765 : ListCell *cell;
1766 :
1767 : /*
1768 : * Open, exclusive-lock, and check all the explicitly-specified relations
1769 : */
1770 2890 : foreach(cell, stmt->relations)
1771 : {
1772 1570 : RangeVar *rv = lfirst(cell);
1773 : Relation rel;
1774 1570 : bool recurse = rv->inh;
1775 : Oid myrelid;
1776 1570 : LOCKMODE lockmode = AccessExclusiveLock;
1777 :
1778 1570 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1779 : 0, RangeVarCallbackForTruncate,
1780 : NULL);
1781 :
1782 : /* don't throw error for "TRUNCATE foo, foo" */
1783 1534 : if (list_member_oid(relids, myrelid))
1784 2 : continue;
1785 :
1786 : /* open the relation, we already hold a lock on it */
1787 1532 : rel = table_open(myrelid, NoLock);
1788 :
1789 : /*
1790 : * RangeVarGetRelidExtended() has done most checks with its callback,
1791 : * but other checks with the now-opened Relation remain.
1792 : */
1793 1532 : truncate_check_activity(rel);
1794 :
1795 1532 : rels = lappend(rels, rel);
1796 1532 : relids = lappend_oid(relids, myrelid);
1797 :
1798 : /* Log this relation only if needed for logical decoding */
1799 1532 : if (RelationIsLogicallyLogged(rel))
1800 66 : relids_logged = lappend_oid(relids_logged, myrelid);
1801 :
1802 1532 : if (recurse)
1803 : {
1804 : ListCell *child;
1805 : List *children;
1806 :
1807 1470 : children = find_all_inheritors(myrelid, lockmode, NULL);
1808 :
1809 4576 : foreach(child, children)
1810 : {
1811 3106 : Oid childrelid = lfirst_oid(child);
1812 :
1813 3106 : if (list_member_oid(relids, childrelid))
1814 1470 : continue;
1815 :
1816 : /* find_all_inheritors already got lock */
1817 1636 : rel = table_open(childrelid, NoLock);
1818 :
1819 : /*
1820 : * It is possible that the parent table has children that are
1821 : * temp tables of other backends. We cannot safely access
1822 : * such tables (because of buffering issues), and the best
1823 : * thing to do is to silently ignore them. Note that this
1824 : * check is the same as one of the checks done in
1825 : * truncate_check_activity() called below, still it is kept
1826 : * here for simplicity.
1827 : */
1828 1636 : if (RELATION_IS_OTHER_TEMP(rel))
1829 : {
1830 8 : table_close(rel, lockmode);
1831 8 : continue;
1832 : }
1833 :
1834 : /*
1835 : * Inherited TRUNCATE commands perform access permission
1836 : * checks on the parent table only. So we skip checking the
1837 : * children's permissions and don't call
1838 : * truncate_check_perms() here.
1839 : */
1840 1628 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1841 1628 : truncate_check_activity(rel);
1842 :
1843 1628 : rels = lappend(rels, rel);
1844 1628 : relids = lappend_oid(relids, childrelid);
1845 :
1846 : /* Log this relation only if needed for logical decoding */
1847 1628 : if (RelationIsLogicallyLogged(rel))
1848 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1849 : }
1850 : }
1851 62 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1852 12 : ereport(ERROR,
1853 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1854 : errmsg("cannot truncate only a partitioned table"),
1855 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1856 : }
1857 :
1858 1320 : ExecuteTruncateGuts(rels, relids, relids_logged,
1859 1320 : stmt->behavior, stmt->restart_seqs, false);
1860 :
1861 : /* And close the rels */
1862 4232 : foreach(cell, rels)
1863 : {
1864 2994 : Relation rel = (Relation) lfirst(cell);
1865 :
1866 2994 : table_close(rel, NoLock);
1867 : }
1868 1238 : }
1869 :
1870 : /*
1871 : * ExecuteTruncateGuts
1872 : *
1873 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1874 : * command (see above) as well as replication subscribers that execute a
1875 : * replicated TRUNCATE action.
1876 : *
1877 : * explicit_rels is the list of Relations to truncate that the command
1878 : * specified. relids is the list of Oids corresponding to explicit_rels.
1879 : * relids_logged is the list of Oids (a subset of relids) that require
1880 : * WAL-logging. This is all a bit redundant, but the existing callers have
1881 : * this information handy in this form.
1882 : */
1883 : void
1884 1358 : ExecuteTruncateGuts(List *explicit_rels,
1885 : List *relids,
1886 : List *relids_logged,
1887 : DropBehavior behavior, bool restart_seqs,
1888 : bool run_as_table_owner)
1889 : {
1890 : List *rels;
1891 1358 : List *seq_relids = NIL;
1892 1358 : HTAB *ft_htab = NULL;
1893 : EState *estate;
1894 : ResultRelInfo *resultRelInfos;
1895 : ResultRelInfo *resultRelInfo;
1896 : SubTransactionId mySubid;
1897 : ListCell *cell;
1898 : Oid *logrelids;
1899 :
1900 : /*
1901 : * Check the explicitly-specified relations.
1902 : *
1903 : * In CASCADE mode, suck in all referencing relations as well. This
1904 : * requires multiple iterations to find indirectly-dependent relations. At
1905 : * each phase, we need to exclusive-lock new rels before looking for their
1906 : * dependencies, else we might miss something. Also, we check each rel as
1907 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1908 : * time on a rel we have no permissions for.
1909 : */
1910 1358 : rels = list_copy(explicit_rels);
1911 1358 : if (behavior == DROP_CASCADE)
1912 : {
1913 : for (;;)
1914 40 : {
1915 : List *newrelids;
1916 :
1917 80 : newrelids = heap_truncate_find_FKs(relids);
1918 80 : if (newrelids == NIL)
1919 40 : break; /* nothing else to add */
1920 :
1921 134 : foreach(cell, newrelids)
1922 : {
1923 94 : Oid relid = lfirst_oid(cell);
1924 : Relation rel;
1925 :
1926 94 : rel = table_open(relid, AccessExclusiveLock);
1927 94 : ereport(NOTICE,
1928 : (errmsg("truncate cascades to table \"%s\"",
1929 : RelationGetRelationName(rel))));
1930 94 : truncate_check_rel(relid, rel->rd_rel);
1931 94 : truncate_check_perms(relid, rel->rd_rel);
1932 94 : truncate_check_activity(rel);
1933 94 : rels = lappend(rels, rel);
1934 94 : relids = lappend_oid(relids, relid);
1935 :
1936 : /* Log this relation only if needed for logical decoding */
1937 94 : if (RelationIsLogicallyLogged(rel))
1938 0 : relids_logged = lappend_oid(relids_logged, relid);
1939 : }
1940 : }
1941 : }
1942 :
1943 : /*
1944 : * Check foreign key references. In CASCADE mode, this should be
1945 : * unnecessary since we just pulled in all the references; but as a
1946 : * cross-check, do it anyway if in an Assert-enabled build.
1947 : */
1948 : #ifdef USE_ASSERT_CHECKING
1949 : heap_truncate_check_FKs(rels, false);
1950 : #else
1951 1358 : if (behavior == DROP_RESTRICT)
1952 1318 : heap_truncate_check_FKs(rels, false);
1953 : #endif
1954 :
1955 : /*
1956 : * If we are asked to restart sequences, find all the sequences, lock them
1957 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1958 : * We want to do this early since it's pointless to do all the truncation
1959 : * work only to fail on sequence permissions.
1960 : */
1961 1284 : if (restart_seqs)
1962 : {
1963 52 : foreach(cell, rels)
1964 : {
1965 26 : Relation rel = (Relation) lfirst(cell);
1966 26 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
1967 : ListCell *seqcell;
1968 :
1969 62 : foreach(seqcell, seqlist)
1970 : {
1971 36 : Oid seq_relid = lfirst_oid(seqcell);
1972 : Relation seq_rel;
1973 :
1974 36 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1975 :
1976 : /* This check must match AlterSequence! */
1977 36 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
1978 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
1979 0 : RelationGetRelationName(seq_rel));
1980 :
1981 36 : seq_relids = lappend_oid(seq_relids, seq_relid);
1982 :
1983 36 : relation_close(seq_rel, NoLock);
1984 : }
1985 : }
1986 : }
1987 :
1988 : /* Prepare to catch AFTER triggers. */
1989 1284 : AfterTriggerBeginQuery();
1990 :
1991 : /*
1992 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1993 : * each relation. We don't need to call ExecOpenIndices, though.
1994 : *
1995 : * We put the ResultRelInfos in the es_opened_result_relations list, even
1996 : * though we don't have a range table and don't populate the
1997 : * es_result_relations array. That's a bit bogus, but it's enough to make
1998 : * ExecGetTriggerResultRel() find them.
1999 : */
2000 1284 : estate = CreateExecutorState();
2001 : resultRelInfos = (ResultRelInfo *)
2002 1284 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2003 1284 : resultRelInfo = resultRelInfos;
2004 4450 : foreach(cell, rels)
2005 : {
2006 3166 : Relation rel = (Relation) lfirst(cell);
2007 :
2008 3166 : InitResultRelInfo(resultRelInfo,
2009 : rel,
2010 : 0, /* dummy rangetable index */
2011 : NULL,
2012 : 0);
2013 3166 : estate->es_opened_result_relations =
2014 3166 : lappend(estate->es_opened_result_relations, resultRelInfo);
2015 3166 : resultRelInfo++;
2016 : }
2017 :
2018 : /*
2019 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2020 : * truncating (this is because one of them might throw an error). Also, if
2021 : * we were to allow them to prevent statement execution, that would need
2022 : * to be handled here.
2023 : */
2024 1284 : resultRelInfo = resultRelInfos;
2025 4450 : foreach(cell, rels)
2026 : {
2027 : UserContext ucxt;
2028 :
2029 3166 : if (run_as_table_owner)
2030 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2031 : &ucxt);
2032 3166 : ExecBSTruncateTriggers(estate, resultRelInfo);
2033 3166 : if (run_as_table_owner)
2034 70 : RestoreUserContext(&ucxt);
2035 3166 : resultRelInfo++;
2036 : }
2037 :
2038 : /*
2039 : * OK, truncate each table.
2040 : */
2041 1284 : mySubid = GetCurrentSubTransactionId();
2042 :
2043 4450 : foreach(cell, rels)
2044 : {
2045 3166 : Relation rel = (Relation) lfirst(cell);
2046 :
2047 : /* Skip partitioned tables as there is nothing to do */
2048 3166 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2049 644 : continue;
2050 :
2051 : /*
2052 : * Build the lists of foreign tables belonging to each foreign server
2053 : * and pass each list to the foreign data wrapper's callback function,
2054 : * so that each server can truncate its all foreign tables in bulk.
2055 : * Each list is saved as a single entry in a hash table that uses the
2056 : * server OID as lookup key.
2057 : */
2058 2522 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2059 : {
2060 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2061 : bool found;
2062 : ForeignTruncateInfo *ft_info;
2063 :
2064 : /* First time through, initialize hashtable for foreign tables */
2065 34 : if (!ft_htab)
2066 : {
2067 : HASHCTL hctl;
2068 :
2069 30 : memset(&hctl, 0, sizeof(HASHCTL));
2070 30 : hctl.keysize = sizeof(Oid);
2071 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2072 30 : hctl.hcxt = CurrentMemoryContext;
2073 :
2074 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2075 : 32, /* start small and extend */
2076 : &hctl,
2077 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2078 : }
2079 :
2080 : /* Find or create cached entry for the foreign table */
2081 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2082 34 : if (!found)
2083 30 : ft_info->rels = NIL;
2084 :
2085 : /*
2086 : * Save the foreign table in the entry of the server that the
2087 : * foreign table belongs to.
2088 : */
2089 34 : ft_info->rels = lappend(ft_info->rels, rel);
2090 34 : continue;
2091 : }
2092 :
2093 : /*
2094 : * Normally, we need a transaction-safe truncation here. However, if
2095 : * the table was either created in the current (sub)transaction or has
2096 : * a new relfilenumber in the current (sub)transaction, then we can
2097 : * just truncate it in-place, because a rollback would cause the whole
2098 : * table or the current physical file to be thrown away anyway.
2099 : */
2100 2488 : if (rel->rd_createSubid == mySubid ||
2101 2462 : rel->rd_newRelfilelocatorSubid == mySubid)
2102 : {
2103 : /* Immediate, non-rollbackable truncation is OK */
2104 90 : heap_truncate_one_rel(rel);
2105 : }
2106 : else
2107 : {
2108 : Oid heap_relid;
2109 : Oid toast_relid;
2110 2398 : ReindexParams reindex_params = {0};
2111 :
2112 : /*
2113 : * This effectively deletes all rows in the table, and may be done
2114 : * in a serializable transaction. In that case we must record a
2115 : * rw-conflict in to this transaction from each transaction
2116 : * holding a predicate lock on the table.
2117 : */
2118 2398 : CheckTableForSerializableConflictIn(rel);
2119 :
2120 : /*
2121 : * Need the full transaction-safe pushups.
2122 : *
2123 : * Create a new empty storage file for the relation, and assign it
2124 : * as the relfilenumber value. The old storage file is scheduled
2125 : * for deletion at commit.
2126 : */
2127 2398 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2128 :
2129 2398 : heap_relid = RelationGetRelid(rel);
2130 :
2131 : /*
2132 : * The same for the toast table, if any.
2133 : */
2134 2398 : toast_relid = rel->rd_rel->reltoastrelid;
2135 2398 : if (OidIsValid(toast_relid))
2136 : {
2137 1404 : Relation toastrel = relation_open(toast_relid,
2138 : AccessExclusiveLock);
2139 :
2140 1404 : RelationSetNewRelfilenumber(toastrel,
2141 1404 : toastrel->rd_rel->relpersistence);
2142 1404 : table_close(toastrel, NoLock);
2143 : }
2144 :
2145 : /*
2146 : * Reconstruct the indexes to match, and we're done.
2147 : */
2148 2398 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2149 : &reindex_params);
2150 : }
2151 :
2152 2488 : pgstat_count_truncate(rel);
2153 : }
2154 :
2155 : /* Now go through the hash table, and truncate foreign tables */
2156 1284 : if (ft_htab)
2157 : {
2158 : ForeignTruncateInfo *ft_info;
2159 : HASH_SEQ_STATUS seq;
2160 :
2161 30 : hash_seq_init(&seq, ft_htab);
2162 :
2163 30 : PG_TRY();
2164 : {
2165 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2166 : {
2167 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2168 :
2169 : /* truncate_check_rel() has checked that already */
2170 : Assert(routine->ExecForeignTruncate != NULL);
2171 :
2172 30 : routine->ExecForeignTruncate(ft_info->rels,
2173 : behavior,
2174 : restart_seqs);
2175 : }
2176 : }
2177 8 : PG_FINALLY();
2178 : {
2179 30 : hash_destroy(ft_htab);
2180 : }
2181 30 : PG_END_TRY();
2182 : }
2183 :
2184 : /*
2185 : * Restart owned sequences if we were asked to.
2186 : */
2187 1312 : foreach(cell, seq_relids)
2188 : {
2189 36 : Oid seq_relid = lfirst_oid(cell);
2190 :
2191 36 : ResetSequence(seq_relid);
2192 : }
2193 :
2194 : /*
2195 : * Write a WAL record to allow this set of actions to be logically
2196 : * decoded.
2197 : *
2198 : * Assemble an array of relids so we can write a single WAL record for the
2199 : * whole action.
2200 : */
2201 1276 : if (relids_logged != NIL)
2202 : {
2203 : xl_heap_truncate xlrec;
2204 52 : int i = 0;
2205 :
2206 : /* should only get here if wal_level >= logical */
2207 : Assert(XLogLogicalInfoActive());
2208 :
2209 52 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2210 140 : foreach(cell, relids_logged)
2211 88 : logrelids[i++] = lfirst_oid(cell);
2212 :
2213 52 : xlrec.dbId = MyDatabaseId;
2214 52 : xlrec.nrelids = list_length(relids_logged);
2215 52 : xlrec.flags = 0;
2216 52 : if (behavior == DROP_CASCADE)
2217 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2218 52 : if (restart_seqs)
2219 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2220 :
2221 52 : XLogBeginInsert();
2222 52 : XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2223 52 : XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2224 :
2225 52 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2226 :
2227 52 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2228 : }
2229 :
2230 : /*
2231 : * Process all AFTER STATEMENT TRUNCATE triggers.
2232 : */
2233 1276 : resultRelInfo = resultRelInfos;
2234 4434 : foreach(cell, rels)
2235 : {
2236 : UserContext ucxt;
2237 :
2238 3158 : if (run_as_table_owner)
2239 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2240 : &ucxt);
2241 3158 : ExecASTruncateTriggers(estate, resultRelInfo);
2242 3158 : if (run_as_table_owner)
2243 70 : RestoreUserContext(&ucxt);
2244 3158 : resultRelInfo++;
2245 : }
2246 :
2247 : /* Handle queued AFTER triggers */
2248 1276 : AfterTriggerEndQuery(estate);
2249 :
2250 : /* We can clean up the EState now */
2251 1276 : FreeExecutorState(estate);
2252 :
2253 : /*
2254 : * Close any rels opened by CASCADE (can't do this while EState still
2255 : * holds refs)
2256 : */
2257 1276 : rels = list_difference_ptr(rels, explicit_rels);
2258 1370 : foreach(cell, rels)
2259 : {
2260 94 : Relation rel = (Relation) lfirst(cell);
2261 :
2262 94 : table_close(rel, NoLock);
2263 : }
2264 1276 : }
2265 :
2266 : /*
2267 : * Check that a given relation is safe to truncate. Subroutine for
2268 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2269 : */
2270 : static void
2271 3360 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2272 : {
2273 3360 : char *relname = NameStr(reltuple->relname);
2274 :
2275 : /*
2276 : * Only allow truncate on regular tables, foreign tables using foreign
2277 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2278 : * latter are only being included here for the following checks; no
2279 : * physical truncation will occur in their case.).
2280 : */
2281 3360 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2282 : {
2283 36 : Oid serverid = GetForeignServerIdByRelId(relid);
2284 36 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2285 :
2286 36 : if (!fdwroutine->ExecForeignTruncate)
2287 2 : ereport(ERROR,
2288 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2289 : errmsg("cannot truncate foreign table \"%s\"",
2290 : relname)));
2291 : }
2292 3324 : else if (reltuple->relkind != RELKIND_RELATION &&
2293 652 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2294 0 : ereport(ERROR,
2295 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2296 : errmsg("\"%s\" is not a table", relname)));
2297 :
2298 : /*
2299 : * Most system catalogs can't be truncated at all, or at least not unless
2300 : * allow_system_table_mods=on. As an exception, however, we allow
2301 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2302 : * to change its relfilenode to match the old cluster, and allowing a
2303 : * TRUNCATE command to be executed is the easiest way of doing that.
2304 : */
2305 3358 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2306 22 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2307 2 : ereport(ERROR,
2308 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2309 : errmsg("permission denied: \"%s\" is a system catalog",
2310 : relname)));
2311 :
2312 3356 : InvokeObjectTruncateHook(relid);
2313 3356 : }
2314 :
2315 : /*
2316 : * Check that current user has the permission to truncate given relation.
2317 : */
2318 : static void
2319 1728 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2320 : {
2321 1728 : char *relname = NameStr(reltuple->relname);
2322 : AclResult aclresult;
2323 :
2324 : /* Permissions checks */
2325 1728 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2326 1728 : if (aclresult != ACLCHECK_OK)
2327 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2328 : relname);
2329 1696 : }
2330 :
2331 : /*
2332 : * Set of extra sanity checks to check if a given relation is safe to
2333 : * truncate. This is split with truncate_check_rel() as
2334 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2335 : */
2336 : static void
2337 3254 : truncate_check_activity(Relation rel)
2338 : {
2339 : /*
2340 : * Don't allow truncate on temp tables of other backends ... their local
2341 : * buffer manager is not going to cope.
2342 : */
2343 3254 : if (RELATION_IS_OTHER_TEMP(rel))
2344 0 : ereport(ERROR,
2345 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2346 : errmsg("cannot truncate temporary tables of other sessions")));
2347 :
2348 : /*
2349 : * Also check for active uses of the relation in the current transaction,
2350 : * including open scans and pending AFTER trigger events.
2351 : */
2352 3254 : CheckTableNotInUse(rel, "TRUNCATE");
2353 3254 : }
2354 :
2355 : /*
2356 : * storage_name
2357 : * returns the name corresponding to a typstorage/attstorage enum value
2358 : */
2359 : static const char *
2360 24 : storage_name(char c)
2361 : {
2362 24 : switch (c)
2363 : {
2364 0 : case TYPSTORAGE_PLAIN:
2365 0 : return "PLAIN";
2366 0 : case TYPSTORAGE_EXTERNAL:
2367 0 : return "EXTERNAL";
2368 12 : case TYPSTORAGE_EXTENDED:
2369 12 : return "EXTENDED";
2370 12 : case TYPSTORAGE_MAIN:
2371 12 : return "MAIN";
2372 0 : default:
2373 0 : return "???";
2374 : }
2375 : }
2376 :
2377 : /*----------
2378 : * MergeAttributes
2379 : * Returns new schema given initial schema and superclasses.
2380 : *
2381 : * Input arguments:
2382 : * 'columns' is the column/attribute definition for the table. (It's a list
2383 : * of ColumnDef's.) It is destructively changed.
2384 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2385 : * 'relpersistence' is the persistence type of the table.
2386 : * 'is_partition' tells if the table is a partition.
2387 : *
2388 : * Output arguments:
2389 : * 'supconstr' receives a list of constraints belonging to the parents,
2390 : * updated as necessary to be valid for the child.
2391 : *
2392 : * Return value:
2393 : * Completed schema list.
2394 : *
2395 : * Notes:
2396 : * The order in which the attributes are inherited is very important.
2397 : * Intuitively, the inherited attributes should come first. If a table
2398 : * inherits from multiple parents, the order of those attributes are
2399 : * according to the order of the parents specified in CREATE TABLE.
2400 : *
2401 : * Here's an example:
2402 : *
2403 : * create table person (name text, age int4, location point);
2404 : * create table emp (salary int4, manager text) inherits(person);
2405 : * create table student (gpa float8) inherits (person);
2406 : * create table stud_emp (percent int4) inherits (emp, student);
2407 : *
2408 : * The order of the attributes of stud_emp is:
2409 : *
2410 : * person {1:name, 2:age, 3:location}
2411 : * / \
2412 : * {6:gpa} student emp {4:salary, 5:manager}
2413 : * \ /
2414 : * stud_emp {7:percent}
2415 : *
2416 : * If the same attribute name appears multiple times, then it appears
2417 : * in the result table in the proper location for its first appearance.
2418 : *
2419 : * Constraints (including not-null constraints) for the child table
2420 : * are the union of all relevant constraints, from both the child schema
2421 : * and parent tables.
2422 : *
2423 : * The default value for a child column is defined as:
2424 : * (1) If the child schema specifies a default, that value is used.
2425 : * (2) If neither the child nor any parent specifies a default, then
2426 : * the column will not have a default.
2427 : * (3) If conflicting defaults are inherited from different parents
2428 : * (and not overridden by the child), an error is raised.
2429 : * (4) Otherwise the inherited default is used.
2430 : *
2431 : * Note that the default-value infrastructure is used for generated
2432 : * columns' expressions too, so most of the preceding paragraph applies
2433 : * to generation expressions too. We insist that a child column be
2434 : * generated if and only if its parent(s) are, but it need not have
2435 : * the same generation expression.
2436 : *----------
2437 : */
2438 : static List *
2439 52564 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2440 : bool is_partition, List **supconstr)
2441 : {
2442 52564 : List *inh_columns = NIL;
2443 52564 : List *constraints = NIL;
2444 52564 : bool have_bogus_defaults = false;
2445 : int child_attno;
2446 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2447 52564 : List *saved_columns = NIL;
2448 : ListCell *lc;
2449 :
2450 : /*
2451 : * Check for and reject tables with too many columns. We perform this
2452 : * check relatively early for two reasons: (a) we don't run the risk of
2453 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2454 : * okay if we're processing <= 1600 columns, but could take minutes to
2455 : * execute if the user attempts to create a table with hundreds of
2456 : * thousands of columns.
2457 : *
2458 : * Note that we also need to check that we do not exceed this figure after
2459 : * including columns from inherited relations.
2460 : */
2461 52564 : if (list_length(columns) > MaxHeapAttributeNumber)
2462 0 : ereport(ERROR,
2463 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2464 : errmsg("tables can have at most %d columns",
2465 : MaxHeapAttributeNumber)));
2466 :
2467 : /*
2468 : * Check for duplicate names in the explicit list of attributes.
2469 : *
2470 : * Although we might consider merging such entries in the same way that we
2471 : * handle name conflicts for inherited attributes, it seems to make more
2472 : * sense to assume such conflicts are errors.
2473 : *
2474 : * We don't use foreach() here because we have two nested loops over the
2475 : * columns list, with possible element deletions in the inner one. If we
2476 : * used foreach_delete_current() it could only fix up the state of one of
2477 : * the loops, so it seems cleaner to use looping over list indexes for
2478 : * both loops. Note that any deletion will happen beyond where the outer
2479 : * loop is, so its index never needs adjustment.
2480 : */
2481 245954 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2482 : {
2483 193414 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2484 :
2485 193414 : if (!is_partition && coldef->typeName == NULL)
2486 : {
2487 : /*
2488 : * Typed table column option that does not belong to a column from
2489 : * the type. This works because the columns from the type come
2490 : * first in the list. (We omit this check for partition column
2491 : * lists; those are processed separately below.)
2492 : */
2493 6 : ereport(ERROR,
2494 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2495 : errmsg("column \"%s\" does not exist",
2496 : coldef->colname)));
2497 : }
2498 :
2499 : /* restpos scans all entries beyond coldef; incr is in loop body */
2500 6165864 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2501 : {
2502 5972474 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2503 :
2504 5972474 : if (strcmp(coldef->colname, restdef->colname) == 0)
2505 : {
2506 50 : if (coldef->is_from_type)
2507 : {
2508 : /*
2509 : * merge the column options into the column from the type
2510 : */
2511 32 : coldef->is_not_null = restdef->is_not_null;
2512 32 : coldef->raw_default = restdef->raw_default;
2513 32 : coldef->cooked_default = restdef->cooked_default;
2514 32 : coldef->constraints = restdef->constraints;
2515 32 : coldef->is_from_type = false;
2516 32 : columns = list_delete_nth_cell(columns, restpos);
2517 : }
2518 : else
2519 18 : ereport(ERROR,
2520 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2521 : errmsg("column \"%s\" specified more than once",
2522 : coldef->colname)));
2523 : }
2524 : else
2525 5972424 : restpos++;
2526 : }
2527 : }
2528 :
2529 : /*
2530 : * In case of a partition, there are no new column definitions, only dummy
2531 : * ColumnDefs created for column constraints. Set them aside for now and
2532 : * process them at the end.
2533 : */
2534 52540 : if (is_partition)
2535 : {
2536 8344 : saved_columns = columns;
2537 8344 : columns = NIL;
2538 : }
2539 :
2540 : /*
2541 : * Scan the parents left-to-right, and merge their attributes to form a
2542 : * list of inherited columns (inh_columns).
2543 : */
2544 52540 : child_attno = 0;
2545 62632 : foreach(lc, supers)
2546 : {
2547 10164 : Oid parent = lfirst_oid(lc);
2548 : Relation relation;
2549 : TupleDesc tupleDesc;
2550 : TupleConstr *constr;
2551 : AttrMap *newattmap;
2552 : List *inherited_defaults;
2553 : List *cols_with_defaults;
2554 : ListCell *lc1;
2555 : ListCell *lc2;
2556 :
2557 : /* caller already got lock */
2558 10164 : relation = table_open(parent, NoLock);
2559 :
2560 : /*
2561 : * Check for active uses of the parent partitioned table in the
2562 : * current transaction, such as being used in some manner by an
2563 : * enclosing command.
2564 : */
2565 10164 : if (is_partition)
2566 8344 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2567 :
2568 : /*
2569 : * We do not allow partitioned tables and partitions to participate in
2570 : * regular inheritance.
2571 : */
2572 10158 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2573 6 : ereport(ERROR,
2574 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2575 : errmsg("cannot inherit from partitioned table \"%s\"",
2576 : RelationGetRelationName(relation))));
2577 10152 : if (relation->rd_rel->relispartition && !is_partition)
2578 6 : ereport(ERROR,
2579 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2580 : errmsg("cannot inherit from partition \"%s\"",
2581 : RelationGetRelationName(relation))));
2582 :
2583 10146 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2584 8340 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2585 8320 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2586 0 : ereport(ERROR,
2587 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2588 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2589 : RelationGetRelationName(relation))));
2590 :
2591 : /*
2592 : * If the parent is permanent, so must be all of its partitions. Note
2593 : * that inheritance allows that case.
2594 : */
2595 10146 : if (is_partition &&
2596 8338 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2597 : relpersistence == RELPERSISTENCE_TEMP)
2598 6 : ereport(ERROR,
2599 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2600 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2601 : RelationGetRelationName(relation))));
2602 :
2603 : /* Permanent rels cannot inherit from temporary ones */
2604 10140 : if (relpersistence != RELPERSISTENCE_TEMP &&
2605 9762 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2606 24 : ereport(ERROR,
2607 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2608 : errmsg(!is_partition
2609 : ? "cannot inherit from temporary relation \"%s\""
2610 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2611 : RelationGetRelationName(relation))));
2612 :
2613 : /* If existing rel is temp, it must belong to this session */
2614 10116 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2615 330 : !relation->rd_islocaltemp)
2616 0 : ereport(ERROR,
2617 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2618 : errmsg(!is_partition
2619 : ? "cannot inherit from temporary relation of another session"
2620 : : "cannot create as partition of temporary relation of another session")));
2621 :
2622 : /*
2623 : * We should have an UNDER permission flag for this, but for now,
2624 : * demand that creator of a child table own the parent.
2625 : */
2626 10116 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2627 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2628 0 : RelationGetRelationName(relation));
2629 :
2630 10116 : tupleDesc = RelationGetDescr(relation);
2631 10116 : constr = tupleDesc->constr;
2632 :
2633 : /*
2634 : * newattmap->attnums[] will contain the child-table attribute numbers
2635 : * for the attributes of this parent table. (They are not the same
2636 : * for parents after the first one, nor if we have dropped columns.)
2637 : */
2638 10116 : newattmap = make_attrmap(tupleDesc->natts);
2639 :
2640 : /* We can't process inherited defaults until newattmap is complete. */
2641 10116 : inherited_defaults = cols_with_defaults = NIL;
2642 :
2643 31572 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2644 21456 : parent_attno++)
2645 : {
2646 21480 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2647 : parent_attno - 1);
2648 21480 : char *attributeName = NameStr(attribute->attname);
2649 : int exist_attno;
2650 : ColumnDef *newdef;
2651 : ColumnDef *mergeddef;
2652 :
2653 : /*
2654 : * Ignore dropped columns in the parent.
2655 : */
2656 21480 : if (attribute->attisdropped)
2657 192 : continue; /* leave newattmap->attnums entry as zero */
2658 :
2659 : /*
2660 : * Create new column definition
2661 : */
2662 21288 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2663 : attribute->atttypmod, attribute->attcollation);
2664 21288 : newdef->is_not_null = attribute->attnotnull;
2665 21288 : newdef->storage = attribute->attstorage;
2666 21288 : newdef->generated = attribute->attgenerated;
2667 21288 : if (CompressionMethodIsValid(attribute->attcompression))
2668 30 : newdef->compression =
2669 30 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2670 :
2671 : /*
2672 : * Regular inheritance children are independent enough not to
2673 : * inherit identity columns. But partitions are integral part of
2674 : * a partitioned table and inherit identity column.
2675 : */
2676 21288 : if (is_partition)
2677 17770 : newdef->identity = attribute->attidentity;
2678 :
2679 : /*
2680 : * Does it match some previously considered column from another
2681 : * parent?
2682 : */
2683 21288 : exist_attno = findAttrByName(attributeName, inh_columns);
2684 21288 : if (exist_attno > 0)
2685 : {
2686 : /*
2687 : * Yes, try to merge the two column definitions.
2688 : */
2689 230 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2690 :
2691 206 : newattmap->attnums[parent_attno - 1] = exist_attno;
2692 :
2693 : /*
2694 : * Partitions have only one parent, so conflict should never
2695 : * occur.
2696 : */
2697 : Assert(!is_partition);
2698 : }
2699 : else
2700 : {
2701 : /*
2702 : * No, create a new inherited column
2703 : */
2704 21058 : newdef->inhcount = 1;
2705 21058 : newdef->is_local = false;
2706 21058 : inh_columns = lappend(inh_columns, newdef);
2707 :
2708 21058 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2709 21058 : mergeddef = newdef;
2710 : }
2711 :
2712 : /*
2713 : * Locate default/generation expression if any
2714 : */
2715 21264 : if (attribute->atthasdef)
2716 : {
2717 : Node *this_default;
2718 :
2719 658 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2720 658 : if (this_default == NULL)
2721 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2722 : parent_attno, RelationGetRelationName(relation));
2723 :
2724 : /*
2725 : * If it's a GENERATED default, it might contain Vars that
2726 : * need to be mapped to the inherited column(s)' new numbers.
2727 : * We can't do that till newattmap is ready, so just remember
2728 : * all the inherited default expressions for the moment.
2729 : */
2730 658 : inherited_defaults = lappend(inherited_defaults, this_default);
2731 658 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2732 : }
2733 : }
2734 :
2735 : /*
2736 : * Now process any inherited default expressions, adjusting attnos
2737 : * using the completed newattmap map.
2738 : */
2739 10750 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2740 : {
2741 658 : Node *this_default = (Node *) lfirst(lc1);
2742 658 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2743 : bool found_whole_row;
2744 :
2745 : /* Adjust Vars to match new table's column numbering */
2746 658 : this_default = map_variable_attnos(this_default,
2747 : 1, 0,
2748 : newattmap,
2749 : InvalidOid, &found_whole_row);
2750 :
2751 : /*
2752 : * For the moment we have to reject whole-row variables. We could
2753 : * convert them, if we knew the new table's rowtype OID, but that
2754 : * hasn't been assigned yet. (A variable could only appear in a
2755 : * generation expression, so the error message is correct.)
2756 : */
2757 658 : if (found_whole_row)
2758 0 : ereport(ERROR,
2759 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2760 : errmsg("cannot convert whole-row table reference"),
2761 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2762 : def->colname,
2763 : RelationGetRelationName(relation))));
2764 :
2765 : /*
2766 : * If we already had a default from some prior parent, check to
2767 : * see if they are the same. If so, no problem; if not, mark the
2768 : * column as having a bogus default. Below, we will complain if
2769 : * the bogus default isn't overridden by the child columns.
2770 : */
2771 : Assert(def->raw_default == NULL);
2772 658 : if (def->cooked_default == NULL)
2773 628 : def->cooked_default = this_default;
2774 30 : else if (!equal(def->cooked_default, this_default))
2775 : {
2776 24 : def->cooked_default = &bogus_marker;
2777 24 : have_bogus_defaults = true;
2778 : }
2779 : }
2780 :
2781 : /*
2782 : * Now copy the CHECK constraints of this parent, adjusting attnos
2783 : * using the completed newattmap map. Identically named constraints
2784 : * are merged if possible, else we throw error.
2785 : */
2786 10092 : if (constr && constr->num_check > 0)
2787 : {
2788 304 : ConstrCheck *check = constr->check;
2789 :
2790 638 : for (int i = 0; i < constr->num_check; i++)
2791 : {
2792 334 : char *name = check[i].ccname;
2793 : Node *expr;
2794 : bool found_whole_row;
2795 :
2796 : /* ignore if the constraint is non-inheritable */
2797 334 : if (check[i].ccnoinherit)
2798 48 : continue;
2799 :
2800 : /* Adjust Vars to match new table's column numbering */
2801 286 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2802 : 1, 0,
2803 : newattmap,
2804 : InvalidOid, &found_whole_row);
2805 :
2806 : /*
2807 : * For the moment we have to reject whole-row variables. We
2808 : * could convert them, if we knew the new table's rowtype OID,
2809 : * but that hasn't been assigned yet.
2810 : */
2811 286 : if (found_whole_row)
2812 0 : ereport(ERROR,
2813 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2814 : errmsg("cannot convert whole-row table reference"),
2815 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2816 : name,
2817 : RelationGetRelationName(relation))));
2818 :
2819 286 : constraints = MergeCheckConstraint(constraints, name, expr);
2820 : }
2821 : }
2822 :
2823 10092 : free_attrmap(newattmap);
2824 :
2825 : /*
2826 : * Close the parent rel, but keep our lock on it until xact commit.
2827 : * That will prevent someone else from deleting or ALTERing the parent
2828 : * before the child is committed.
2829 : */
2830 10092 : table_close(relation, NoLock);
2831 : }
2832 :
2833 : /*
2834 : * If we had no inherited attributes, the result columns are just the
2835 : * explicitly declared columns. Otherwise, we need to merge the declared
2836 : * columns into the inherited column list. Although, we never have any
2837 : * explicitly declared columns if the table is a partition.
2838 : */
2839 52468 : if (inh_columns != NIL)
2840 : {
2841 9826 : int newcol_attno = 0;
2842 :
2843 10556 : foreach(lc, columns)
2844 : {
2845 778 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2846 778 : char *attributeName = newdef->colname;
2847 : int exist_attno;
2848 :
2849 : /*
2850 : * Partitions have only one parent and have no column definitions
2851 : * of their own, so conflict should never occur.
2852 : */
2853 : Assert(!is_partition);
2854 :
2855 778 : newcol_attno++;
2856 :
2857 : /*
2858 : * Does it match some inherited column?
2859 : */
2860 778 : exist_attno = findAttrByName(attributeName, inh_columns);
2861 778 : if (exist_attno > 0)
2862 : {
2863 : /*
2864 : * Yes, try to merge the two column definitions.
2865 : */
2866 244 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2867 : }
2868 : else
2869 : {
2870 : /*
2871 : * No, attach new column unchanged to result columns.
2872 : */
2873 534 : inh_columns = lappend(inh_columns, newdef);
2874 : }
2875 : }
2876 :
2877 9778 : columns = inh_columns;
2878 :
2879 : /*
2880 : * Check that we haven't exceeded the legal # of columns after merging
2881 : * in inherited columns.
2882 : */
2883 9778 : if (list_length(columns) > MaxHeapAttributeNumber)
2884 0 : ereport(ERROR,
2885 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2886 : errmsg("tables can have at most %d columns",
2887 : MaxHeapAttributeNumber)));
2888 : }
2889 :
2890 : /*
2891 : * Now that we have the column definition list for a partition, we can
2892 : * check whether the columns referenced in the column constraint specs
2893 : * actually exist. Also, we merge parent's not-null constraints and
2894 : * defaults into each corresponding column definition.
2895 : */
2896 52420 : if (is_partition)
2897 : {
2898 8514 : foreach(lc, saved_columns)
2899 : {
2900 212 : ColumnDef *restdef = lfirst(lc);
2901 212 : bool found = false;
2902 : ListCell *l;
2903 :
2904 804 : foreach(l, columns)
2905 : {
2906 604 : ColumnDef *coldef = lfirst(l);
2907 :
2908 604 : if (strcmp(coldef->colname, restdef->colname) == 0)
2909 : {
2910 212 : found = true;
2911 212 : coldef->is_not_null |= restdef->is_not_null;
2912 :
2913 : /*
2914 : * Check for conflicts related to generated columns.
2915 : *
2916 : * Same rules as above: generated-ness has to match the
2917 : * parent, but the contents of the generation expression
2918 : * can be different.
2919 : */
2920 212 : if (coldef->generated)
2921 : {
2922 110 : if (restdef->raw_default && !restdef->generated)
2923 6 : ereport(ERROR,
2924 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2925 : errmsg("column \"%s\" inherits from generated column but specifies default",
2926 : restdef->colname)));
2927 104 : if (restdef->identity)
2928 0 : ereport(ERROR,
2929 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2930 : errmsg("column \"%s\" inherits from generated column but specifies identity",
2931 : restdef->colname)));
2932 : }
2933 : else
2934 : {
2935 102 : if (restdef->generated)
2936 6 : ereport(ERROR,
2937 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2938 : errmsg("child column \"%s\" specifies generation expression",
2939 : restdef->colname),
2940 : errhint("A child table column cannot be generated unless its parent column is.")));
2941 : }
2942 :
2943 : /*
2944 : * Override the parent's default value for this column
2945 : * (coldef->cooked_default) with the partition's local
2946 : * definition (restdef->raw_default), if there's one. It
2947 : * should be physically impossible to get a cooked default
2948 : * in the local definition or a raw default in the
2949 : * inherited definition, but make sure they're nulls, for
2950 : * future-proofing.
2951 : */
2952 : Assert(restdef->cooked_default == NULL);
2953 : Assert(coldef->raw_default == NULL);
2954 200 : if (restdef->raw_default)
2955 : {
2956 128 : coldef->raw_default = restdef->raw_default;
2957 128 : coldef->cooked_default = NULL;
2958 : }
2959 : }
2960 : }
2961 :
2962 : /* complain for constraints on columns not in parent */
2963 200 : if (!found)
2964 0 : ereport(ERROR,
2965 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2966 : errmsg("column \"%s\" does not exist",
2967 : restdef->colname)));
2968 : }
2969 : }
2970 :
2971 : /*
2972 : * If we found any conflicting parent default values, check to make sure
2973 : * they were overridden by the child.
2974 : */
2975 52408 : if (have_bogus_defaults)
2976 : {
2977 54 : foreach(lc, columns)
2978 : {
2979 42 : ColumnDef *def = lfirst(lc);
2980 :
2981 42 : if (def->cooked_default == &bogus_marker)
2982 : {
2983 12 : if (def->generated)
2984 6 : ereport(ERROR,
2985 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2986 : errmsg("column \"%s\" inherits conflicting generation expressions",
2987 : def->colname),
2988 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
2989 : else
2990 6 : ereport(ERROR,
2991 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2992 : errmsg("column \"%s\" inherits conflicting default values",
2993 : def->colname),
2994 : errhint("To resolve the conflict, specify a default explicitly.")));
2995 : }
2996 : }
2997 : }
2998 :
2999 52396 : *supconstr = constraints;
3000 :
3001 52396 : return columns;
3002 : }
3003 :
3004 :
3005 : /*
3006 : * MergeCheckConstraint
3007 : * Try to merge an inherited CHECK constraint with previous ones
3008 : *
3009 : * If we inherit identically-named constraints from multiple parents, we must
3010 : * merge them, or throw an error if they don't have identical definitions.
3011 : *
3012 : * constraints is a list of CookedConstraint structs for previous constraints.
3013 : *
3014 : * If the new constraint matches an existing one, then the existing
3015 : * constraint's inheritance count is updated. If there is a conflict (same
3016 : * name but different expression), throw an error. If the constraint neither
3017 : * matches nor conflicts with an existing one, a new constraint is appended to
3018 : * the list.
3019 : */
3020 : static List *
3021 286 : MergeCheckConstraint(List *constraints, const char *name, Node *expr)
3022 : {
3023 : ListCell *lc;
3024 : CookedConstraint *newcon;
3025 :
3026 316 : foreach(lc, constraints)
3027 : {
3028 72 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3029 :
3030 : Assert(ccon->contype == CONSTR_CHECK);
3031 :
3032 : /* Non-matching names never conflict */
3033 72 : if (strcmp(ccon->name, name) != 0)
3034 30 : continue;
3035 :
3036 42 : if (equal(expr, ccon->expr))
3037 : {
3038 : /* OK to merge constraint with existing */
3039 42 : ccon->inhcount++;
3040 42 : if (ccon->inhcount < 0)
3041 0 : ereport(ERROR,
3042 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3043 : errmsg("too many inheritance parents"));
3044 42 : return constraints;
3045 : }
3046 :
3047 0 : ereport(ERROR,
3048 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3049 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3050 : name)));
3051 : }
3052 :
3053 : /*
3054 : * Constraint couldn't be merged with an existing one and also didn't
3055 : * conflict with an existing one, so add it as a new one to the list.
3056 : */
3057 244 : newcon = palloc0_object(CookedConstraint);
3058 244 : newcon->contype = CONSTR_CHECK;
3059 244 : newcon->name = pstrdup(name);
3060 244 : newcon->expr = expr;
3061 244 : newcon->inhcount = 1;
3062 244 : return lappend(constraints, newcon);
3063 : }
3064 :
3065 : /*
3066 : * MergeChildAttribute
3067 : * Merge given child attribute definition into given inherited attribute.
3068 : *
3069 : * Input arguments:
3070 : * 'inh_columns' is the list of inherited ColumnDefs.
3071 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3072 : * 'newcol_attno' is the attribute number in child table's schema definition
3073 : * 'newdef' is the column/attribute definition from the child table.
3074 : *
3075 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3076 : * ColumnDef remains unchanged.
3077 : *
3078 : * Notes:
3079 : * - The attribute is merged according to the rules laid out in the prologue
3080 : * of MergeAttributes().
3081 : * - If matching inherited attribute exists but the child attribute can not be
3082 : * merged into it, the function throws respective errors.
3083 : * - A partition can not have its own column definitions. Hence this function
3084 : * is applicable only to a regular inheritance child.
3085 : */
3086 : static void
3087 244 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3088 : {
3089 244 : char *attributeName = newdef->colname;
3090 : ColumnDef *inhdef;
3091 : Oid inhtypeid,
3092 : newtypeid;
3093 : int32 inhtypmod,
3094 : newtypmod;
3095 : Oid inhcollid,
3096 : newcollid;
3097 :
3098 244 : if (exist_attno == newcol_attno)
3099 222 : ereport(NOTICE,
3100 : (errmsg("merging column \"%s\" with inherited definition",
3101 : attributeName)));
3102 : else
3103 22 : ereport(NOTICE,
3104 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3105 : errdetail("User-specified column moved to the position of the inherited column.")));
3106 :
3107 244 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3108 :
3109 : /*
3110 : * Must have the same type and typmod
3111 : */
3112 244 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3113 244 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3114 244 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3115 12 : ereport(ERROR,
3116 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3117 : errmsg("column \"%s\" has a type conflict",
3118 : attributeName),
3119 : errdetail("%s versus %s",
3120 : format_type_with_typemod(inhtypeid, inhtypmod),
3121 : format_type_with_typemod(newtypeid, newtypmod))));
3122 :
3123 : /*
3124 : * Must have the same collation
3125 : */
3126 232 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3127 232 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3128 232 : if (inhcollid != newcollid)
3129 6 : ereport(ERROR,
3130 : (errcode(ERRCODE_COLLATION_MISMATCH),
3131 : errmsg("column \"%s\" has a collation conflict",
3132 : attributeName),
3133 : errdetail("\"%s\" versus \"%s\"",
3134 : get_collation_name(inhcollid),
3135 : get_collation_name(newcollid))));
3136 :
3137 : /*
3138 : * Identity is never inherited by a regular inheritance child. Pick
3139 : * child's identity definition if there's one.
3140 : */
3141 226 : inhdef->identity = newdef->identity;
3142 :
3143 : /*
3144 : * Copy storage parameter
3145 : */
3146 226 : if (inhdef->storage == 0)
3147 0 : inhdef->storage = newdef->storage;
3148 226 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3149 6 : ereport(ERROR,
3150 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3151 : errmsg("column \"%s\" has a storage parameter conflict",
3152 : attributeName),
3153 : errdetail("%s versus %s",
3154 : storage_name(inhdef->storage),
3155 : storage_name(newdef->storage))));
3156 :
3157 : /*
3158 : * Copy compression parameter
3159 : */
3160 220 : if (inhdef->compression == NULL)
3161 214 : inhdef->compression = newdef->compression;
3162 6 : else if (newdef->compression != NULL)
3163 : {
3164 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3165 6 : ereport(ERROR,
3166 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3167 : errmsg("column \"%s\" has a compression method conflict",
3168 : attributeName),
3169 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3170 : }
3171 :
3172 : /*
3173 : * Merge of not-null constraints = OR 'em together
3174 : */
3175 214 : inhdef->is_not_null |= newdef->is_not_null;
3176 :
3177 : /*
3178 : * Check for conflicts related to generated columns.
3179 : *
3180 : * If the parent column is generated, the child column will be made a
3181 : * generated column if it isn't already. If it is a generated column,
3182 : * we'll take its generation expression in preference to the parent's. We
3183 : * must check that the child column doesn't specify a default value or
3184 : * identity, which matches the rules for a single column in
3185 : * parse_utilcmd.c.
3186 : *
3187 : * Conversely, if the parent column is not generated, the child column
3188 : * can't be either. (We used to allow that, but it results in being able
3189 : * to override the generation expression via UPDATEs through the parent.)
3190 : */
3191 214 : if (inhdef->generated)
3192 : {
3193 26 : if (newdef->raw_default && !newdef->generated)
3194 6 : ereport(ERROR,
3195 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3196 : errmsg("column \"%s\" inherits from generated column but specifies default",
3197 : inhdef->colname)));
3198 20 : if (newdef->identity)
3199 6 : ereport(ERROR,
3200 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3201 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3202 : inhdef->colname)));
3203 : }
3204 : else
3205 : {
3206 188 : if (newdef->generated)
3207 6 : ereport(ERROR,
3208 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3209 : errmsg("child column \"%s\" specifies generation expression",
3210 : inhdef->colname),
3211 : errhint("A child table column cannot be generated unless its parent column is.")));
3212 : }
3213 :
3214 : /*
3215 : * If new def has a default, override previous default
3216 : */
3217 196 : if (newdef->raw_default != NULL)
3218 : {
3219 18 : inhdef->raw_default = newdef->raw_default;
3220 18 : inhdef->cooked_default = newdef->cooked_default;
3221 : }
3222 :
3223 : /* Mark the column as locally defined */
3224 196 : inhdef->is_local = true;
3225 196 : }
3226 :
3227 : /*
3228 : * MergeInheritedAttribute
3229 : * Merge given parent attribute definition into specified attribute
3230 : * inherited from the previous parents.
3231 : *
3232 : * Input arguments:
3233 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3234 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3235 : * 'newdef' is the new parent column/attribute definition to be merged.
3236 : *
3237 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3238 : *
3239 : * Notes:
3240 : * - The attribute is merged according to the rules laid out in the prologue
3241 : * of MergeAttributes().
3242 : * - If matching inherited attribute exists but the new attribute can not be
3243 : * merged into it, the function throws respective errors.
3244 : * - A partition inherits from only a single parent. Hence this function is
3245 : * applicable only to a regular inheritance.
3246 : */
3247 : static ColumnDef *
3248 230 : MergeInheritedAttribute(List *inh_columns,
3249 : int exist_attno,
3250 : const ColumnDef *newdef)
3251 : {
3252 230 : char *attributeName = newdef->colname;
3253 : ColumnDef *prevdef;
3254 : Oid prevtypeid,
3255 : newtypeid;
3256 : int32 prevtypmod,
3257 : newtypmod;
3258 : Oid prevcollid,
3259 : newcollid;
3260 :
3261 230 : ereport(NOTICE,
3262 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3263 : attributeName)));
3264 230 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3265 :
3266 : /*
3267 : * Must have the same type and typmod
3268 : */
3269 230 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3270 230 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3271 230 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3272 0 : ereport(ERROR,
3273 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3274 : errmsg("inherited column \"%s\" has a type conflict",
3275 : attributeName),
3276 : errdetail("%s versus %s",
3277 : format_type_with_typemod(prevtypeid, prevtypmod),
3278 : format_type_with_typemod(newtypeid, newtypmod))));
3279 :
3280 : /*
3281 : * Merge of not-null constraints = OR 'em together
3282 : */
3283 230 : prevdef->is_not_null |= newdef->is_not_null;
3284 :
3285 : /*
3286 : * Must have the same collation
3287 : */
3288 230 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3289 230 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3290 230 : if (prevcollid != newcollid)
3291 0 : ereport(ERROR,
3292 : (errcode(ERRCODE_COLLATION_MISMATCH),
3293 : errmsg("inherited column \"%s\" has a collation conflict",
3294 : attributeName),
3295 : errdetail("\"%s\" versus \"%s\"",
3296 : get_collation_name(prevcollid),
3297 : get_collation_name(newcollid))));
3298 :
3299 : /*
3300 : * Copy/check storage parameter
3301 : */
3302 230 : if (prevdef->storage == 0)
3303 0 : prevdef->storage = newdef->storage;
3304 230 : else if (prevdef->storage != newdef->storage)
3305 6 : ereport(ERROR,
3306 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3307 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3308 : attributeName),
3309 : errdetail("%s versus %s",
3310 : storage_name(prevdef->storage),
3311 : storage_name(newdef->storage))));
3312 :
3313 : /*
3314 : * Copy/check compression parameter
3315 : */
3316 224 : if (prevdef->compression == NULL)
3317 212 : prevdef->compression = newdef->compression;
3318 12 : else if (newdef->compression != NULL)
3319 : {
3320 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3321 6 : ereport(ERROR,
3322 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3323 : errmsg("column \"%s\" has a compression method conflict",
3324 : attributeName),
3325 : errdetail("%s versus %s",
3326 : prevdef->compression, newdef->compression)));
3327 : }
3328 :
3329 : /*
3330 : * Check for GENERATED conflicts
3331 : */
3332 218 : if (prevdef->generated != newdef->generated)
3333 12 : ereport(ERROR,
3334 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3335 : errmsg("inherited column \"%s\" has a generation conflict",
3336 : attributeName)));
3337 :
3338 : /*
3339 : * Default and other constraints are handled by the caller.
3340 : */
3341 :
3342 206 : prevdef->inhcount++;
3343 206 : if (prevdef->inhcount < 0)
3344 0 : ereport(ERROR,
3345 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3346 : errmsg("too many inheritance parents"));
3347 :
3348 206 : return prevdef;
3349 : }
3350 :
3351 : /*
3352 : * StoreCatalogInheritance
3353 : * Updates the system catalogs with proper inheritance information.
3354 : *
3355 : * supers is a list of the OIDs of the new relation's direct ancestors.
3356 : */
3357 : static void
3358 51850 : StoreCatalogInheritance(Oid relationId, List *supers,
3359 : bool child_is_partition)
3360 : {
3361 : Relation relation;
3362 : int32 seqNumber;
3363 : ListCell *entry;
3364 :
3365 : /*
3366 : * sanity checks
3367 : */
3368 : Assert(OidIsValid(relationId));
3369 :
3370 51850 : if (supers == NIL)
3371 42414 : return;
3372 :
3373 : /*
3374 : * Store INHERITS information in pg_inherits using direct ancestors only.
3375 : * Also enter dependencies on the direct ancestors, and make sure they are
3376 : * marked with relhassubclass = true.
3377 : *
3378 : * (Once upon a time, both direct and indirect ancestors were found here
3379 : * and then entered into pg_ipl. Since that catalog doesn't exist
3380 : * anymore, there's no need to look for indirect ancestors.)
3381 : */
3382 9436 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3383 :
3384 9436 : seqNumber = 1;
3385 19066 : foreach(entry, supers)
3386 : {
3387 9630 : Oid parentOid = lfirst_oid(entry);
3388 :
3389 9630 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3390 : child_is_partition);
3391 9630 : seqNumber++;
3392 : }
3393 :
3394 9436 : table_close(relation, RowExclusiveLock);
3395 : }
3396 :
3397 : /*
3398 : * Make catalog entries showing relationId as being an inheritance child
3399 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3400 : */
3401 : static void
3402 12320 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3403 : int32 seqNumber, Relation inhRelation,
3404 : bool child_is_partition)
3405 : {
3406 : ObjectAddress childobject,
3407 : parentobject;
3408 :
3409 : /* store the pg_inherits row */
3410 12320 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3411 :
3412 : /*
3413 : * Store a dependency too
3414 : */
3415 12320 : parentobject.classId = RelationRelationId;
3416 12320 : parentobject.objectId = parentOid;
3417 12320 : parentobject.objectSubId = 0;
3418 12320 : childobject.classId = RelationRelationId;
3419 12320 : childobject.objectId = relationId;
3420 12320 : childobject.objectSubId = 0;
3421 :
3422 12320 : recordDependencyOn(&childobject, &parentobject,
3423 : child_dependency_type(child_is_partition));
3424 :
3425 : /*
3426 : * Post creation hook of this inheritance. Since object_access_hook
3427 : * doesn't take multiple object identifiers, we relay oid of parent
3428 : * relation using auxiliary_id argument.
3429 : */
3430 12320 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3431 : relationId, 0,
3432 : parentOid, false);
3433 :
3434 : /*
3435 : * Mark the parent as having subclasses.
3436 : */
3437 12320 : SetRelationHasSubclass(parentOid, true);
3438 12320 : }
3439 :
3440 : /*
3441 : * Look for an existing column entry with the given name.
3442 : *
3443 : * Returns the index (starting with 1) if attribute already exists in columns,
3444 : * 0 if it doesn't.
3445 : */
3446 : static int
3447 22066 : findAttrByName(const char *attributeName, const List *columns)
3448 : {
3449 : ListCell *lc;
3450 22066 : int i = 1;
3451 :
3452 41200 : foreach(lc, columns)
3453 : {
3454 19608 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3455 474 : return i;
3456 :
3457 19134 : i++;
3458 : }
3459 21592 : return 0;
3460 : }
3461 :
3462 :
3463 : /*
3464 : * SetRelationHasSubclass
3465 : * Set the value of the relation's relhassubclass field in pg_class.
3466 : *
3467 : * It's always safe to set this field to true, because all SQL commands are
3468 : * ready to see true and then find no children. On the other hand, commands
3469 : * generally assume zero children if this is false.
3470 : *
3471 : * Caller must hold any self-exclusive lock until end of transaction. If the
3472 : * new value is false, caller must have acquired that lock before reading the
3473 : * evidence that justified the false value. That way, it properly waits if
3474 : * another backend is simultaneously concluding no need to change the tuple
3475 : * (new and old values are true).
3476 : *
3477 : * NOTE: an important side-effect of this operation is that an SI invalidation
3478 : * message is sent out to all backends --- including me --- causing plans
3479 : * referencing the relation to be rebuilt with the new list of children.
3480 : * This must happen even if we find that no change is needed in the pg_class
3481 : * row.
3482 : */
3483 : void
3484 15680 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3485 : {
3486 : Relation relationRelation;
3487 : HeapTuple tuple;
3488 : Form_pg_class classtuple;
3489 :
3490 : Assert(CheckRelationOidLockedByMe(relationId,
3491 : ShareUpdateExclusiveLock, false) ||
3492 : CheckRelationOidLockedByMe(relationId,
3493 : ShareRowExclusiveLock, true));
3494 :
3495 : /*
3496 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3497 : */
3498 15680 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3499 15680 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3500 15680 : if (!HeapTupleIsValid(tuple))
3501 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3502 15680 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3503 :
3504 15680 : if (classtuple->relhassubclass != relhassubclass)
3505 : {
3506 7260 : classtuple->relhassubclass = relhassubclass;
3507 7260 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3508 : }
3509 : else
3510 : {
3511 : /* no need to change tuple, but force relcache rebuild anyway */
3512 8420 : CacheInvalidateRelcacheByTuple(tuple);
3513 : }
3514 :
3515 15680 : heap_freetuple(tuple);
3516 15680 : table_close(relationRelation, RowExclusiveLock);
3517 15680 : }
3518 :
3519 : /*
3520 : * CheckRelationTableSpaceMove
3521 : * Check if relation can be moved to new tablespace.
3522 : *
3523 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3524 : *
3525 : * Returns true if the relation can be moved to the new tablespace; raises
3526 : * an error if it is not possible to do the move; returns false if the move
3527 : * would have no effect.
3528 : */
3529 : bool
3530 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3531 : {
3532 : Oid oldTableSpaceId;
3533 :
3534 : /*
3535 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3536 : * stored as 0.
3537 : */
3538 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3539 226 : if (newTableSpaceId == oldTableSpaceId ||
3540 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3541 10 : return false;
3542 :
3543 : /*
3544 : * We cannot support moving mapped relations into different tablespaces.
3545 : * (In particular this eliminates all shared catalogs.)
3546 : */
3547 216 : if (RelationIsMapped(rel))
3548 0 : ereport(ERROR,
3549 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3550 : errmsg("cannot move system relation \"%s\"",
3551 : RelationGetRelationName(rel))));
3552 :
3553 : /* Cannot move a non-shared relation into pg_global */
3554 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3555 12 : ereport(ERROR,
3556 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3557 : errmsg("only shared relations can be placed in pg_global tablespace")));
3558 :
3559 : /*
3560 : * Do not allow moving temp tables of other backends ... their local
3561 : * buffer manager is not going to cope.
3562 : */
3563 204 : if (RELATION_IS_OTHER_TEMP(rel))
3564 0 : ereport(ERROR,
3565 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3566 : errmsg("cannot move temporary tables of other sessions")));
3567 :
3568 204 : return true;
3569 : }
3570 :
3571 : /*
3572 : * SetRelationTableSpace
3573 : * Set new reltablespace and relfilenumber in pg_class entry.
3574 : *
3575 : * newTableSpaceId is the new tablespace for the relation, and
3576 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3577 : * InvalidRelFileNumber, this field is not updated.
3578 : *
3579 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3580 : *
3581 : * The caller of this routine had better check if a relation can be
3582 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3583 : * first, and is responsible for making the change visible with
3584 : * CommandCounterIncrement().
3585 : */
3586 : void
3587 204 : SetRelationTableSpace(Relation rel,
3588 : Oid newTableSpaceId,
3589 : RelFileNumber newRelFilenumber)
3590 : {
3591 : Relation pg_class;
3592 : HeapTuple tuple;
3593 : Form_pg_class rd_rel;
3594 204 : Oid reloid = RelationGetRelid(rel);
3595 :
3596 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3597 :
3598 : /* Get a modifiable copy of the relation's pg_class row. */
3599 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3600 :
3601 204 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3602 204 : if (!HeapTupleIsValid(tuple))
3603 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3604 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3605 :
3606 : /* Update the pg_class row. */
3607 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3608 204 : InvalidOid : newTableSpaceId;
3609 204 : if (RelFileNumberIsValid(newRelFilenumber))
3610 160 : rd_rel->relfilenode = newRelFilenumber;
3611 204 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3612 :
3613 : /*
3614 : * Record dependency on tablespace. This is only required for relations
3615 : * that have no physical storage.
3616 : */
3617 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3618 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3619 : rd_rel->reltablespace);
3620 :
3621 204 : heap_freetuple(tuple);
3622 204 : table_close(pg_class, RowExclusiveLock);
3623 204 : }
3624 :
3625 : /*
3626 : * renameatt_check - basic sanity checks before attribute rename
3627 : */
3628 : static void
3629 968 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3630 : {
3631 968 : char relkind = classform->relkind;
3632 :
3633 968 : if (classform->reloftype && !recursing)
3634 6 : ereport(ERROR,
3635 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3636 : errmsg("cannot rename column of typed table")));
3637 :
3638 : /*
3639 : * Renaming the columns of sequences or toast tables doesn't actually
3640 : * break anything from the system's point of view, since internal
3641 : * references are by attnum. But it doesn't seem right to allow users to
3642 : * change names that are hardcoded into the system, hence the following
3643 : * restriction.
3644 : */
3645 962 : if (relkind != RELKIND_RELATION &&
3646 86 : relkind != RELKIND_VIEW &&
3647 86 : relkind != RELKIND_MATVIEW &&
3648 38 : relkind != RELKIND_COMPOSITE_TYPE &&
3649 38 : relkind != RELKIND_INDEX &&
3650 38 : relkind != RELKIND_PARTITIONED_INDEX &&
3651 0 : relkind != RELKIND_FOREIGN_TABLE &&
3652 : relkind != RELKIND_PARTITIONED_TABLE)
3653 0 : ereport(ERROR,
3654 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3655 : errmsg("cannot rename columns of relation \"%s\"",
3656 : NameStr(classform->relname)),
3657 : errdetail_relkind_not_supported(relkind)));
3658 :
3659 : /*
3660 : * permissions checking. only the owner of a class can change its schema.
3661 : */
3662 962 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3663 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3664 0 : NameStr(classform->relname));
3665 962 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3666 2 : ereport(ERROR,
3667 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3668 : errmsg("permission denied: \"%s\" is a system catalog",
3669 : NameStr(classform->relname))));
3670 960 : }
3671 :
3672 : /*
3673 : * renameatt_internal - workhorse for renameatt
3674 : *
3675 : * Return value is the attribute number in the 'myrelid' relation.
3676 : */
3677 : static AttrNumber
3678 534 : renameatt_internal(Oid myrelid,
3679 : const char *oldattname,
3680 : const char *newattname,
3681 : bool recurse,
3682 : bool recursing,
3683 : int expected_parents,
3684 : DropBehavior behavior)
3685 : {
3686 : Relation targetrelation;
3687 : Relation attrelation;
3688 : HeapTuple atttup;
3689 : Form_pg_attribute attform;
3690 : AttrNumber attnum;
3691 :
3692 : /*
3693 : * Grab an exclusive lock on the target table, which we will NOT release
3694 : * until end of transaction.
3695 : */
3696 534 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3697 534 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3698 :
3699 : /*
3700 : * if the 'recurse' flag is set then we are supposed to rename this
3701 : * attribute in all classes that inherit from 'relname' (as well as in
3702 : * 'relname').
3703 : *
3704 : * any permissions or problems with duplicate attributes will cause the
3705 : * whole transaction to abort, which is what we want -- all or nothing.
3706 : */
3707 534 : if (recurse)
3708 : {
3709 : List *child_oids,
3710 : *child_numparents;
3711 : ListCell *lo,
3712 : *li;
3713 :
3714 : /*
3715 : * we need the number of parents for each child so that the recursive
3716 : * calls to renameatt() can determine whether there are any parents
3717 : * outside the inheritance hierarchy being processed.
3718 : */
3719 230 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3720 : &child_numparents);
3721 :
3722 : /*
3723 : * find_all_inheritors does the recursive search of the inheritance
3724 : * hierarchy, so all we have to do is process all of the relids in the
3725 : * list that it returns.
3726 : */
3727 698 : forboth(lo, child_oids, li, child_numparents)
3728 : {
3729 498 : Oid childrelid = lfirst_oid(lo);
3730 498 : int numparents = lfirst_int(li);
3731 :
3732 498 : if (childrelid == myrelid)
3733 230 : continue;
3734 : /* note we need not recurse again */
3735 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3736 : }
3737 : }
3738 : else
3739 : {
3740 : /*
3741 : * If we are told not to recurse, there had better not be any child
3742 : * tables; else the rename would put them out of step.
3743 : *
3744 : * expected_parents will only be 0 if we are not already recursing.
3745 : */
3746 340 : if (expected_parents == 0 &&
3747 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3748 12 : ereport(ERROR,
3749 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3750 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3751 : oldattname)));
3752 : }
3753 :
3754 : /* rename attributes in typed tables of composite type */
3755 492 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3756 : {
3757 : List *child_oids;
3758 : ListCell *lo;
3759 :
3760 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3761 24 : RelationGetRelationName(targetrelation),
3762 : behavior);
3763 :
3764 24 : foreach(lo, child_oids)
3765 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3766 : }
3767 :
3768 486 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3769 :
3770 486 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3771 486 : if (!HeapTupleIsValid(atttup))
3772 24 : ereport(ERROR,
3773 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3774 : errmsg("column \"%s\" does not exist",
3775 : oldattname)));
3776 462 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3777 :
3778 462 : attnum = attform->attnum;
3779 462 : if (attnum <= 0)
3780 0 : ereport(ERROR,
3781 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3782 : errmsg("cannot rename system column \"%s\"",
3783 : oldattname)));
3784 :
3785 : /*
3786 : * if the attribute is inherited, forbid the renaming. if this is a
3787 : * top-level call to renameatt(), then expected_parents will be 0, so the
3788 : * effect of this code will be to prohibit the renaming if the attribute
3789 : * is inherited at all. if this is a recursive call to renameatt(),
3790 : * expected_parents will be the number of parents the current relation has
3791 : * within the inheritance hierarchy being processed, so we'll prohibit the
3792 : * renaming only if there are additional parents from elsewhere.
3793 : */
3794 462 : if (attform->attinhcount > expected_parents)
3795 30 : ereport(ERROR,
3796 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3797 : errmsg("cannot rename inherited column \"%s\"",
3798 : oldattname)));
3799 :
3800 : /* new name should not already exist */
3801 432 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3802 :
3803 : /* apply the update */
3804 420 : namestrcpy(&(attform->attname), newattname);
3805 :
3806 420 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3807 :
3808 420 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3809 :
3810 420 : heap_freetuple(atttup);
3811 :
3812 420 : table_close(attrelation, RowExclusiveLock);
3813 :
3814 420 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3815 :
3816 420 : return attnum;
3817 : }
3818 :
3819 : /*
3820 : * Perform permissions and integrity checks before acquiring a relation lock.
3821 : */
3822 : static void
3823 392 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3824 : void *arg)
3825 : {
3826 : HeapTuple tuple;
3827 : Form_pg_class form;
3828 :
3829 392 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3830 392 : if (!HeapTupleIsValid(tuple))
3831 36 : return; /* concurrently dropped */
3832 356 : form = (Form_pg_class) GETSTRUCT(tuple);
3833 356 : renameatt_check(relid, form, false);
3834 348 : ReleaseSysCache(tuple);
3835 : }
3836 :
3837 : /*
3838 : * renameatt - changes the name of an attribute in a relation
3839 : *
3840 : * The returned ObjectAddress is that of the renamed column.
3841 : */
3842 : ObjectAddress
3843 298 : renameatt(RenameStmt *stmt)
3844 : {
3845 : Oid relid;
3846 : AttrNumber attnum;
3847 : ObjectAddress address;
3848 :
3849 : /* lock level taken here should match renameatt_internal */
3850 298 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3851 298 : stmt->missing_ok ? RVR_MISSING_OK : 0,
3852 : RangeVarCallbackForRenameAttribute,
3853 : NULL);
3854 :
3855 284 : if (!OidIsValid(relid))
3856 : {
3857 24 : ereport(NOTICE,
3858 : (errmsg("relation \"%s\" does not exist, skipping",
3859 : stmt->relation->relname)));
3860 24 : return InvalidObjectAddress;
3861 : }
3862 :
3863 : attnum =
3864 260 : renameatt_internal(relid,
3865 260 : stmt->subname, /* old att name */
3866 260 : stmt->newname, /* new att name */
3867 260 : stmt->relation->inh, /* recursive? */
3868 : false, /* recursing? */
3869 : 0, /* expected inhcount */
3870 : stmt->behavior);
3871 :
3872 176 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3873 :
3874 176 : return address;
3875 : }
3876 :
3877 : /*
3878 : * same logic as renameatt_internal
3879 : */
3880 : static ObjectAddress
3881 84 : rename_constraint_internal(Oid myrelid,
3882 : Oid mytypid,
3883 : const char *oldconname,
3884 : const char *newconname,
3885 : bool recurse,
3886 : bool recursing,
3887 : int expected_parents)
3888 : {
3889 84 : Relation targetrelation = NULL;
3890 : Oid constraintOid;
3891 : HeapTuple tuple;
3892 : Form_pg_constraint con;
3893 : ObjectAddress address;
3894 :
3895 : Assert(!myrelid || !mytypid);
3896 :
3897 84 : if (mytypid)
3898 : {
3899 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3900 : }
3901 : else
3902 : {
3903 78 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3904 :
3905 : /*
3906 : * don't tell it whether we're recursing; we allow changing typed
3907 : * tables here
3908 : */
3909 78 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3910 :
3911 78 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3912 : }
3913 :
3914 84 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3915 84 : if (!HeapTupleIsValid(tuple))
3916 0 : elog(ERROR, "cache lookup failed for constraint %u",
3917 : constraintOid);
3918 84 : con = (Form_pg_constraint) GETSTRUCT(tuple);
3919 :
3920 84 : if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
3921 : {
3922 48 : if (recurse)
3923 : {
3924 : List *child_oids,
3925 : *child_numparents;
3926 : ListCell *lo,
3927 : *li;
3928 :
3929 30 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3930 : &child_numparents);
3931 :
3932 72 : forboth(lo, child_oids, li, child_numparents)
3933 : {
3934 42 : Oid childrelid = lfirst_oid(lo);
3935 42 : int numparents = lfirst_int(li);
3936 :
3937 42 : if (childrelid == myrelid)
3938 30 : continue;
3939 :
3940 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3941 : }
3942 : }
3943 : else
3944 : {
3945 24 : if (expected_parents == 0 &&
3946 6 : find_inheritance_children(myrelid, NoLock) != NIL)
3947 6 : ereport(ERROR,
3948 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3949 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3950 : oldconname)));
3951 : }
3952 :
3953 42 : if (con->coninhcount > expected_parents)
3954 6 : ereport(ERROR,
3955 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3956 : errmsg("cannot rename inherited constraint \"%s\"",
3957 : oldconname)));
3958 : }
3959 :
3960 72 : if (con->conindid
3961 18 : && (con->contype == CONSTRAINT_PRIMARY
3962 6 : || con->contype == CONSTRAINT_UNIQUE
3963 0 : || con->contype == CONSTRAINT_EXCLUSION))
3964 : /* rename the index; this renames the constraint as well */
3965 18 : RenameRelationInternal(con->conindid, newconname, false, true);
3966 : else
3967 54 : RenameConstraintById(constraintOid, newconname);
3968 :
3969 72 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
3970 :
3971 72 : ReleaseSysCache(tuple);
3972 :
3973 72 : if (targetrelation)
3974 : {
3975 : /*
3976 : * Invalidate relcache so as others can see the new constraint name.
3977 : */
3978 66 : CacheInvalidateRelcache(targetrelation);
3979 :
3980 66 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3981 : }
3982 :
3983 72 : return address;
3984 : }
3985 :
3986 : ObjectAddress
3987 78 : RenameConstraint(RenameStmt *stmt)
3988 : {
3989 78 : Oid relid = InvalidOid;
3990 78 : Oid typid = InvalidOid;
3991 :
3992 78 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
3993 : {
3994 : Relation rel;
3995 : HeapTuple tup;
3996 :
3997 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
3998 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
3999 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4000 6 : if (!HeapTupleIsValid(tup))
4001 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4002 6 : checkDomainOwner(tup);
4003 6 : ReleaseSysCache(tup);
4004 6 : table_close(rel, NoLock);
4005 : }
4006 : else
4007 : {
4008 : /* lock level taken here should match rename_constraint_internal */
4009 72 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4010 72 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4011 : RangeVarCallbackForRenameAttribute,
4012 : NULL);
4013 72 : if (!OidIsValid(relid))
4014 : {
4015 6 : ereport(NOTICE,
4016 : (errmsg("relation \"%s\" does not exist, skipping",
4017 : stmt->relation->relname)));
4018 6 : return InvalidObjectAddress;
4019 : }
4020 : }
4021 :
4022 : return
4023 72 : rename_constraint_internal(relid, typid,
4024 72 : stmt->subname,
4025 72 : stmt->newname,
4026 138 : (stmt->relation &&
4027 66 : stmt->relation->inh), /* recursive? */
4028 : false, /* recursing? */
4029 : 0 /* expected inhcount */ );
4030 : }
4031 :
4032 : /*
4033 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4034 : * RENAME
4035 : */
4036 : ObjectAddress
4037 510 : RenameRelation(RenameStmt *stmt)
4038 : {
4039 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4040 : Oid relid;
4041 : ObjectAddress address;
4042 :
4043 : /*
4044 : * Grab an exclusive lock on the target table, index, sequence, view,
4045 : * materialized view, or foreign table, which we will NOT release until
4046 : * end of transaction.
4047 : *
4048 : * Lock level used here should match RenameRelationInternal, to avoid lock
4049 : * escalation. However, because ALTER INDEX can be used with any relation
4050 : * type, we mustn't believe without verification.
4051 : */
4052 : for (;;)
4053 12 : {
4054 : LOCKMODE lockmode;
4055 : char relkind;
4056 : bool obj_is_index;
4057 :
4058 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4059 :
4060 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4061 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4062 : RangeVarCallbackForAlterRelation,
4063 : (void *) stmt);
4064 :
4065 472 : if (!OidIsValid(relid))
4066 : {
4067 18 : ereport(NOTICE,
4068 : (errmsg("relation \"%s\" does not exist, skipping",
4069 : stmt->relation->relname)));
4070 18 : return InvalidObjectAddress;
4071 : }
4072 :
4073 : /*
4074 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4075 : * to rename a table), but we might've used the wrong lock level. If
4076 : * that happens, retry with the correct lock level. We don't bother
4077 : * if we already acquired AccessExclusiveLock with an index, however.
4078 : */
4079 454 : relkind = get_rel_relkind(relid);
4080 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4081 : relkind == RELKIND_PARTITIONED_INDEX);
4082 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4083 : break;
4084 :
4085 12 : UnlockRelationOid(relid, lockmode);
4086 12 : is_index_stmt = obj_is_index;
4087 : }
4088 :
4089 : /* Do the work */
4090 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4091 :
4092 430 : ObjectAddressSet(address, RelationRelationId, relid);
4093 :
4094 430 : return address;
4095 : }
4096 :
4097 : /*
4098 : * RenameRelationInternal - change the name of a relation
4099 : */
4100 : void
4101 1616 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4102 : {
4103 : Relation targetrelation;
4104 : Relation relrelation; /* for RELATION relation */
4105 : HeapTuple reltup;
4106 : Form_pg_class relform;
4107 : Oid namespaceId;
4108 :
4109 : /*
4110 : * Grab a lock on the target relation, which we will NOT release until end
4111 : * of transaction. We need at least a self-exclusive lock so that
4112 : * concurrent DDL doesn't overwrite the rename if they start updating
4113 : * while still seeing the old version. The lock also guards against
4114 : * triggering relcache reloads in concurrent sessions, which might not
4115 : * handle this information changing under them. For indexes, we can use a
4116 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4117 : * specially.
4118 : */
4119 1616 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4120 1616 : namespaceId = RelationGetNamespace(targetrelation);
4121 :
4122 : /*
4123 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4124 : */
4125 1616 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4126 :
4127 1616 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4128 1616 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4129 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4130 1616 : relform = (Form_pg_class) GETSTRUCT(reltup);
4131 :
4132 1616 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4133 12 : ereport(ERROR,
4134 : (errcode(ERRCODE_DUPLICATE_TABLE),
4135 : errmsg("relation \"%s\" already exists",
4136 : newrelname)));
4137 :
4138 : /*
4139 : * RenameRelation is careful not to believe the caller's idea of the
4140 : * relation kind being handled. We don't have to worry about this, but
4141 : * let's not be totally oblivious to it. We can process an index as
4142 : * not-an-index, but not the other way around.
4143 : */
4144 : Assert(!is_index ||
4145 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4146 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4147 :
4148 : /*
4149 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4150 : * because it's a copy...)
4151 : */
4152 1604 : namestrcpy(&(relform->relname), newrelname);
4153 :
4154 1604 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4155 :
4156 1604 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4157 : InvalidOid, is_internal);
4158 :
4159 1604 : heap_freetuple(reltup);
4160 1604 : table_close(relrelation, RowExclusiveLock);
4161 :
4162 : /*
4163 : * Also rename the associated type, if any.
4164 : */
4165 1604 : if (OidIsValid(targetrelation->rd_rel->reltype))
4166 166 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4167 : newrelname, namespaceId);
4168 :
4169 : /*
4170 : * Also rename the associated constraint, if any.
4171 : */
4172 1604 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4173 870 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4174 : {
4175 752 : Oid constraintId = get_index_constraint(myrelid);
4176 :
4177 752 : if (OidIsValid(constraintId))
4178 36 : RenameConstraintById(constraintId, newrelname);
4179 : }
4180 :
4181 : /*
4182 : * Close rel, but keep lock!
4183 : */
4184 1604 : relation_close(targetrelation, NoLock);
4185 1604 : }
4186 :
4187 : /*
4188 : * ResetRelRewrite - reset relrewrite
4189 : */
4190 : void
4191 552 : ResetRelRewrite(Oid myrelid)
4192 : {
4193 : Relation relrelation; /* for RELATION relation */
4194 : HeapTuple reltup;
4195 : Form_pg_class relform;
4196 :
4197 : /*
4198 : * Find relation's pg_class tuple.
4199 : */
4200 552 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4201 :
4202 552 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4203 552 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4204 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4205 552 : relform = (Form_pg_class) GETSTRUCT(reltup);
4206 :
4207 : /*
4208 : * Update pg_class tuple.
4209 : */
4210 552 : relform->relrewrite = InvalidOid;
4211 :
4212 552 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4213 :
4214 552 : heap_freetuple(reltup);
4215 552 : table_close(relrelation, RowExclusiveLock);
4216 552 : }
4217 :
4218 : /*
4219 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4220 : * any open reference to the target table besides the one just acquired by
4221 : * the calling command; this implies there's an open cursor or active plan.
4222 : * We need this check because our lock doesn't protect us against stomping
4223 : * on our own foot, only other people's feet!
4224 : *
4225 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4226 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4227 : * possibly be relaxed to only error out for certain types of alterations.
4228 : * But the use-case for allowing any of these things is not obvious, so we
4229 : * won't work hard at it for now.
4230 : *
4231 : * We also reject these commands if there are any pending AFTER trigger events
4232 : * for the rel. This is certainly necessary for the rewriting variants of
4233 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4234 : * events would try to fetch the wrong tuples. It might be overly cautious
4235 : * in other cases, but again it seems better to err on the side of paranoia.
4236 : *
4237 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4238 : * we are worried about active indexscans on the index. The trigger-event
4239 : * check can be skipped, since we are doing no damage to the parent table.
4240 : *
4241 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4242 : */
4243 : void
4244 128504 : CheckTableNotInUse(Relation rel, const char *stmt)
4245 : {
4246 : int expected_refcnt;
4247 :
4248 128504 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4249 128504 : if (rel->rd_refcnt != expected_refcnt)
4250 24 : ereport(ERROR,
4251 : (errcode(ERRCODE_OBJECT_IN_USE),
4252 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4253 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4254 : stmt, RelationGetRelationName(rel))));
4255 :
4256 128480 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4257 199012 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4258 98462 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4259 18 : ereport(ERROR,
4260 : (errcode(ERRCODE_OBJECT_IN_USE),
4261 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4262 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4263 : stmt, RelationGetRelationName(rel))));
4264 128462 : }
4265 :
4266 : /*
4267 : * CheckAlterTableIsSafe
4268 : * Verify that it's safe to allow ALTER TABLE on this relation.
4269 : *
4270 : * This consists of CheckTableNotInUse() plus a check that the relation
4271 : * isn't another session's temp table. We must split out the temp-table
4272 : * check because there are callers of CheckTableNotInUse() that don't want
4273 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4274 : * an orphaned temp schema.) Compare truncate_check_activity().
4275 : */
4276 : static void
4277 30264 : CheckAlterTableIsSafe(Relation rel)
4278 : {
4279 : /*
4280 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4281 : * manager is not going to cope if we need to change the table's contents.
4282 : * Even if we don't, there may be optimizations that assume temp tables
4283 : * aren't subject to such interference.
4284 : */
4285 30264 : if (RELATION_IS_OTHER_TEMP(rel))
4286 0 : ereport(ERROR,
4287 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4288 : errmsg("cannot alter temporary tables of other sessions")));
4289 :
4290 : /*
4291 : * Also check for active uses of the relation in the current transaction,
4292 : * including open scans and pending AFTER trigger events.
4293 : */
4294 30264 : CheckTableNotInUse(rel, "ALTER TABLE");
4295 30234 : }
4296 :
4297 : /*
4298 : * AlterTableLookupRelation
4299 : * Look up, and lock, the OID for the relation named by an alter table
4300 : * statement.
4301 : */
4302 : Oid
4303 27252 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4304 : {
4305 54408 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4306 27252 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4307 : RangeVarCallbackForAlterRelation,
4308 : (void *) stmt);
4309 : }
4310 :
4311 : /*
4312 : * AlterTable
4313 : * Execute ALTER TABLE, which can be a list of subcommands
4314 : *
4315 : * ALTER TABLE is performed in three phases:
4316 : * 1. Examine subcommands and perform pre-transformation checking.
4317 : * 2. Validate and transform subcommands, and update system catalogs.
4318 : * 3. Scan table(s) to check new constraints, and optionally recopy
4319 : * the data into new table(s).
4320 : * Phase 3 is not performed unless one or more of the subcommands requires
4321 : * it. The intention of this design is to allow multiple independent
4322 : * updates of the table schema to be performed with only one pass over the
4323 : * data.
4324 : *
4325 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4326 : * each table to be affected (there may be multiple affected tables if the
4327 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4328 : * validation of the subcommands. Because earlier subcommands may change
4329 : * the catalog state seen by later commands, there are limits to what can
4330 : * be done in this phase. Generally, this phase acquires table locks,
4331 : * checks permissions and relkind, and recurses to find child tables.
4332 : *
4333 : * ATRewriteCatalogs performs phase 2 for each affected table.
4334 : * Certain subcommands need to be performed before others to avoid
4335 : * unnecessary conflicts; for example, DROP COLUMN should come before
4336 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4337 : * lists, one for each logical "pass" of phase 2.
4338 : *
4339 : * ATRewriteTables performs phase 3 for those tables that need it.
4340 : *
4341 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4342 : * since phase 1 already does it. However, for certain subcommand types
4343 : * it is only possible to determine how to recurse at phase 2 time; for
4344 : * those cases, phase 1 sets the cmd->recurse flag.
4345 : *
4346 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4347 : * the whole operation; we don't have to do anything special to clean up.
4348 : *
4349 : * The caller must lock the relation, with an appropriate lock level
4350 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4351 : * or higher. We pass the lock level down
4352 : * so that we can apply it recursively to inherited tables. Note that the
4353 : * lock level we want as we recurse might well be higher than required for
4354 : * that specific subcommand. So we pass down the overall lock requirement,
4355 : * rather than reassess it at lower levels.
4356 : *
4357 : * The caller also provides a "context" which is to be passed back to
4358 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4359 : * Some of the fields therein, such as the relid, are used here as well.
4360 : */
4361 : void
4362 27018 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4363 : AlterTableUtilityContext *context)
4364 : {
4365 : Relation rel;
4366 :
4367 : /* Caller is required to provide an adequate lock. */
4368 27018 : rel = relation_open(context->relid, NoLock);
4369 :
4370 27018 : CheckAlterTableIsSafe(rel);
4371 :
4372 27000 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4373 24026 : }
4374 :
4375 : /*
4376 : * AlterTableInternal
4377 : *
4378 : * ALTER TABLE with target specified by OID
4379 : *
4380 : * We do not reject if the relation is already open, because it's quite
4381 : * likely that one or more layers of caller have it open. That means it
4382 : * is unsafe to use this entry point for alterations that could break
4383 : * existing query plans. On the assumption it's not used for such, we
4384 : * don't have to reject pending AFTER triggers, either.
4385 : *
4386 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4387 : * used for any subcommand types that require parse transformation or
4388 : * could generate subcommands that have to be passed to ProcessUtility.
4389 : */
4390 : void
4391 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4392 : {
4393 : Relation rel;
4394 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4395 :
4396 278 : rel = relation_open(relid, lockmode);
4397 :
4398 278 : EventTriggerAlterTableRelid(relid);
4399 :
4400 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4401 278 : }
4402 :
4403 : /*
4404 : * AlterTableGetLockLevel
4405 : *
4406 : * Sets the overall lock level required for the supplied list of subcommands.
4407 : * Policy for doing this set according to needs of AlterTable(), see
4408 : * comments there for overall explanation.
4409 : *
4410 : * Function is called before and after parsing, so it must give same
4411 : * answer each time it is called. Some subcommands are transformed
4412 : * into other subcommand types, so the transform must never be made to a
4413 : * lower lock level than previously assigned. All transforms are noted below.
4414 : *
4415 : * Since this is called before we lock the table we cannot use table metadata
4416 : * to influence the type of lock we acquire.
4417 : *
4418 : * There should be no lockmodes hardcoded into the subcommand functions. All
4419 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4420 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4421 : * and does not travel through this section of code and cannot be combined with
4422 : * any of the subcommands given here.
4423 : *
4424 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4425 : * so any changes that might affect SELECTs running on standbys need to use
4426 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4427 : * have a solution for that also.
4428 : *
4429 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4430 : * that takes a lock less than AccessExclusiveLock can change object definitions
4431 : * while pg_dump is running. Be careful to check that the appropriate data is
4432 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4433 : * otherwise we might end up with an inconsistent dump that can't restore.
4434 : */
4435 : LOCKMODE
4436 27530 : AlterTableGetLockLevel(List *cmds)
4437 : {
4438 : /*
4439 : * This only works if we read catalog tables using MVCC snapshots.
4440 : */
4441 : ListCell *lcmd;
4442 27530 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4443 :
4444 56016 : foreach(lcmd, cmds)
4445 : {
4446 28486 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4447 28486 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4448 :
4449 28486 : switch (cmd->subtype)
4450 : {
4451 : /*
4452 : * These subcommands rewrite the heap, so require full locks.
4453 : */
4454 3180 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4455 : * to SELECT */
4456 : case AT_SetAccessMethod: /* must rewrite heap */
4457 : case AT_SetTableSpace: /* must rewrite heap */
4458 : case AT_AlterColumnType: /* must rewrite heap */
4459 3180 : cmd_lockmode = AccessExclusiveLock;
4460 3180 : break;
4461 :
4462 : /*
4463 : * These subcommands may require addition of toast tables. If
4464 : * we add a toast table to a table currently being scanned, we
4465 : * might miss data added to the new toast table by concurrent
4466 : * insert transactions.
4467 : */
4468 212 : case AT_SetStorage: /* may add toast tables, see
4469 : * ATRewriteCatalogs() */
4470 212 : cmd_lockmode = AccessExclusiveLock;
4471 212 : break;
4472 :
4473 : /*
4474 : * Removing constraints can affect SELECTs that have been
4475 : * optimized assuming the constraint holds true. See also
4476 : * CloneFkReferenced.
4477 : */
4478 780 : case AT_DropConstraint: /* as DROP INDEX */
4479 : case AT_DropNotNull: /* may change some SQL plans */
4480 780 : cmd_lockmode = AccessExclusiveLock;
4481 780 : break;
4482 :
4483 : /*
4484 : * Subcommands that may be visible to concurrent SELECTs
4485 : */
4486 1698 : case AT_DropColumn: /* change visible to SELECT */
4487 : case AT_AddColumnToView: /* CREATE VIEW */
4488 : case AT_DropOids: /* used to equiv to DropColumn */
4489 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4490 : case AT_EnableReplicaRule: /* may change SELECT rules */
4491 : case AT_EnableRule: /* may change SELECT rules */
4492 : case AT_DisableRule: /* may change SELECT rules */
4493 1698 : cmd_lockmode = AccessExclusiveLock;
4494 1698 : break;
4495 :
4496 : /*
4497 : * Changing owner may remove implicit SELECT privileges
4498 : */
4499 1794 : case AT_ChangeOwner: /* change visible to SELECT */
4500 1794 : cmd_lockmode = AccessExclusiveLock;
4501 1794 : break;
4502 :
4503 : /*
4504 : * Changing foreign table options may affect optimization.
4505 : */
4506 238 : case AT_GenericOptions:
4507 : case AT_AlterColumnGenericOptions:
4508 238 : cmd_lockmode = AccessExclusiveLock;
4509 238 : break;
4510 :
4511 : /*
4512 : * These subcommands affect write operations only.
4513 : */
4514 340 : case AT_EnableTrig:
4515 : case AT_EnableAlwaysTrig:
4516 : case AT_EnableReplicaTrig:
4517 : case AT_EnableTrigAll:
4518 : case AT_EnableTrigUser:
4519 : case AT_DisableTrig:
4520 : case AT_DisableTrigAll:
4521 : case AT_DisableTrigUser:
4522 340 : cmd_lockmode = ShareRowExclusiveLock;
4523 340 : break;
4524 :
4525 : /*
4526 : * These subcommands affect write operations only. XXX
4527 : * Theoretically, these could be ShareRowExclusiveLock.
4528 : */
4529 2412 : case AT_ColumnDefault:
4530 : case AT_CookedColumnDefault:
4531 : case AT_AlterConstraint:
4532 : case AT_AddIndex: /* from ADD CONSTRAINT */
4533 : case AT_AddIndexConstraint:
4534 : case AT_ReplicaIdentity:
4535 : case AT_SetNotNull:
4536 : case AT_EnableRowSecurity:
4537 : case AT_DisableRowSecurity:
4538 : case AT_ForceRowSecurity:
4539 : case AT_NoForceRowSecurity:
4540 : case AT_AddIdentity:
4541 : case AT_DropIdentity:
4542 : case AT_SetIdentity:
4543 : case AT_SetExpression:
4544 : case AT_DropExpression:
4545 : case AT_SetCompression:
4546 2412 : cmd_lockmode = AccessExclusiveLock;
4547 2412 : break;
4548 :
4549 12118 : case AT_AddConstraint:
4550 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4551 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4552 12118 : if (IsA(cmd->def, Constraint))
4553 : {
4554 12118 : Constraint *con = (Constraint *) cmd->def;
4555 :
4556 12118 : switch (con->contype)
4557 : {
4558 9394 : case CONSTR_EXCLUSION:
4559 : case CONSTR_PRIMARY:
4560 : case CONSTR_UNIQUE:
4561 :
4562 : /*
4563 : * Cases essentially the same as CREATE INDEX. We
4564 : * could reduce the lock strength to ShareLock if
4565 : * we can work out how to allow concurrent catalog
4566 : * updates. XXX Might be set down to
4567 : * ShareRowExclusiveLock but requires further
4568 : * analysis.
4569 : */
4570 9394 : cmd_lockmode = AccessExclusiveLock;
4571 9394 : break;
4572 2040 : case CONSTR_FOREIGN:
4573 :
4574 : /*
4575 : * We add triggers to both tables when we add a
4576 : * Foreign Key, so the lock level must be at least
4577 : * as strong as CREATE TRIGGER.
4578 : */
4579 2040 : cmd_lockmode = ShareRowExclusiveLock;
4580 2040 : break;
4581 :
4582 684 : default:
4583 684 : cmd_lockmode = AccessExclusiveLock;
4584 : }
4585 0 : }
4586 12118 : break;
4587 :
4588 : /*
4589 : * These subcommands affect inheritance behaviour. Queries
4590 : * started before us will continue to see the old inheritance
4591 : * behaviour, while queries started after we commit will see
4592 : * new behaviour. No need to prevent reads or writes to the
4593 : * subtable while we hook it up though. Changing the TupDesc
4594 : * may be a problem, so keep highest lock.
4595 : */
4596 310 : case AT_AddInherit:
4597 : case AT_DropInherit:
4598 310 : cmd_lockmode = AccessExclusiveLock;
4599 310 : break;
4600 :
4601 : /*
4602 : * These subcommands affect implicit row type conversion. They
4603 : * have affects similar to CREATE/DROP CAST on queries. don't
4604 : * provide for invalidating parse trees as a result of such
4605 : * changes, so we keep these at AccessExclusiveLock.
4606 : */
4607 72 : case AT_AddOf:
4608 : case AT_DropOf:
4609 72 : cmd_lockmode = AccessExclusiveLock;
4610 72 : break;
4611 :
4612 : /*
4613 : * Only used by CREATE OR REPLACE VIEW which must conflict
4614 : * with an SELECTs currently using the view.
4615 : */
4616 194 : case AT_ReplaceRelOptions:
4617 194 : cmd_lockmode = AccessExclusiveLock;
4618 194 : break;
4619 :
4620 : /*
4621 : * These subcommands affect general strategies for performance
4622 : * and maintenance, though don't change the semantic results
4623 : * from normal data reads and writes. Delaying an ALTER TABLE
4624 : * behind currently active writes only delays the point where
4625 : * the new strategy begins to take effect, so there is no
4626 : * benefit in waiting. In this case the minimum restriction
4627 : * applies: we don't currently allow concurrent catalog
4628 : * updates.
4629 : */
4630 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4631 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4632 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4633 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4634 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4635 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4636 234 : break;
4637 :
4638 88 : case AT_SetLogged:
4639 : case AT_SetUnLogged:
4640 88 : cmd_lockmode = AccessExclusiveLock;
4641 88 : break;
4642 :
4643 388 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4644 388 : cmd_lockmode = ShareUpdateExclusiveLock;
4645 388 : break;
4646 :
4647 : /*
4648 : * Rel options are more complex than first appears. Options
4649 : * are set here for tables, views and indexes; for historical
4650 : * reasons these can all be used with ALTER TABLE, so we can't
4651 : * decide between them using the basic grammar.
4652 : */
4653 752 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4654 : * getTables() */
4655 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4656 : * getTables() */
4657 752 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4658 752 : break;
4659 :
4660 2624 : case AT_AttachPartition:
4661 2624 : cmd_lockmode = ShareUpdateExclusiveLock;
4662 2624 : break;
4663 :
4664 552 : case AT_DetachPartition:
4665 552 : if (((PartitionCmd *) cmd->def)->concurrent)
4666 158 : cmd_lockmode = ShareUpdateExclusiveLock;
4667 : else
4668 394 : cmd_lockmode = AccessExclusiveLock;
4669 552 : break;
4670 :
4671 14 : case AT_DetachPartitionFinalize:
4672 14 : cmd_lockmode = ShareUpdateExclusiveLock;
4673 14 : break;
4674 :
4675 300 : case AT_SplitPartition:
4676 300 : cmd_lockmode = AccessExclusiveLock;
4677 300 : break;
4678 :
4679 186 : case AT_MergePartitions:
4680 186 : cmd_lockmode = AccessExclusiveLock;
4681 186 : break;
4682 :
4683 0 : case AT_CheckNotNull:
4684 :
4685 : /*
4686 : * This only examines the table's schema; but lock must be
4687 : * strong enough to prevent concurrent DROP NOT NULL.
4688 : */
4689 0 : cmd_lockmode = AccessShareLock;
4690 0 : break;
4691 :
4692 0 : default: /* oops */
4693 0 : elog(ERROR, "unrecognized alter table type: %d",
4694 : (int) cmd->subtype);
4695 : break;
4696 : }
4697 :
4698 : /*
4699 : * Take the greatest lockmode from any subcommand
4700 : */
4701 28486 : if (cmd_lockmode > lockmode)
4702 23546 : lockmode = cmd_lockmode;
4703 : }
4704 :
4705 27530 : return lockmode;
4706 : }
4707 :
4708 : /*
4709 : * ATController provides top level control over the phases.
4710 : *
4711 : * parsetree is passed in to allow it to be passed to event triggers
4712 : * when requested.
4713 : */
4714 : static void
4715 27278 : ATController(AlterTableStmt *parsetree,
4716 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4717 : AlterTableUtilityContext *context)
4718 : {
4719 27278 : List *wqueue = NIL;
4720 : ListCell *lcmd;
4721 :
4722 : /* Phase 1: preliminary examination of commands, create work queue */
4723 55222 : foreach(lcmd, cmds)
4724 : {
4725 28228 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4726 :
4727 28228 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4728 : }
4729 :
4730 : /* Close the relation, but keep lock until commit */
4731 26994 : relation_close(rel, NoLock);
4732 :
4733 : /* Phase 2: update system catalogs */
4734 26994 : ATRewriteCatalogs(&wqueue, lockmode, context);
4735 :
4736 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4737 24616 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4738 24304 : }
4739 :
4740 : /*
4741 : * ATPrepCmd
4742 : *
4743 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4744 : * recursion and permission checks.
4745 : *
4746 : * Caller must have acquired appropriate lock type on relation already.
4747 : * This lock should be held until commit.
4748 : */
4749 : static void
4750 28920 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4751 : bool recurse, bool recursing, LOCKMODE lockmode,
4752 : AlterTableUtilityContext *context)
4753 : {
4754 : AlteredTableInfo *tab;
4755 28920 : AlterTablePass pass = AT_PASS_UNSET;
4756 :
4757 : /* Find or create work queue entry for this table */
4758 28920 : tab = ATGetQueueEntry(wqueue, rel);
4759 :
4760 : /*
4761 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4762 : * partitions that are pending detach.
4763 : */
4764 28920 : if (rel->rd_rel->relispartition &&
4765 2864 : cmd->subtype != AT_DetachPartitionFinalize &&
4766 1432 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4767 2 : ereport(ERROR,
4768 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4769 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4770 : RelationGetRelationName(rel)),
4771 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4772 :
4773 : /*
4774 : * Copy the original subcommand for each table, so we can scribble on it.
4775 : * This avoids conflicts when different child tables need to make
4776 : * different parse transformations (for example, the same column may have
4777 : * different column numbers in different children).
4778 : */
4779 28918 : cmd = copyObject(cmd);
4780 :
4781 : /*
4782 : * Do permissions and relkind checking, recursion to child tables if
4783 : * needed, and any additional phase-1 processing needed. (But beware of
4784 : * adding any processing that looks at table details that another
4785 : * subcommand could change. In some cases we reject multiple subcommands
4786 : * that could try to change the same state in contrary ways.)
4787 : */
4788 28918 : switch (cmd->subtype)
4789 : {
4790 1932 : case AT_AddColumn: /* ADD COLUMN */
4791 1932 : ATSimplePermissions(cmd->subtype, rel,
4792 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4793 1932 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4794 : lockmode, context);
4795 : /* Recursion occurs during execution phase */
4796 1920 : pass = AT_PASS_ADD_COL;
4797 1920 : break;
4798 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4799 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4800 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4801 : lockmode, context);
4802 : /* Recursion occurs during execution phase */
4803 24 : pass = AT_PASS_ADD_COL;
4804 24 : break;
4805 556 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4806 :
4807 : /*
4808 : * We allow defaults on views so that INSERT into a view can have
4809 : * default-ish behavior. This works because the rewriter
4810 : * substitutes default values into INSERTs before it expands
4811 : * rules.
4812 : */
4813 556 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4814 556 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4815 : /* No command-specific prep needed */
4816 556 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4817 556 : break;
4818 110 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4819 : /* This is currently used only in CREATE TABLE */
4820 : /* (so the permission check really isn't necessary) */
4821 110 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4822 : /* This command never recurses */
4823 110 : pass = AT_PASS_ADD_OTHERCONSTR;
4824 110 : break;
4825 156 : case AT_AddIdentity:
4826 156 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4827 : /* Set up recursion for phase 2; no other prep needed */
4828 156 : if (recurse)
4829 150 : cmd->recurse = true;
4830 156 : pass = AT_PASS_ADD_OTHERCONSTR;
4831 156 : break;
4832 62 : case AT_SetIdentity:
4833 62 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4834 : /* Set up recursion for phase 2; no other prep needed */
4835 62 : if (recurse)
4836 56 : cmd->recurse = true;
4837 : /* This should run after AddIdentity, so do it in MISC pass */
4838 62 : pass = AT_PASS_MISC;
4839 62 : break;
4840 56 : case AT_DropIdentity:
4841 56 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4842 : /* Set up recursion for phase 2; no other prep needed */
4843 56 : if (recurse)
4844 50 : cmd->recurse = true;
4845 56 : pass = AT_PASS_DROP;
4846 56 : break;
4847 236 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4848 236 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4849 224 : ATPrepDropNotNull(rel, recurse, recursing);
4850 218 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4851 212 : pass = AT_PASS_DROP;
4852 212 : break;
4853 590 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4854 590 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4855 : /* Need command-specific recursion decision */
4856 584 : ATPrepSetNotNull(wqueue, rel, cmd, recurse, recursing,
4857 : lockmode, context);
4858 584 : pass = AT_PASS_COL_ATTRS;
4859 584 : break;
4860 84 : case AT_CheckNotNull: /* check column is already marked NOT NULL */
4861 84 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4862 84 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4863 : /* No command-specific prep needed */
4864 84 : pass = AT_PASS_COL_ATTRS;
4865 84 : break;
4866 84 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4867 84 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4868 84 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4869 84 : pass = AT_PASS_SET_EXPRESSION;
4870 84 : break;
4871 44 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4872 44 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4873 44 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4874 44 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4875 32 : pass = AT_PASS_DROP;
4876 32 : break;
4877 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4878 164 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4879 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4880 : /* No command-specific prep needed */
4881 164 : pass = AT_PASS_MISC;
4882 164 : break;
4883 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4884 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4885 44 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4886 : /* This command never recurses */
4887 32 : pass = AT_PASS_MISC;
4888 32 : break;
4889 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4890 234 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4891 234 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4892 : /* No command-specific prep needed */
4893 234 : pass = AT_PASS_MISC;
4894 234 : break;
4895 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4896 68 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4897 : /* This command never recurses */
4898 : /* No command-specific prep needed */
4899 68 : pass = AT_PASS_MISC;
4900 68 : break;
4901 1604 : case AT_DropColumn: /* DROP COLUMN */
4902 1604 : ATSimplePermissions(cmd->subtype, rel,
4903 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4904 1598 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4905 : lockmode, context);
4906 : /* Recursion occurs during execution phase */
4907 1586 : pass = AT_PASS_DROP;
4908 1586 : break;
4909 0 : case AT_AddIndex: /* ADD INDEX */
4910 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4911 : /* This command never recurses */
4912 : /* No command-specific prep needed */
4913 0 : pass = AT_PASS_ADD_INDEX;
4914 0 : break;
4915 12092 : case AT_AddConstraint: /* ADD CONSTRAINT */
4916 12092 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4917 : /* Recursion occurs during execution phase */
4918 : /* No command-specific prep needed except saving recurse flag */
4919 12092 : if (recurse)
4920 11782 : cmd->recurse = true;
4921 12092 : pass = AT_PASS_ADD_CONSTR;
4922 12092 : break;
4923 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4924 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4925 : /* This command never recurses */
4926 : /* No command-specific prep needed */
4927 0 : pass = AT_PASS_ADD_INDEXCONSTR;
4928 0 : break;
4929 564 : case AT_DropConstraint: /* DROP CONSTRAINT */
4930 564 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4931 564 : ATCheckPartitionsNotInUse(rel, lockmode);
4932 : /* Other recursion occurs during execution phase */
4933 : /* No command-specific prep needed except saving recurse flag */
4934 558 : if (recurse)
4935 534 : cmd->recurse = true;
4936 558 : pass = AT_PASS_DROP;
4937 558 : break;
4938 1128 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4939 1128 : ATSimplePermissions(cmd->subtype, rel,
4940 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4941 : /* See comments for ATPrepAlterColumnType */
4942 1128 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4943 : AT_PASS_UNSET, context);
4944 : Assert(cmd != NULL);
4945 : /* Performs own recursion */
4946 1122 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4947 : lockmode, context);
4948 978 : pass = AT_PASS_ALTER_TYPE;
4949 978 : break;
4950 164 : case AT_AlterColumnGenericOptions:
4951 164 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
4952 : /* This command never recurses */
4953 : /* No command-specific prep needed */
4954 164 : pass = AT_PASS_MISC;
4955 164 : break;
4956 1770 : case AT_ChangeOwner: /* ALTER OWNER */
4957 : /* This command never recurses */
4958 : /* No command-specific prep needed */
4959 1770 : pass = AT_PASS_MISC;
4960 1770 : break;
4961 64 : case AT_ClusterOn: /* CLUSTER ON */
4962 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
4963 64 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4964 : /* These commands never recurse */
4965 : /* No command-specific prep needed */
4966 64 : pass = AT_PASS_MISC;
4967 64 : break;
4968 38 : case AT_SetLogged: /* SET LOGGED */
4969 38 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
4970 38 : if (tab->chgPersistence)
4971 0 : ereport(ERROR,
4972 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4973 : errmsg("cannot change persistence setting twice")));
4974 38 : tab->chgPersistence = ATPrepChangePersistence(rel, true);
4975 : /* force rewrite if necessary; see comment in ATRewriteTables */
4976 32 : if (tab->chgPersistence)
4977 : {
4978 26 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4979 26 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4980 : }
4981 32 : pass = AT_PASS_MISC;
4982 32 : break;
4983 50 : case AT_SetUnLogged: /* SET UNLOGGED */
4984 50 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
4985 50 : if (tab->chgPersistence)
4986 0 : ereport(ERROR,
4987 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4988 : errmsg("cannot change persistence setting twice")));
4989 50 : tab->chgPersistence = ATPrepChangePersistence(rel, false);
4990 : /* force rewrite if necessary; see comment in ATRewriteTables */
4991 44 : if (tab->chgPersistence)
4992 : {
4993 38 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4994 38 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
4995 : }
4996 44 : pass = AT_PASS_MISC;
4997 44 : break;
4998 6 : case AT_DropOids: /* SET WITHOUT OIDS */
4999 6 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5000 6 : pass = AT_PASS_DROP;
5001 6 : break;
5002 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5003 128 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5004 :
5005 : /* check if another access method change was already requested */
5006 128 : if (tab->chgAccessMethod)
5007 18 : ereport(ERROR,
5008 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5009 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5010 :
5011 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5012 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5013 110 : break;
5014 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5015 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
5016 : ATT_PARTITIONED_INDEX);
5017 : /* This command never recurses */
5018 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5019 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5020 158 : break;
5021 946 : case AT_SetRelOptions: /* SET (...) */
5022 : case AT_ResetRelOptions: /* RESET (...) */
5023 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5024 946 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
5025 : /* This command never recurses */
5026 : /* No command-specific prep needed */
5027 946 : pass = AT_PASS_MISC;
5028 946 : break;
5029 272 : case AT_AddInherit: /* INHERIT */
5030 272 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5031 : /* This command never recurses */
5032 272 : ATPrepAddInherit(rel);
5033 254 : pass = AT_PASS_MISC;
5034 254 : break;
5035 38 : case AT_DropInherit: /* NO INHERIT */
5036 38 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5037 : /* This command never recurses */
5038 : /* No command-specific prep needed */
5039 38 : pass = AT_PASS_MISC;
5040 38 : break;
5041 72 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5042 72 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5043 : /* Recursion occurs during execution phase */
5044 66 : pass = AT_PASS_MISC;
5045 66 : break;
5046 388 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5047 388 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5048 : /* Recursion occurs during execution phase */
5049 : /* No command-specific prep needed except saving recurse flag */
5050 388 : if (recurse)
5051 388 : cmd->recurse = true;
5052 388 : pass = AT_PASS_MISC;
5053 388 : break;
5054 418 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5055 418 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5056 418 : pass = AT_PASS_MISC;
5057 : /* This command never recurses */
5058 : /* No command-specific prep needed */
5059 418 : break;
5060 340 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5061 : case AT_EnableAlwaysTrig:
5062 : case AT_EnableReplicaTrig:
5063 : case AT_EnableTrigAll:
5064 : case AT_EnableTrigUser:
5065 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5066 : case AT_DisableTrigAll:
5067 : case AT_DisableTrigUser:
5068 340 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5069 : /* Set up recursion for phase 2; no other prep needed */
5070 340 : if (recurse)
5071 312 : cmd->recurse = true;
5072 340 : pass = AT_PASS_MISC;
5073 340 : break;
5074 532 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5075 : case AT_EnableAlwaysRule:
5076 : case AT_EnableReplicaRule:
5077 : case AT_DisableRule:
5078 : case AT_AddOf: /* OF */
5079 : case AT_DropOf: /* NOT OF */
5080 : case AT_EnableRowSecurity:
5081 : case AT_DisableRowSecurity:
5082 : case AT_ForceRowSecurity:
5083 : case AT_NoForceRowSecurity:
5084 532 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5085 : /* These commands never recurse */
5086 : /* No command-specific prep needed */
5087 532 : pass = AT_PASS_MISC;
5088 532 : break;
5089 50 : case AT_GenericOptions:
5090 50 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5091 : /* No command-specific prep needed */
5092 50 : pass = AT_PASS_MISC;
5093 50 : break;
5094 2612 : case AT_AttachPartition:
5095 2612 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
5096 : /* No command-specific prep needed */
5097 2612 : pass = AT_PASS_MISC;
5098 2612 : break;
5099 552 : case AT_DetachPartition:
5100 552 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5101 : /* No command-specific prep needed */
5102 546 : pass = AT_PASS_MISC;
5103 546 : break;
5104 14 : case AT_DetachPartitionFinalize:
5105 14 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5106 : /* No command-specific prep needed */
5107 14 : pass = AT_PASS_MISC;
5108 14 : break;
5109 294 : case AT_SplitPartition:
5110 294 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5111 : /* No command-specific prep needed */
5112 294 : pass = AT_PASS_MISC;
5113 294 : break;
5114 180 : case AT_MergePartitions:
5115 180 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5116 : /* No command-specific prep needed */
5117 180 : pass = AT_PASS_MISC;
5118 180 : break;
5119 0 : default: /* oops */
5120 0 : elog(ERROR, "unrecognized alter table type: %d",
5121 : (int) cmd->subtype);
5122 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5123 : break;
5124 : }
5125 : Assert(pass > AT_PASS_UNSET);
5126 :
5127 : /* Add the subcommand to the appropriate list for phase 2 */
5128 28618 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5129 28618 : }
5130 :
5131 : /*
5132 : * ATRewriteCatalogs
5133 : *
5134 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5135 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5136 : * conflicts).
5137 : */
5138 : static void
5139 26994 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5140 : AlterTableUtilityContext *context)
5141 : {
5142 : ListCell *ltab;
5143 :
5144 : /*
5145 : * We process all the tables "in parallel", one pass at a time. This is
5146 : * needed because we may have to propagate work from one table to another
5147 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5148 : * re-adding of the foreign key constraint to the other table). Work can
5149 : * only be propagated into later passes, however.
5150 : */
5151 340888 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5152 : {
5153 : /* Go through each table that needs to be processed */
5154 644944 : foreach(ltab, *wqueue)
5155 : {
5156 331050 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5157 331050 : List *subcmds = tab->subcmds[pass];
5158 : ListCell *lcmd;
5159 :
5160 331050 : if (subcmds == NIL)
5161 285302 : continue;
5162 :
5163 : /*
5164 : * Open the relation and store it in tab. This allows subroutines
5165 : * close and reopen, if necessary. Appropriate lock was obtained
5166 : * by phase 1, needn't get it again.
5167 : */
5168 45748 : tab->rel = relation_open(tab->relid, NoLock);
5169 :
5170 91658 : foreach(lcmd, subcmds)
5171 48288 : ATExecCmd(wqueue, tab,
5172 48288 : lfirst_node(AlterTableCmd, lcmd),
5173 : lockmode, pass, context);
5174 :
5175 : /*
5176 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5177 : * (this is not done in ATExecAlterColumnType since it should be
5178 : * done only once if multiple columns of a table are altered).
5179 : */
5180 43370 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5181 960 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5182 :
5183 43370 : if (tab->rel)
5184 : {
5185 43370 : relation_close(tab->rel, NoLock);
5186 43370 : tab->rel = NULL;
5187 : }
5188 : }
5189 : }
5190 :
5191 : /* Check to see if a toast table must be added. */
5192 52904 : foreach(ltab, *wqueue)
5193 : {
5194 28288 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5195 :
5196 : /*
5197 : * If the table is source table of ATTACH PARTITION command, we did
5198 : * not modify anything about it that will change its toasting
5199 : * requirement, so no need to check.
5200 : */
5201 28288 : if (((tab->relkind == RELKIND_RELATION ||
5202 5934 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5203 26432 : tab->partition_constraint == NULL) ||
5204 3802 : tab->relkind == RELKIND_MATVIEW)
5205 24536 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5206 : }
5207 24616 : }
5208 :
5209 : /*
5210 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5211 : */
5212 : static void
5213 48288 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5214 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5215 : AlterTableUtilityContext *context)
5216 : {
5217 48288 : ObjectAddress address = InvalidObjectAddress;
5218 48288 : Relation rel = tab->rel;
5219 :
5220 48288 : switch (cmd->subtype)
5221 : {
5222 1938 : case AT_AddColumn: /* ADD COLUMN */
5223 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5224 1938 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5225 1938 : cmd->recurse, false,
5226 : lockmode, cur_pass, context);
5227 1824 : break;
5228 544 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5229 544 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5230 484 : break;
5231 110 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5232 110 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5233 110 : break;
5234 156 : case AT_AddIdentity:
5235 156 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5236 : cur_pass, context);
5237 : Assert(cmd != NULL);
5238 150 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5239 102 : break;
5240 62 : case AT_SetIdentity:
5241 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5242 : cur_pass, context);
5243 : Assert(cmd != NULL);
5244 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5245 38 : break;
5246 56 : case AT_DropIdentity:
5247 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5248 38 : break;
5249 200 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5250 200 : address = ATExecDropNotNull(rel, cmd->name, lockmode);
5251 134 : break;
5252 7598 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5253 7598 : address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
5254 7562 : break;
5255 30 : case AT_CheckNotNull: /* check column is already marked NOT NULL */
5256 30 : ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
5257 18 : break;
5258 84 : case AT_SetExpression:
5259 84 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5260 78 : break;
5261 32 : case AT_DropExpression:
5262 32 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5263 26 : break;
5264 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5265 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5266 116 : break;
5267 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5268 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5269 26 : break;
5270 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5271 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5272 6 : break;
5273 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5274 234 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5275 222 : break;
5276 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5277 68 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5278 : lockmode);
5279 62 : break;
5280 1586 : case AT_DropColumn: /* DROP COLUMN */
5281 1586 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5282 1586 : cmd->behavior, cmd->recurse, false,
5283 1586 : cmd->missing_ok, lockmode,
5284 : NULL);
5285 1418 : break;
5286 934 : case AT_AddIndex: /* ADD INDEX */
5287 934 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5288 : lockmode);
5289 812 : break;
5290 416 : case AT_ReAddIndex: /* ADD INDEX */
5291 416 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5292 : lockmode);
5293 416 : break;
5294 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5295 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5296 : true, lockmode);
5297 14 : break;
5298 14846 : case AT_AddConstraint: /* ADD CONSTRAINT */
5299 : /* Transform the command only during initial examination */
5300 14846 : if (cur_pass == AT_PASS_ADD_CONSTR)
5301 12074 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5302 12092 : cmd->recurse, lockmode,
5303 : cur_pass, context);
5304 : /* Depending on constraint type, might be no more work to do now */
5305 14828 : if (cmd != NULL)
5306 : address =
5307 2754 : ATExecAddConstraint(wqueue, tab, rel,
5308 2754 : (Constraint *) cmd->def,
5309 2754 : cmd->recurse, false, lockmode);
5310 14454 : break;
5311 144 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5312 : address =
5313 144 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5314 : true, true, lockmode);
5315 132 : break;
5316 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5317 : * constraint */
5318 : address =
5319 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5320 14 : ((AlterDomainStmt *) cmd->def)->def,
5321 : NULL);
5322 8 : break;
5323 54 : case AT_ReAddComment: /* Re-add existing comment */
5324 54 : address = CommentObject((CommentStmt *) cmd->def);
5325 54 : break;
5326 8422 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5327 8422 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5328 : lockmode);
5329 8410 : break;
5330 66 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5331 66 : address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5332 54 : break;
5333 388 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5334 388 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5335 : false, lockmode);
5336 388 : break;
5337 558 : case AT_DropConstraint: /* DROP CONSTRAINT */
5338 558 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5339 558 : cmd->recurse, false,
5340 558 : cmd->missing_ok, lockmode);
5341 372 : break;
5342 948 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5343 : /* parse transformation was done earlier */
5344 948 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5345 912 : break;
5346 164 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5347 : address =
5348 164 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5349 164 : (List *) cmd->def, lockmode);
5350 158 : break;
5351 1770 : case AT_ChangeOwner: /* ALTER OWNER */
5352 1764 : ATExecChangeOwner(RelationGetRelid(rel),
5353 1770 : get_rolespec_oid(cmd->newowner, false),
5354 : false, lockmode);
5355 1752 : break;
5356 64 : case AT_ClusterOn: /* CLUSTER ON */
5357 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5358 58 : break;
5359 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5360 18 : ATExecDropCluster(rel, lockmode);
5361 12 : break;
5362 76 : case AT_SetLogged: /* SET LOGGED */
5363 : case AT_SetUnLogged: /* SET UNLOGGED */
5364 76 : break;
5365 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5366 : /* nothing to do here, oid columns don't exist anymore */
5367 6 : break;
5368 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5369 :
5370 : /*
5371 : * Only do this for partitioned tables, for which this is just a
5372 : * catalog change. Tables with storage are handled by Phase 3.
5373 : */
5374 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5375 50 : tab->chgAccessMethod)
5376 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5377 92 : break;
5378 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5379 :
5380 : /*
5381 : * Only do this for partitioned tables and indexes, for which this
5382 : * is just a catalog change. Other relation types which have
5383 : * storage are handled by Phase 3.
5384 : */
5385 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5386 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5387 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5388 :
5389 152 : break;
5390 946 : case AT_SetRelOptions: /* SET (...) */
5391 : case AT_ResetRelOptions: /* RESET (...) */
5392 : case AT_ReplaceRelOptions: /* replace entire option list */
5393 946 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5394 894 : break;
5395 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5396 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5397 : TRIGGER_FIRES_ON_ORIGIN, false,
5398 122 : cmd->recurse,
5399 : lockmode);
5400 122 : break;
5401 40 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5402 40 : ATExecEnableDisableTrigger(rel, cmd->name,
5403 : TRIGGER_FIRES_ALWAYS, false,
5404 40 : cmd->recurse,
5405 : lockmode);
5406 40 : break;
5407 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5408 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5409 : TRIGGER_FIRES_ON_REPLICA, false,
5410 16 : cmd->recurse,
5411 : lockmode);
5412 16 : break;
5413 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5414 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5415 : TRIGGER_DISABLED, false,
5416 138 : cmd->recurse,
5417 : lockmode);
5418 138 : break;
5419 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5420 0 : ATExecEnableDisableTrigger(rel, NULL,
5421 : TRIGGER_FIRES_ON_ORIGIN, false,
5422 0 : cmd->recurse,
5423 : lockmode);
5424 0 : break;
5425 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5426 12 : ATExecEnableDisableTrigger(rel, NULL,
5427 : TRIGGER_DISABLED, false,
5428 12 : cmd->recurse,
5429 : lockmode);
5430 12 : break;
5431 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5432 0 : ATExecEnableDisableTrigger(rel, NULL,
5433 : TRIGGER_FIRES_ON_ORIGIN, true,
5434 0 : cmd->recurse,
5435 : lockmode);
5436 0 : break;
5437 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5438 12 : ATExecEnableDisableTrigger(rel, NULL,
5439 : TRIGGER_DISABLED, true,
5440 12 : cmd->recurse,
5441 : lockmode);
5442 12 : break;
5443 :
5444 8 : case AT_EnableRule: /* ENABLE RULE name */
5445 8 : ATExecEnableDisableRule(rel, cmd->name,
5446 : RULE_FIRES_ON_ORIGIN, lockmode);
5447 8 : break;
5448 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5449 0 : ATExecEnableDisableRule(rel, cmd->name,
5450 : RULE_FIRES_ALWAYS, lockmode);
5451 0 : break;
5452 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5453 6 : ATExecEnableDisableRule(rel, cmd->name,
5454 : RULE_FIRES_ON_REPLICA, lockmode);
5455 6 : break;
5456 32 : case AT_DisableRule: /* DISABLE RULE name */
5457 32 : ATExecEnableDisableRule(rel, cmd->name,
5458 : RULE_DISABLED, lockmode);
5459 32 : break;
5460 :
5461 254 : case AT_AddInherit:
5462 254 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5463 182 : break;
5464 38 : case AT_DropInherit:
5465 38 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5466 32 : break;
5467 66 : case AT_AddOf:
5468 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5469 30 : break;
5470 6 : case AT_DropOf:
5471 6 : ATExecDropOf(rel, lockmode);
5472 6 : break;
5473 436 : case AT_ReplicaIdentity:
5474 436 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5475 388 : break;
5476 284 : case AT_EnableRowSecurity:
5477 284 : ATExecSetRowSecurity(rel, true);
5478 284 : break;
5479 10 : case AT_DisableRowSecurity:
5480 10 : ATExecSetRowSecurity(rel, false);
5481 10 : break;
5482 88 : case AT_ForceRowSecurity:
5483 88 : ATExecForceNoForceRowSecurity(rel, true);
5484 88 : break;
5485 32 : case AT_NoForceRowSecurity:
5486 32 : ATExecForceNoForceRowSecurity(rel, false);
5487 32 : break;
5488 50 : case AT_GenericOptions:
5489 50 : ATExecGenericOptions(rel, (List *) cmd->def);
5490 48 : break;
5491 2612 : case AT_AttachPartition:
5492 2612 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5493 : cur_pass, context);
5494 : Assert(cmd != NULL);
5495 2582 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5496 2190 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5497 : context);
5498 : else
5499 392 : address = ATExecAttachPartitionIdx(wqueue, rel,
5500 392 : ((PartitionCmd *) cmd->def)->name);
5501 2258 : break;
5502 546 : case AT_DetachPartition:
5503 546 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5504 : cur_pass, context);
5505 : Assert(cmd != NULL);
5506 : /* ATPrepCmd ensures it must be a table */
5507 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5508 540 : address = ATExecDetachPartition(wqueue, tab, rel,
5509 540 : ((PartitionCmd *) cmd->def)->name,
5510 540 : ((PartitionCmd *) cmd->def)->concurrent);
5511 410 : break;
5512 14 : case AT_DetachPartitionFinalize:
5513 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5514 14 : break;
5515 294 : case AT_SplitPartition:
5516 294 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5517 : cur_pass, context);
5518 : Assert(cmd != NULL);
5519 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5520 156 : ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5521 : context);
5522 150 : break;
5523 180 : case AT_MergePartitions:
5524 180 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5525 : cur_pass, context);
5526 : Assert(cmd != NULL);
5527 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5528 114 : ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5529 : context);
5530 102 : break;
5531 0 : default: /* oops */
5532 0 : elog(ERROR, "unrecognized alter table type: %d",
5533 : (int) cmd->subtype);
5534 : break;
5535 : }
5536 :
5537 : /*
5538 : * Report the subcommand to interested event triggers.
5539 : */
5540 45910 : if (cmd)
5541 33836 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5542 :
5543 : /*
5544 : * Bump the command counter to ensure the next subcommand in the sequence
5545 : * can see the changes so far
5546 : */
5547 45910 : CommandCounterIncrement();
5548 45910 : }
5549 :
5550 : /*
5551 : * ATParseTransformCmd: perform parse transformation for one subcommand
5552 : *
5553 : * Returns the transformed subcommand tree, if there is one, else NULL.
5554 : *
5555 : * The parser may hand back additional AlterTableCmd(s) and/or other
5556 : * utility statements, either before or after the original subcommand.
5557 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5558 : * AlteredTableInfo (they had better be for later passes than the current one).
5559 : * Utility statements that are supposed to happen before the AlterTableCmd
5560 : * are executed immediately. Those that are supposed to happen afterwards
5561 : * are added to the tab->afterStmts list to be done at the very end.
5562 : */
5563 : static AlterTableCmd *
5564 18888 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5565 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5566 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5567 : {
5568 18888 : AlterTableCmd *newcmd = NULL;
5569 18888 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5570 : List *beforeStmts;
5571 : List *afterStmts;
5572 : ListCell *lc;
5573 :
5574 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5575 18888 : atstmt->relation =
5576 18888 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5577 18888 : pstrdup(RelationGetRelationName(rel)),
5578 : -1);
5579 18888 : atstmt->relation->inh = recurse;
5580 18888 : atstmt->cmds = list_make1(cmd);
5581 18888 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5582 18888 : atstmt->missing_ok = false;
5583 :
5584 : /* Transform the AlterTableStmt */
5585 18888 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5586 : atstmt,
5587 : context->queryString,
5588 : &beforeStmts,
5589 : &afterStmts);
5590 :
5591 : /* Execute any statements that should happen before these subcommand(s) */
5592 19096 : foreach(lc, beforeStmts)
5593 : {
5594 478 : Node *stmt = (Node *) lfirst(lc);
5595 :
5596 478 : ProcessUtilityForAlterTable(stmt, context);
5597 466 : CommandCounterIncrement();
5598 : }
5599 :
5600 : /* Examine the transformed subcommands and schedule them appropriately */
5601 44310 : foreach(lc, atstmt->cmds)
5602 : {
5603 25692 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5604 : AlterTablePass pass;
5605 :
5606 : /*
5607 : * This switch need only cover the subcommand types that can be added
5608 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5609 : * executing the subcommand immediately, as a substitute for the
5610 : * original subcommand. (Note, however, that this does cause
5611 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5612 : * which is important for index and foreign key constraints.)
5613 : *
5614 : * We assume we needn't do any phase-1 checks for added subcommands.
5615 : */
5616 25692 : switch (cmd2->subtype)
5617 : {
5618 7014 : case AT_SetNotNull:
5619 : /* Need command-specific recursion decision */
5620 7014 : ATPrepSetNotNull(wqueue, rel, cmd2,
5621 : recurse, false,
5622 : lockmode, context);
5623 7014 : pass = AT_PASS_COL_ATTRS;
5624 7014 : break;
5625 958 : case AT_AddIndex:
5626 : /* This command never recurses */
5627 : /* No command-specific prep needed */
5628 958 : pass = AT_PASS_ADD_INDEX;
5629 958 : break;
5630 8422 : case AT_AddIndexConstraint:
5631 : /* This command never recurses */
5632 : /* No command-specific prep needed */
5633 8422 : pass = AT_PASS_ADD_INDEXCONSTR;
5634 8422 : break;
5635 2754 : case AT_AddConstraint:
5636 : /* Recursion occurs during execution phase */
5637 2754 : if (recurse)
5638 2712 : cmd2->recurse = true;
5639 2754 : switch (castNode(Constraint, cmd2->def)->contype)
5640 : {
5641 0 : case CONSTR_PRIMARY:
5642 : case CONSTR_UNIQUE:
5643 : case CONSTR_EXCLUSION:
5644 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5645 0 : break;
5646 2754 : default:
5647 2754 : pass = AT_PASS_ADD_OTHERCONSTR;
5648 2754 : break;
5649 : }
5650 2754 : break;
5651 0 : case AT_AlterColumnGenericOptions:
5652 : /* This command never recurses */
5653 : /* No command-specific prep needed */
5654 0 : pass = AT_PASS_MISC;
5655 0 : break;
5656 6544 : default:
5657 6544 : pass = cur_pass;
5658 6544 : break;
5659 : }
5660 :
5661 25692 : if (pass < cur_pass)
5662 : {
5663 : /* Cannot schedule into a pass we already finished */
5664 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5665 : pass);
5666 : }
5667 25692 : else if (pass > cur_pass)
5668 : {
5669 : /* OK, queue it up for later */
5670 19148 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5671 : }
5672 : else
5673 : {
5674 : /*
5675 : * We should see at most one subcommand for the current pass,
5676 : * which is the transformed version of the original subcommand.
5677 : */
5678 6544 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5679 : {
5680 : /* Found the transformed version of our subcommand */
5681 6544 : newcmd = cmd2;
5682 : }
5683 : else
5684 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5685 : pass);
5686 : }
5687 : }
5688 :
5689 : /* Queue up any after-statements to happen at the end */
5690 18618 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5691 :
5692 18618 : return newcmd;
5693 : }
5694 :
5695 : /*
5696 : * ATRewriteTables: ALTER TABLE phase 3
5697 : */
5698 : static void
5699 24616 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5700 : AlterTableUtilityContext *context)
5701 : {
5702 : ListCell *ltab;
5703 :
5704 : /* Go through each table that needs to be checked or rewritten */
5705 52648 : foreach(ltab, *wqueue)
5706 : {
5707 28276 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5708 :
5709 : /* Relations without storage may be ignored here */
5710 28276 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5711 5658 : continue;
5712 :
5713 : /*
5714 : * If we change column data types, the operation has to be propagated
5715 : * to tables that use this table's rowtype as a column type.
5716 : * tab->newvals will also be non-NULL in the case where we're adding a
5717 : * column with a default. We choose to forbid that case as well,
5718 : * since composite types might eventually support defaults.
5719 : *
5720 : * (Eventually we'll probably need to check for composite type
5721 : * dependencies even when we're just scanning the table without a
5722 : * rewrite, but at the moment a composite type does not enforce any
5723 : * constraints, so it's not necessary/appropriate to enforce them just
5724 : * during ALTER.)
5725 : */
5726 22618 : if (tab->newvals != NIL || tab->rewrite > 0)
5727 : {
5728 : Relation rel;
5729 :
5730 1454 : rel = table_open(tab->relid, NoLock);
5731 1454 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5732 1442 : table_close(rel, NoLock);
5733 : }
5734 :
5735 : /*
5736 : * We only need to rewrite the table if at least one column needs to
5737 : * be recomputed, or we are changing its persistence or access method.
5738 : *
5739 : * There are two reasons for requiring a rewrite when changing
5740 : * persistence: on one hand, we need to ensure that the buffers
5741 : * belonging to each of the two relations are marked with or without
5742 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5743 : * and assigns a new relfilenumber, we automatically create or drop an
5744 : * init fork for the relation as appropriate.
5745 : */
5746 22606 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5747 830 : {
5748 : /* Build a temporary relation and copy data */
5749 : Relation OldHeap;
5750 : Oid OIDNewHeap;
5751 : Oid NewAccessMethod;
5752 : Oid NewTableSpace;
5753 : char persistence;
5754 :
5755 868 : OldHeap = table_open(tab->relid, NoLock);
5756 :
5757 : /*
5758 : * We don't support rewriting of system catalogs; there are too
5759 : * many corner cases and too little benefit. In particular this
5760 : * is certainly not going to work for mapped catalogs.
5761 : */
5762 868 : if (IsSystemRelation(OldHeap))
5763 0 : ereport(ERROR,
5764 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5765 : errmsg("cannot rewrite system relation \"%s\"",
5766 : RelationGetRelationName(OldHeap))));
5767 :
5768 868 : if (RelationIsUsedAsCatalogTable(OldHeap))
5769 2 : ereport(ERROR,
5770 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5771 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5772 : RelationGetRelationName(OldHeap))));
5773 :
5774 : /*
5775 : * Don't allow rewrite on temp tables of other backends ... their
5776 : * local buffer manager is not going to cope. (This is redundant
5777 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5778 : * check here too.)
5779 : */
5780 866 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5781 0 : ereport(ERROR,
5782 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5783 : errmsg("cannot rewrite temporary tables of other sessions")));
5784 :
5785 : /*
5786 : * Select destination tablespace (same as original unless user
5787 : * requested a change)
5788 : */
5789 866 : if (tab->newTableSpace)
5790 0 : NewTableSpace = tab->newTableSpace;
5791 : else
5792 866 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5793 :
5794 : /*
5795 : * Select destination access method (same as original unless user
5796 : * requested a change)
5797 : */
5798 866 : if (tab->chgAccessMethod)
5799 36 : NewAccessMethod = tab->newAccessMethod;
5800 : else
5801 830 : NewAccessMethod = OldHeap->rd_rel->relam;
5802 :
5803 : /*
5804 : * Select persistence of transient table (same as original unless
5805 : * user requested a change)
5806 : */
5807 866 : persistence = tab->chgPersistence ?
5808 814 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5809 :
5810 866 : table_close(OldHeap, NoLock);
5811 :
5812 : /*
5813 : * Fire off an Event Trigger now, before actually rewriting the
5814 : * table.
5815 : *
5816 : * We don't support Event Trigger for nested commands anywhere,
5817 : * here included, and parsetree is given NULL when coming from
5818 : * AlterTableInternal.
5819 : *
5820 : * And fire it only once.
5821 : */
5822 866 : if (parsetree)
5823 866 : EventTriggerTableRewrite((Node *) parsetree,
5824 : tab->relid,
5825 : tab->rewrite);
5826 :
5827 : /*
5828 : * Create transient table that will receive the modified data.
5829 : *
5830 : * Ensure it is marked correctly as logged or unlogged. We have
5831 : * to do this here so that buffers for the new relfilenumber will
5832 : * have the right persistence set, and at the same time ensure
5833 : * that the original filenumbers's buffers will get read in with
5834 : * the correct setting (i.e. the original one). Otherwise a
5835 : * rollback after the rewrite would possibly result with buffers
5836 : * for the original filenumbers having the wrong persistence
5837 : * setting.
5838 : *
5839 : * NB: This relies on swap_relation_files() also swapping the
5840 : * persistence. That wouldn't work for pg_class, but that can't be
5841 : * unlogged anyway.
5842 : */
5843 860 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5844 : persistence, lockmode);
5845 :
5846 : /*
5847 : * Copy the heap data into the new table with the desired
5848 : * modifications, and test the current data within the table
5849 : * against new constraints generated by ALTER TABLE commands.
5850 : */
5851 860 : ATRewriteTable(tab, OIDNewHeap, lockmode);
5852 :
5853 : /*
5854 : * Swap the physical files of the old and new heaps, then rebuild
5855 : * indexes and discard the old heap. We can use RecentXmin for
5856 : * the table's new relfrozenxid because we rewrote all the tuples
5857 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5858 : * we never try to swap toast tables by content, since we have no
5859 : * interest in letting this code work on system catalogs.
5860 : */
5861 836 : finish_heap_swap(tab->relid, OIDNewHeap,
5862 : false, false, true,
5863 836 : !OidIsValid(tab->newTableSpace),
5864 : RecentXmin,
5865 : ReadNextMultiXactId(),
5866 : persistence);
5867 :
5868 830 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5869 : }
5870 21738 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5871 : {
5872 12 : if (tab->chgPersistence)
5873 12 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5874 : }
5875 : else
5876 : {
5877 : /*
5878 : * If required, test the current data within the table against new
5879 : * constraints generated by ALTER TABLE commands, but don't
5880 : * rebuild data.
5881 : */
5882 21726 : if (tab->constraints != NIL || tab->verify_new_notnull ||
5883 19540 : tab->partition_constraint != NULL)
5884 3996 : ATRewriteTable(tab, InvalidOid, lockmode);
5885 :
5886 : /*
5887 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5888 : * just do a block-by-block copy.
5889 : */
5890 21532 : if (tab->newTableSpace)
5891 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5892 : }
5893 :
5894 : /*
5895 : * Also change persistence of owned sequences, so that it matches the
5896 : * table persistence.
5897 : */
5898 22374 : if (tab->chgPersistence)
5899 : {
5900 64 : List *seqlist = getOwnedSequences(tab->relid);
5901 : ListCell *lc;
5902 :
5903 112 : foreach(lc, seqlist)
5904 : {
5905 48 : Oid seq_relid = lfirst_oid(lc);
5906 :
5907 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5908 : }
5909 : }
5910 : }
5911 :
5912 : /*
5913 : * Foreign key constraints are checked in a final pass, since (a) it's
5914 : * generally best to examine each one separately, and (b) it's at least
5915 : * theoretically possible that we have changed both relations of the
5916 : * foreign key, and we'd better have finished both rewrites before we try
5917 : * to read the tables.
5918 : */
5919 52212 : foreach(ltab, *wqueue)
5920 : {
5921 27908 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5922 27908 : Relation rel = NULL;
5923 : ListCell *lcon;
5924 :
5925 : /* Relations without storage may be ignored here too */
5926 27908 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5927 5584 : continue;
5928 :
5929 23858 : foreach(lcon, tab->constraints)
5930 : {
5931 1602 : NewConstraint *con = lfirst(lcon);
5932 :
5933 1602 : if (con->contype == CONSTR_FOREIGN)
5934 : {
5935 944 : Constraint *fkconstraint = (Constraint *) con->qual;
5936 : Relation refrel;
5937 :
5938 944 : if (rel == NULL)
5939 : {
5940 : /* Long since locked, no need for another */
5941 932 : rel = table_open(tab->relid, NoLock);
5942 : }
5943 :
5944 944 : refrel = table_open(con->refrelid, RowShareLock);
5945 :
5946 944 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5947 : con->refindid,
5948 : con->conid);
5949 :
5950 : /*
5951 : * No need to mark the constraint row as validated, we did
5952 : * that when we inserted the row earlier.
5953 : */
5954 :
5955 876 : table_close(refrel, NoLock);
5956 : }
5957 : }
5958 :
5959 22256 : if (rel)
5960 864 : table_close(rel, NoLock);
5961 : }
5962 :
5963 : /* Finally, run any afterStmts that were queued up */
5964 52106 : foreach(ltab, *wqueue)
5965 : {
5966 27802 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5967 : ListCell *lc;
5968 :
5969 27888 : foreach(lc, tab->afterStmts)
5970 : {
5971 86 : Node *stmt = (Node *) lfirst(lc);
5972 :
5973 86 : ProcessUtilityForAlterTable(stmt, context);
5974 86 : CommandCounterIncrement();
5975 : }
5976 : }
5977 24304 : }
5978 :
5979 : /*
5980 : * ATRewriteTable: scan or rewrite one table
5981 : *
5982 : * OIDNewHeap is InvalidOid if we don't need to rewrite
5983 : */
5984 : static void
5985 4856 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
5986 : {
5987 : Relation oldrel;
5988 : Relation newrel;
5989 : TupleDesc oldTupDesc;
5990 : TupleDesc newTupDesc;
5991 4856 : bool needscan = false;
5992 : List *notnull_attrs;
5993 : int i;
5994 : ListCell *l;
5995 : EState *estate;
5996 : CommandId mycid;
5997 : BulkInsertState bistate;
5998 : int ti_options;
5999 4856 : ExprState *partqualstate = NULL;
6000 :
6001 : /*
6002 : * Open the relation(s). We have surely already locked the existing
6003 : * table.
6004 : */
6005 4856 : oldrel = table_open(tab->relid, NoLock);
6006 4856 : oldTupDesc = tab->oldDesc;
6007 4856 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6008 :
6009 4856 : if (OidIsValid(OIDNewHeap))
6010 860 : newrel = table_open(OIDNewHeap, lockmode);
6011 : else
6012 3996 : newrel = NULL;
6013 :
6014 : /*
6015 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6016 : * is empty, so don't bother using it.
6017 : */
6018 4856 : if (newrel)
6019 : {
6020 860 : mycid = GetCurrentCommandId(true);
6021 860 : bistate = GetBulkInsertState();
6022 860 : ti_options = TABLE_INSERT_SKIP_FSM;
6023 : }
6024 : else
6025 : {
6026 : /* keep compiler quiet about using these uninitialized */
6027 3996 : mycid = 0;
6028 3996 : bistate = NULL;
6029 3996 : ti_options = 0;
6030 : }
6031 :
6032 : /*
6033 : * Generate the constraint and default execution states
6034 : */
6035 :
6036 4856 : estate = CreateExecutorState();
6037 :
6038 : /* Build the needed expression execution states */
6039 6554 : foreach(l, tab->constraints)
6040 : {
6041 1698 : NewConstraint *con = lfirst(l);
6042 :
6043 1698 : switch (con->contype)
6044 : {
6045 748 : case CONSTR_CHECK:
6046 748 : needscan = true;
6047 748 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6048 748 : break;
6049 950 : case CONSTR_FOREIGN:
6050 : /* Nothing to do here */
6051 950 : break;
6052 0 : default:
6053 0 : elog(ERROR, "unrecognized constraint type: %d",
6054 : (int) con->contype);
6055 : }
6056 : }
6057 :
6058 : /* Build expression execution states for partition check quals */
6059 4856 : if (tab->partition_constraint)
6060 : {
6061 1940 : needscan = true;
6062 1940 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6063 : }
6064 :
6065 5712 : foreach(l, tab->newvals)
6066 : {
6067 856 : NewColumnValue *ex = lfirst(l);
6068 :
6069 : /* expr already planned */
6070 856 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6071 : }
6072 :
6073 4856 : notnull_attrs = NIL;
6074 4856 : if (newrel || tab->verify_new_notnull)
6075 : {
6076 : /*
6077 : * If we are rebuilding the tuples OR if we added any new but not
6078 : * verified not-null constraints, check all not-null constraints. This
6079 : * is a bit of overkill but it minimizes risk of bugs, and
6080 : * heap_attisnull is a pretty cheap test anyway.
6081 : */
6082 5600 : for (i = 0; i < newTupDesc->natts; i++)
6083 : {
6084 4106 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6085 :
6086 4106 : if (attr->attnotnull && !attr->attisdropped)
6087 1528 : notnull_attrs = lappend_int(notnull_attrs, i);
6088 : }
6089 1494 : if (notnull_attrs)
6090 1086 : needscan = true;
6091 : }
6092 :
6093 4856 : if (newrel || needscan)
6094 : {
6095 : ExprContext *econtext;
6096 : TupleTableSlot *oldslot;
6097 : TupleTableSlot *newslot;
6098 : TableScanDesc scan;
6099 : MemoryContext oldCxt;
6100 4090 : List *dropped_attrs = NIL;
6101 : ListCell *lc;
6102 : Snapshot snapshot;
6103 :
6104 4090 : if (newrel)
6105 860 : ereport(DEBUG1,
6106 : (errmsg_internal("rewriting table \"%s\"",
6107 : RelationGetRelationName(oldrel))));
6108 : else
6109 3230 : ereport(DEBUG1,
6110 : (errmsg_internal("verifying table \"%s\"",
6111 : RelationGetRelationName(oldrel))));
6112 :
6113 4090 : if (newrel)
6114 : {
6115 : /*
6116 : * All predicate locks on the tuples or pages are about to be made
6117 : * invalid, because we move tuples around. Promote them to
6118 : * relation locks.
6119 : */
6120 860 : TransferPredicateLocksToHeapRelation(oldrel);
6121 : }
6122 :
6123 4090 : econtext = GetPerTupleExprContext(estate);
6124 :
6125 : /*
6126 : * Create necessary tuple slots. When rewriting, two slots are needed,
6127 : * otherwise one suffices. In the case where one slot suffices, we
6128 : * need to use the new tuple descriptor, otherwise some constraints
6129 : * can't be evaluated. Note that even when the tuple layout is the
6130 : * same and no rewrite is required, the tupDescs might not be
6131 : * (consider ADD COLUMN without a default).
6132 : */
6133 4090 : if (tab->rewrite)
6134 : {
6135 : Assert(newrel != NULL);
6136 860 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6137 : table_slot_callbacks(oldrel));
6138 860 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6139 : table_slot_callbacks(newrel));
6140 :
6141 : /*
6142 : * Set all columns in the new slot to NULL initially, to ensure
6143 : * columns added as part of the rewrite are initialized to NULL.
6144 : * That is necessary as tab->newvals will not contain an
6145 : * expression for columns with a NULL default, e.g. when adding a
6146 : * column without a default together with a column with a default
6147 : * requiring an actual rewrite.
6148 : */
6149 860 : ExecStoreAllNullTuple(newslot);
6150 : }
6151 : else
6152 : {
6153 3230 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6154 : table_slot_callbacks(oldrel));
6155 3230 : newslot = NULL;
6156 : }
6157 :
6158 : /*
6159 : * Any attributes that are dropped according to the new tuple
6160 : * descriptor can be set to NULL. We precompute the list of dropped
6161 : * attributes to avoid needing to do so in the per-tuple loop.
6162 : */
6163 14516 : for (i = 0; i < newTupDesc->natts; i++)
6164 : {
6165 10426 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6166 790 : dropped_attrs = lappend_int(dropped_attrs, i);
6167 : }
6168 :
6169 : /*
6170 : * Scan through the rows, generating a new row if needed and then
6171 : * checking all the constraints.
6172 : */
6173 4090 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6174 4090 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6175 :
6176 : /*
6177 : * Switch to per-tuple memory context and reset it for each tuple
6178 : * produced, so we don't leak memory.
6179 : */
6180 4090 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6181 :
6182 766644 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6183 : {
6184 : TupleTableSlot *insertslot;
6185 :
6186 762772 : if (tab->rewrite > 0)
6187 : {
6188 : /* Extract data from old tuple */
6189 97564 : slot_getallattrs(oldslot);
6190 97564 : ExecClearTuple(newslot);
6191 :
6192 : /* copy attributes */
6193 97564 : memcpy(newslot->tts_values, oldslot->tts_values,
6194 97564 : sizeof(Datum) * oldslot->tts_nvalid);
6195 97564 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6196 97564 : sizeof(bool) * oldslot->tts_nvalid);
6197 :
6198 : /* Set dropped attributes to null in new tuple */
6199 97650 : foreach(lc, dropped_attrs)
6200 86 : newslot->tts_isnull[lfirst_int(lc)] = true;
6201 :
6202 : /*
6203 : * Constraints and GENERATED expressions might reference the
6204 : * tableoid column, so fill tts_tableOid with the desired
6205 : * value. (We must do this each time, because it gets
6206 : * overwritten with newrel's OID during storing.)
6207 : */
6208 97564 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6209 :
6210 : /*
6211 : * Process supplied expressions to replace selected columns.
6212 : *
6213 : * First, evaluate expressions whose inputs come from the old
6214 : * tuple.
6215 : */
6216 97564 : econtext->ecxt_scantuple = oldslot;
6217 :
6218 200948 : foreach(l, tab->newvals)
6219 : {
6220 103396 : NewColumnValue *ex = lfirst(l);
6221 :
6222 103396 : if (ex->is_generated)
6223 150 : continue;
6224 :
6225 103246 : newslot->tts_values[ex->attnum - 1]
6226 103234 : = ExecEvalExpr(ex->exprstate,
6227 : econtext,
6228 103246 : &newslot->tts_isnull[ex->attnum - 1]);
6229 : }
6230 :
6231 97552 : ExecStoreVirtualTuple(newslot);
6232 :
6233 : /*
6234 : * Now, evaluate any expressions whose inputs come from the
6235 : * new tuple. We assume these columns won't reference each
6236 : * other, so that there's no ordering dependency.
6237 : */
6238 97552 : econtext->ecxt_scantuple = newslot;
6239 :
6240 200936 : foreach(l, tab->newvals)
6241 : {
6242 103384 : NewColumnValue *ex = lfirst(l);
6243 :
6244 103384 : if (!ex->is_generated)
6245 103234 : continue;
6246 :
6247 150 : newslot->tts_values[ex->attnum - 1]
6248 150 : = ExecEvalExpr(ex->exprstate,
6249 : econtext,
6250 150 : &newslot->tts_isnull[ex->attnum - 1]);
6251 : }
6252 :
6253 97552 : insertslot = newslot;
6254 : }
6255 : else
6256 : {
6257 : /*
6258 : * If there's no rewrite, old and new table are guaranteed to
6259 : * have the same AM, so we can just use the old slot to verify
6260 : * new constraints etc.
6261 : */
6262 665208 : insertslot = oldslot;
6263 : }
6264 :
6265 : /* Now check any constraints on the possibly-changed tuple */
6266 762760 : econtext->ecxt_scantuple = insertslot;
6267 :
6268 3339042 : foreach(l, notnull_attrs)
6269 : {
6270 2576342 : int attn = lfirst_int(l);
6271 :
6272 2576342 : if (slot_attisnull(insertslot, attn + 1))
6273 : {
6274 60 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6275 :
6276 60 : ereport(ERROR,
6277 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6278 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6279 : NameStr(attr->attname),
6280 : RelationGetRelationName(oldrel)),
6281 : errtablecol(oldrel, attn + 1)));
6282 : }
6283 : }
6284 :
6285 770800 : foreach(l, tab->constraints)
6286 : {
6287 8172 : NewConstraint *con = lfirst(l);
6288 :
6289 8172 : switch (con->contype)
6290 : {
6291 8072 : case CONSTR_CHECK:
6292 8072 : if (!ExecCheck(con->qualstate, econtext))
6293 72 : ereport(ERROR,
6294 : (errcode(ERRCODE_CHECK_VIOLATION),
6295 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6296 : con->name,
6297 : RelationGetRelationName(oldrel)),
6298 : errtableconstraint(oldrel, con->name)));
6299 8000 : break;
6300 100 : case CONSTR_FOREIGN:
6301 : /* Nothing to do here */
6302 100 : break;
6303 0 : default:
6304 0 : elog(ERROR, "unrecognized constraint type: %d",
6305 : (int) con->contype);
6306 : }
6307 : }
6308 :
6309 762628 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6310 : {
6311 74 : if (tab->validate_default)
6312 26 : ereport(ERROR,
6313 : (errcode(ERRCODE_CHECK_VIOLATION),
6314 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6315 : RelationGetRelationName(oldrel)),
6316 : errtable(oldrel)));
6317 : else
6318 48 : ereport(ERROR,
6319 : (errcode(ERRCODE_CHECK_VIOLATION),
6320 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6321 : RelationGetRelationName(oldrel)),
6322 : errtable(oldrel)));
6323 : }
6324 :
6325 : /* Write the tuple out to the new relation */
6326 762554 : if (newrel)
6327 97540 : table_tuple_insert(newrel, insertslot, mycid,
6328 : ti_options, bistate);
6329 :
6330 762554 : ResetExprContext(econtext);
6331 :
6332 762554 : CHECK_FOR_INTERRUPTS();
6333 : }
6334 :
6335 3872 : MemoryContextSwitchTo(oldCxt);
6336 3872 : table_endscan(scan);
6337 3872 : UnregisterSnapshot(snapshot);
6338 :
6339 3872 : ExecDropSingleTupleTableSlot(oldslot);
6340 3872 : if (newslot)
6341 836 : ExecDropSingleTupleTableSlot(newslot);
6342 : }
6343 :
6344 4638 : FreeExecutorState(estate);
6345 :
6346 4638 : table_close(oldrel, NoLock);
6347 4638 : if (newrel)
6348 : {
6349 836 : FreeBulkInsertState(bistate);
6350 :
6351 836 : table_finish_bulk_insert(newrel, ti_options);
6352 :
6353 836 : table_close(newrel, NoLock);
6354 : }
6355 4638 : }
6356 :
6357 : /*
6358 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6359 : */
6360 : static AlteredTableInfo *
6361 33676 : ATGetQueueEntry(List **wqueue, Relation rel)
6362 : {
6363 33676 : Oid relid = RelationGetRelid(rel);
6364 : AlteredTableInfo *tab;
6365 : ListCell *ltab;
6366 :
6367 42414 : foreach(ltab, *wqueue)
6368 : {
6369 11244 : tab = (AlteredTableInfo *) lfirst(ltab);
6370 11244 : if (tab->relid == relid)
6371 2506 : return tab;
6372 : }
6373 :
6374 : /*
6375 : * Not there, so add it. Note that we make a copy of the relation's
6376 : * existing descriptor before anything interesting can happen to it.
6377 : */
6378 31170 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6379 31170 : tab->relid = relid;
6380 31170 : tab->rel = NULL; /* set later */
6381 31170 : tab->relkind = rel->rd_rel->relkind;
6382 31170 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6383 31170 : tab->newAccessMethod = InvalidOid;
6384 31170 : tab->chgAccessMethod = false;
6385 31170 : tab->newTableSpace = InvalidOid;
6386 31170 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6387 31170 : tab->chgPersistence = false;
6388 :
6389 31170 : *wqueue = lappend(*wqueue, tab);
6390 :
6391 31170 : return tab;
6392 : }
6393 :
6394 : static const char *
6395 42 : alter_table_type_to_string(AlterTableType cmdtype)
6396 : {
6397 42 : switch (cmdtype)
6398 : {
6399 0 : case AT_AddColumn:
6400 : case AT_AddColumnToView:
6401 0 : return "ADD COLUMN";
6402 0 : case AT_ColumnDefault:
6403 : case AT_CookedColumnDefault:
6404 0 : return "ALTER COLUMN ... SET DEFAULT";
6405 6 : case AT_DropNotNull:
6406 6 : return "ALTER COLUMN ... DROP NOT NULL";
6407 6 : case AT_SetNotNull:
6408 6 : return "ALTER COLUMN ... SET NOT NULL";
6409 0 : case AT_SetExpression:
6410 0 : return "ALTER COLUMN ... SET EXPRESSION";
6411 0 : case AT_DropExpression:
6412 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6413 0 : case AT_CheckNotNull:
6414 0 : return NULL; /* not real grammar */
6415 0 : case AT_SetStatistics:
6416 0 : return "ALTER COLUMN ... SET STATISTICS";
6417 12 : case AT_SetOptions:
6418 12 : return "ALTER COLUMN ... SET";
6419 0 : case AT_ResetOptions:
6420 0 : return "ALTER COLUMN ... RESET";
6421 0 : case AT_SetStorage:
6422 0 : return "ALTER COLUMN ... SET STORAGE";
6423 0 : case AT_SetCompression:
6424 0 : return "ALTER COLUMN ... SET COMPRESSION";
6425 6 : case AT_DropColumn:
6426 6 : return "DROP COLUMN";
6427 0 : case AT_AddIndex:
6428 : case AT_ReAddIndex:
6429 0 : return NULL; /* not real grammar */
6430 0 : case AT_AddConstraint:
6431 : case AT_ReAddConstraint:
6432 : case AT_ReAddDomainConstraint:
6433 : case AT_AddIndexConstraint:
6434 0 : return "ADD CONSTRAINT";
6435 6 : case AT_AlterConstraint:
6436 6 : return "ALTER CONSTRAINT";
6437 0 : case AT_ValidateConstraint:
6438 0 : return "VALIDATE CONSTRAINT";
6439 0 : case AT_DropConstraint:
6440 0 : return "DROP CONSTRAINT";
6441 0 : case AT_ReAddComment:
6442 0 : return NULL; /* not real grammar */
6443 0 : case AT_AlterColumnType:
6444 0 : return "ALTER COLUMN ... SET DATA TYPE";
6445 0 : case AT_AlterColumnGenericOptions:
6446 0 : return "ALTER COLUMN ... OPTIONS";
6447 0 : case AT_ChangeOwner:
6448 0 : return "OWNER TO";
6449 0 : case AT_ClusterOn:
6450 0 : return "CLUSTER ON";
6451 0 : case AT_DropCluster:
6452 0 : return "SET WITHOUT CLUSTER";
6453 0 : case AT_SetAccessMethod:
6454 0 : return "SET ACCESS METHOD";
6455 0 : case AT_SetLogged:
6456 0 : return "SET LOGGED";
6457 0 : case AT_SetUnLogged:
6458 0 : return "SET UNLOGGED";
6459 0 : case AT_DropOids:
6460 0 : return "SET WITHOUT OIDS";
6461 0 : case AT_SetTableSpace:
6462 0 : return "SET TABLESPACE";
6463 0 : case AT_SetRelOptions:
6464 0 : return "SET";
6465 0 : case AT_ResetRelOptions:
6466 0 : return "RESET";
6467 0 : case AT_ReplaceRelOptions:
6468 0 : return NULL; /* not real grammar */
6469 0 : case AT_EnableTrig:
6470 0 : return "ENABLE TRIGGER";
6471 0 : case AT_EnableAlwaysTrig:
6472 0 : return "ENABLE ALWAYS TRIGGER";
6473 0 : case AT_EnableReplicaTrig:
6474 0 : return "ENABLE REPLICA TRIGGER";
6475 0 : case AT_DisableTrig:
6476 0 : return "DISABLE TRIGGER";
6477 0 : case AT_EnableTrigAll:
6478 0 : return "ENABLE TRIGGER ALL";
6479 0 : case AT_DisableTrigAll:
6480 0 : return "DISABLE TRIGGER ALL";
6481 0 : case AT_EnableTrigUser:
6482 0 : return "ENABLE TRIGGER USER";
6483 0 : case AT_DisableTrigUser:
6484 0 : return "DISABLE TRIGGER USER";
6485 0 : case AT_EnableRule:
6486 0 : return "ENABLE RULE";
6487 0 : case AT_EnableAlwaysRule:
6488 0 : return "ENABLE ALWAYS RULE";
6489 0 : case AT_EnableReplicaRule:
6490 0 : return "ENABLE REPLICA RULE";
6491 0 : case AT_DisableRule:
6492 0 : return "DISABLE RULE";
6493 0 : case AT_AddInherit:
6494 0 : return "INHERIT";
6495 0 : case AT_DropInherit:
6496 0 : return "NO INHERIT";
6497 0 : case AT_AddOf:
6498 0 : return "OF";
6499 0 : case AT_DropOf:
6500 0 : return "NOT OF";
6501 0 : case AT_ReplicaIdentity:
6502 0 : return "REPLICA IDENTITY";
6503 0 : case AT_EnableRowSecurity:
6504 0 : return "ENABLE ROW SECURITY";
6505 0 : case AT_DisableRowSecurity:
6506 0 : return "DISABLE ROW SECURITY";
6507 0 : case AT_ForceRowSecurity:
6508 0 : return "FORCE ROW SECURITY";
6509 0 : case AT_NoForceRowSecurity:
6510 0 : return "NO FORCE ROW SECURITY";
6511 0 : case AT_GenericOptions:
6512 0 : return "OPTIONS";
6513 0 : case AT_AttachPartition:
6514 0 : return "ATTACH PARTITION";
6515 6 : case AT_DetachPartition:
6516 6 : return "DETACH PARTITION";
6517 0 : case AT_DetachPartitionFinalize:
6518 0 : return "DETACH PARTITION ... FINALIZE";
6519 0 : case AT_SplitPartition:
6520 0 : return "SPLIT PARTITION";
6521 0 : case AT_MergePartitions:
6522 0 : return "MERGE PARTITIONS";
6523 0 : case AT_AddIdentity:
6524 0 : return "ALTER COLUMN ... ADD IDENTITY";
6525 0 : case AT_SetIdentity:
6526 0 : return "ALTER COLUMN ... SET";
6527 0 : case AT_DropIdentity:
6528 0 : return "ALTER COLUMN ... DROP IDENTITY";
6529 0 : case AT_ReAddStatistics:
6530 0 : return NULL; /* not real grammar */
6531 : }
6532 :
6533 0 : return NULL;
6534 : }
6535 :
6536 : /*
6537 : * ATSimplePermissions
6538 : *
6539 : * - Ensure that it is a relation (or possibly a view)
6540 : * - Ensure this user is the owner
6541 : * - Ensure that it is not a system table
6542 : */
6543 : static void
6544 31300 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6545 : {
6546 : int actual_target;
6547 :
6548 31300 : switch (rel->rd_rel->relkind)
6549 : {
6550 29098 : case RELKIND_RELATION:
6551 : case RELKIND_PARTITIONED_TABLE:
6552 29098 : actual_target = ATT_TABLE;
6553 29098 : break;
6554 396 : case RELKIND_VIEW:
6555 396 : actual_target = ATT_VIEW;
6556 396 : break;
6557 46 : case RELKIND_MATVIEW:
6558 46 : actual_target = ATT_MATVIEW;
6559 46 : break;
6560 226 : case RELKIND_INDEX:
6561 226 : actual_target = ATT_INDEX;
6562 226 : break;
6563 434 : case RELKIND_PARTITIONED_INDEX:
6564 434 : actual_target = ATT_PARTITIONED_INDEX;
6565 434 : break;
6566 214 : case RELKIND_COMPOSITE_TYPE:
6567 214 : actual_target = ATT_COMPOSITE_TYPE;
6568 214 : break;
6569 874 : case RELKIND_FOREIGN_TABLE:
6570 874 : actual_target = ATT_FOREIGN_TABLE;
6571 874 : break;
6572 12 : case RELKIND_SEQUENCE:
6573 12 : actual_target = ATT_SEQUENCE;
6574 12 : break;
6575 0 : default:
6576 0 : actual_target = 0;
6577 0 : break;
6578 : }
6579 :
6580 : /* Wrong target type? */
6581 31300 : if ((actual_target & allowed_targets) == 0)
6582 : {
6583 42 : const char *action_str = alter_table_type_to_string(cmdtype);
6584 :
6585 42 : if (action_str)
6586 42 : ereport(ERROR,
6587 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6588 : /* translator: %s is a group of some SQL keywords */
6589 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6590 : action_str, RelationGetRelationName(rel)),
6591 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6592 : else
6593 : /* internal error? */
6594 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6595 : RelationGetRelationName(rel));
6596 : }
6597 :
6598 : /* Permissions checks */
6599 31258 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6600 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6601 12 : RelationGetRelationName(rel));
6602 :
6603 31246 : if (!allowSystemTableMods && IsSystemRelation(rel))
6604 0 : ereport(ERROR,
6605 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6606 : errmsg("permission denied: \"%s\" is a system catalog",
6607 : RelationGetRelationName(rel))));
6608 31246 : }
6609 :
6610 : /*
6611 : * ATSimpleRecursion
6612 : *
6613 : * Simple table recursion sufficient for most ALTER TABLE operations.
6614 : * All direct and indirect children are processed in an unspecified order.
6615 : * Note that if a child inherits from the original table via multiple
6616 : * inheritance paths, it will be visited just once.
6617 : */
6618 : static void
6619 8758 : ATSimpleRecursion(List **wqueue, Relation rel,
6620 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6621 : AlterTableUtilityContext *context)
6622 : {
6623 : /*
6624 : * Propagate to children, if desired and if there are (or might be) any
6625 : * children.
6626 : */
6627 8758 : if (recurse && rel->rd_rel->relhassubclass)
6628 : {
6629 214 : Oid relid = RelationGetRelid(rel);
6630 : ListCell *child;
6631 : List *children;
6632 :
6633 214 : children = find_all_inheritors(relid, lockmode, NULL);
6634 :
6635 : /*
6636 : * find_all_inheritors does the recursive search of the inheritance
6637 : * hierarchy, so all we have to do is process all of the relids in the
6638 : * list that it returns.
6639 : */
6640 870 : foreach(child, children)
6641 : {
6642 662 : Oid childrelid = lfirst_oid(child);
6643 : Relation childrel;
6644 :
6645 662 : if (childrelid == relid)
6646 214 : continue;
6647 : /* find_all_inheritors already got lock */
6648 448 : childrel = relation_open(childrelid, NoLock);
6649 448 : CheckAlterTableIsSafe(childrel);
6650 448 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6651 442 : relation_close(childrel, NoLock);
6652 : }
6653 : }
6654 8752 : }
6655 :
6656 : /*
6657 : * Obtain list of partitions of the given table, locking them all at the given
6658 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6659 : *
6660 : * This function is a no-op if the given relation is not a partitioned table;
6661 : * in particular, nothing is done if it's a legacy inheritance parent.
6662 : */
6663 : static void
6664 564 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6665 : {
6666 564 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6667 : {
6668 : List *inh;
6669 : ListCell *cell;
6670 :
6671 122 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6672 : /* first element is the parent rel; must ignore it */
6673 412 : for_each_from(cell, inh, 1)
6674 : {
6675 : Relation childrel;
6676 :
6677 : /* find_all_inheritors already got lock */
6678 296 : childrel = table_open(lfirst_oid(cell), NoLock);
6679 296 : CheckAlterTableIsSafe(childrel);
6680 290 : table_close(childrel, NoLock);
6681 : }
6682 116 : list_free(inh);
6683 : }
6684 558 : }
6685 :
6686 : /*
6687 : * ATTypedTableRecursion
6688 : *
6689 : * Propagate ALTER TYPE operations to the typed tables of that type.
6690 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6691 : * recursion to inheritance children of the typed tables.
6692 : */
6693 : static void
6694 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6695 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6696 : {
6697 : ListCell *child;
6698 : List *children;
6699 :
6700 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6701 :
6702 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6703 190 : RelationGetRelationName(rel),
6704 : cmd->behavior);
6705 :
6706 202 : foreach(child, children)
6707 : {
6708 30 : Oid childrelid = lfirst_oid(child);
6709 : Relation childrel;
6710 :
6711 30 : childrel = relation_open(childrelid, lockmode);
6712 30 : CheckAlterTableIsSafe(childrel);
6713 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6714 30 : relation_close(childrel, NoLock);
6715 : }
6716 172 : }
6717 :
6718 :
6719 : /*
6720 : * find_composite_type_dependencies
6721 : *
6722 : * Check to see if the type "typeOid" is being used as a column in some table
6723 : * (possibly nested several levels deep in composite types, arrays, etc!).
6724 : * Eventually, we'd like to propagate the check or rewrite operation
6725 : * into such tables, but for now, just error out if we find any.
6726 : *
6727 : * Caller should provide either the associated relation of a rowtype,
6728 : * or a type name (not both) for use in the error message, if any.
6729 : *
6730 : * Note that "typeOid" is not necessarily a composite type; it could also be
6731 : * another container type such as an array or range, or a domain over one of
6732 : * these things. The name of this function is therefore somewhat historical,
6733 : * but it's not worth changing.
6734 : *
6735 : * We assume that functions and views depending on the type are not reasons
6736 : * to reject the ALTER. (How safe is this really?)
6737 : */
6738 : void
6739 3874 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6740 : const char *origTypeName)
6741 : {
6742 : Relation depRel;
6743 : ScanKeyData key[2];
6744 : SysScanDesc depScan;
6745 : HeapTuple depTup;
6746 :
6747 : /* since this function recurses, it could be driven to stack overflow */
6748 3874 : check_stack_depth();
6749 :
6750 : /*
6751 : * We scan pg_depend to find those things that depend on the given type.
6752 : * (We assume we can ignore refobjsubid for a type.)
6753 : */
6754 3874 : depRel = table_open(DependRelationId, AccessShareLock);
6755 :
6756 3874 : ScanKeyInit(&key[0],
6757 : Anum_pg_depend_refclassid,
6758 : BTEqualStrategyNumber, F_OIDEQ,
6759 : ObjectIdGetDatum(TypeRelationId));
6760 3874 : ScanKeyInit(&key[1],
6761 : Anum_pg_depend_refobjid,
6762 : BTEqualStrategyNumber, F_OIDEQ,
6763 : ObjectIdGetDatum(typeOid));
6764 :
6765 3874 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6766 : NULL, 2, key);
6767 :
6768 5988 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6769 : {
6770 2210 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6771 : Relation rel;
6772 : TupleDesc tupleDesc;
6773 : Form_pg_attribute att;
6774 :
6775 : /* Check for directly dependent types */
6776 2210 : if (pg_depend->classid == TypeRelationId)
6777 : {
6778 : /*
6779 : * This must be an array, domain, or range containing the given
6780 : * type, so recursively check for uses of this type. Note that
6781 : * any error message will mention the original type not the
6782 : * container; this is intentional.
6783 : */
6784 1864 : find_composite_type_dependencies(pg_depend->objid,
6785 : origRelation, origTypeName);
6786 1840 : continue;
6787 : }
6788 :
6789 : /* Else, ignore dependees that aren't relations */
6790 346 : if (pg_depend->classid != RelationRelationId)
6791 122 : continue;
6792 :
6793 224 : rel = relation_open(pg_depend->objid, AccessShareLock);
6794 224 : tupleDesc = RelationGetDescr(rel);
6795 :
6796 : /*
6797 : * If objsubid identifies a specific column, refer to that in error
6798 : * messages. Otherwise, search to see if there's a user column of the
6799 : * type. (We assume system columns are never of interesting types.)
6800 : * The search is needed because an index containing an expression
6801 : * column of the target type will just be recorded as a whole-relation
6802 : * dependency. If we do not find a column of the type, the dependency
6803 : * must indicate that the type is transiently referenced in an index
6804 : * expression but not stored on disk, which we assume is OK, just as
6805 : * we do for references in views. (It could also be that the target
6806 : * type is embedded in some container type that is stored in an index
6807 : * column, but the previous recursion should catch such cases.)
6808 : */
6809 224 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6810 66 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6811 : else
6812 : {
6813 158 : att = NULL;
6814 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6815 : {
6816 254 : att = TupleDescAttr(tupleDesc, attno - 1);
6817 254 : if (att->atttypid == typeOid && !att->attisdropped)
6818 6 : break;
6819 248 : att = NULL;
6820 : }
6821 158 : if (att == NULL)
6822 : {
6823 : /* No such column, so assume OK */
6824 152 : relation_close(rel, AccessShareLock);
6825 152 : continue;
6826 : }
6827 : }
6828 :
6829 : /*
6830 : * We definitely should reject if the relation has storage. If it's
6831 : * partitioned, then perhaps we don't have to reject: if there are
6832 : * partitions then we'll fail when we find one, else there is no
6833 : * stored data to worry about. However, it's possible that the type
6834 : * change would affect conclusions about whether the type is sortable
6835 : * or hashable and thus (if it's a partitioning column) break the
6836 : * partitioning rule. For now, reject for partitioned rels too.
6837 : */
6838 72 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6839 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6840 : {
6841 72 : if (origTypeName)
6842 30 : ereport(ERROR,
6843 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6844 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6845 : origTypeName,
6846 : RelationGetRelationName(rel),
6847 : NameStr(att->attname))));
6848 42 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6849 18 : ereport(ERROR,
6850 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6851 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6852 : RelationGetRelationName(origRelation),
6853 : RelationGetRelationName(rel),
6854 : NameStr(att->attname))));
6855 24 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6856 6 : ereport(ERROR,
6857 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6858 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6859 : RelationGetRelationName(origRelation),
6860 : RelationGetRelationName(rel),
6861 : NameStr(att->attname))));
6862 : else
6863 18 : ereport(ERROR,
6864 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6865 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6866 : RelationGetRelationName(origRelation),
6867 : RelationGetRelationName(rel),
6868 : NameStr(att->attname))));
6869 : }
6870 0 : else if (OidIsValid(rel->rd_rel->reltype))
6871 : {
6872 : /*
6873 : * A view or composite type itself isn't a problem, but we must
6874 : * recursively check for indirect dependencies via its rowtype.
6875 : */
6876 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
6877 : origRelation, origTypeName);
6878 : }
6879 :
6880 0 : relation_close(rel, AccessShareLock);
6881 : }
6882 :
6883 3778 : systable_endscan(depScan);
6884 :
6885 3778 : relation_close(depRel, AccessShareLock);
6886 3778 : }
6887 :
6888 :
6889 : /*
6890 : * find_typed_table_dependencies
6891 : *
6892 : * Check to see if a composite type is being used as the type of a
6893 : * typed table. Abort if any are found and behavior is RESTRICT.
6894 : * Else return the list of tables.
6895 : */
6896 : static List *
6897 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6898 : {
6899 : Relation classRel;
6900 : ScanKeyData key[1];
6901 : TableScanDesc scan;
6902 : HeapTuple tuple;
6903 214 : List *result = NIL;
6904 :
6905 214 : classRel = table_open(RelationRelationId, AccessShareLock);
6906 :
6907 214 : ScanKeyInit(&key[0],
6908 : Anum_pg_class_reloftype,
6909 : BTEqualStrategyNumber, F_OIDEQ,
6910 : ObjectIdGetDatum(typeOid));
6911 :
6912 214 : scan = table_beginscan_catalog(classRel, 1, key);
6913 :
6914 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6915 : {
6916 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6917 :
6918 60 : if (behavior == DROP_RESTRICT)
6919 24 : ereport(ERROR,
6920 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6921 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6922 : typeName),
6923 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6924 : else
6925 36 : result = lappend_oid(result, classform->oid);
6926 : }
6927 :
6928 190 : table_endscan(scan);
6929 190 : table_close(classRel, AccessShareLock);
6930 :
6931 190 : return result;
6932 : }
6933 :
6934 :
6935 : /*
6936 : * check_of_type
6937 : *
6938 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6939 : * isn't suitable, throw an error. Currently, we require that the type
6940 : * originated with CREATE TYPE AS. We could support any row type, but doing so
6941 : * would require handling a number of extra corner cases in the DDL commands.
6942 : * (Also, allowing domain-over-composite would open up a can of worms about
6943 : * whether and how the domain's constraints should apply to derived tables.)
6944 : */
6945 : void
6946 176 : check_of_type(HeapTuple typetuple)
6947 : {
6948 176 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6949 176 : bool typeOk = false;
6950 :
6951 176 : if (typ->typtype == TYPTYPE_COMPOSITE)
6952 : {
6953 : Relation typeRelation;
6954 :
6955 : Assert(OidIsValid(typ->typrelid));
6956 170 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
6957 170 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6958 :
6959 : /*
6960 : * Close the parent rel, but keep our AccessShareLock on it until xact
6961 : * commit. That will prevent someone else from deleting or ALTERing
6962 : * the type before the typed table creation/conversion commits.
6963 : */
6964 170 : relation_close(typeRelation, NoLock);
6965 :
6966 170 : if (!typeOk)
6967 6 : ereport(ERROR,
6968 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6969 : errmsg("type %s is the row type of another table",
6970 : format_type_be(typ->oid)),
6971 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
6972 : }
6973 : else
6974 6 : ereport(ERROR,
6975 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6976 : errmsg("type %s is not a composite type",
6977 : format_type_be(typ->oid))));
6978 164 : }
6979 :
6980 :
6981 : /*
6982 : * ALTER TABLE ADD COLUMN
6983 : *
6984 : * Adds an additional attribute to a relation making the assumption that
6985 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6986 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6987 : * AlterTableCmd's.
6988 : *
6989 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6990 : * have to decide at runtime whether to recurse or not depending on whether we
6991 : * actually add a column or merely merge with an existing column. (We can't
6992 : * check this in a static pre-pass because it won't handle multiple inheritance
6993 : * situations correctly.)
6994 : */
6995 : static void
6996 1956 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6997 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
6998 : AlterTableUtilityContext *context)
6999 : {
7000 1956 : if (rel->rd_rel->reloftype && !recursing)
7001 6 : ereport(ERROR,
7002 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7003 : errmsg("cannot add column to typed table")));
7004 :
7005 1950 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7006 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7007 :
7008 1944 : if (recurse && !is_view)
7009 1844 : cmd->recurse = true;
7010 1944 : }
7011 :
7012 : /*
7013 : * Add a column to a table. The return value is the address of the
7014 : * new column in the parent relation.
7015 : *
7016 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7017 : * copy (but that happens only after we check for IF NOT EXISTS).
7018 : */
7019 : static ObjectAddress
7020 2556 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7021 : AlterTableCmd **cmd, bool recurse, bool recursing,
7022 : LOCKMODE lockmode, AlterTablePass cur_pass,
7023 : AlterTableUtilityContext *context)
7024 : {
7025 2556 : Oid myrelid = RelationGetRelid(rel);
7026 2556 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7027 2556 : bool if_not_exists = (*cmd)->missing_ok;
7028 : Relation pgclass,
7029 : attrdesc;
7030 : HeapTuple reltup;
7031 : Form_pg_attribute attribute;
7032 : int newattnum;
7033 : char relkind;
7034 : Expr *defval;
7035 : List *children;
7036 : ListCell *child;
7037 : AlterTableCmd *childcmd;
7038 : ObjectAddress address;
7039 : TupleDesc tupdesc;
7040 :
7041 : /* since this function recurses, it could be driven to stack overflow */
7042 2556 : check_stack_depth();
7043 :
7044 : /* At top level, permission check was done in ATPrepCmd, else do it */
7045 2556 : if (recursing)
7046 618 : ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7047 :
7048 2556 : if (rel->rd_rel->relispartition && !recursing)
7049 12 : ereport(ERROR,
7050 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7051 : errmsg("cannot add column to a partition")));
7052 :
7053 2544 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7054 :
7055 : /*
7056 : * Are we adding the column to a recursion child? If so, check whether to
7057 : * merge with an existing definition for the column. If we do merge, we
7058 : * must not recurse. Children will already have the column, and recursing
7059 : * into them would mess up attinhcount.
7060 : */
7061 2544 : if (colDef->inhcount > 0)
7062 : {
7063 : HeapTuple tuple;
7064 :
7065 : /* Does child already have a column by this name? */
7066 618 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7067 618 : if (HeapTupleIsValid(tuple))
7068 : {
7069 36 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7070 : Oid ctypeId;
7071 : int32 ctypmod;
7072 : Oid ccollid;
7073 :
7074 : /* Child column must match on type, typmod, and collation */
7075 36 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7076 36 : if (ctypeId != childatt->atttypid ||
7077 36 : ctypmod != childatt->atttypmod)
7078 0 : ereport(ERROR,
7079 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7080 : errmsg("child table \"%s\" has different type for column \"%s\"",
7081 : RelationGetRelationName(rel), colDef->colname)));
7082 36 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7083 36 : if (ccollid != childatt->attcollation)
7084 0 : ereport(ERROR,
7085 : (errcode(ERRCODE_COLLATION_MISMATCH),
7086 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7087 : RelationGetRelationName(rel), colDef->colname),
7088 : errdetail("\"%s\" versus \"%s\"",
7089 : get_collation_name(ccollid),
7090 : get_collation_name(childatt->attcollation))));
7091 :
7092 : /* Bump the existing child att's inhcount */
7093 36 : childatt->attinhcount++;
7094 36 : if (childatt->attinhcount < 0)
7095 0 : ereport(ERROR,
7096 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7097 : errmsg("too many inheritance parents"));
7098 36 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7099 :
7100 36 : heap_freetuple(tuple);
7101 :
7102 : /* Inform the user about the merge */
7103 36 : ereport(NOTICE,
7104 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7105 : colDef->colname, RelationGetRelationName(rel))));
7106 :
7107 36 : table_close(attrdesc, RowExclusiveLock);
7108 :
7109 : /* Make the child column change visible */
7110 36 : CommandCounterIncrement();
7111 :
7112 36 : return InvalidObjectAddress;
7113 : }
7114 : }
7115 :
7116 : /* skip if the name already exists and if_not_exists is true */
7117 2508 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7118 : {
7119 54 : table_close(attrdesc, RowExclusiveLock);
7120 54 : return InvalidObjectAddress;
7121 : }
7122 :
7123 : /*
7124 : * Okay, we need to add the column, so go ahead and do parse
7125 : * transformation. This can result in queueing up, or even immediately
7126 : * executing, subsidiary operations (such as creation of unique indexes);
7127 : * so we mustn't do it until we have made the if_not_exists check.
7128 : *
7129 : * When recursing, the command was already transformed and we needn't do
7130 : * so again. Also, if context isn't given we can't transform. (That
7131 : * currently happens only for AT_AddColumnToView; we expect that view.c
7132 : * passed us a ColumnDef that doesn't need work.)
7133 : */
7134 2424 : if (context != NULL && !recursing)
7135 : {
7136 1818 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7137 : cur_pass, context);
7138 : Assert(*cmd != NULL);
7139 1818 : colDef = castNode(ColumnDef, (*cmd)->def);
7140 : }
7141 :
7142 : /*
7143 : * Regular inheritance children are independent enough not to inherit the
7144 : * identity column from parent hence cannot recursively add identity
7145 : * column if the table has inheritance children.
7146 : *
7147 : * Partitions, on the other hand, are integral part of a partitioned table
7148 : * and inherit identity column. Hence propagate identity column down the
7149 : * partition hierarchy.
7150 : */
7151 2424 : if (colDef->identity &&
7152 54 : recurse &&
7153 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7154 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7155 6 : ereport(ERROR,
7156 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7157 : errmsg("cannot recursively add identity column to table that has child tables")));
7158 :
7159 2418 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7160 :
7161 2418 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7162 2418 : if (!HeapTupleIsValid(reltup))
7163 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7164 2418 : relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7165 :
7166 : /* Determine the new attribute's number */
7167 2418 : newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7168 2418 : if (newattnum > MaxHeapAttributeNumber)
7169 0 : ereport(ERROR,
7170 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7171 : errmsg("tables can have at most %d columns",
7172 : MaxHeapAttributeNumber)));
7173 :
7174 : /*
7175 : * Construct new attribute's pg_attribute entry.
7176 : */
7177 2418 : tupdesc = BuildDescForRelation(list_make1(colDef));
7178 :
7179 2406 : attribute = TupleDescAttr(tupdesc, 0);
7180 :
7181 : /* Fix up attribute number */
7182 2406 : attribute->attnum = newattnum;
7183 :
7184 : /* make sure datatype is legal for a column */
7185 2406 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7186 2406 : list_make1_oid(rel->rd_rel->reltype),
7187 : 0);
7188 :
7189 2376 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7190 :
7191 2376 : table_close(attrdesc, RowExclusiveLock);
7192 :
7193 : /*
7194 : * Update pg_class tuple as appropriate
7195 : */
7196 2376 : ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7197 :
7198 2376 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7199 :
7200 2376 : heap_freetuple(reltup);
7201 :
7202 : /* Post creation hook for new attribute */
7203 2376 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7204 :
7205 2376 : table_close(pgclass, RowExclusiveLock);
7206 :
7207 : /* Make the attribute's catalog entry visible */
7208 2376 : CommandCounterIncrement();
7209 :
7210 : /*
7211 : * Store the DEFAULT, if any, in the catalogs
7212 : */
7213 2376 : if (colDef->raw_default)
7214 : {
7215 : RawColumnDefault *rawEnt;
7216 :
7217 708 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7218 708 : rawEnt->attnum = attribute->attnum;
7219 708 : rawEnt->raw_default = copyObject(colDef->raw_default);
7220 :
7221 : /*
7222 : * Attempt to skip a complete table rewrite by storing the specified
7223 : * DEFAULT value outside of the heap. This may be disabled inside
7224 : * AddRelationNewConstraints if the optimization cannot be applied.
7225 : */
7226 708 : rawEnt->missingMode = (!colDef->generated);
7227 :
7228 708 : rawEnt->generated = colDef->generated;
7229 :
7230 : /*
7231 : * This function is intended for CREATE TABLE, so it processes a
7232 : * _list_ of defaults, but we just do one.
7233 : */
7234 708 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7235 : false, true, false, NULL);
7236 :
7237 : /* Make the additional catalog changes visible */
7238 696 : CommandCounterIncrement();
7239 :
7240 : /*
7241 : * Did the request for a missing value work? If not we'll have to do a
7242 : * rewrite
7243 : */
7244 696 : if (!rawEnt->missingMode)
7245 108 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7246 : }
7247 :
7248 : /*
7249 : * Tell Phase 3 to fill in the default expression, if there is one.
7250 : *
7251 : * If there is no default, Phase 3 doesn't have to do anything, because
7252 : * that effectively means that the default is NULL. The heap tuple access
7253 : * routines always check for attnum > # of attributes in tuple, and return
7254 : * NULL if so, so without any modification of the tuple data we will get
7255 : * the effect of NULL values in the new column.
7256 : *
7257 : * An exception occurs when the new column is of a domain type: the domain
7258 : * might have a not-null constraint, or a check constraint that indirectly
7259 : * rejects nulls. If there are any domain constraints then we construct
7260 : * an explicit NULL default value that will be passed through
7261 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7262 : * rewriting the table which we really don't have to do, but the present
7263 : * design of domain processing doesn't offer any simple way of checking
7264 : * the constraints more directly.)
7265 : *
7266 : * Note: we use build_column_default, and not just the cooked default
7267 : * returned by AddRelationNewConstraints, so that the right thing happens
7268 : * when a datatype's default applies.
7269 : *
7270 : * Note: it might seem that this should happen at the end of Phase 2, so
7271 : * that the effects of subsequent subcommands can be taken into account.
7272 : * It's intentional that we do it now, though. The new column should be
7273 : * filled according to what is said in the ADD COLUMN subcommand, so that
7274 : * the effects are the same as if this subcommand had been run by itself
7275 : * and the later subcommands had been issued in new ALTER TABLE commands.
7276 : *
7277 : * We can skip this entirely for relations without storage, since Phase 3
7278 : * is certainly not going to touch them. System attributes don't have
7279 : * interesting defaults, either.
7280 : */
7281 2364 : if (RELKIND_HAS_STORAGE(relkind))
7282 : {
7283 : /*
7284 : * For an identity column, we can't use build_column_default(),
7285 : * because the sequence ownership isn't set yet. So do it manually.
7286 : */
7287 2012 : if (colDef->identity)
7288 : {
7289 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7290 :
7291 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7292 42 : nve->typeId = attribute->atttypid;
7293 :
7294 42 : defval = (Expr *) nve;
7295 :
7296 : /* must do a rewrite for identity columns */
7297 42 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7298 : }
7299 : else
7300 1970 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7301 :
7302 2012 : if (!defval && DomainHasConstraints(attribute->atttypid))
7303 : {
7304 : Oid baseTypeId;
7305 : int32 baseTypeMod;
7306 : Oid baseTypeColl;
7307 :
7308 6 : baseTypeMod = attribute->atttypmod;
7309 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7310 6 : baseTypeColl = get_typcollation(baseTypeId);
7311 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7312 6 : defval = (Expr *) coerce_to_target_type(NULL,
7313 : (Node *) defval,
7314 : baseTypeId,
7315 : attribute->atttypid,
7316 : attribute->atttypmod,
7317 : COERCION_ASSIGNMENT,
7318 : COERCE_IMPLICIT_CAST,
7319 : -1);
7320 6 : if (defval == NULL) /* should not happen */
7321 0 : elog(ERROR, "failed to coerce base type to domain");
7322 : }
7323 :
7324 2012 : if (defval)
7325 : {
7326 : NewColumnValue *newval;
7327 :
7328 610 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7329 610 : newval->attnum = attribute->attnum;
7330 610 : newval->expr = expression_planner(defval);
7331 610 : newval->is_generated = (colDef->generated != '\0');
7332 :
7333 610 : tab->newvals = lappend(tab->newvals, newval);
7334 : }
7335 :
7336 2012 : if (DomainHasConstraints(attribute->atttypid))
7337 12 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7338 :
7339 2012 : if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7340 : {
7341 : /*
7342 : * If the new column is NOT NULL, and there is no missing value,
7343 : * tell Phase 3 it needs to check for NULLs.
7344 : */
7345 1558 : tab->verify_new_notnull |= colDef->is_not_null;
7346 : }
7347 : }
7348 :
7349 : /*
7350 : * Add needed dependency entries for the new column.
7351 : */
7352 2364 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7353 2364 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7354 :
7355 : /*
7356 : * Propagate to children as appropriate. Unlike most other ALTER
7357 : * routines, we have to do this one level of recursion at a time; we can't
7358 : * use find_all_inheritors to do it in one pass.
7359 : */
7360 : children =
7361 2364 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7362 :
7363 : /*
7364 : * If we are told not to recurse, there had better not be any child
7365 : * tables; else the addition would put them out of step.
7366 : */
7367 2364 : if (children && !recurse)
7368 12 : ereport(ERROR,
7369 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7370 : errmsg("column must be added to child tables too")));
7371 :
7372 : /* Children should see column as singly inherited */
7373 2352 : if (!recursing)
7374 : {
7375 1770 : childcmd = copyObject(*cmd);
7376 1770 : colDef = castNode(ColumnDef, childcmd->def);
7377 1770 : colDef->inhcount = 1;
7378 1770 : colDef->is_local = false;
7379 : }
7380 : else
7381 582 : childcmd = *cmd; /* no need to copy again */
7382 :
7383 2970 : foreach(child, children)
7384 : {
7385 618 : Oid childrelid = lfirst_oid(child);
7386 : Relation childrel;
7387 : AlteredTableInfo *childtab;
7388 :
7389 : /* find_inheritance_children already got lock */
7390 618 : childrel = table_open(childrelid, NoLock);
7391 618 : CheckAlterTableIsSafe(childrel);
7392 :
7393 : /* Find or create work queue entry for this table */
7394 618 : childtab = ATGetQueueEntry(wqueue, childrel);
7395 :
7396 : /* Recurse to child; return value is ignored */
7397 618 : ATExecAddColumn(wqueue, childtab, childrel,
7398 : &childcmd, recurse, true,
7399 : lockmode, cur_pass, context);
7400 :
7401 618 : table_close(childrel, NoLock);
7402 : }
7403 :
7404 2352 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7405 2352 : return address;
7406 : }
7407 :
7408 : /*
7409 : * If a new or renamed column will collide with the name of an existing
7410 : * column and if_not_exists is false then error out, else do nothing.
7411 : */
7412 : static bool
7413 2940 : check_for_column_name_collision(Relation rel, const char *colname,
7414 : bool if_not_exists)
7415 : {
7416 : HeapTuple attTuple;
7417 : int attnum;
7418 :
7419 : /*
7420 : * this test is deliberately not attisdropped-aware, since if one tries to
7421 : * add a column matching a dropped column name, it's gonna fail anyway.
7422 : */
7423 2940 : attTuple = SearchSysCache2(ATTNAME,
7424 : ObjectIdGetDatum(RelationGetRelid(rel)),
7425 : PointerGetDatum(colname));
7426 2940 : if (!HeapTupleIsValid(attTuple))
7427 2844 : return true;
7428 :
7429 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7430 96 : ReleaseSysCache(attTuple);
7431 :
7432 : /*
7433 : * We throw a different error message for conflicts with system column
7434 : * names, since they are normally not shown and the user might otherwise
7435 : * be confused about the reason for the conflict.
7436 : */
7437 96 : if (attnum <= 0)
7438 12 : ereport(ERROR,
7439 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7440 : errmsg("column name \"%s\" conflicts with a system column name",
7441 : colname)));
7442 : else
7443 : {
7444 84 : if (if_not_exists)
7445 : {
7446 54 : ereport(NOTICE,
7447 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7448 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7449 : colname, RelationGetRelationName(rel))));
7450 54 : return false;
7451 : }
7452 :
7453 30 : ereport(ERROR,
7454 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7455 : errmsg("column \"%s\" of relation \"%s\" already exists",
7456 : colname, RelationGetRelationName(rel))));
7457 : }
7458 :
7459 : return true;
7460 : }
7461 :
7462 : /*
7463 : * Install a column's dependency on its datatype.
7464 : */
7465 : static void
7466 3276 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7467 : {
7468 : ObjectAddress myself,
7469 : referenced;
7470 :
7471 3276 : myself.classId = RelationRelationId;
7472 3276 : myself.objectId = relid;
7473 3276 : myself.objectSubId = attnum;
7474 3276 : referenced.classId = TypeRelationId;
7475 3276 : referenced.objectId = typid;
7476 3276 : referenced.objectSubId = 0;
7477 3276 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7478 3276 : }
7479 :
7480 : /*
7481 : * Install a column's dependency on its collation.
7482 : */
7483 : static void
7484 3276 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7485 : {
7486 : ObjectAddress myself,
7487 : referenced;
7488 :
7489 : /* We know the default collation is pinned, so don't bother recording it */
7490 3276 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7491 : {
7492 18 : myself.classId = RelationRelationId;
7493 18 : myself.objectId = relid;
7494 18 : myself.objectSubId = attnum;
7495 18 : referenced.classId = CollationRelationId;
7496 18 : referenced.objectId = collid;
7497 18 : referenced.objectSubId = 0;
7498 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7499 : }
7500 3276 : }
7501 :
7502 : /*
7503 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7504 : */
7505 :
7506 : static void
7507 224 : ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
7508 : {
7509 : /*
7510 : * If the parent is a partitioned table, like check constraints, we do not
7511 : * support removing the NOT NULL while partitions exist.
7512 : */
7513 224 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7514 : {
7515 18 : PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
7516 :
7517 : Assert(partdesc != NULL);
7518 18 : if (partdesc->nparts > 0 && !recurse && !recursing)
7519 6 : ereport(ERROR,
7520 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7521 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7522 : errhint("Do not specify the ONLY keyword.")));
7523 : }
7524 218 : }
7525 :
7526 : /*
7527 : * Return the address of the modified column. If the column was already
7528 : * nullable, InvalidObjectAddress is returned.
7529 : */
7530 : static ObjectAddress
7531 200 : ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
7532 : {
7533 : HeapTuple tuple;
7534 : Form_pg_attribute attTup;
7535 : AttrNumber attnum;
7536 : Relation attr_rel;
7537 : List *indexoidlist;
7538 : ObjectAddress address;
7539 :
7540 : /*
7541 : * lookup the attribute
7542 : */
7543 200 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7544 :
7545 200 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7546 200 : if (!HeapTupleIsValid(tuple))
7547 18 : ereport(ERROR,
7548 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7549 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7550 : colName, RelationGetRelationName(rel))));
7551 182 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7552 182 : attnum = attTup->attnum;
7553 :
7554 : /* Prevent them from altering a system attribute */
7555 182 : if (attnum <= 0)
7556 0 : ereport(ERROR,
7557 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7558 : errmsg("cannot alter system column \"%s\"",
7559 : colName)));
7560 :
7561 182 : if (attTup->attidentity)
7562 18 : ereport(ERROR,
7563 : (errcode(ERRCODE_SYNTAX_ERROR),
7564 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7565 : colName, RelationGetRelationName(rel))));
7566 :
7567 : /*
7568 : * Check that the attribute is not in a primary key or in an index used as
7569 : * a replica identity.
7570 : *
7571 : * Note: we'll throw error even if the pkey index is not valid.
7572 : */
7573 :
7574 : /* Loop over all indexes on the relation */
7575 164 : indexoidlist = RelationGetIndexList(rel);
7576 :
7577 328 : foreach_oid(indexoid, indexoidlist)
7578 : {
7579 : HeapTuple indexTuple;
7580 : Form_pg_index indexStruct;
7581 :
7582 24 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7583 24 : if (!HeapTupleIsValid(indexTuple))
7584 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
7585 24 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
7586 :
7587 : /*
7588 : * If the index is not a primary key or an index used as replica
7589 : * identity, skip the check.
7590 : */
7591 24 : if (indexStruct->indisprimary || indexStruct->indisreplident)
7592 : {
7593 : /*
7594 : * Loop over each attribute in the primary key or the index used
7595 : * as replica identity and see if it matches the to-be-altered
7596 : * attribute.
7597 : */
7598 24 : for (int i = 0; i < indexStruct->indnkeyatts; i++)
7599 : {
7600 18 : if (indexStruct->indkey.values[i] == attnum)
7601 : {
7602 12 : if (indexStruct->indisprimary)
7603 6 : ereport(ERROR,
7604 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7605 : errmsg("column \"%s\" is in a primary key",
7606 : colName)));
7607 : else
7608 6 : ereport(ERROR,
7609 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7610 : errmsg("column \"%s\" is in index used as replica identity",
7611 : colName)));
7612 : }
7613 : }
7614 : }
7615 :
7616 12 : ReleaseSysCache(indexTuple);
7617 : }
7618 :
7619 152 : list_free(indexoidlist);
7620 :
7621 : /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
7622 152 : if (rel->rd_rel->relispartition)
7623 : {
7624 18 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7625 18 : Relation parent = table_open(parentId, AccessShareLock);
7626 18 : TupleDesc tupDesc = RelationGetDescr(parent);
7627 : AttrNumber parent_attnum;
7628 :
7629 18 : parent_attnum = get_attnum(parentId, colName);
7630 18 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7631 18 : ereport(ERROR,
7632 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7633 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7634 : colName)));
7635 0 : table_close(parent, AccessShareLock);
7636 : }
7637 :
7638 : /*
7639 : * Okay, actually perform the catalog change ... if needed
7640 : */
7641 134 : if (attTup->attnotnull)
7642 : {
7643 128 : attTup->attnotnull = false;
7644 :
7645 128 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7646 :
7647 128 : ObjectAddressSubSet(address, RelationRelationId,
7648 : RelationGetRelid(rel), attnum);
7649 : }
7650 : else
7651 6 : address = InvalidObjectAddress;
7652 :
7653 134 : InvokeObjectPostAlterHook(RelationRelationId,
7654 : RelationGetRelid(rel), attnum);
7655 :
7656 134 : table_close(attr_rel, RowExclusiveLock);
7657 :
7658 134 : return address;
7659 : }
7660 :
7661 : /*
7662 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7663 : */
7664 :
7665 : static void
7666 7598 : ATPrepSetNotNull(List **wqueue, Relation rel,
7667 : AlterTableCmd *cmd, bool recurse, bool recursing,
7668 : LOCKMODE lockmode, AlterTableUtilityContext *context)
7669 : {
7670 : /*
7671 : * If we're already recursing, there's nothing to do; the topmost
7672 : * invocation of ATSimpleRecursion already visited all children.
7673 : */
7674 7598 : if (recursing)
7675 168 : return;
7676 :
7677 : /*
7678 : * If the target column is already marked NOT NULL, we can skip recursing
7679 : * to children, because their columns should already be marked NOT NULL as
7680 : * well. But there's no point in checking here unless the relation has
7681 : * some children; else we can just wait till execution to check. (If it
7682 : * does have children, however, this can save taking per-child locks
7683 : * unnecessarily. This greatly improves concurrency in some parallel
7684 : * restore scenarios.)
7685 : *
7686 : * Unfortunately, we can only apply this optimization to partitioned
7687 : * tables, because traditional inheritance doesn't enforce that child
7688 : * columns be NOT NULL when their parent is. (That's a bug that should
7689 : * get fixed someday.)
7690 : */
7691 7430 : if (rel->rd_rel->relhassubclass &&
7692 200 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7693 : {
7694 : HeapTuple tuple;
7695 : bool attnotnull;
7696 :
7697 156 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), cmd->name);
7698 :
7699 : /* Might as well throw the error now, if name is bad */
7700 156 : if (!HeapTupleIsValid(tuple))
7701 0 : ereport(ERROR,
7702 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7703 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7704 : cmd->name, RelationGetRelationName(rel))));
7705 :
7706 156 : attnotnull = ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull;
7707 156 : ReleaseSysCache(tuple);
7708 156 : if (attnotnull)
7709 56 : return;
7710 : }
7711 :
7712 : /*
7713 : * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
7714 : * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use
7715 : * normal recursion logic.
7716 : */
7717 7374 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
7718 136 : !recurse)
7719 36 : {
7720 36 : AlterTableCmd *newcmd = makeNode(AlterTableCmd);
7721 :
7722 36 : newcmd->subtype = AT_CheckNotNull;
7723 36 : newcmd->name = pstrdup(cmd->name);
7724 36 : ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context);
7725 : }
7726 : else
7727 7338 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
7728 : }
7729 :
7730 : /*
7731 : * Return the address of the modified column. If the column was already NOT
7732 : * NULL, InvalidObjectAddress is returned.
7733 : */
7734 : static ObjectAddress
7735 7598 : ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
7736 : const char *colName, LOCKMODE lockmode)
7737 : {
7738 : HeapTuple tuple;
7739 : AttrNumber attnum;
7740 : Relation attr_rel;
7741 : ObjectAddress address;
7742 :
7743 : /*
7744 : * lookup the attribute
7745 : */
7746 7598 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7747 :
7748 7598 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7749 :
7750 7598 : if (!HeapTupleIsValid(tuple))
7751 36 : ereport(ERROR,
7752 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7753 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7754 : colName, RelationGetRelationName(rel))));
7755 :
7756 7562 : attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
7757 :
7758 : /* Prevent them from altering a system attribute */
7759 7562 : if (attnum <= 0)
7760 0 : ereport(ERROR,
7761 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7762 : errmsg("cannot alter system column \"%s\"",
7763 : colName)));
7764 :
7765 : /*
7766 : * Okay, actually perform the catalog change ... if needed
7767 : */
7768 7562 : if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7769 : {
7770 928 : ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = true;
7771 :
7772 928 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7773 :
7774 : /*
7775 : * Ordinarily phase 3 must ensure that no NULLs exist in columns that
7776 : * are set NOT NULL; however, if we can find a constraint which proves
7777 : * this then we can skip that. We needn't bother looking if we've
7778 : * already found that we must verify some other not-null constraint.
7779 : */
7780 928 : if (!tab->verify_new_notnull &&
7781 836 : !NotNullImpliedByRelConstraints(rel, (Form_pg_attribute) GETSTRUCT(tuple)))
7782 : {
7783 : /* Tell Phase 3 it needs to test the constraint */
7784 806 : tab->verify_new_notnull = true;
7785 : }
7786 :
7787 928 : ObjectAddressSubSet(address, RelationRelationId,
7788 : RelationGetRelid(rel), attnum);
7789 : }
7790 : else
7791 6634 : address = InvalidObjectAddress;
7792 :
7793 7562 : InvokeObjectPostAlterHook(RelationRelationId,
7794 : RelationGetRelid(rel), attnum);
7795 :
7796 7562 : table_close(attr_rel, RowExclusiveLock);
7797 :
7798 7562 : return address;
7799 : }
7800 :
7801 : /*
7802 : * ALTER TABLE ALTER COLUMN CHECK NOT NULL
7803 : *
7804 : * This doesn't exist in the grammar, but we generate AT_CheckNotNull
7805 : * commands against the partitions of a partitioned table if the user
7806 : * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
7807 : * or tries to create a primary key on it (which internally creates
7808 : * AT_SetNotNull on the partitioned table). Such a command doesn't
7809 : * allow us to actually modify any partition, but we want to let it
7810 : * go through if the partitions are already properly marked.
7811 : *
7812 : * In future, this might need to adjust the child table's state, likely
7813 : * by incrementing an inheritance count for the attnotnull constraint.
7814 : * For now we need only check for the presence of the flag.
7815 : */
7816 : static void
7817 30 : ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
7818 : const char *colName, LOCKMODE lockmode)
7819 : {
7820 : HeapTuple tuple;
7821 :
7822 30 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7823 :
7824 30 : if (!HeapTupleIsValid(tuple))
7825 0 : ereport(ERROR,
7826 : errcode(ERRCODE_UNDEFINED_COLUMN),
7827 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7828 : colName, RelationGetRelationName(rel)));
7829 :
7830 30 : if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7831 12 : ereport(ERROR,
7832 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7833 : errmsg("constraint must be added to child tables too"),
7834 : errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
7835 : colName, RelationGetRelationName(rel)),
7836 : errhint("Do not specify the ONLY keyword.")));
7837 :
7838 18 : ReleaseSysCache(tuple);
7839 18 : }
7840 :
7841 : /*
7842 : * NotNullImpliedByRelConstraints
7843 : * Does rel's existing constraints imply NOT NULL for the given attribute?
7844 : */
7845 : static bool
7846 836 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7847 : {
7848 836 : NullTest *nnulltest = makeNode(NullTest);
7849 :
7850 1672 : nnulltest->arg = (Expr *) makeVar(1,
7851 836 : attr->attnum,
7852 : attr->atttypid,
7853 : attr->atttypmod,
7854 : attr->attcollation,
7855 : 0);
7856 836 : nnulltest->nulltesttype = IS_NOT_NULL;
7857 :
7858 : /*
7859 : * argisrow = false is correct even for a composite column, because
7860 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7861 : * case, just IS DISTINCT FROM NULL.
7862 : */
7863 836 : nnulltest->argisrow = false;
7864 836 : nnulltest->location = -1;
7865 :
7866 836 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7867 : {
7868 30 : ereport(DEBUG1,
7869 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7870 : RelationGetRelationName(rel), NameStr(attr->attname))));
7871 30 : return true;
7872 : }
7873 :
7874 806 : return false;
7875 : }
7876 :
7877 : /*
7878 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7879 : *
7880 : * Return the address of the affected column.
7881 : */
7882 : static ObjectAddress
7883 544 : ATExecColumnDefault(Relation rel, const char *colName,
7884 : Node *newDefault, LOCKMODE lockmode)
7885 : {
7886 544 : TupleDesc tupdesc = RelationGetDescr(rel);
7887 : AttrNumber attnum;
7888 : ObjectAddress address;
7889 :
7890 : /*
7891 : * get the number of the attribute
7892 : */
7893 544 : attnum = get_attnum(RelationGetRelid(rel), colName);
7894 544 : if (attnum == InvalidAttrNumber)
7895 30 : ereport(ERROR,
7896 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7897 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7898 : colName, RelationGetRelationName(rel))));
7899 :
7900 : /* Prevent them from altering a system attribute */
7901 514 : if (attnum <= 0)
7902 0 : ereport(ERROR,
7903 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7904 : errmsg("cannot alter system column \"%s\"",
7905 : colName)));
7906 :
7907 514 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7908 18 : ereport(ERROR,
7909 : (errcode(ERRCODE_SYNTAX_ERROR),
7910 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7911 : colName, RelationGetRelationName(rel)),
7912 : /* translator: %s is an SQL ALTER command */
7913 : newDefault ? 0 : errhint("Use %s instead.",
7914 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
7915 :
7916 496 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7917 6 : ereport(ERROR,
7918 : (errcode(ERRCODE_SYNTAX_ERROR),
7919 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
7920 : colName, RelationGetRelationName(rel)),
7921 : newDefault ?
7922 : /* translator: %s is an SQL ALTER command */
7923 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
7924 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
7925 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
7926 :
7927 : /*
7928 : * Remove any old default for the column. We use RESTRICT here for
7929 : * safety, but at present we do not expect anything to depend on the
7930 : * default.
7931 : *
7932 : * We treat removing the existing default as an internal operation when it
7933 : * is preparatory to adding a new default, but as a user-initiated
7934 : * operation when the user asked for a drop.
7935 : */
7936 490 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7937 : newDefault != NULL);
7938 :
7939 490 : if (newDefault)
7940 : {
7941 : /* SET DEFAULT */
7942 : RawColumnDefault *rawEnt;
7943 :
7944 316 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7945 316 : rawEnt->attnum = attnum;
7946 316 : rawEnt->raw_default = newDefault;
7947 316 : rawEnt->missingMode = false;
7948 316 : rawEnt->generated = '\0';
7949 :
7950 : /*
7951 : * This function is intended for CREATE TABLE, so it processes a
7952 : * _list_ of defaults, but we just do one.
7953 : */
7954 316 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7955 : false, true, false, NULL);
7956 : }
7957 :
7958 484 : ObjectAddressSubSet(address, RelationRelationId,
7959 : RelationGetRelid(rel), attnum);
7960 484 : return address;
7961 : }
7962 :
7963 : /*
7964 : * Add a pre-cooked default expression.
7965 : *
7966 : * Return the address of the affected column.
7967 : */
7968 : static ObjectAddress
7969 110 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
7970 : Node *newDefault)
7971 : {
7972 : ObjectAddress address;
7973 :
7974 : /* We assume no checking is required */
7975 :
7976 : /*
7977 : * Remove any old default for the column. We use RESTRICT here for
7978 : * safety, but at present we do not expect anything to depend on the
7979 : * default. (In ordinary cases, there could not be a default in place
7980 : * anyway, but it's possible when combining LIKE with inheritance.)
7981 : */
7982 110 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7983 : true);
7984 :
7985 110 : (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
7986 :
7987 110 : ObjectAddressSubSet(address, RelationRelationId,
7988 : RelationGetRelid(rel), attnum);
7989 110 : return address;
7990 : }
7991 :
7992 : /*
7993 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
7994 : *
7995 : * Return the address of the affected column.
7996 : */
7997 : static ObjectAddress
7998 156 : ATExecAddIdentity(Relation rel, const char *colName,
7999 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8000 : {
8001 : Relation attrelation;
8002 : HeapTuple tuple;
8003 : Form_pg_attribute attTup;
8004 : AttrNumber attnum;
8005 : ObjectAddress address;
8006 156 : ColumnDef *cdef = castNode(ColumnDef, def);
8007 : bool ispartitioned;
8008 :
8009 156 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8010 156 : if (ispartitioned && !recurse)
8011 6 : ereport(ERROR,
8012 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8013 : errmsg("cannot add identity to a column of only the partitioned table"),
8014 : errhint("Do not specify the ONLY keyword.")));
8015 :
8016 150 : if (rel->rd_rel->relispartition && !recursing)
8017 12 : ereport(ERROR,
8018 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8019 : errmsg("cannot add identity to a column of a partition"));
8020 :
8021 138 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8022 :
8023 138 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8024 138 : if (!HeapTupleIsValid(tuple))
8025 0 : ereport(ERROR,
8026 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8027 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8028 : colName, RelationGetRelationName(rel))));
8029 138 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8030 138 : attnum = attTup->attnum;
8031 :
8032 : /* Can't alter a system attribute */
8033 138 : if (attnum <= 0)
8034 0 : ereport(ERROR,
8035 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8036 : errmsg("cannot alter system column \"%s\"",
8037 : colName)));
8038 :
8039 : /*
8040 : * Creating a column as identity implies NOT NULL, so adding the identity
8041 : * to an existing column that is not NOT NULL would create a state that
8042 : * cannot be reproduced without contortions.
8043 : */
8044 138 : if (!attTup->attnotnull)
8045 6 : ereport(ERROR,
8046 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8047 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8048 : colName, RelationGetRelationName(rel))));
8049 :
8050 132 : if (attTup->attidentity)
8051 18 : ereport(ERROR,
8052 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8053 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8054 : colName, RelationGetRelationName(rel))));
8055 :
8056 114 : if (attTup->atthasdef)
8057 6 : ereport(ERROR,
8058 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8059 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8060 : colName, RelationGetRelationName(rel))));
8061 :
8062 108 : attTup->attidentity = cdef->identity;
8063 108 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8064 :
8065 108 : InvokeObjectPostAlterHook(RelationRelationId,
8066 : RelationGetRelid(rel),
8067 : attTup->attnum);
8068 108 : ObjectAddressSubSet(address, RelationRelationId,
8069 : RelationGetRelid(rel), attnum);
8070 108 : heap_freetuple(tuple);
8071 :
8072 108 : table_close(attrelation, RowExclusiveLock);
8073 :
8074 : /*
8075 : * Recurse to propagate the identity column to partitions. Identity is
8076 : * not inherited in regular inheritance children.
8077 : */
8078 108 : if (recurse && ispartitioned)
8079 : {
8080 : List *children;
8081 : ListCell *lc;
8082 :
8083 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8084 :
8085 16 : foreach(lc, children)
8086 : {
8087 : Relation childrel;
8088 :
8089 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8090 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8091 6 : table_close(childrel, NoLock);
8092 : }
8093 : }
8094 :
8095 108 : return address;
8096 : }
8097 :
8098 : /*
8099 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8100 : *
8101 : * Return the address of the affected column.
8102 : */
8103 : static ObjectAddress
8104 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8105 : LOCKMODE lockmode, bool recurse, bool recursing)
8106 : {
8107 : ListCell *option;
8108 74 : DefElem *generatedEl = NULL;
8109 : HeapTuple tuple;
8110 : Form_pg_attribute attTup;
8111 : AttrNumber attnum;
8112 : Relation attrelation;
8113 : ObjectAddress address;
8114 : bool ispartitioned;
8115 :
8116 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8117 74 : if (ispartitioned && !recurse)
8118 6 : ereport(ERROR,
8119 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8120 : errmsg("cannot change identity column of only the partitioned table"),
8121 : errhint("Do not specify the ONLY keyword.")));
8122 :
8123 68 : if (rel->rd_rel->relispartition && !recursing)
8124 12 : ereport(ERROR,
8125 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8126 : errmsg("cannot change identity column of a partition"));
8127 :
8128 100 : foreach(option, castNode(List, def))
8129 : {
8130 44 : DefElem *defel = lfirst_node(DefElem, option);
8131 :
8132 44 : if (strcmp(defel->defname, "generated") == 0)
8133 : {
8134 44 : if (generatedEl)
8135 0 : ereport(ERROR,
8136 : (errcode(ERRCODE_SYNTAX_ERROR),
8137 : errmsg("conflicting or redundant options")));
8138 44 : generatedEl = defel;
8139 : }
8140 : else
8141 0 : elog(ERROR, "option \"%s\" not recognized",
8142 : defel->defname);
8143 : }
8144 :
8145 : /*
8146 : * Even if there is nothing to change here, we run all the checks. There
8147 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8148 : * there.
8149 : */
8150 :
8151 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8152 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8153 56 : 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 :
8159 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8160 56 : attnum = attTup->attnum;
8161 :
8162 56 : if (attnum <= 0)
8163 0 : ereport(ERROR,
8164 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8165 : errmsg("cannot alter system column \"%s\"",
8166 : colName)));
8167 :
8168 56 : if (!attTup->attidentity)
8169 6 : ereport(ERROR,
8170 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8171 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8172 : colName, RelationGetRelationName(rel))));
8173 :
8174 50 : if (generatedEl)
8175 : {
8176 44 : attTup->attidentity = defGetInt32(generatedEl);
8177 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8178 :
8179 44 : InvokeObjectPostAlterHook(RelationRelationId,
8180 : RelationGetRelid(rel),
8181 : attTup->attnum);
8182 44 : ObjectAddressSubSet(address, RelationRelationId,
8183 : RelationGetRelid(rel), attnum);
8184 : }
8185 : else
8186 6 : address = InvalidObjectAddress;
8187 :
8188 50 : heap_freetuple(tuple);
8189 50 : table_close(attrelation, RowExclusiveLock);
8190 :
8191 : /*
8192 : * Recurse to propagate the identity change to partitions. Identity is not
8193 : * inherited in regular inheritance children.
8194 : */
8195 50 : if (generatedEl && recurse && ispartitioned)
8196 : {
8197 : List *children;
8198 : ListCell *lc;
8199 :
8200 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8201 :
8202 18 : foreach(lc, children)
8203 : {
8204 : Relation childrel;
8205 :
8206 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8207 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8208 12 : table_close(childrel, NoLock);
8209 : }
8210 : }
8211 :
8212 50 : return address;
8213 : }
8214 :
8215 : /*
8216 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8217 : *
8218 : * Return the address of the affected column.
8219 : */
8220 : static ObjectAddress
8221 92 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8222 : bool recurse, bool recursing)
8223 : {
8224 : HeapTuple tuple;
8225 : Form_pg_attribute attTup;
8226 : AttrNumber attnum;
8227 : Relation attrelation;
8228 : ObjectAddress address;
8229 : Oid seqid;
8230 : ObjectAddress seqaddress;
8231 : bool ispartitioned;
8232 :
8233 92 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8234 92 : if (ispartitioned && !recurse)
8235 6 : ereport(ERROR,
8236 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8237 : errmsg("cannot drop identity from a column of only the partitioned table"),
8238 : errhint("Do not specify the ONLY keyword.")));
8239 :
8240 86 : if (rel->rd_rel->relispartition && !recursing)
8241 6 : ereport(ERROR,
8242 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8243 : errmsg("cannot drop identity from a column of a partition"));
8244 :
8245 80 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8246 80 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8247 80 : if (!HeapTupleIsValid(tuple))
8248 0 : ereport(ERROR,
8249 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8250 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8251 : colName, RelationGetRelationName(rel))));
8252 :
8253 80 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8254 80 : attnum = attTup->attnum;
8255 :
8256 80 : if (attnum <= 0)
8257 0 : ereport(ERROR,
8258 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8259 : errmsg("cannot alter system column \"%s\"",
8260 : colName)));
8261 :
8262 80 : if (!attTup->attidentity)
8263 : {
8264 12 : if (!missing_ok)
8265 6 : ereport(ERROR,
8266 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8267 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8268 : colName, RelationGetRelationName(rel))));
8269 : else
8270 : {
8271 6 : ereport(NOTICE,
8272 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8273 : colName, RelationGetRelationName(rel))));
8274 6 : heap_freetuple(tuple);
8275 6 : table_close(attrelation, RowExclusiveLock);
8276 6 : return InvalidObjectAddress;
8277 : }
8278 : }
8279 :
8280 68 : attTup->attidentity = '\0';
8281 68 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8282 :
8283 68 : InvokeObjectPostAlterHook(RelationRelationId,
8284 : RelationGetRelid(rel),
8285 : attTup->attnum);
8286 68 : ObjectAddressSubSet(address, RelationRelationId,
8287 : RelationGetRelid(rel), attnum);
8288 68 : heap_freetuple(tuple);
8289 :
8290 68 : table_close(attrelation, RowExclusiveLock);
8291 :
8292 : /*
8293 : * Recurse to drop the identity from column in partitions. Identity is
8294 : * not inherited in regular inheritance children so ignore them.
8295 : */
8296 68 : if (recurse && ispartitioned)
8297 : {
8298 : List *children;
8299 : ListCell *lc;
8300 :
8301 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8302 :
8303 12 : foreach(lc, children)
8304 : {
8305 : Relation childrel;
8306 :
8307 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8308 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8309 6 : table_close(childrel, NoLock);
8310 : }
8311 : }
8312 :
8313 68 : if (!recursing)
8314 : {
8315 : /* drop the internal sequence */
8316 32 : seqid = getIdentitySequence(rel, attnum, false);
8317 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8318 : RelationRelationId, DEPENDENCY_INTERNAL);
8319 32 : CommandCounterIncrement();
8320 32 : seqaddress.classId = RelationRelationId;
8321 32 : seqaddress.objectId = seqid;
8322 32 : seqaddress.objectSubId = 0;
8323 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8324 : }
8325 :
8326 68 : return address;
8327 : }
8328 :
8329 : /*
8330 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8331 : *
8332 : * Return the address of the affected column.
8333 : */
8334 : static ObjectAddress
8335 84 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8336 : Node *newExpr, LOCKMODE lockmode)
8337 : {
8338 : HeapTuple tuple;
8339 : Form_pg_attribute attTup;
8340 : AttrNumber attnum;
8341 : Oid attrdefoid;
8342 : ObjectAddress address;
8343 : Expr *defval;
8344 : NewColumnValue *newval;
8345 : RawColumnDefault *rawEnt;
8346 :
8347 84 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8348 84 : if (!HeapTupleIsValid(tuple))
8349 0 : ereport(ERROR,
8350 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8351 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8352 : colName, RelationGetRelationName(rel))));
8353 :
8354 84 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8355 84 : attnum = attTup->attnum;
8356 :
8357 84 : if (attnum <= 0)
8358 0 : ereport(ERROR,
8359 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8360 : errmsg("cannot alter system column \"%s\"",
8361 : colName)));
8362 :
8363 84 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8364 6 : ereport(ERROR,
8365 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8366 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8367 : colName, RelationGetRelationName(rel))));
8368 78 : ReleaseSysCache(tuple);
8369 :
8370 : /*
8371 : * Clear all the missing values if we're rewriting the table, since this
8372 : * renders them pointless.
8373 : */
8374 78 : RelationClearMissing(rel);
8375 :
8376 : /* make sure we don't conflict with later attribute modifications */
8377 78 : CommandCounterIncrement();
8378 :
8379 : /*
8380 : * Find everything that depends on the column (constraints, indexes, etc),
8381 : * and record enough information to let us recreate the objects after
8382 : * rewrite.
8383 : */
8384 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8385 :
8386 : /*
8387 : * Drop the dependency records of the GENERATED expression, in particular
8388 : * its INTERNAL dependency on the column, which would otherwise cause
8389 : * dependency.c to refuse to perform the deletion.
8390 : */
8391 78 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8392 78 : if (!OidIsValid(attrdefoid))
8393 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8394 : RelationGetRelid(rel), attnum);
8395 78 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8396 :
8397 : /* Make above changes visible */
8398 78 : CommandCounterIncrement();
8399 :
8400 : /*
8401 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8402 : * safety, but at present we do not expect anything to depend on the
8403 : * expression.
8404 : */
8405 78 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8406 : false, false);
8407 :
8408 : /* Prepare to store the new expression, in the catalogs */
8409 78 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8410 78 : rawEnt->attnum = attnum;
8411 78 : rawEnt->raw_default = newExpr;
8412 78 : rawEnt->missingMode = false;
8413 78 : rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8414 :
8415 : /* Store the generated expression */
8416 78 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8417 : false, true, false, NULL);
8418 :
8419 : /* Make above new expression visible */
8420 78 : CommandCounterIncrement();
8421 :
8422 : /* Prepare for table rewrite */
8423 78 : defval = (Expr *) build_column_default(rel, attnum);
8424 :
8425 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8426 78 : newval->attnum = attnum;
8427 78 : newval->expr = expression_planner(defval);
8428 78 : newval->is_generated = true;
8429 :
8430 78 : tab->newvals = lappend(tab->newvals, newval);
8431 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8432 :
8433 : /* Drop any pg_statistic entry for the column */
8434 78 : RemoveStatistics(RelationGetRelid(rel), attnum);
8435 :
8436 78 : InvokeObjectPostAlterHook(RelationRelationId,
8437 : RelationGetRelid(rel), attnum);
8438 :
8439 78 : ObjectAddressSubSet(address, RelationRelationId,
8440 : RelationGetRelid(rel), attnum);
8441 78 : return address;
8442 : }
8443 :
8444 : /*
8445 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8446 : */
8447 : static void
8448 44 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8449 : {
8450 : /*
8451 : * Reject ONLY if there are child tables. We could implement this, but it
8452 : * is a bit complicated. GENERATED clauses must be attached to the column
8453 : * definition and cannot be added later like DEFAULT, so if a child table
8454 : * has a generation expression that the parent does not have, the child
8455 : * column will necessarily be an attislocal column. So to implement ONLY
8456 : * here, we'd need extra code to update attislocal of the direct child
8457 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8458 : * resulting state can be properly dumped and restored.
8459 : */
8460 56 : if (!recurse &&
8461 12 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8462 6 : ereport(ERROR,
8463 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8464 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8465 :
8466 : /*
8467 : * Cannot drop generation expression from inherited columns.
8468 : */
8469 38 : if (!recursing)
8470 : {
8471 : HeapTuple tuple;
8472 : Form_pg_attribute attTup;
8473 :
8474 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8475 32 : if (!HeapTupleIsValid(tuple))
8476 0 : ereport(ERROR,
8477 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8478 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8479 : cmd->name, RelationGetRelationName(rel))));
8480 :
8481 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8482 :
8483 32 : if (attTup->attinhcount > 0)
8484 6 : ereport(ERROR,
8485 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8486 : errmsg("cannot drop generation expression from inherited column")));
8487 : }
8488 32 : }
8489 :
8490 : /*
8491 : * Return the address of the affected column.
8492 : */
8493 : static ObjectAddress
8494 32 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8495 : {
8496 : HeapTuple tuple;
8497 : Form_pg_attribute attTup;
8498 : AttrNumber attnum;
8499 : Relation attrelation;
8500 : Oid attrdefoid;
8501 : ObjectAddress address;
8502 :
8503 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8504 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8505 32 : if (!HeapTupleIsValid(tuple))
8506 0 : ereport(ERROR,
8507 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8508 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8509 : colName, RelationGetRelationName(rel))));
8510 :
8511 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8512 32 : attnum = attTup->attnum;
8513 :
8514 32 : if (attnum <= 0)
8515 0 : ereport(ERROR,
8516 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8517 : errmsg("cannot alter system column \"%s\"",
8518 : colName)));
8519 :
8520 32 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8521 : {
8522 12 : if (!missing_ok)
8523 6 : ereport(ERROR,
8524 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8525 : errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8526 : colName, RelationGetRelationName(rel))));
8527 : else
8528 : {
8529 6 : ereport(NOTICE,
8530 : (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8531 : colName, RelationGetRelationName(rel))));
8532 6 : heap_freetuple(tuple);
8533 6 : table_close(attrelation, RowExclusiveLock);
8534 6 : return InvalidObjectAddress;
8535 : }
8536 : }
8537 :
8538 : /*
8539 : * Mark the column as no longer generated. (The atthasdef flag needs to
8540 : * get cleared too, but RemoveAttrDefault will handle that.)
8541 : */
8542 20 : attTup->attgenerated = '\0';
8543 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8544 :
8545 20 : InvokeObjectPostAlterHook(RelationRelationId,
8546 : RelationGetRelid(rel),
8547 : attnum);
8548 20 : heap_freetuple(tuple);
8549 :
8550 20 : table_close(attrelation, RowExclusiveLock);
8551 :
8552 : /*
8553 : * Drop the dependency records of the GENERATED expression, in particular
8554 : * its INTERNAL dependency on the column, which would otherwise cause
8555 : * dependency.c to refuse to perform the deletion.
8556 : */
8557 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8558 20 : if (!OidIsValid(attrdefoid))
8559 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8560 : RelationGetRelid(rel), attnum);
8561 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8562 :
8563 : /* Make above changes visible */
8564 20 : CommandCounterIncrement();
8565 :
8566 : /*
8567 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8568 : * safety, but at present we do not expect anything to depend on the
8569 : * default.
8570 : */
8571 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8572 : false, false);
8573 :
8574 20 : ObjectAddressSubSet(address, RelationRelationId,
8575 : RelationGetRelid(rel), attnum);
8576 20 : return address;
8577 : }
8578 :
8579 : /*
8580 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8581 : *
8582 : * Return value is the address of the modified column
8583 : */
8584 : static ObjectAddress
8585 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8586 : {
8587 164 : int newtarget = 0;
8588 : bool newtarget_default;
8589 : Relation attrelation;
8590 : HeapTuple tuple,
8591 : newtuple;
8592 : Form_pg_attribute attrtuple;
8593 : AttrNumber attnum;
8594 : ObjectAddress address;
8595 : Datum repl_val[Natts_pg_attribute];
8596 : bool repl_null[Natts_pg_attribute];
8597 : bool repl_repl[Natts_pg_attribute];
8598 :
8599 : /*
8600 : * We allow referencing columns by numbers only for indexes, since table
8601 : * column numbers could contain gaps if columns are later dropped.
8602 : */
8603 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8604 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8605 : !colName)
8606 0 : ereport(ERROR,
8607 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8608 : errmsg("cannot refer to non-index column by number")));
8609 :
8610 : /* -1 was used in previous versions for the default setting */
8611 164 : if (newValue && intVal(newValue) != -1)
8612 : {
8613 120 : newtarget = intVal(newValue);
8614 120 : newtarget_default = false;
8615 : }
8616 : else
8617 44 : newtarget_default = true;
8618 :
8619 164 : if (!newtarget_default)
8620 : {
8621 : /*
8622 : * Limit target to a sane range
8623 : */
8624 120 : if (newtarget < 0)
8625 : {
8626 0 : ereport(ERROR,
8627 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8628 : errmsg("statistics target %d is too low",
8629 : newtarget)));
8630 : }
8631 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8632 : {
8633 0 : newtarget = MAX_STATISTICS_TARGET;
8634 0 : ereport(WARNING,
8635 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8636 : errmsg("lowering statistics target to %d",
8637 : newtarget)));
8638 : }
8639 : }
8640 :
8641 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8642 :
8643 164 : if (colName)
8644 : {
8645 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8646 :
8647 100 : if (!HeapTupleIsValid(tuple))
8648 12 : ereport(ERROR,
8649 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8650 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8651 : colName, RelationGetRelationName(rel))));
8652 : }
8653 : else
8654 : {
8655 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8656 :
8657 64 : if (!HeapTupleIsValid(tuple))
8658 12 : ereport(ERROR,
8659 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8660 : errmsg("column number %d of relation \"%s\" does not exist",
8661 : colNum, RelationGetRelationName(rel))));
8662 : }
8663 :
8664 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8665 :
8666 140 : attnum = attrtuple->attnum;
8667 140 : if (attnum <= 0)
8668 0 : ereport(ERROR,
8669 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8670 : errmsg("cannot alter system column \"%s\"",
8671 : colName)));
8672 :
8673 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8674 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8675 : {
8676 52 : if (attnum > rel->rd_index->indnkeyatts)
8677 6 : ereport(ERROR,
8678 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8679 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8680 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8681 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8682 18 : ereport(ERROR,
8683 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8684 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8685 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8686 : errhint("Alter statistics on table column instead.")));
8687 : }
8688 :
8689 : /* Build new tuple. */
8690 116 : memset(repl_null, false, sizeof(repl_null));
8691 116 : memset(repl_repl, false, sizeof(repl_repl));
8692 116 : if (!newtarget_default)
8693 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8694 : else
8695 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8696 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8697 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8698 : repl_val, repl_null, repl_repl);
8699 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8700 :
8701 116 : InvokeObjectPostAlterHook(RelationRelationId,
8702 : RelationGetRelid(rel),
8703 : attrtuple->attnum);
8704 116 : ObjectAddressSubSet(address, RelationRelationId,
8705 : RelationGetRelid(rel), attnum);
8706 :
8707 116 : heap_freetuple(newtuple);
8708 :
8709 116 : ReleaseSysCache(tuple);
8710 :
8711 116 : table_close(attrelation, RowExclusiveLock);
8712 :
8713 116 : return address;
8714 : }
8715 :
8716 : /*
8717 : * Return value is the address of the modified column
8718 : */
8719 : static ObjectAddress
8720 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8721 : bool isReset, LOCKMODE lockmode)
8722 : {
8723 : Relation attrelation;
8724 : HeapTuple tuple,
8725 : newtuple;
8726 : Form_pg_attribute attrtuple;
8727 : AttrNumber attnum;
8728 : Datum datum,
8729 : newOptions;
8730 : bool isnull;
8731 : ObjectAddress address;
8732 : Datum repl_val[Natts_pg_attribute];
8733 : bool repl_null[Natts_pg_attribute];
8734 : bool repl_repl[Natts_pg_attribute];
8735 :
8736 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8737 :
8738 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8739 :
8740 32 : if (!HeapTupleIsValid(tuple))
8741 0 : ereport(ERROR,
8742 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8743 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8744 : colName, RelationGetRelationName(rel))));
8745 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8746 :
8747 32 : attnum = attrtuple->attnum;
8748 32 : if (attnum <= 0)
8749 0 : ereport(ERROR,
8750 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8751 : errmsg("cannot alter system column \"%s\"",
8752 : colName)));
8753 :
8754 : /* Generate new proposed attoptions (text array) */
8755 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8756 : &isnull);
8757 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8758 : castNode(List, options), NULL, NULL,
8759 : false, isReset);
8760 : /* Validate new options */
8761 32 : (void) attribute_reloptions(newOptions, true);
8762 :
8763 : /* Build new tuple. */
8764 32 : memset(repl_null, false, sizeof(repl_null));
8765 32 : memset(repl_repl, false, sizeof(repl_repl));
8766 32 : if (newOptions != (Datum) 0)
8767 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8768 : else
8769 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
8770 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8771 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8772 : repl_val, repl_null, repl_repl);
8773 :
8774 : /* Update system catalog. */
8775 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8776 :
8777 32 : InvokeObjectPostAlterHook(RelationRelationId,
8778 : RelationGetRelid(rel),
8779 : attrtuple->attnum);
8780 32 : ObjectAddressSubSet(address, RelationRelationId,
8781 : RelationGetRelid(rel), attnum);
8782 :
8783 32 : heap_freetuple(newtuple);
8784 :
8785 32 : ReleaseSysCache(tuple);
8786 :
8787 32 : table_close(attrelation, RowExclusiveLock);
8788 :
8789 32 : return address;
8790 : }
8791 :
8792 : /*
8793 : * Helper function for ATExecSetStorage and ATExecSetCompression
8794 : *
8795 : * Set the attstorage and/or attcompression fields for index columns
8796 : * associated with the specified table column.
8797 : */
8798 : static void
8799 284 : SetIndexStorageProperties(Relation rel, Relation attrelation,
8800 : AttrNumber attnum,
8801 : bool setstorage, char newstorage,
8802 : bool setcompression, char newcompression,
8803 : LOCKMODE lockmode)
8804 : {
8805 : ListCell *lc;
8806 :
8807 356 : foreach(lc, RelationGetIndexList(rel))
8808 : {
8809 72 : Oid indexoid = lfirst_oid(lc);
8810 : Relation indrel;
8811 72 : AttrNumber indattnum = 0;
8812 : HeapTuple tuple;
8813 :
8814 72 : indrel = index_open(indexoid, lockmode);
8815 :
8816 120 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
8817 : {
8818 78 : if (indrel->rd_index->indkey.values[i] == attnum)
8819 : {
8820 30 : indattnum = i + 1;
8821 30 : break;
8822 : }
8823 : }
8824 :
8825 72 : if (indattnum == 0)
8826 : {
8827 42 : index_close(indrel, lockmode);
8828 42 : continue;
8829 : }
8830 :
8831 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8832 :
8833 30 : if (HeapTupleIsValid(tuple))
8834 : {
8835 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8836 :
8837 30 : if (setstorage)
8838 24 : attrtuple->attstorage = newstorage;
8839 :
8840 30 : if (setcompression)
8841 6 : attrtuple->attcompression = newcompression;
8842 :
8843 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8844 :
8845 30 : InvokeObjectPostAlterHook(RelationRelationId,
8846 : RelationGetRelid(rel),
8847 : attrtuple->attnum);
8848 :
8849 30 : heap_freetuple(tuple);
8850 : }
8851 :
8852 30 : index_close(indrel, lockmode);
8853 : }
8854 284 : }
8855 :
8856 : /*
8857 : * ALTER TABLE ALTER COLUMN SET STORAGE
8858 : *
8859 : * Return value is the address of the modified column
8860 : */
8861 : static ObjectAddress
8862 234 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8863 : {
8864 : Relation attrelation;
8865 : HeapTuple tuple;
8866 : Form_pg_attribute attrtuple;
8867 : AttrNumber attnum;
8868 : ObjectAddress address;
8869 :
8870 234 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8871 :
8872 234 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8873 :
8874 234 : if (!HeapTupleIsValid(tuple))
8875 12 : ereport(ERROR,
8876 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8877 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8878 : colName, RelationGetRelationName(rel))));
8879 222 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8880 :
8881 222 : attnum = attrtuple->attnum;
8882 222 : if (attnum <= 0)
8883 0 : ereport(ERROR,
8884 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8885 : errmsg("cannot alter system column \"%s\"",
8886 : colName)));
8887 :
8888 222 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8889 :
8890 222 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8891 :
8892 222 : InvokeObjectPostAlterHook(RelationRelationId,
8893 : RelationGetRelid(rel),
8894 : attrtuple->attnum);
8895 :
8896 : /*
8897 : * Apply the change to indexes as well (only for simple index columns,
8898 : * matching behavior of index.c ConstructTupleDescriptor()).
8899 : */
8900 222 : SetIndexStorageProperties(rel, attrelation, attnum,
8901 222 : true, attrtuple->attstorage,
8902 : false, 0,
8903 : lockmode);
8904 :
8905 222 : heap_freetuple(tuple);
8906 :
8907 222 : table_close(attrelation, RowExclusiveLock);
8908 :
8909 222 : ObjectAddressSubSet(address, RelationRelationId,
8910 : RelationGetRelid(rel), attnum);
8911 222 : return address;
8912 : }
8913 :
8914 :
8915 : /*
8916 : * ALTER TABLE DROP COLUMN
8917 : *
8918 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8919 : * because we have to decide at runtime whether to recurse or not depending
8920 : * on whether attinhcount goes to zero or not. (We can't check this in a
8921 : * static pre-pass because it won't handle multiple inheritance situations
8922 : * correctly.)
8923 : */
8924 : static void
8925 1598 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8926 : AlterTableCmd *cmd, LOCKMODE lockmode,
8927 : AlterTableUtilityContext *context)
8928 : {
8929 1598 : if (rel->rd_rel->reloftype && !recursing)
8930 6 : ereport(ERROR,
8931 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8932 : errmsg("cannot drop column from typed table")));
8933 :
8934 1592 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
8935 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8936 :
8937 1586 : if (recurse)
8938 1318 : cmd->recurse = true;
8939 1586 : }
8940 :
8941 : /*
8942 : * Drops column 'colName' from relation 'rel' and returns the address of the
8943 : * dropped column. The column is also dropped (or marked as no longer
8944 : * inherited from relation) from the relation's inheritance children, if any.
8945 : *
8946 : * In the recursive invocations for inheritance child relations, instead of
8947 : * dropping the column directly (if to be dropped at all), its object address
8948 : * is added to 'addrs', which must be non-NULL in such invocations. All
8949 : * columns are dropped at the same time after all the children have been
8950 : * checked recursively.
8951 : */
8952 : static ObjectAddress
8953 2142 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
8954 : DropBehavior behavior,
8955 : bool recurse, bool recursing,
8956 : bool missing_ok, LOCKMODE lockmode,
8957 : ObjectAddresses *addrs)
8958 : {
8959 : HeapTuple tuple;
8960 : Form_pg_attribute targetatt;
8961 : AttrNumber attnum;
8962 : List *children;
8963 : ObjectAddress object;
8964 : bool is_expr;
8965 :
8966 : /* At top level, permission check was done in ATPrepCmd, else do it */
8967 2142 : if (recursing)
8968 556 : ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8969 :
8970 : /* Initialize addrs on the first invocation */
8971 : Assert(!recursing || addrs != NULL);
8972 :
8973 : /* since this function recurses, it could be driven to stack overflow */
8974 2142 : check_stack_depth();
8975 :
8976 2142 : if (!recursing)
8977 1586 : addrs = new_object_addresses();
8978 :
8979 : /*
8980 : * get the number of the attribute
8981 : */
8982 2142 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8983 2142 : if (!HeapTupleIsValid(tuple))
8984 : {
8985 54 : if (!missing_ok)
8986 : {
8987 36 : ereport(ERROR,
8988 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8989 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8990 : colName, RelationGetRelationName(rel))));
8991 : }
8992 : else
8993 : {
8994 18 : ereport(NOTICE,
8995 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8996 : colName, RelationGetRelationName(rel))));
8997 18 : return InvalidObjectAddress;
8998 : }
8999 : }
9000 2088 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9001 :
9002 2088 : attnum = targetatt->attnum;
9003 :
9004 : /* Can't drop a system attribute */
9005 2088 : if (attnum <= 0)
9006 6 : ereport(ERROR,
9007 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9008 : errmsg("cannot drop system column \"%s\"",
9009 : colName)));
9010 :
9011 : /*
9012 : * Don't drop inherited columns, unless recursing (presumably from a drop
9013 : * of the parent column)
9014 : */
9015 2082 : if (targetatt->attinhcount > 0 && !recursing)
9016 48 : ereport(ERROR,
9017 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9018 : errmsg("cannot drop inherited column \"%s\"",
9019 : colName)));
9020 :
9021 : /*
9022 : * Don't drop columns used in the partition key, either. (If we let this
9023 : * go through, the key column's dependencies would cause a cascaded drop
9024 : * of the whole table, which is surely not what the user expected.)
9025 : */
9026 2034 : if (has_partition_attrs(rel,
9027 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9028 : &is_expr))
9029 30 : ereport(ERROR,
9030 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9031 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9032 : colName, RelationGetRelationName(rel))));
9033 :
9034 2004 : ReleaseSysCache(tuple);
9035 :
9036 : /*
9037 : * Propagate to children as appropriate. Unlike most other ALTER
9038 : * routines, we have to do this one level of recursion at a time; we can't
9039 : * use find_all_inheritors to do it in one pass.
9040 : */
9041 : children =
9042 2004 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9043 :
9044 2004 : if (children)
9045 : {
9046 : Relation attr_rel;
9047 : ListCell *child;
9048 :
9049 : /*
9050 : * In case of a partitioned table, the column must be dropped from the
9051 : * partitions as well.
9052 : */
9053 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9054 6 : ereport(ERROR,
9055 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9056 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9057 : errhint("Do not specify the ONLY keyword.")));
9058 :
9059 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9060 882 : foreach(child, children)
9061 : {
9062 592 : Oid childrelid = lfirst_oid(child);
9063 : Relation childrel;
9064 : Form_pg_attribute childatt;
9065 :
9066 : /* find_inheritance_children already got lock */
9067 592 : childrel = table_open(childrelid, NoLock);
9068 592 : CheckAlterTableIsSafe(childrel);
9069 :
9070 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9071 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9072 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9073 : colName, childrelid);
9074 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9075 :
9076 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9077 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9078 : childrelid, colName);
9079 :
9080 592 : if (recurse)
9081 : {
9082 : /*
9083 : * If the child column has other definition sources, just
9084 : * decrement its inheritance count; if not, recurse to delete
9085 : * it.
9086 : */
9087 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9088 : {
9089 : /* Time to delete this child column, too */
9090 556 : ATExecDropColumn(wqueue, childrel, colName,
9091 : behavior, true, true,
9092 : false, lockmode, addrs);
9093 : }
9094 : else
9095 : {
9096 : /* Child column must survive my deletion */
9097 12 : childatt->attinhcount--;
9098 :
9099 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9100 :
9101 : /* Make update visible */
9102 12 : CommandCounterIncrement();
9103 : }
9104 : }
9105 : else
9106 : {
9107 : /*
9108 : * If we were told to drop ONLY in this table (no recursion),
9109 : * we need to mark the inheritors' attributes as locally
9110 : * defined rather than inherited.
9111 : */
9112 24 : childatt->attinhcount--;
9113 24 : childatt->attislocal = true;
9114 :
9115 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9116 :
9117 : /* Make update visible */
9118 24 : CommandCounterIncrement();
9119 : }
9120 :
9121 586 : heap_freetuple(tuple);
9122 :
9123 586 : table_close(childrel, NoLock);
9124 : }
9125 290 : table_close(attr_rel, RowExclusiveLock);
9126 : }
9127 :
9128 : /* Add object to delete */
9129 1992 : object.classId = RelationRelationId;
9130 1992 : object.objectId = RelationGetRelid(rel);
9131 1992 : object.objectSubId = attnum;
9132 1992 : add_exact_object_address(&object, addrs);
9133 :
9134 1992 : if (!recursing)
9135 : {
9136 : /* Recursion has ended, drop everything that was collected */
9137 1442 : performMultipleDeletions(addrs, behavior, 0);
9138 1400 : free_object_addresses(addrs);
9139 : }
9140 :
9141 1950 : return object;
9142 : }
9143 :
9144 : /*
9145 : * ALTER TABLE ADD INDEX
9146 : *
9147 : * There is no such command in the grammar, but parse_utilcmd.c converts
9148 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9149 : * us schedule creation of the index at the appropriate time during ALTER.
9150 : *
9151 : * Return value is the address of the new index.
9152 : */
9153 : static ObjectAddress
9154 1350 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9155 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9156 : {
9157 : bool check_rights;
9158 : bool skip_build;
9159 : bool quiet;
9160 : ObjectAddress address;
9161 :
9162 : Assert(IsA(stmt, IndexStmt));
9163 : Assert(!stmt->concurrent);
9164 :
9165 : /* The IndexStmt has already been through transformIndexStmt */
9166 : Assert(stmt->transformed);
9167 :
9168 : /* suppress schema rights check when rebuilding existing index */
9169 1350 : check_rights = !is_rebuild;
9170 : /* skip index build if phase 3 will do it or we're reusing an old one */
9171 1350 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9172 : /* suppress notices when rebuilding existing index */
9173 1350 : quiet = is_rebuild;
9174 :
9175 1350 : address = DefineIndex(RelationGetRelid(rel),
9176 : stmt,
9177 : InvalidOid, /* no predefined OID */
9178 : InvalidOid, /* no parent index */
9179 : InvalidOid, /* no parent constraint */
9180 : -1, /* total_parts unknown */
9181 : true, /* is_alter_table */
9182 : check_rights,
9183 : false, /* check_not_in_use - we did it already */
9184 : skip_build,
9185 : quiet);
9186 :
9187 : /*
9188 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9189 : * new index instead of building from scratch. Restore associated fields.
9190 : * This may store InvalidSubTransactionId in both fields, in which case
9191 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9192 : * this after the CCI that made catalog rows visible to any rebuild. The
9193 : * DROP of the old edition of this index will have scheduled the storage
9194 : * for deletion at commit, so cancel that pending deletion.
9195 : */
9196 1228 : if (RelFileNumberIsValid(stmt->oldNumber))
9197 : {
9198 72 : Relation irel = index_open(address.objectId, NoLock);
9199 :
9200 72 : irel->rd_createSubid = stmt->oldCreateSubid;
9201 72 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9202 72 : RelationPreserveStorage(irel->rd_locator, true);
9203 72 : index_close(irel, NoLock);
9204 : }
9205 :
9206 1228 : return address;
9207 : }
9208 :
9209 : /*
9210 : * ALTER TABLE ADD STATISTICS
9211 : *
9212 : * This is no such command in the grammar, but we use this internally to add
9213 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9214 : * column type change.
9215 : */
9216 : static ObjectAddress
9217 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9218 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9219 : {
9220 : ObjectAddress address;
9221 :
9222 : Assert(IsA(stmt, CreateStatsStmt));
9223 :
9224 : /* The CreateStatsStmt has already been through transformStatsStmt */
9225 : Assert(stmt->transformed);
9226 :
9227 14 : address = CreateStatistics(stmt);
9228 :
9229 14 : return address;
9230 : }
9231 :
9232 : /*
9233 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9234 : *
9235 : * Returns the address of the new constraint.
9236 : */
9237 : static ObjectAddress
9238 8422 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9239 : IndexStmt *stmt, LOCKMODE lockmode)
9240 : {
9241 8422 : Oid index_oid = stmt->indexOid;
9242 : Relation indexRel;
9243 : char *indexName;
9244 : IndexInfo *indexInfo;
9245 : char *constraintName;
9246 : char constraintType;
9247 : ObjectAddress address;
9248 : bits16 flags;
9249 :
9250 : Assert(IsA(stmt, IndexStmt));
9251 : Assert(OidIsValid(index_oid));
9252 : Assert(stmt->isconstraint);
9253 :
9254 : /*
9255 : * Doing this on partitioned tables is not a simple feature to implement,
9256 : * so let's punt for now.
9257 : */
9258 8422 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9259 6 : ereport(ERROR,
9260 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9261 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9262 :
9263 8416 : indexRel = index_open(index_oid, AccessShareLock);
9264 :
9265 8416 : indexName = pstrdup(RelationGetRelationName(indexRel));
9266 :
9267 8416 : indexInfo = BuildIndexInfo(indexRel);
9268 :
9269 : /* this should have been checked at parse time */
9270 8416 : if (!indexInfo->ii_Unique)
9271 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9272 :
9273 : /*
9274 : * Determine name to assign to constraint. We require a constraint to
9275 : * have the same name as the underlying index; therefore, use the index's
9276 : * existing name as the default constraint name, and if the user
9277 : * explicitly gives some other name for the constraint, rename the index
9278 : * to match.
9279 : */
9280 8416 : constraintName = stmt->idxname;
9281 8416 : if (constraintName == NULL)
9282 8402 : constraintName = indexName;
9283 14 : else if (strcmp(constraintName, indexName) != 0)
9284 : {
9285 8 : ereport(NOTICE,
9286 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9287 : indexName, constraintName)));
9288 8 : RenameRelationInternal(index_oid, constraintName, false, true);
9289 : }
9290 :
9291 : /* Extra checks needed if making primary key */
9292 8416 : if (stmt->primary)
9293 4760 : index_check_primary_key(rel, indexInfo, true, stmt);
9294 :
9295 : /* Note we currently don't support EXCLUSION constraints here */
9296 8410 : if (stmt->primary)
9297 4754 : constraintType = CONSTRAINT_PRIMARY;
9298 : else
9299 3656 : constraintType = CONSTRAINT_UNIQUE;
9300 :
9301 : /* Create the catalog entries for the constraint */
9302 8410 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9303 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9304 16820 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9305 8410 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9306 8410 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9307 :
9308 8410 : address = index_constraint_create(rel,
9309 : index_oid,
9310 : InvalidOid,
9311 : indexInfo,
9312 : constraintName,
9313 : constraintType,
9314 : flags,
9315 : allowSystemTableMods,
9316 : false); /* is_internal */
9317 :
9318 8410 : index_close(indexRel, NoLock);
9319 :
9320 8410 : return address;
9321 : }
9322 :
9323 : /*
9324 : * ALTER TABLE ADD CONSTRAINT
9325 : *
9326 : * Return value is the address of the new constraint; if no constraint was
9327 : * added, InvalidObjectAddress is returned.
9328 : */
9329 : static ObjectAddress
9330 2898 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9331 : Constraint *newConstraint, bool recurse, bool is_readd,
9332 : LOCKMODE lockmode)
9333 : {
9334 2898 : ObjectAddress address = InvalidObjectAddress;
9335 :
9336 : Assert(IsA(newConstraint, Constraint));
9337 :
9338 : /*
9339 : * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
9340 : * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
9341 : * switch anyway to make it easier to add more code later.
9342 : */
9343 2898 : switch (newConstraint->contype)
9344 : {
9345 780 : case CONSTR_CHECK:
9346 : address =
9347 780 : ATAddCheckConstraint(wqueue, tab, rel,
9348 : newConstraint, recurse, false, is_readd,
9349 : lockmode);
9350 714 : break;
9351 :
9352 2118 : case CONSTR_FOREIGN:
9353 :
9354 : /*
9355 : * Assign or validate constraint name
9356 : */
9357 2118 : if (newConstraint->conname)
9358 : {
9359 796 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9360 : RelationGetRelid(rel),
9361 796 : newConstraint->conname))
9362 0 : ereport(ERROR,
9363 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9364 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9365 : newConstraint->conname,
9366 : RelationGetRelationName(rel))));
9367 : }
9368 : else
9369 1322 : newConstraint->conname =
9370 1322 : ChooseConstraintName(RelationGetRelationName(rel),
9371 1322 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9372 : "fkey",
9373 1322 : RelationGetNamespace(rel),
9374 : NIL);
9375 :
9376 2118 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9377 : newConstraint,
9378 : recurse, false,
9379 : lockmode);
9380 1798 : break;
9381 :
9382 0 : default:
9383 0 : elog(ERROR, "unrecognized constraint type: %d",
9384 : (int) newConstraint->contype);
9385 : }
9386 :
9387 2512 : return address;
9388 : }
9389 :
9390 : /*
9391 : * Generate the column-name portion of the constraint name for a new foreign
9392 : * key given the list of column names that reference the referenced
9393 : * table. This will be passed to ChooseConstraintName along with the parent
9394 : * table name and the "fkey" suffix.
9395 : *
9396 : * We know that less than NAMEDATALEN characters will actually be used, so we
9397 : * can truncate the result once we've generated that many.
9398 : *
9399 : * XXX see also ChooseExtendedStatisticNameAddition and
9400 : * ChooseIndexNameAddition.
9401 : */
9402 : static char *
9403 2024 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9404 : {
9405 : char buf[NAMEDATALEN * 2];
9406 2024 : int buflen = 0;
9407 : ListCell *lc;
9408 :
9409 2024 : buf[0] = '\0';
9410 4446 : foreach(lc, colnames)
9411 : {
9412 2422 : const char *name = strVal(lfirst(lc));
9413 :
9414 2422 : if (buflen > 0)
9415 398 : buf[buflen++] = '_'; /* insert _ between names */
9416 :
9417 : /*
9418 : * At this point we have buflen <= NAMEDATALEN. name should be less
9419 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9420 : */
9421 2422 : strlcpy(buf + buflen, name, NAMEDATALEN);
9422 2422 : buflen += strlen(buf + buflen);
9423 2422 : if (buflen >= NAMEDATALEN)
9424 0 : break;
9425 : }
9426 2024 : return pstrdup(buf);
9427 : }
9428 :
9429 : /*
9430 : * Add a check constraint to a single table and its children. Returns the
9431 : * address of the constraint added to the parent relation, if one gets added,
9432 : * or InvalidObjectAddress otherwise.
9433 : *
9434 : * Subroutine for ATExecAddConstraint.
9435 : *
9436 : * We must recurse to child tables during execution, rather than using
9437 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9438 : * constraints *must* be given the same name, else they won't be seen as
9439 : * related later. If the user didn't explicitly specify a name, then
9440 : * AddRelationNewConstraints would normally assign different names to the
9441 : * child constraints. To fix that, we must capture the name assigned at
9442 : * the parent table and pass that down.
9443 : */
9444 : static ObjectAddress
9445 1314 : ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9446 : Constraint *constr, bool recurse, bool recursing,
9447 : bool is_readd, LOCKMODE lockmode)
9448 : {
9449 : List *newcons;
9450 : ListCell *lcon;
9451 : List *children;
9452 : ListCell *child;
9453 1314 : ObjectAddress address = InvalidObjectAddress;
9454 :
9455 : /* At top level, permission check was done in ATPrepCmd, else do it */
9456 1314 : if (recursing)
9457 400 : ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9458 :
9459 : /*
9460 : * Call AddRelationNewConstraints to do the work, making sure it works on
9461 : * a copy of the Constraint so transformExpr can't modify the original. It
9462 : * returns a list of cooked constraints.
9463 : *
9464 : * If the constraint ends up getting merged with a pre-existing one, it's
9465 : * omitted from the returned list, which is what we want: we do not need
9466 : * to do any validation work. That can only happen at child tables,
9467 : * though, since we disallow merging at the top level.
9468 : */
9469 1314 : newcons = AddRelationNewConstraints(rel, NIL,
9470 1314 : list_make1(copyObject(constr)),
9471 1314 : recursing || is_readd, /* allow_merge */
9472 1314 : !recursing, /* is_local */
9473 : is_readd, /* is_internal */
9474 1314 : NULL); /* queryString not available
9475 : * here */
9476 :
9477 : /* we don't expect more than one constraint here */
9478 : Assert(list_length(newcons) <= 1);
9479 :
9480 : /* Add each to-be-validated constraint to Phase 3's queue */
9481 2450 : foreach(lcon, newcons)
9482 : {
9483 1196 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9484 :
9485 1196 : if (!ccon->skip_validation)
9486 : {
9487 : NewConstraint *newcon;
9488 :
9489 802 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9490 802 : newcon->name = ccon->name;
9491 802 : newcon->contype = ccon->contype;
9492 802 : newcon->qual = ccon->expr;
9493 :
9494 802 : tab->constraints = lappend(tab->constraints, newcon);
9495 : }
9496 :
9497 : /* Save the actually assigned name if it was defaulted */
9498 1196 : if (constr->conname == NULL)
9499 254 : constr->conname = ccon->name;
9500 :
9501 1196 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9502 : }
9503 :
9504 : /* At this point we must have a locked-down name to use */
9505 : Assert(constr->conname != NULL);
9506 :
9507 : /* Advance command counter in case same table is visited multiple times */
9508 1254 : CommandCounterIncrement();
9509 :
9510 : /*
9511 : * If the constraint got merged with an existing constraint, we're done.
9512 : * We mustn't recurse to child tables in this case, because they've
9513 : * already got the constraint, and visiting them again would lead to an
9514 : * incorrect value for coninhcount.
9515 : */
9516 1254 : if (newcons == NIL)
9517 58 : return address;
9518 :
9519 : /*
9520 : * If adding a NO INHERIT constraint, no need to find our children.
9521 : */
9522 1196 : if (constr->is_no_inherit)
9523 54 : return address;
9524 :
9525 : /*
9526 : * Propagate to children as appropriate. Unlike most other ALTER
9527 : * routines, we have to do this one level of recursion at a time; we can't
9528 : * use find_all_inheritors to do it in one pass.
9529 : */
9530 : children =
9531 1142 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9532 :
9533 : /*
9534 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9535 : * constraint creation only if there are no children currently. Error out
9536 : * otherwise.
9537 : */
9538 1142 : if (!recurse && children != NIL)
9539 6 : ereport(ERROR,
9540 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9541 : errmsg("constraint must be added to child tables too")));
9542 :
9543 1530 : foreach(child, children)
9544 : {
9545 400 : Oid childrelid = lfirst_oid(child);
9546 : Relation childrel;
9547 : AlteredTableInfo *childtab;
9548 :
9549 : /* find_inheritance_children already got lock */
9550 400 : childrel = table_open(childrelid, NoLock);
9551 400 : CheckAlterTableIsSafe(childrel);
9552 :
9553 : /* Find or create work queue entry for this table */
9554 400 : childtab = ATGetQueueEntry(wqueue, childrel);
9555 :
9556 : /* Recurse to child */
9557 400 : ATAddCheckConstraint(wqueue, childtab, childrel,
9558 : constr, recurse, true, is_readd, lockmode);
9559 :
9560 394 : table_close(childrel, NoLock);
9561 : }
9562 :
9563 1130 : return address;
9564 : }
9565 :
9566 : /*
9567 : * Add a foreign-key constraint to a single table; return the new constraint's
9568 : * address.
9569 : *
9570 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9571 : * lock on the rel, and have done appropriate validity checks for it.
9572 : * We do permissions checks here, however.
9573 : *
9574 : * When the referenced or referencing tables (or both) are partitioned,
9575 : * multiple pg_constraint rows are required -- one for each partitioned table
9576 : * and each partition on each side (fortunately, not one for every combination
9577 : * thereof). We also need action triggers on each leaf partition on the
9578 : * referenced side, and check triggers on each leaf partition on the
9579 : * referencing side.
9580 : */
9581 : static ObjectAddress
9582 2118 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9583 : Constraint *fkconstraint,
9584 : bool recurse, bool recursing, LOCKMODE lockmode)
9585 : {
9586 : Relation pkrel;
9587 2118 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9588 2118 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9589 2118 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9590 2118 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9591 2118 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9592 2118 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9593 2118 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9594 2118 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9595 2118 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9596 : int i;
9597 : int numfks,
9598 : numpks,
9599 : numfkdelsetcols;
9600 : Oid indexOid;
9601 : bool old_check_ok;
9602 : ObjectAddress address;
9603 2118 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9604 :
9605 : /*
9606 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9607 : * delete rows out from under us.
9608 : */
9609 2118 : if (OidIsValid(fkconstraint->old_pktable_oid))
9610 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9611 : else
9612 2046 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9613 :
9614 : /*
9615 : * Validity checks (permission checks wait till we have the column
9616 : * numbers)
9617 : */
9618 2118 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9619 : {
9620 300 : if (!recurse)
9621 6 : ereport(ERROR,
9622 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9623 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9624 : RelationGetRelationName(rel),
9625 : RelationGetRelationName(pkrel))));
9626 294 : if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9627 6 : ereport(ERROR,
9628 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9629 : errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9630 : RelationGetRelationName(rel),
9631 : RelationGetRelationName(pkrel)),
9632 : errdetail("This feature is not yet supported on partitioned tables.")));
9633 : }
9634 :
9635 2106 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9636 266 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9637 0 : ereport(ERROR,
9638 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9639 : errmsg("referenced relation \"%s\" is not a table",
9640 : RelationGetRelationName(pkrel))));
9641 :
9642 2106 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
9643 2 : ereport(ERROR,
9644 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9645 : errmsg("permission denied: \"%s\" is a system catalog",
9646 : RelationGetRelationName(pkrel))));
9647 :
9648 : /*
9649 : * References from permanent or unlogged tables to temp tables, and from
9650 : * permanent tables to unlogged tables, are disallowed because the
9651 : * referenced data can vanish out from under us. References from temp
9652 : * tables to any other table type are also disallowed, because other
9653 : * backends might need to run the RI triggers on the perm table, but they
9654 : * can't reliably see tuples in the local buffers of other backends.
9655 : */
9656 2104 : switch (rel->rd_rel->relpersistence)
9657 : {
9658 1794 : case RELPERSISTENCE_PERMANENT:
9659 1794 : if (!RelationIsPermanent(pkrel))
9660 0 : ereport(ERROR,
9661 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9662 : errmsg("constraints on permanent tables may reference only permanent tables")));
9663 1794 : break;
9664 32 : case RELPERSISTENCE_UNLOGGED:
9665 32 : if (!RelationIsPermanent(pkrel)
9666 32 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9667 0 : ereport(ERROR,
9668 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9669 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9670 32 : break;
9671 278 : case RELPERSISTENCE_TEMP:
9672 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9673 0 : ereport(ERROR,
9674 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9675 : errmsg("constraints on temporary tables may reference only temporary tables")));
9676 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9677 0 : ereport(ERROR,
9678 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9679 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
9680 278 : break;
9681 : }
9682 :
9683 : /*
9684 : * Look up the referencing attributes to make sure they exist, and record
9685 : * their attnums and type OIDs.
9686 : */
9687 2104 : numfks = transformColumnNameList(RelationGetRelid(rel),
9688 : fkconstraint->fk_attrs,
9689 : fkattnum, fktypoid);
9690 :
9691 2074 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9692 : fkconstraint->fk_del_set_cols,
9693 : fkdelsetcols, NULL);
9694 2068 : validateFkOnDeleteSetColumns(numfks, fkattnum,
9695 : numfkdelsetcols, fkdelsetcols,
9696 : fkconstraint->fk_del_set_cols);
9697 :
9698 : /*
9699 : * If the attribute list for the referenced table was omitted, lookup the
9700 : * definition of the primary key and use it. Otherwise, validate the
9701 : * supplied attribute list. In either case, discover the index OID and
9702 : * index opclasses, and the attnums and type OIDs of the attributes.
9703 : */
9704 2062 : if (fkconstraint->pk_attrs == NIL)
9705 : {
9706 944 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9707 : &fkconstraint->pk_attrs,
9708 : pkattnum, pktypoid,
9709 : opclasses);
9710 : }
9711 : else
9712 : {
9713 1118 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
9714 : fkconstraint->pk_attrs,
9715 : pkattnum, pktypoid);
9716 : /* Look for an index matching the column list */
9717 1088 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9718 : opclasses);
9719 : }
9720 :
9721 : /*
9722 : * Now we can check permissions.
9723 : */
9724 2020 : checkFkeyPermissions(pkrel, pkattnum, numpks);
9725 :
9726 : /*
9727 : * Check some things for generated columns.
9728 : */
9729 4544 : for (i = 0; i < numfks; i++)
9730 : {
9731 2536 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9732 :
9733 2536 : if (attgenerated)
9734 : {
9735 : /*
9736 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
9737 : */
9738 30 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9739 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9740 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9741 6 : ereport(ERROR,
9742 : (errcode(ERRCODE_SYNTAX_ERROR),
9743 : errmsg("invalid %s action for foreign key constraint containing generated column",
9744 : "ON UPDATE")));
9745 24 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9746 18 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9747 6 : ereport(ERROR,
9748 : (errcode(ERRCODE_SYNTAX_ERROR),
9749 : errmsg("invalid %s action for foreign key constraint containing generated column",
9750 : "ON DELETE")));
9751 : }
9752 : }
9753 :
9754 : /*
9755 : * Look up the equality operators to use in the constraint.
9756 : *
9757 : * Note that we have to be careful about the difference between the actual
9758 : * PK column type and the opclass' declared input type, which might be
9759 : * only binary-compatible with it. The declared opcintype is the right
9760 : * thing to probe pg_amop with.
9761 : */
9762 2008 : if (numfks != numpks)
9763 0 : ereport(ERROR,
9764 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9765 : errmsg("number of referencing and referenced columns for foreign key disagree")));
9766 :
9767 : /*
9768 : * On the strength of a previous constraint, we might avoid scanning
9769 : * tables to validate this one. See below.
9770 : */
9771 2008 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9772 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9773 :
9774 4178 : for (i = 0; i < numpks; i++)
9775 : {
9776 2380 : Oid pktype = pktypoid[i];
9777 2380 : Oid fktype = fktypoid[i];
9778 : Oid fktyped;
9779 : HeapTuple cla_ht;
9780 : Form_pg_opclass cla_tup;
9781 : Oid amid;
9782 : Oid opfamily;
9783 : Oid opcintype;
9784 : Oid pfeqop;
9785 : Oid ppeqop;
9786 : Oid ffeqop;
9787 : int16 eqstrategy;
9788 : Oid pfeqop_right;
9789 :
9790 : /* We need several fields out of the pg_opclass entry */
9791 2380 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9792 2380 : if (!HeapTupleIsValid(cla_ht))
9793 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
9794 2380 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9795 2380 : amid = cla_tup->opcmethod;
9796 2380 : opfamily = cla_tup->opcfamily;
9797 2380 : opcintype = cla_tup->opcintype;
9798 2380 : ReleaseSysCache(cla_ht);
9799 :
9800 : /*
9801 : * Check it's a btree; currently this can never fail since no other
9802 : * index AMs support unique indexes. If we ever did have other types
9803 : * of unique indexes, we'd need a way to determine which operator
9804 : * strategy number is equality. (Is it reasonable to insist that
9805 : * every such index AM use btree's number for equality?)
9806 : */
9807 2380 : if (amid != BTREE_AM_OID)
9808 0 : elog(ERROR, "only b-tree indexes are supported for foreign keys");
9809 2380 : eqstrategy = BTEqualStrategyNumber;
9810 :
9811 : /*
9812 : * There had better be a primary equality operator for the index.
9813 : * We'll use it for PK = PK comparisons.
9814 : */
9815 2380 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
9816 : eqstrategy);
9817 :
9818 2380 : if (!OidIsValid(ppeqop))
9819 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
9820 : eqstrategy, opcintype, opcintype, opfamily);
9821 :
9822 : /*
9823 : * Are there equality operators that take exactly the FK type? Assume
9824 : * we should look through any domain here.
9825 : */
9826 2380 : fktyped = getBaseType(fktype);
9827 :
9828 2380 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
9829 : eqstrategy);
9830 2380 : if (OidIsValid(pfeqop))
9831 : {
9832 2122 : pfeqop_right = fktyped;
9833 2122 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
9834 : eqstrategy);
9835 : }
9836 : else
9837 : {
9838 : /* keep compiler quiet */
9839 258 : pfeqop_right = InvalidOid;
9840 258 : ffeqop = InvalidOid;
9841 : }
9842 :
9843 2380 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9844 : {
9845 : /*
9846 : * Otherwise, look for an implicit cast from the FK type to the
9847 : * opcintype, and if found, use the primary equality operator.
9848 : * This is a bit tricky because opcintype might be a polymorphic
9849 : * type such as ANYARRAY or ANYENUM; so what we have to test is
9850 : * whether the two actual column types can be concurrently cast to
9851 : * that type. (Otherwise, we'd fail to reject combinations such
9852 : * as int[] and point[].)
9853 : */
9854 : Oid input_typeids[2];
9855 : Oid target_typeids[2];
9856 :
9857 258 : input_typeids[0] = pktype;
9858 258 : input_typeids[1] = fktype;
9859 258 : target_typeids[0] = opcintype;
9860 258 : target_typeids[1] = opcintype;
9861 258 : if (can_coerce_type(2, input_typeids, target_typeids,
9862 : COERCION_IMPLICIT))
9863 : {
9864 48 : pfeqop = ffeqop = ppeqop;
9865 48 : pfeqop_right = opcintype;
9866 : }
9867 : }
9868 :
9869 2380 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9870 210 : ereport(ERROR,
9871 : (errcode(ERRCODE_DATATYPE_MISMATCH),
9872 : errmsg("foreign key constraint \"%s\" cannot be implemented",
9873 : fkconstraint->conname),
9874 : errdetail("Key columns \"%s\" and \"%s\" "
9875 : "are of incompatible types: %s and %s.",
9876 : strVal(list_nth(fkconstraint->fk_attrs, i)),
9877 : strVal(list_nth(fkconstraint->pk_attrs, i)),
9878 : format_type_be(fktype),
9879 : format_type_be(pktype))));
9880 :
9881 2170 : if (old_check_ok)
9882 : {
9883 : /*
9884 : * When a pfeqop changes, revalidate the constraint. We could
9885 : * permit intra-opfamily changes, but that adds subtle complexity
9886 : * without any concrete benefit for core types. We need not
9887 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9888 : */
9889 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
9890 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
9891 : old_pfeqop_item);
9892 : }
9893 2170 : if (old_check_ok)
9894 : {
9895 : Oid old_fktype;
9896 : Oid new_fktype;
9897 : CoercionPathType old_pathtype;
9898 : CoercionPathType new_pathtype;
9899 : Oid old_castfunc;
9900 : Oid new_castfunc;
9901 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
9902 : fkattnum[i] - 1);
9903 :
9904 : /*
9905 : * Identify coercion pathways from each of the old and new FK-side
9906 : * column types to the right (foreign) operand type of the pfeqop.
9907 : * We may assume that pg_constraint.conkey is not changing.
9908 : */
9909 6 : old_fktype = attr->atttypid;
9910 6 : new_fktype = fktype;
9911 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
9912 : &old_castfunc);
9913 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
9914 : &new_castfunc);
9915 :
9916 : /*
9917 : * Upon a change to the cast from the FK column to its pfeqop
9918 : * operand, revalidate the constraint. For this evaluation, a
9919 : * binary coercion cast is equivalent to no cast at all. While
9920 : * type implementors should design implicit casts with an eye
9921 : * toward consistency of operations like equality, we cannot
9922 : * assume here that they have done so.
9923 : *
9924 : * A function with a polymorphic argument could change behavior
9925 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9926 : * when the cast destination is polymorphic, we only avoid
9927 : * revalidation if the input type has not changed at all. Given
9928 : * just the core data types and operator classes, this requirement
9929 : * prevents no would-be optimizations.
9930 : *
9931 : * If the cast converts from a base type to a domain thereon, then
9932 : * that domain type must be the opcintype of the unique index.
9933 : * Necessarily, the primary key column must then be of the domain
9934 : * type. Since the constraint was previously valid, all values on
9935 : * the foreign side necessarily exist on the primary side and in
9936 : * turn conform to the domain. Consequently, we need not treat
9937 : * domains specially here.
9938 : *
9939 : * Since we require that all collations share the same notion of
9940 : * equality (which they do, because texteq reduces to bitwise
9941 : * equality), we don't compare collation here.
9942 : *
9943 : * We need not directly consider the PK type. It's necessarily
9944 : * binary coercible to the opcintype of the unique index column,
9945 : * and ri_triggers.c will only deal with PK datums in terms of
9946 : * that opcintype. Changing the opcintype also changes pfeqop.
9947 : */
9948 6 : old_check_ok = (new_pathtype == old_pathtype &&
9949 12 : new_castfunc == old_castfunc &&
9950 6 : (!IsPolymorphicType(pfeqop_right) ||
9951 : new_fktype == old_fktype));
9952 : }
9953 :
9954 2170 : pfeqoperators[i] = pfeqop;
9955 2170 : ppeqoperators[i] = ppeqop;
9956 2170 : ffeqoperators[i] = ffeqop;
9957 : }
9958 :
9959 : /*
9960 : * Create all the constraint and trigger objects, recursing to partitions
9961 : * as necessary. First handle the referenced side.
9962 : */
9963 1798 : address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
9964 : indexOid,
9965 : InvalidOid, /* no parent constraint */
9966 : numfks,
9967 : pkattnum,
9968 : fkattnum,
9969 : pfeqoperators,
9970 : ppeqoperators,
9971 : ffeqoperators,
9972 : numfkdelsetcols,
9973 : fkdelsetcols,
9974 : old_check_ok,
9975 : InvalidOid, InvalidOid);
9976 :
9977 : /* Now handle the referencing side. */
9978 1798 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
9979 : indexOid,
9980 : address.objectId,
9981 : numfks,
9982 : pkattnum,
9983 : fkattnum,
9984 : pfeqoperators,
9985 : ppeqoperators,
9986 : ffeqoperators,
9987 : numfkdelsetcols,
9988 : fkdelsetcols,
9989 : old_check_ok,
9990 : lockmode,
9991 : InvalidOid, InvalidOid);
9992 :
9993 : /*
9994 : * Done. Close pk table, but keep lock until we've committed.
9995 : */
9996 1798 : table_close(pkrel, NoLock);
9997 :
9998 1798 : return address;
9999 : }
10000 :
10001 : /*
10002 : * validateFkOnDeleteSetColumns
10003 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10004 : * column lists are valid.
10005 : */
10006 : void
10007 2068 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10008 : int numfksetcols, const int16 *fksetcolsattnums,
10009 : List *fksetcols)
10010 : {
10011 2092 : for (int i = 0; i < numfksetcols; i++)
10012 : {
10013 30 : int16 setcol_attnum = fksetcolsattnums[i];
10014 30 : bool seen = false;
10015 :
10016 54 : for (int j = 0; j < numfks; j++)
10017 : {
10018 48 : if (fkattnums[j] == setcol_attnum)
10019 : {
10020 24 : seen = true;
10021 24 : break;
10022 : }
10023 : }
10024 :
10025 30 : if (!seen)
10026 : {
10027 6 : char *col = strVal(list_nth(fksetcols, i));
10028 :
10029 6 : ereport(ERROR,
10030 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10031 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10032 : }
10033 : }
10034 2062 : }
10035 :
10036 : /*
10037 : * addFkRecurseReferenced
10038 : * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10039 : * side of the constraint
10040 : *
10041 : * Create pg_constraint rows for the referenced side of the constraint,
10042 : * referencing the parent of the referencing side; also create action triggers
10043 : * on leaf partitions. If the table is partitioned, recurse to handle each
10044 : * partition.
10045 : *
10046 : * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10047 : * of an ALTER TABLE sequence.
10048 : * fkconstraint is the constraint being added.
10049 : * rel is the root referencing relation.
10050 : * pkrel is the referenced relation; might be a partition, if recursing.
10051 : * indexOid is the OID of the index (on pkrel) implementing this constraint.
10052 : * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10053 : * top-level constraint.
10054 : * numfks is the number of columns in the foreign key
10055 : * pkattnum is the attnum array of referenced attributes.
10056 : * fkattnum is the attnum array of referencing attributes.
10057 : * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10058 : * (...) clause
10059 : * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10060 : * NULL/DEFAULT clause
10061 : * pf/pp/ffeqoperators are OID array of operators between columns.
10062 : * old_check_ok signals that this constraint replaces an existing one that
10063 : * was already validated (thus this one doesn't need validation).
10064 : * parentDelTrigger and parentUpdTrigger, when being recursively called on
10065 : * a partition, are the OIDs of the parent action triggers for DELETE and
10066 : * UPDATE respectively.
10067 : */
10068 : static ObjectAddress
10069 2494 : addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
10070 : Relation pkrel, Oid indexOid, Oid parentConstr,
10071 : int numfks,
10072 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10073 : Oid *ppeqoperators, Oid *ffeqoperators,
10074 : int numfkdelsetcols, int16 *fkdelsetcols,
10075 : bool old_check_ok,
10076 : Oid parentDelTrigger, Oid parentUpdTrigger)
10077 : {
10078 : ObjectAddress address;
10079 : Oid constrOid;
10080 : char *conname;
10081 : bool conislocal;
10082 : int coninhcount;
10083 : bool connoinherit;
10084 : Oid deleteTriggerOid,
10085 : updateTriggerOid;
10086 :
10087 : /*
10088 : * Verify relkind for each referenced partition. At the top level, this
10089 : * is redundant with a previous check, but we need it when recursing.
10090 : */
10091 2494 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10092 366 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10093 0 : ereport(ERROR,
10094 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10095 : errmsg("referenced relation \"%s\" is not a table",
10096 : RelationGetRelationName(pkrel))));
10097 :
10098 : /*
10099 : * Caller supplies us with a constraint name; however, it may be used in
10100 : * this partition, so come up with a different one in that case.
10101 : */
10102 2494 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10103 : RelationGetRelid(rel),
10104 2494 : fkconstraint->conname))
10105 696 : conname = ChooseConstraintName(RelationGetRelationName(rel),
10106 696 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10107 : "fkey",
10108 696 : RelationGetNamespace(rel), NIL);
10109 : else
10110 1798 : conname = fkconstraint->conname;
10111 :
10112 2494 : if (OidIsValid(parentConstr))
10113 : {
10114 696 : conislocal = false;
10115 696 : coninhcount = 1;
10116 696 : connoinherit = false;
10117 : }
10118 : else
10119 : {
10120 1798 : conislocal = true;
10121 1798 : coninhcount = 0;
10122 :
10123 : /*
10124 : * always inherit for partitioned tables, never for legacy inheritance
10125 : */
10126 1798 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10127 : }
10128 :
10129 : /*
10130 : * Record the FK constraint in pg_constraint.
10131 : */
10132 2494 : constrOid = CreateConstraintEntry(conname,
10133 2494 : RelationGetNamespace(rel),
10134 : CONSTRAINT_FOREIGN,
10135 2494 : fkconstraint->deferrable,
10136 2494 : fkconstraint->initdeferred,
10137 2494 : fkconstraint->initially_valid,
10138 : parentConstr,
10139 : RelationGetRelid(rel),
10140 : fkattnum,
10141 : numfks,
10142 : numfks,
10143 : InvalidOid, /* not a domain constraint */
10144 : indexOid,
10145 : RelationGetRelid(pkrel),
10146 : pkattnum,
10147 : pfeqoperators,
10148 : ppeqoperators,
10149 : ffeqoperators,
10150 : numfks,
10151 2494 : fkconstraint->fk_upd_action,
10152 2494 : fkconstraint->fk_del_action,
10153 : fkdelsetcols,
10154 : numfkdelsetcols,
10155 2494 : fkconstraint->fk_matchtype,
10156 : NULL, /* no exclusion constraint */
10157 : NULL, /* no check constraint */
10158 : NULL,
10159 : conislocal, /* islocal */
10160 : coninhcount, /* inhcount */
10161 : connoinherit, /* conNoInherit */
10162 : false); /* is_internal */
10163 :
10164 2494 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10165 :
10166 : /*
10167 : * Mark the child constraint as part of the parent constraint; it must not
10168 : * be dropped on its own. (This constraint is deleted when the partition
10169 : * is detached, but a special check needs to occur that the partition
10170 : * contains no referenced values.)
10171 : */
10172 2494 : if (OidIsValid(parentConstr))
10173 : {
10174 : ObjectAddress referenced;
10175 :
10176 696 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10177 696 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10178 : }
10179 :
10180 : /* make new constraint visible, in case we add more */
10181 2494 : CommandCounterIncrement();
10182 :
10183 : /*
10184 : * Create the action triggers that enforce the constraint.
10185 : */
10186 2494 : createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10187 : fkconstraint,
10188 : constrOid, indexOid,
10189 : parentDelTrigger, parentUpdTrigger,
10190 : &deleteTriggerOid, &updateTriggerOid);
10191 :
10192 : /*
10193 : * If the referenced table is partitioned, recurse on ourselves to handle
10194 : * each partition. We need one pg_constraint row created for each
10195 : * partition in addition to the pg_constraint row for the parent table.
10196 : */
10197 2494 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10198 : {
10199 366 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10200 :
10201 912 : for (int i = 0; i < pd->nparts; i++)
10202 : {
10203 : Relation partRel;
10204 : AttrMap *map;
10205 : AttrNumber *mapped_pkattnum;
10206 : Oid partIndexId;
10207 :
10208 546 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10209 :
10210 : /*
10211 : * Map the attribute numbers in the referenced side of the FK
10212 : * definition to match the partition's column layout.
10213 : */
10214 546 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10215 : RelationGetDescr(pkrel),
10216 : false);
10217 546 : if (map)
10218 : {
10219 46 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10220 92 : for (int j = 0; j < numfks; j++)
10221 46 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10222 : }
10223 : else
10224 500 : mapped_pkattnum = pkattnum;
10225 :
10226 : /* do the deed */
10227 546 : partIndexId = index_get_partition(partRel, indexOid);
10228 546 : if (!OidIsValid(partIndexId))
10229 0 : elog(ERROR, "index for %u not found in partition %s",
10230 : indexOid, RelationGetRelationName(partRel));
10231 546 : addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10232 : partIndexId, constrOid, numfks,
10233 : mapped_pkattnum, fkattnum,
10234 : pfeqoperators, ppeqoperators, ffeqoperators,
10235 : numfkdelsetcols, fkdelsetcols,
10236 : old_check_ok,
10237 : deleteTriggerOid, updateTriggerOid);
10238 :
10239 : /* Done -- clean up (but keep the lock) */
10240 546 : table_close(partRel, NoLock);
10241 546 : if (map)
10242 : {
10243 46 : pfree(mapped_pkattnum);
10244 46 : free_attrmap(map);
10245 : }
10246 : }
10247 : }
10248 :
10249 2494 : return address;
10250 : }
10251 :
10252 : /*
10253 : * addFkRecurseReferencing
10254 : * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10255 : *
10256 : * If the referencing relation is a plain relation, create the necessary check
10257 : * triggers that implement the constraint, and set up for Phase 3 constraint
10258 : * verification. If the referencing relation is a partitioned table, then
10259 : * we create a pg_constraint row for it and recurse on this routine for each
10260 : * partition.
10261 : *
10262 : * We assume that the referenced relation is locked against concurrent
10263 : * deletions. If it's a partitioned relation, every partition must be so
10264 : * locked.
10265 : *
10266 : * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10267 : * of an ALTER TABLE sequence.
10268 : * fkconstraint is the constraint being added.
10269 : * rel is the referencing relation; might be a partition, if recursing.
10270 : * pkrel is the root referenced relation.
10271 : * indexOid is the OID of the index (on pkrel) implementing this constraint.
10272 : * parentConstr is the OID of the parent constraint (there is always one).
10273 : * numfks is the number of columns in the foreign key
10274 : * pkattnum is the attnum array of referenced attributes.
10275 : * fkattnum is the attnum array of referencing attributes.
10276 : * pf/pp/ffeqoperators are OID array of operators between columns.
10277 : * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10278 : * (...) clause
10279 : * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10280 : * NULL/DEFAULT clause
10281 : * old_check_ok signals that this constraint replaces an existing one that
10282 : * was already validated (thus this one doesn't need validation).
10283 : * lockmode is the lockmode to acquire on partitions when recursing.
10284 : * parentInsTrigger and parentUpdTrigger, when being recursively called on
10285 : * a partition, are the OIDs of the parent check triggers for INSERT and
10286 : * UPDATE respectively.
10287 : */
10288 : static void
10289 2544 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10290 : Relation pkrel, Oid indexOid, Oid parentConstr,
10291 : int numfks, int16 *pkattnum, int16 *fkattnum,
10292 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10293 : int numfkdelsetcols, int16 *fkdelsetcols,
10294 : bool old_check_ok, LOCKMODE lockmode,
10295 : Oid parentInsTrigger, Oid parentUpdTrigger)
10296 : {
10297 : Oid insertTriggerOid,
10298 : updateTriggerOid;
10299 :
10300 : Assert(OidIsValid(parentConstr));
10301 :
10302 2544 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10303 0 : ereport(ERROR,
10304 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10305 : errmsg("foreign key constraints are not supported on foreign tables")));
10306 :
10307 : /*
10308 : * Add the check triggers to it and, if necessary, schedule it to be
10309 : * checked in Phase 3.
10310 : *
10311 : * If the relation is partitioned, drill down to do it to its partitions.
10312 : */
10313 2544 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10314 : RelationGetRelid(pkrel),
10315 : fkconstraint,
10316 : parentConstr,
10317 : indexOid,
10318 : parentInsTrigger, parentUpdTrigger,
10319 : &insertTriggerOid, &updateTriggerOid);
10320 :
10321 2544 : if (rel->rd_rel->relkind == RELKIND_RELATION)
10322 : {
10323 : /*
10324 : * Tell Phase 3 to check that the constraint is satisfied by existing
10325 : * rows. We can skip this during table creation, when requested
10326 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10327 : * and when we're recreating a constraint following a SET DATA TYPE
10328 : * operation that did not impugn its validity.
10329 : */
10330 2144 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10331 : {
10332 : NewConstraint *newcon;
10333 : AlteredTableInfo *tab;
10334 :
10335 652 : tab = ATGetQueueEntry(wqueue, rel);
10336 :
10337 652 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10338 652 : newcon->name = get_constraint_name(parentConstr);
10339 652 : newcon->contype = CONSTR_FOREIGN;
10340 652 : newcon->refrelid = RelationGetRelid(pkrel);
10341 652 : newcon->refindid = indexOid;
10342 652 : newcon->conid = parentConstr;
10343 652 : newcon->qual = (Node *) fkconstraint;
10344 :
10345 652 : tab->constraints = lappend(tab->constraints, newcon);
10346 : }
10347 : }
10348 400 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10349 : {
10350 400 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10351 : Relation trigrel;
10352 :
10353 : /*
10354 : * Triggers of the foreign keys will be manipulated a bunch of times
10355 : * in the loop below. To avoid repeatedly opening/closing the trigger
10356 : * catalog relation, we open it here and pass it to the subroutines
10357 : * called below.
10358 : */
10359 400 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10360 :
10361 : /*
10362 : * Recurse to take appropriate action on each partition; either we
10363 : * find an existing constraint to reparent to ours, or we create a new
10364 : * one.
10365 : */
10366 764 : for (int i = 0; i < pd->nparts; i++)
10367 : {
10368 364 : Oid partitionId = pd->oids[i];
10369 364 : Relation partition = table_open(partitionId, lockmode);
10370 : List *partFKs;
10371 : AttrMap *attmap;
10372 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10373 : bool attached;
10374 : char *conname;
10375 : Oid constrOid;
10376 : ObjectAddress address,
10377 : referenced;
10378 : ListCell *cell;
10379 :
10380 364 : CheckAlterTableIsSafe(partition);
10381 :
10382 364 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10383 : RelationGetDescr(rel),
10384 : false);
10385 932 : for (int j = 0; j < numfks; j++)
10386 568 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10387 :
10388 : /* Check whether an existing constraint can be repurposed */
10389 364 : partFKs = copyObject(RelationGetFKeyList(partition));
10390 364 : attached = false;
10391 382 : foreach(cell, partFKs)
10392 : {
10393 : ForeignKeyCacheInfo *fk;
10394 :
10395 30 : fk = lfirst_node(ForeignKeyCacheInfo, cell);
10396 30 : if (tryAttachPartitionForeignKey(fk,
10397 : partitionId,
10398 : parentConstr,
10399 : numfks,
10400 : mapped_fkattnum,
10401 : pkattnum,
10402 : pfeqoperators,
10403 : insertTriggerOid,
10404 : updateTriggerOid,
10405 : trigrel))
10406 : {
10407 12 : attached = true;
10408 12 : break;
10409 : }
10410 : }
10411 364 : if (attached)
10412 : {
10413 12 : table_close(partition, NoLock);
10414 12 : continue;
10415 : }
10416 :
10417 : /*
10418 : * No luck finding a good constraint to reuse; create our own.
10419 : */
10420 352 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10421 : RelationGetRelid(partition),
10422 352 : fkconstraint->conname))
10423 0 : conname = ChooseConstraintName(RelationGetRelationName(partition),
10424 0 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10425 : "fkey",
10426 0 : RelationGetNamespace(partition), NIL);
10427 : else
10428 352 : conname = fkconstraint->conname;
10429 : constrOid =
10430 352 : CreateConstraintEntry(conname,
10431 352 : RelationGetNamespace(partition),
10432 : CONSTRAINT_FOREIGN,
10433 352 : fkconstraint->deferrable,
10434 352 : fkconstraint->initdeferred,
10435 352 : fkconstraint->initially_valid,
10436 : parentConstr,
10437 : partitionId,
10438 : mapped_fkattnum,
10439 : numfks,
10440 : numfks,
10441 : InvalidOid,
10442 : indexOid,
10443 : RelationGetRelid(pkrel),
10444 : pkattnum,
10445 : pfeqoperators,
10446 : ppeqoperators,
10447 : ffeqoperators,
10448 : numfks,
10449 352 : fkconstraint->fk_upd_action,
10450 352 : fkconstraint->fk_del_action,
10451 : fkdelsetcols,
10452 : numfkdelsetcols,
10453 352 : fkconstraint->fk_matchtype,
10454 : NULL,
10455 : NULL,
10456 : NULL,
10457 : false,
10458 : 1,
10459 : false,
10460 : false);
10461 :
10462 : /*
10463 : * Give this constraint partition-type dependencies on the parent
10464 : * constraint as well as the table.
10465 : */
10466 352 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10467 352 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10468 352 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10469 352 : ObjectAddressSet(referenced, RelationRelationId, partitionId);
10470 352 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10471 :
10472 : /* Make all this visible before recursing */
10473 352 : CommandCounterIncrement();
10474 :
10475 : /* call ourselves to finalize the creation and we're done */
10476 352 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10477 : indexOid,
10478 : constrOid,
10479 : numfks,
10480 : pkattnum,
10481 : mapped_fkattnum,
10482 : pfeqoperators,
10483 : ppeqoperators,
10484 : ffeqoperators,
10485 : numfkdelsetcols,
10486 : fkdelsetcols,
10487 : old_check_ok,
10488 : lockmode,
10489 : insertTriggerOid,
10490 : updateTriggerOid);
10491 :
10492 352 : table_close(partition, NoLock);
10493 : }
10494 :
10495 400 : table_close(trigrel, RowExclusiveLock);
10496 : }
10497 2544 : }
10498 :
10499 : /*
10500 : * CloneForeignKeyConstraints
10501 : * Clone foreign keys from a partitioned table to a newly acquired
10502 : * partition.
10503 : *
10504 : * partitionRel is a partition of parentRel, so we can be certain that it has
10505 : * the same columns with the same datatypes. The columns may be in different
10506 : * order, though.
10507 : *
10508 : * wqueue must be passed to set up phase 3 constraint checking, unless the
10509 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
10510 : * PARTITION OF).
10511 : */
10512 : static void
10513 10396 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10514 : Relation partitionRel)
10515 : {
10516 : /* This only works for declarative partitioning */
10517 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10518 :
10519 : /*
10520 : * Clone constraints for which the parent is on the referenced side.
10521 : */
10522 10396 : CloneFkReferenced(parentRel, partitionRel);
10523 :
10524 : /*
10525 : * Now clone constraints where the parent is on the referencing side.
10526 : */
10527 10396 : CloneFkReferencing(wqueue, parentRel, partitionRel);
10528 10396 : }
10529 :
10530 : /*
10531 : * CloneFkReferenced
10532 : * Subroutine for CloneForeignKeyConstraints
10533 : *
10534 : * Find all the FKs that have the parent relation on the referenced side;
10535 : * clone those constraints to the given partition. This is to be called
10536 : * when the partition is being created or attached.
10537 : *
10538 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10539 : *
10540 : * This recurses to partitions, if the relation being attached is partitioned.
10541 : * Recursion is done by calling addFkRecurseReferenced.
10542 : */
10543 : static void
10544 10396 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
10545 : {
10546 : Relation pg_constraint;
10547 : AttrMap *attmap;
10548 : ListCell *cell;
10549 : SysScanDesc scan;
10550 : ScanKeyData key[2];
10551 : HeapTuple tuple;
10552 10396 : List *clone = NIL;
10553 : Relation trigrel;
10554 :
10555 : /*
10556 : * Search for any constraints where this partition's parent is in the
10557 : * referenced side. However, we must not clone any constraint whose
10558 : * parent constraint is also going to be cloned, to avoid duplicates. So
10559 : * do it in two steps: first construct the list of constraints to clone,
10560 : * then go over that list cloning those whose parents are not in the list.
10561 : * (We must not rely on the parent being seen first, since the catalog
10562 : * scan could return children first.)
10563 : */
10564 10396 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10565 10396 : ScanKeyInit(&key[0],
10566 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10567 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10568 10396 : ScanKeyInit(&key[1],
10569 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
10570 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10571 : /* This is a seqscan, as we don't have a usable index ... */
10572 10396 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
10573 : NULL, 2, key);
10574 10726 : while ((tuple = systable_getnext(scan)) != NULL)
10575 : {
10576 330 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10577 :
10578 330 : clone = lappend_oid(clone, constrForm->oid);
10579 : }
10580 10396 : systable_endscan(scan);
10581 10396 : table_close(pg_constraint, RowShareLock);
10582 :
10583 : /*
10584 : * Triggers of the foreign keys will be manipulated a bunch of times in
10585 : * the loop below. To avoid repeatedly opening/closing the trigger
10586 : * catalog relation, we open it here and pass it to the subroutines called
10587 : * below.
10588 : */
10589 10396 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10590 :
10591 10396 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10592 : RelationGetDescr(parentRel),
10593 : false);
10594 10726 : foreach(cell, clone)
10595 : {
10596 330 : Oid constrOid = lfirst_oid(cell);
10597 : Form_pg_constraint constrForm;
10598 : Relation fkRel;
10599 : Oid indexOid;
10600 : Oid partIndexId;
10601 : int numfks;
10602 : AttrNumber conkey[INDEX_MAX_KEYS];
10603 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10604 : AttrNumber confkey[INDEX_MAX_KEYS];
10605 : Oid conpfeqop[INDEX_MAX_KEYS];
10606 : Oid conppeqop[INDEX_MAX_KEYS];
10607 : Oid conffeqop[INDEX_MAX_KEYS];
10608 : int numfkdelsetcols;
10609 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10610 : Constraint *fkconstraint;
10611 : Oid deleteTriggerOid,
10612 : updateTriggerOid;
10613 :
10614 330 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10615 330 : if (!HeapTupleIsValid(tuple))
10616 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10617 330 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10618 :
10619 : /*
10620 : * As explained above: don't try to clone a constraint for which we're
10621 : * going to clone the parent.
10622 : */
10623 330 : if (list_member_oid(clone, constrForm->conparentid))
10624 : {
10625 126 : ReleaseSysCache(tuple);
10626 180 : continue;
10627 : }
10628 :
10629 : /*
10630 : * Don't clone self-referencing foreign keys, which can be in the
10631 : * partitioned table or in the partition-to-be.
10632 : */
10633 204 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10634 162 : constrForm->conrelid == RelationGetRelid(partitionRel))
10635 : {
10636 54 : ReleaseSysCache(tuple);
10637 54 : continue;
10638 : }
10639 :
10640 : /*
10641 : * Because we're only expanding the key space at the referenced side,
10642 : * we don't need to prevent any operation in the referencing table, so
10643 : * AccessShareLock suffices (assumes that dropping the constraint
10644 : * acquires AEL).
10645 : */
10646 150 : fkRel = table_open(constrForm->conrelid, AccessShareLock);
10647 :
10648 150 : indexOid = constrForm->conindid;
10649 150 : DeconstructFkConstraintRow(tuple,
10650 : &numfks,
10651 : conkey,
10652 : confkey,
10653 : conpfeqop,
10654 : conppeqop,
10655 : conffeqop,
10656 : &numfkdelsetcols,
10657 : confdelsetcols);
10658 :
10659 300 : for (int i = 0; i < numfks; i++)
10660 150 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
10661 :
10662 150 : fkconstraint = makeNode(Constraint);
10663 150 : fkconstraint->contype = CONSTRAINT_FOREIGN;
10664 150 : fkconstraint->conname = NameStr(constrForm->conname);
10665 150 : fkconstraint->deferrable = constrForm->condeferrable;
10666 150 : fkconstraint->initdeferred = constrForm->condeferred;
10667 150 : fkconstraint->location = -1;
10668 150 : fkconstraint->pktable = NULL;
10669 : /* ->fk_attrs determined below */
10670 150 : fkconstraint->pk_attrs = NIL;
10671 150 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
10672 150 : fkconstraint->fk_upd_action = constrForm->confupdtype;
10673 150 : fkconstraint->fk_del_action = constrForm->confdeltype;
10674 150 : fkconstraint->fk_del_set_cols = NIL;
10675 150 : fkconstraint->old_conpfeqop = NIL;
10676 150 : fkconstraint->old_pktable_oid = InvalidOid;
10677 150 : fkconstraint->skip_validation = false;
10678 150 : fkconstraint->initially_valid = true;
10679 :
10680 : /* set up colnames that are used to generate the constraint name */
10681 300 : for (int i = 0; i < numfks; i++)
10682 : {
10683 : Form_pg_attribute att;
10684 :
10685 150 : att = TupleDescAttr(RelationGetDescr(fkRel),
10686 : conkey[i] - 1);
10687 150 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10688 150 : makeString(NameStr(att->attname)));
10689 : }
10690 :
10691 : /*
10692 : * Add the new foreign key constraint pointing to the new partition.
10693 : * Because this new partition appears in the referenced side of the
10694 : * constraint, we don't need to set up for Phase 3 check.
10695 : */
10696 150 : partIndexId = index_get_partition(partitionRel, indexOid);
10697 150 : if (!OidIsValid(partIndexId))
10698 0 : elog(ERROR, "index for %u not found in partition %s",
10699 : indexOid, RelationGetRelationName(partitionRel));
10700 :
10701 : /*
10702 : * Get the "action" triggers belonging to the constraint to pass as
10703 : * parent OIDs for similar triggers that will be created on the
10704 : * partition in addFkRecurseReferenced().
10705 : */
10706 150 : GetForeignKeyActionTriggers(trigrel, constrOid,
10707 : constrForm->confrelid, constrForm->conrelid,
10708 : &deleteTriggerOid, &updateTriggerOid);
10709 :
10710 150 : addFkRecurseReferenced(NULL,
10711 : fkconstraint,
10712 : fkRel,
10713 : partitionRel,
10714 : partIndexId,
10715 : constrOid,
10716 : numfks,
10717 : mapped_confkey,
10718 : conkey,
10719 : conpfeqop,
10720 : conppeqop,
10721 : conffeqop,
10722 : numfkdelsetcols,
10723 : confdelsetcols,
10724 : true,
10725 : deleteTriggerOid,
10726 : updateTriggerOid);
10727 :
10728 150 : table_close(fkRel, NoLock);
10729 150 : ReleaseSysCache(tuple);
10730 : }
10731 :
10732 10396 : table_close(trigrel, RowExclusiveLock);
10733 10396 : }
10734 :
10735 : /*
10736 : * CloneFkReferencing
10737 : * Subroutine for CloneForeignKeyConstraints
10738 : *
10739 : * For each FK constraint of the parent relation in the given list, find an
10740 : * equivalent constraint in its partition relation that can be reparented;
10741 : * if one cannot be found, create a new constraint in the partition as its
10742 : * child.
10743 : *
10744 : * If wqueue is given, it is used to set up phase-3 verification for each
10745 : * cloned constraint; if omitted, we assume that such verification is not
10746 : * needed (example: the partition is being created anew).
10747 : */
10748 : static void
10749 10396 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
10750 : {
10751 : AttrMap *attmap;
10752 : List *partFKs;
10753 10396 : List *clone = NIL;
10754 : ListCell *cell;
10755 : Relation trigrel;
10756 :
10757 : /* obtain a list of constraints that we need to clone */
10758 11256 : foreach(cell, RelationGetFKeyList(parentRel))
10759 : {
10760 860 : ForeignKeyCacheInfo *fk = lfirst(cell);
10761 :
10762 860 : clone = lappend_oid(clone, fk->conoid);
10763 : }
10764 :
10765 : /*
10766 : * Silently do nothing if there's nothing to do. In particular, this
10767 : * avoids throwing a spurious error for foreign tables.
10768 : */
10769 10396 : if (clone == NIL)
10770 9960 : return;
10771 :
10772 436 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10773 0 : ereport(ERROR,
10774 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10775 : errmsg("foreign key constraints are not supported on foreign tables")));
10776 :
10777 : /*
10778 : * Triggers of the foreign keys will be manipulated a bunch of times in
10779 : * the loop below. To avoid repeatedly opening/closing the trigger
10780 : * catalog relation, we open it here and pass it to the subroutines called
10781 : * below.
10782 : */
10783 436 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10784 :
10785 : /*
10786 : * The constraint key may differ, if the columns in the partition are
10787 : * different. This map is used to convert them.
10788 : */
10789 436 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
10790 : RelationGetDescr(parentRel),
10791 : false);
10792 :
10793 436 : partFKs = copyObject(RelationGetFKeyList(partRel));
10794 :
10795 1296 : foreach(cell, clone)
10796 : {
10797 860 : Oid parentConstrOid = lfirst_oid(cell);
10798 : Form_pg_constraint constrForm;
10799 : Relation pkrel;
10800 : HeapTuple tuple;
10801 : int numfks;
10802 : AttrNumber conkey[INDEX_MAX_KEYS];
10803 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
10804 : AttrNumber confkey[INDEX_MAX_KEYS];
10805 : Oid conpfeqop[INDEX_MAX_KEYS];
10806 : Oid conppeqop[INDEX_MAX_KEYS];
10807 : Oid conffeqop[INDEX_MAX_KEYS];
10808 : int numfkdelsetcols;
10809 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10810 : Constraint *fkconstraint;
10811 : bool attached;
10812 : Oid indexOid;
10813 : Oid constrOid;
10814 : ObjectAddress address,
10815 : referenced;
10816 : ListCell *lc;
10817 : Oid insertTriggerOid,
10818 : updateTriggerOid;
10819 :
10820 860 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
10821 860 : if (!HeapTupleIsValid(tuple))
10822 0 : elog(ERROR, "cache lookup failed for constraint %u",
10823 : parentConstrOid);
10824 860 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10825 :
10826 : /* Don't clone constraints whose parents are being cloned */
10827 860 : if (list_member_oid(clone, constrForm->conparentid))
10828 : {
10829 400 : ReleaseSysCache(tuple);
10830 466 : continue;
10831 : }
10832 :
10833 : /*
10834 : * Need to prevent concurrent deletions. If pkrel is a partitioned
10835 : * relation, that means to lock all partitions.
10836 : */
10837 460 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
10838 460 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10839 172 : (void) find_all_inheritors(RelationGetRelid(pkrel),
10840 : ShareRowExclusiveLock, NULL);
10841 :
10842 460 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
10843 : conpfeqop, conppeqop, conffeqop,
10844 : &numfkdelsetcols, confdelsetcols);
10845 1046 : for (int i = 0; i < numfks; i++)
10846 586 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
10847 :
10848 : /*
10849 : * Get the "check" triggers belonging to the constraint to pass as
10850 : * parent OIDs for similar triggers that will be created on the
10851 : * partition in addFkRecurseReferencing(). They are also passed to
10852 : * tryAttachPartitionForeignKey() below to simply assign as parents to
10853 : * the partition's existing "check" triggers, that is, if the
10854 : * corresponding constraints is deemed attachable to the parent
10855 : * constraint.
10856 : */
10857 460 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
10858 : constrForm->confrelid, constrForm->conrelid,
10859 : &insertTriggerOid, &updateTriggerOid);
10860 :
10861 : /*
10862 : * Before creating a new constraint, see whether any existing FKs are
10863 : * fit for the purpose. If one is, attach the parent constraint to
10864 : * it, and don't clone anything. This way we avoid the expensive
10865 : * verification step and don't end up with a duplicate FK, and we
10866 : * don't need to recurse to partitions for this constraint.
10867 : */
10868 460 : attached = false;
10869 544 : foreach(lc, partFKs)
10870 : {
10871 150 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
10872 :
10873 150 : if (tryAttachPartitionForeignKey(fk,
10874 : RelationGetRelid(partRel),
10875 : parentConstrOid,
10876 : numfks,
10877 : mapped_conkey,
10878 : confkey,
10879 : conpfeqop,
10880 : insertTriggerOid,
10881 : updateTriggerOid,
10882 : trigrel))
10883 : {
10884 66 : attached = true;
10885 66 : table_close(pkrel, NoLock);
10886 66 : break;
10887 : }
10888 : }
10889 460 : if (attached)
10890 : {
10891 66 : ReleaseSysCache(tuple);
10892 66 : continue;
10893 : }
10894 :
10895 : /* No dice. Set up to create our own constraint */
10896 394 : fkconstraint = makeNode(Constraint);
10897 394 : fkconstraint->contype = CONSTRAINT_FOREIGN;
10898 : /* ->conname determined below */
10899 394 : fkconstraint->deferrable = constrForm->condeferrable;
10900 394 : fkconstraint->initdeferred = constrForm->condeferred;
10901 394 : fkconstraint->location = -1;
10902 394 : fkconstraint->pktable = NULL;
10903 : /* ->fk_attrs determined below */
10904 394 : fkconstraint->pk_attrs = NIL;
10905 394 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
10906 394 : fkconstraint->fk_upd_action = constrForm->confupdtype;
10907 394 : fkconstraint->fk_del_action = constrForm->confdeltype;
10908 394 : fkconstraint->fk_del_set_cols = NIL;
10909 394 : fkconstraint->old_conpfeqop = NIL;
10910 394 : fkconstraint->old_pktable_oid = InvalidOid;
10911 394 : fkconstraint->skip_validation = false;
10912 394 : fkconstraint->initially_valid = true;
10913 866 : for (int i = 0; i < numfks; i++)
10914 : {
10915 : Form_pg_attribute att;
10916 :
10917 472 : att = TupleDescAttr(RelationGetDescr(partRel),
10918 : mapped_conkey[i] - 1);
10919 472 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10920 472 : makeString(NameStr(att->attname)));
10921 : }
10922 394 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10923 : RelationGetRelid(partRel),
10924 394 : NameStr(constrForm->conname)))
10925 6 : fkconstraint->conname =
10926 6 : ChooseConstraintName(RelationGetRelationName(partRel),
10927 6 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10928 : "fkey",
10929 6 : RelationGetNamespace(partRel), NIL);
10930 : else
10931 388 : fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
10932 :
10933 394 : indexOid = constrForm->conindid;
10934 : constrOid =
10935 394 : CreateConstraintEntry(fkconstraint->conname,
10936 : constrForm->connamespace,
10937 : CONSTRAINT_FOREIGN,
10938 394 : fkconstraint->deferrable,
10939 394 : fkconstraint->initdeferred,
10940 394 : constrForm->convalidated,
10941 : parentConstrOid,
10942 : RelationGetRelid(partRel),
10943 : mapped_conkey,
10944 : numfks,
10945 : numfks,
10946 : InvalidOid, /* not a domain constraint */
10947 : indexOid,
10948 : constrForm->confrelid, /* same foreign rel */
10949 : confkey,
10950 : conpfeqop,
10951 : conppeqop,
10952 : conffeqop,
10953 : numfks,
10954 394 : fkconstraint->fk_upd_action,
10955 394 : fkconstraint->fk_del_action,
10956 : confdelsetcols,
10957 : numfkdelsetcols,
10958 394 : fkconstraint->fk_matchtype,
10959 : NULL,
10960 : NULL,
10961 : NULL,
10962 : false, /* islocal */
10963 : 1, /* inhcount */
10964 : false, /* conNoInherit */
10965 : true);
10966 :
10967 : /* Set up partition dependencies for the new constraint */
10968 394 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10969 394 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
10970 394 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10971 394 : ObjectAddressSet(referenced, RelationRelationId,
10972 : RelationGetRelid(partRel));
10973 394 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10974 :
10975 : /* Done with the cloned constraint's tuple */
10976 394 : ReleaseSysCache(tuple);
10977 :
10978 : /* Make all this visible before recursing */
10979 394 : CommandCounterIncrement();
10980 :
10981 394 : addFkRecurseReferencing(wqueue,
10982 : fkconstraint,
10983 : partRel,
10984 : pkrel,
10985 : indexOid,
10986 : constrOid,
10987 : numfks,
10988 : confkey,
10989 : mapped_conkey,
10990 : conpfeqop,
10991 : conppeqop,
10992 : conffeqop,
10993 : numfkdelsetcols,
10994 : confdelsetcols,
10995 : false, /* no old check exists */
10996 : AccessExclusiveLock,
10997 : insertTriggerOid,
10998 : updateTriggerOid);
10999 394 : table_close(pkrel, NoLock);
11000 : }
11001 :
11002 436 : table_close(trigrel, RowExclusiveLock);
11003 : }
11004 :
11005 : /*
11006 : * When the parent of a partition receives [the referencing side of] a foreign
11007 : * key, we must propagate that foreign key to the partition. However, the
11008 : * partition might already have an equivalent foreign key; this routine
11009 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11010 : * by the other parameters. If they are equivalent, create the link between
11011 : * the two constraints and return true.
11012 : *
11013 : * If the given FK does not match the one defined by rest of the params,
11014 : * return false.
11015 : */
11016 : static bool
11017 180 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11018 : Oid partRelid,
11019 : Oid parentConstrOid,
11020 : int numfks,
11021 : AttrNumber *mapped_conkey,
11022 : AttrNumber *confkey,
11023 : Oid *conpfeqop,
11024 : Oid parentInsTrigger,
11025 : Oid parentUpdTrigger,
11026 : Relation trigrel)
11027 : {
11028 : HeapTuple parentConstrTup;
11029 : Form_pg_constraint parentConstr;
11030 : HeapTuple partcontup;
11031 : Form_pg_constraint partConstr;
11032 : ScanKeyData key;
11033 : SysScanDesc scan;
11034 : HeapTuple trigtup;
11035 : Oid insertTriggerOid,
11036 : updateTriggerOid;
11037 :
11038 180 : parentConstrTup = SearchSysCache1(CONSTROID,
11039 : ObjectIdGetDatum(parentConstrOid));
11040 180 : if (!HeapTupleIsValid(parentConstrTup))
11041 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11042 180 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11043 :
11044 : /*
11045 : * Do some quick & easy initial checks. If any of these fail, we cannot
11046 : * use this constraint.
11047 : */
11048 180 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11049 : {
11050 0 : ReleaseSysCache(parentConstrTup);
11051 0 : return false;
11052 : }
11053 510 : for (int i = 0; i < numfks; i++)
11054 : {
11055 330 : if (fk->conkey[i] != mapped_conkey[i] ||
11056 330 : fk->confkey[i] != confkey[i] ||
11057 330 : fk->conpfeqop[i] != conpfeqop[i])
11058 : {
11059 0 : ReleaseSysCache(parentConstrTup);
11060 0 : return false;
11061 : }
11062 : }
11063 :
11064 : /*
11065 : * Looks good so far; do some more extensive checks. Presumably the check
11066 : * for 'convalidated' could be dropped, since we don't really care about
11067 : * that, but let's be careful for now.
11068 : */
11069 180 : partcontup = SearchSysCache1(CONSTROID,
11070 : ObjectIdGetDatum(fk->conoid));
11071 180 : if (!HeapTupleIsValid(partcontup))
11072 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11073 180 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11074 180 : if (OidIsValid(partConstr->conparentid) ||
11075 156 : !partConstr->convalidated ||
11076 156 : partConstr->condeferrable != parentConstr->condeferrable ||
11077 128 : partConstr->condeferred != parentConstr->condeferred ||
11078 128 : partConstr->confupdtype != parentConstr->confupdtype ||
11079 92 : partConstr->confdeltype != parentConstr->confdeltype ||
11080 92 : partConstr->confmatchtype != parentConstr->confmatchtype)
11081 : {
11082 102 : ReleaseSysCache(parentConstrTup);
11083 102 : ReleaseSysCache(partcontup);
11084 102 : return false;
11085 : }
11086 :
11087 78 : ReleaseSysCache(partcontup);
11088 78 : ReleaseSysCache(parentConstrTup);
11089 :
11090 : /*
11091 : * Looks good! Attach this constraint. The action triggers in the new
11092 : * partition become redundant -- the parent table already has equivalent
11093 : * ones, and those will be able to reach the partition. Remove the ones
11094 : * in the partition. We identify them because they have our constraint
11095 : * OID, as well as being on the referenced rel.
11096 : */
11097 78 : ScanKeyInit(&key,
11098 : Anum_pg_trigger_tgconstraint,
11099 : BTEqualStrategyNumber, F_OIDEQ,
11100 : ObjectIdGetDatum(fk->conoid));
11101 78 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11102 : NULL, 1, &key);
11103 390 : while ((trigtup = systable_getnext(scan)) != NULL)
11104 : {
11105 312 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11106 : ObjectAddress trigger;
11107 :
11108 312 : if (trgform->tgconstrrelid != fk->conrelid)
11109 156 : continue;
11110 156 : if (trgform->tgrelid != fk->confrelid)
11111 0 : continue;
11112 :
11113 : /*
11114 : * The constraint is originally set up to contain this trigger as an
11115 : * implementation object, so there's a dependency record that links
11116 : * the two; however, since the trigger is no longer needed, we remove
11117 : * the dependency link in order to be able to drop the trigger while
11118 : * keeping the constraint intact.
11119 : */
11120 156 : deleteDependencyRecordsFor(TriggerRelationId,
11121 : trgform->oid,
11122 : false);
11123 : /* make dependency deletion visible to performDeletion */
11124 156 : CommandCounterIncrement();
11125 156 : ObjectAddressSet(trigger, TriggerRelationId,
11126 : trgform->oid);
11127 156 : performDeletion(&trigger, DROP_RESTRICT, 0);
11128 : /* make trigger drop visible, in case the loop iterates */
11129 156 : CommandCounterIncrement();
11130 : }
11131 :
11132 78 : systable_endscan(scan);
11133 :
11134 78 : ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11135 :
11136 : /*
11137 : * Like the constraint, attach partition's "check" triggers to the
11138 : * corresponding parent triggers.
11139 : */
11140 78 : GetForeignKeyCheckTriggers(trigrel,
11141 : fk->conoid, fk->confrelid, fk->conrelid,
11142 : &insertTriggerOid, &updateTriggerOid);
11143 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11144 78 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11145 : partRelid);
11146 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11147 78 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11148 : partRelid);
11149 :
11150 78 : CommandCounterIncrement();
11151 78 : return true;
11152 : }
11153 :
11154 : /*
11155 : * GetForeignKeyActionTriggers
11156 : * Returns delete and update "action" triggers of the given relation
11157 : * belonging to the given constraint
11158 : */
11159 : static void
11160 150 : GetForeignKeyActionTriggers(Relation trigrel,
11161 : Oid conoid, Oid confrelid, Oid conrelid,
11162 : Oid *deleteTriggerOid,
11163 : Oid *updateTriggerOid)
11164 : {
11165 : ScanKeyData key;
11166 : SysScanDesc scan;
11167 : HeapTuple trigtup;
11168 :
11169 150 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11170 150 : ScanKeyInit(&key,
11171 : Anum_pg_trigger_tgconstraint,
11172 : BTEqualStrategyNumber, F_OIDEQ,
11173 : ObjectIdGetDatum(conoid));
11174 :
11175 150 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11176 : NULL, 1, &key);
11177 306 : while ((trigtup = systable_getnext(scan)) != NULL)
11178 : {
11179 306 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11180 :
11181 306 : if (trgform->tgconstrrelid != conrelid)
11182 6 : continue;
11183 300 : if (trgform->tgrelid != confrelid)
11184 0 : continue;
11185 : /* Only ever look at "action" triggers on the PK side. */
11186 300 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11187 0 : continue;
11188 300 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11189 : {
11190 : Assert(*deleteTriggerOid == InvalidOid);
11191 150 : *deleteTriggerOid = trgform->oid;
11192 : }
11193 150 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11194 : {
11195 : Assert(*updateTriggerOid == InvalidOid);
11196 150 : *updateTriggerOid = trgform->oid;
11197 : }
11198 : #ifndef USE_ASSERT_CHECKING
11199 : /* In an assert-enabled build, continue looking to find duplicates */
11200 300 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11201 150 : break;
11202 : #endif
11203 : }
11204 :
11205 150 : if (!OidIsValid(*deleteTriggerOid))
11206 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11207 : conoid);
11208 150 : if (!OidIsValid(*updateTriggerOid))
11209 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11210 : conoid);
11211 :
11212 150 : systable_endscan(scan);
11213 150 : }
11214 :
11215 : /*
11216 : * GetForeignKeyCheckTriggers
11217 : * Returns insert and update "check" triggers of the given relation
11218 : * belonging to the given constraint
11219 : */
11220 : static void
11221 604 : GetForeignKeyCheckTriggers(Relation trigrel,
11222 : Oid conoid, Oid confrelid, Oid conrelid,
11223 : Oid *insertTriggerOid,
11224 : Oid *updateTriggerOid)
11225 : {
11226 : ScanKeyData key;
11227 : SysScanDesc scan;
11228 : HeapTuple trigtup;
11229 :
11230 604 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11231 604 : ScanKeyInit(&key,
11232 : Anum_pg_trigger_tgconstraint,
11233 : BTEqualStrategyNumber, F_OIDEQ,
11234 : ObjectIdGetDatum(conoid));
11235 :
11236 604 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11237 : NULL, 1, &key);
11238 2014 : while ((trigtup = systable_getnext(scan)) != NULL)
11239 : {
11240 2014 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11241 :
11242 2014 : if (trgform->tgconstrrelid != confrelid)
11243 722 : continue;
11244 1292 : if (trgform->tgrelid != conrelid)
11245 0 : continue;
11246 : /* Only ever look at "check" triggers on the FK side. */
11247 1292 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11248 84 : continue;
11249 1208 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
11250 : {
11251 : Assert(*insertTriggerOid == InvalidOid);
11252 604 : *insertTriggerOid = trgform->oid;
11253 : }
11254 604 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11255 : {
11256 : Assert(*updateTriggerOid == InvalidOid);
11257 604 : *updateTriggerOid = trgform->oid;
11258 : }
11259 : #ifndef USE_ASSERT_CHECKING
11260 : /* In an assert-enabled build, continue looking to find duplicates. */
11261 1208 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11262 604 : break;
11263 : #endif
11264 : }
11265 :
11266 604 : if (!OidIsValid(*insertTriggerOid))
11267 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11268 : conoid);
11269 604 : if (!OidIsValid(*updateTriggerOid))
11270 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11271 : conoid);
11272 :
11273 604 : systable_endscan(scan);
11274 604 : }
11275 :
11276 : /*
11277 : * ALTER TABLE ALTER CONSTRAINT
11278 : *
11279 : * Update the attributes of a constraint.
11280 : *
11281 : * Currently only works for Foreign Key constraints.
11282 : *
11283 : * If the constraint is modified, returns its address; otherwise, return
11284 : * InvalidObjectAddress.
11285 : */
11286 : static ObjectAddress
11287 66 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11288 : bool recursing, LOCKMODE lockmode)
11289 : {
11290 : Constraint *cmdcon;
11291 : Relation conrel;
11292 : Relation tgrel;
11293 : SysScanDesc scan;
11294 : ScanKeyData skey[3];
11295 : HeapTuple contuple;
11296 : Form_pg_constraint currcon;
11297 : ObjectAddress address;
11298 66 : List *otherrelids = NIL;
11299 : ListCell *lc;
11300 :
11301 66 : cmdcon = castNode(Constraint, cmd->def);
11302 :
11303 66 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11304 66 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11305 :
11306 : /*
11307 : * Find and check the target constraint
11308 : */
11309 66 : ScanKeyInit(&skey[0],
11310 : Anum_pg_constraint_conrelid,
11311 : BTEqualStrategyNumber, F_OIDEQ,
11312 : ObjectIdGetDatum(RelationGetRelid(rel)));
11313 66 : ScanKeyInit(&skey[1],
11314 : Anum_pg_constraint_contypid,
11315 : BTEqualStrategyNumber, F_OIDEQ,
11316 : ObjectIdGetDatum(InvalidOid));
11317 66 : ScanKeyInit(&skey[2],
11318 : Anum_pg_constraint_conname,
11319 : BTEqualStrategyNumber, F_NAMEEQ,
11320 66 : CStringGetDatum(cmdcon->conname));
11321 66 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11322 : true, NULL, 3, skey);
11323 :
11324 : /* There can be at most one matching row */
11325 66 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11326 0 : ereport(ERROR,
11327 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11328 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11329 : cmdcon->conname, RelationGetRelationName(rel))));
11330 :
11331 66 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11332 66 : if (currcon->contype != CONSTRAINT_FOREIGN)
11333 0 : ereport(ERROR,
11334 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11335 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11336 : cmdcon->conname, RelationGetRelationName(rel))));
11337 :
11338 : /*
11339 : * If it's not the topmost constraint, raise an error.
11340 : *
11341 : * Altering a non-topmost constraint leaves some triggers untouched, since
11342 : * they are not directly connected to this constraint; also, pg_dump would
11343 : * ignore the deferrability status of the individual constraint, since it
11344 : * only dumps topmost constraints. Avoid these problems by refusing this
11345 : * operation and telling the user to alter the parent constraint instead.
11346 : */
11347 66 : if (OidIsValid(currcon->conparentid))
11348 : {
11349 : HeapTuple tp;
11350 12 : Oid parent = currcon->conparentid;
11351 12 : char *ancestorname = NULL;
11352 12 : char *ancestortable = NULL;
11353 :
11354 : /* Loop to find the topmost constraint */
11355 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11356 : {
11357 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11358 :
11359 : /* If no parent, this is the constraint we want */
11360 24 : if (!OidIsValid(contup->conparentid))
11361 : {
11362 12 : ancestorname = pstrdup(NameStr(contup->conname));
11363 12 : ancestortable = get_rel_name(contup->conrelid);
11364 12 : ReleaseSysCache(tp);
11365 12 : break;
11366 : }
11367 :
11368 12 : parent = contup->conparentid;
11369 12 : ReleaseSysCache(tp);
11370 : }
11371 :
11372 12 : ereport(ERROR,
11373 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11374 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11375 : cmdcon->conname, RelationGetRelationName(rel)),
11376 : ancestorname && ancestortable ?
11377 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11378 : cmdcon->conname, ancestorname, ancestortable) : 0,
11379 : errhint("You may alter the constraint it derives from instead.")));
11380 : }
11381 :
11382 : /*
11383 : * Do the actual catalog work. We can skip changing if already in the
11384 : * desired state, but not if a partitioned table: partitions need to be
11385 : * processed regardless, in case they had the constraint locally changed.
11386 : */
11387 54 : address = InvalidObjectAddress;
11388 54 : if (currcon->condeferrable != cmdcon->deferrable ||
11389 6 : currcon->condeferred != cmdcon->initdeferred ||
11390 0 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11391 : {
11392 54 : if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11393 : &otherrelids, lockmode))
11394 54 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11395 : }
11396 :
11397 : /*
11398 : * ATExecAlterConstrRecurse already invalidated relcache for the relations
11399 : * having the constraint itself; here we also invalidate for relations
11400 : * that have any triggers that are part of the constraint.
11401 : */
11402 138 : foreach(lc, otherrelids)
11403 84 : CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11404 :
11405 54 : systable_endscan(scan);
11406 :
11407 54 : table_close(tgrel, RowExclusiveLock);
11408 54 : table_close(conrel, RowExclusiveLock);
11409 :
11410 54 : return address;
11411 : }
11412 :
11413 : /*
11414 : * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11415 : * constraint is altered.
11416 : *
11417 : * *otherrelids is appended OIDs of relations containing affected triggers.
11418 : *
11419 : * Note that we must recurse even when the values are correct, in case
11420 : * indirect descendants have had their constraints altered locally.
11421 : * (This could be avoided if we forbade altering constraints in partitions
11422 : * but existing releases don't do that.)
11423 : */
11424 : static bool
11425 120 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11426 : Relation rel, HeapTuple contuple, List **otherrelids,
11427 : LOCKMODE lockmode)
11428 : {
11429 : Form_pg_constraint currcon;
11430 : Oid conoid;
11431 : Oid refrelid;
11432 120 : bool changed = false;
11433 :
11434 : /* since this function recurses, it could be driven to stack overflow */
11435 120 : check_stack_depth();
11436 :
11437 120 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11438 120 : conoid = currcon->oid;
11439 120 : refrelid = currcon->confrelid;
11440 :
11441 : /*
11442 : * Update pg_constraint with the flags from cmdcon.
11443 : *
11444 : * If called to modify a constraint that's already in the desired state,
11445 : * silently do nothing.
11446 : */
11447 120 : if (currcon->condeferrable != cmdcon->deferrable ||
11448 6 : currcon->condeferred != cmdcon->initdeferred)
11449 : {
11450 : HeapTuple copyTuple;
11451 : Form_pg_constraint copy_con;
11452 : HeapTuple tgtuple;
11453 : ScanKeyData tgkey;
11454 : SysScanDesc tgscan;
11455 :
11456 120 : copyTuple = heap_copytuple(contuple);
11457 120 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11458 120 : copy_con->condeferrable = cmdcon->deferrable;
11459 120 : copy_con->condeferred = cmdcon->initdeferred;
11460 120 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11461 :
11462 120 : InvokeObjectPostAlterHook(ConstraintRelationId,
11463 : conoid, 0);
11464 :
11465 120 : heap_freetuple(copyTuple);
11466 120 : changed = true;
11467 :
11468 : /* Make new constraint flags visible to others */
11469 120 : CacheInvalidateRelcache(rel);
11470 :
11471 : /*
11472 : * Now we need to update the multiple entries in pg_trigger that
11473 : * implement the constraint.
11474 : */
11475 120 : ScanKeyInit(&tgkey,
11476 : Anum_pg_trigger_tgconstraint,
11477 : BTEqualStrategyNumber, F_OIDEQ,
11478 : ObjectIdGetDatum(conoid));
11479 120 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11480 : NULL, 1, &tgkey);
11481 468 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11482 : {
11483 348 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11484 : Form_pg_trigger copy_tg;
11485 : HeapTuple tgCopyTuple;
11486 :
11487 : /*
11488 : * Remember OIDs of other relation(s) involved in FK constraint.
11489 : * (Note: it's likely that we could skip forcing a relcache inval
11490 : * for other rels that don't have a trigger whose properties
11491 : * change, but let's be conservative.)
11492 : */
11493 348 : if (tgform->tgrelid != RelationGetRelid(rel))
11494 168 : *otherrelids = list_append_unique_oid(*otherrelids,
11495 : tgform->tgrelid);
11496 :
11497 : /*
11498 : * Update deferrability of RI_FKey_noaction_del,
11499 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11500 : * triggers, but not others; see createForeignKeyActionTriggers
11501 : * and CreateFKCheckTrigger.
11502 : */
11503 348 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11504 282 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11505 198 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11506 108 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11507 18 : continue;
11508 :
11509 330 : tgCopyTuple = heap_copytuple(tgtuple);
11510 330 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11511 :
11512 330 : copy_tg->tgdeferrable = cmdcon->deferrable;
11513 330 : copy_tg->tginitdeferred = cmdcon->initdeferred;
11514 330 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11515 :
11516 330 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11517 :
11518 330 : heap_freetuple(tgCopyTuple);
11519 : }
11520 :
11521 120 : systable_endscan(tgscan);
11522 : }
11523 :
11524 : /*
11525 : * If the table at either end of the constraint is partitioned, we need to
11526 : * recurse and handle every constraint that is a child of this one.
11527 : *
11528 : * (This assumes that the recurse flag is forcibly set for partitioned
11529 : * tables, and not set for legacy inheritance, though we don't check for
11530 : * that here.)
11531 : */
11532 216 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11533 96 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11534 : {
11535 : ScanKeyData pkey;
11536 : SysScanDesc pscan;
11537 : HeapTuple childtup;
11538 :
11539 42 : ScanKeyInit(&pkey,
11540 : Anum_pg_constraint_conparentid,
11541 : BTEqualStrategyNumber, F_OIDEQ,
11542 : ObjectIdGetDatum(conoid));
11543 :
11544 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11545 : true, NULL, 1, &pkey);
11546 :
11547 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11548 : {
11549 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11550 : Relation childrel;
11551 :
11552 66 : childrel = table_open(childcon->conrelid, lockmode);
11553 66 : ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11554 : otherrelids, lockmode);
11555 66 : table_close(childrel, NoLock);
11556 : }
11557 :
11558 42 : systable_endscan(pscan);
11559 : }
11560 :
11561 120 : return changed;
11562 : }
11563 :
11564 : /*
11565 : * ALTER TABLE VALIDATE CONSTRAINT
11566 : *
11567 : * XXX The reason we handle recursion here rather than at Phase 1 is because
11568 : * there's no good way to skip recursing when handling foreign keys: there is
11569 : * no need to lock children in that case, yet we wouldn't be able to avoid
11570 : * doing so at that level.
11571 : *
11572 : * Return value is the address of the validated constraint. If the constraint
11573 : * was already validated, InvalidObjectAddress is returned.
11574 : */
11575 : static ObjectAddress
11576 436 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
11577 : bool recurse, bool recursing, LOCKMODE lockmode)
11578 : {
11579 : Relation conrel;
11580 : SysScanDesc scan;
11581 : ScanKeyData skey[3];
11582 : HeapTuple tuple;
11583 : Form_pg_constraint con;
11584 : ObjectAddress address;
11585 :
11586 436 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11587 :
11588 : /*
11589 : * Find and check the target constraint
11590 : */
11591 436 : ScanKeyInit(&skey[0],
11592 : Anum_pg_constraint_conrelid,
11593 : BTEqualStrategyNumber, F_OIDEQ,
11594 : ObjectIdGetDatum(RelationGetRelid(rel)));
11595 436 : ScanKeyInit(&skey[1],
11596 : Anum_pg_constraint_contypid,
11597 : BTEqualStrategyNumber, F_OIDEQ,
11598 : ObjectIdGetDatum(InvalidOid));
11599 436 : ScanKeyInit(&skey[2],
11600 : Anum_pg_constraint_conname,
11601 : BTEqualStrategyNumber, F_NAMEEQ,
11602 : CStringGetDatum(constrName));
11603 436 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11604 : true, NULL, 3, skey);
11605 :
11606 : /* There can be at most one matching row */
11607 436 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11608 0 : ereport(ERROR,
11609 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11610 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11611 : constrName, RelationGetRelationName(rel))));
11612 :
11613 436 : con = (Form_pg_constraint) GETSTRUCT(tuple);
11614 436 : if (con->contype != CONSTRAINT_FOREIGN &&
11615 132 : con->contype != CONSTRAINT_CHECK)
11616 0 : ereport(ERROR,
11617 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11618 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11619 : constrName, RelationGetRelationName(rel))));
11620 :
11621 436 : if (!con->convalidated)
11622 : {
11623 : AlteredTableInfo *tab;
11624 : HeapTuple copyTuple;
11625 : Form_pg_constraint copy_con;
11626 :
11627 418 : if (con->contype == CONSTRAINT_FOREIGN)
11628 : {
11629 : NewConstraint *newcon;
11630 : Constraint *fkconstraint;
11631 :
11632 : /* Queue validation for phase 3 */
11633 298 : fkconstraint = makeNode(Constraint);
11634 : /* for now this is all we need */
11635 298 : fkconstraint->conname = constrName;
11636 :
11637 298 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11638 298 : newcon->name = constrName;
11639 298 : newcon->contype = CONSTR_FOREIGN;
11640 298 : newcon->refrelid = con->confrelid;
11641 298 : newcon->refindid = con->conindid;
11642 298 : newcon->conid = con->oid;
11643 298 : newcon->qual = (Node *) fkconstraint;
11644 :
11645 : /* Find or create work queue entry for this table */
11646 298 : tab = ATGetQueueEntry(wqueue, rel);
11647 298 : tab->constraints = lappend(tab->constraints, newcon);
11648 :
11649 : /*
11650 : * We disallow creating invalid foreign keys to or from
11651 : * partitioned tables, so ignoring the recursion bit is okay.
11652 : */
11653 : }
11654 120 : else if (con->contype == CONSTRAINT_CHECK)
11655 : {
11656 120 : List *children = NIL;
11657 : ListCell *child;
11658 : NewConstraint *newcon;
11659 : Datum val;
11660 : char *conbin;
11661 :
11662 : /*
11663 : * If we're recursing, the parent has already done this, so skip
11664 : * it. Also, if the constraint is a NO INHERIT constraint, we
11665 : * shouldn't try to look for it in the children.
11666 : */
11667 120 : if (!recursing && !con->connoinherit)
11668 66 : children = find_all_inheritors(RelationGetRelid(rel),
11669 : lockmode, NULL);
11670 :
11671 : /*
11672 : * For CHECK constraints, we must ensure that we only mark the
11673 : * constraint as validated on the parent if it's already validated
11674 : * on the children.
11675 : *
11676 : * We recurse before validating on the parent, to reduce risk of
11677 : * deadlocks.
11678 : */
11679 234 : foreach(child, children)
11680 : {
11681 114 : Oid childoid = lfirst_oid(child);
11682 : Relation childrel;
11683 :
11684 114 : if (childoid == RelationGetRelid(rel))
11685 66 : continue;
11686 :
11687 : /*
11688 : * If we are told not to recurse, there had better not be any
11689 : * child tables, because we can't mark the constraint on the
11690 : * parent valid unless it is valid for all child tables.
11691 : */
11692 48 : if (!recurse)
11693 0 : ereport(ERROR,
11694 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11695 : errmsg("constraint must be validated on child tables too")));
11696 :
11697 : /* find_all_inheritors already got lock */
11698 48 : childrel = table_open(childoid, NoLock);
11699 :
11700 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
11701 : true, lockmode);
11702 48 : table_close(childrel, NoLock);
11703 : }
11704 :
11705 : /* Queue validation for phase 3 */
11706 120 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11707 120 : newcon->name = constrName;
11708 120 : newcon->contype = CONSTR_CHECK;
11709 120 : newcon->refrelid = InvalidOid;
11710 120 : newcon->refindid = InvalidOid;
11711 120 : newcon->conid = con->oid;
11712 :
11713 120 : val = SysCacheGetAttrNotNull(CONSTROID, tuple,
11714 : Anum_pg_constraint_conbin);
11715 120 : conbin = TextDatumGetCString(val);
11716 120 : newcon->qual = (Node *) stringToNode(conbin);
11717 :
11718 : /* Find or create work queue entry for this table */
11719 120 : tab = ATGetQueueEntry(wqueue, rel);
11720 120 : tab->constraints = lappend(tab->constraints, newcon);
11721 :
11722 : /*
11723 : * Invalidate relcache so that others see the new validated
11724 : * constraint.
11725 : */
11726 120 : CacheInvalidateRelcache(rel);
11727 : }
11728 :
11729 : /*
11730 : * Now update the catalog, while we have the door open.
11731 : */
11732 418 : copyTuple = heap_copytuple(tuple);
11733 418 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11734 418 : copy_con->convalidated = true;
11735 418 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11736 :
11737 418 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
11738 :
11739 418 : heap_freetuple(copyTuple);
11740 :
11741 418 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
11742 : }
11743 : else
11744 18 : address = InvalidObjectAddress; /* already validated */
11745 :
11746 436 : systable_endscan(scan);
11747 :
11748 436 : table_close(conrel, RowExclusiveLock);
11749 :
11750 436 : return address;
11751 : }
11752 :
11753 :
11754 : /*
11755 : * transformColumnNameList - transform list of column names
11756 : *
11757 : * Lookup each name and return its attnum and, optionally, type OID
11758 : *
11759 : * Note: the name of this function suggests that it's general-purpose,
11760 : * but actually it's only used to look up names appearing in foreign-key
11761 : * clauses. The error messages would need work to use it in other cases,
11762 : * and perhaps the validity checks as well.
11763 : */
11764 : static int
11765 5296 : transformColumnNameList(Oid relId, List *colList,
11766 : int16 *attnums, Oid *atttypids)
11767 : {
11768 : ListCell *l;
11769 : int attnum;
11770 :
11771 5296 : attnum = 0;
11772 9300 : foreach(l, colList)
11773 : {
11774 4070 : char *attname = strVal(lfirst(l));
11775 : HeapTuple atttuple;
11776 : Form_pg_attribute attform;
11777 :
11778 4070 : atttuple = SearchSysCacheAttName(relId, attname);
11779 4070 : if (!HeapTupleIsValid(atttuple))
11780 54 : ereport(ERROR,
11781 : (errcode(ERRCODE_UNDEFINED_COLUMN),
11782 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11783 : attname)));
11784 4016 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
11785 4016 : if (attform->attnum < 0)
11786 12 : ereport(ERROR,
11787 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11788 : errmsg("system columns cannot be used in foreign keys")));
11789 4004 : if (attnum >= INDEX_MAX_KEYS)
11790 0 : ereport(ERROR,
11791 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
11792 : errmsg("cannot have more than %d keys in a foreign key",
11793 : INDEX_MAX_KEYS)));
11794 4004 : attnums[attnum] = attform->attnum;
11795 4004 : if (atttypids != NULL)
11796 3974 : atttypids[attnum] = attform->atttypid;
11797 4004 : ReleaseSysCache(atttuple);
11798 4004 : attnum++;
11799 : }
11800 :
11801 5230 : return attnum;
11802 : }
11803 :
11804 : /*
11805 : * transformFkeyGetPrimaryKey -
11806 : *
11807 : * Look up the names, attnums, and types of the primary key attributes
11808 : * for the pkrel. Also return the index OID and index opclasses of the
11809 : * index supporting the primary key.
11810 : *
11811 : * All parameters except pkrel are output parameters. Also, the function
11812 : * return value is the number of attributes in the primary key.
11813 : *
11814 : * Used when the column list in the REFERENCES specification is omitted.
11815 : */
11816 : static int
11817 944 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
11818 : List **attnamelist,
11819 : int16 *attnums, Oid *atttypids,
11820 : Oid *opclasses)
11821 : {
11822 : List *indexoidlist;
11823 : ListCell *indexoidscan;
11824 944 : HeapTuple indexTuple = NULL;
11825 944 : Form_pg_index indexStruct = NULL;
11826 : Datum indclassDatum;
11827 : oidvector *indclass;
11828 : int i;
11829 :
11830 : /*
11831 : * Get the list of index OIDs for the table from the relcache, and look up
11832 : * each one in the pg_index syscache until we find one marked primary key
11833 : * (hopefully there isn't more than one such). Insist it's valid, too.
11834 : */
11835 944 : *indexOid = InvalidOid;
11836 :
11837 944 : indexoidlist = RelationGetIndexList(pkrel);
11838 :
11839 950 : foreach(indexoidscan, indexoidlist)
11840 : {
11841 950 : Oid indexoid = lfirst_oid(indexoidscan);
11842 :
11843 950 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11844 950 : if (!HeapTupleIsValid(indexTuple))
11845 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
11846 950 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11847 950 : if (indexStruct->indisprimary && indexStruct->indisvalid)
11848 : {
11849 : /*
11850 : * Refuse to use a deferrable primary key. This is per SQL spec,
11851 : * and there would be a lot of interesting semantic problems if we
11852 : * tried to allow it.
11853 : */
11854 944 : if (!indexStruct->indimmediate)
11855 0 : ereport(ERROR,
11856 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11857 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11858 : RelationGetRelationName(pkrel))));
11859 :
11860 944 : *indexOid = indexoid;
11861 944 : break;
11862 : }
11863 6 : ReleaseSysCache(indexTuple);
11864 : }
11865 :
11866 944 : list_free(indexoidlist);
11867 :
11868 : /*
11869 : * Check that we found it
11870 : */
11871 944 : if (!OidIsValid(*indexOid))
11872 0 : ereport(ERROR,
11873 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11874 : errmsg("there is no primary key for referenced table \"%s\"",
11875 : RelationGetRelationName(pkrel))));
11876 :
11877 : /* Must get indclass the hard way */
11878 944 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11879 : Anum_pg_index_indclass);
11880 944 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
11881 :
11882 : /*
11883 : * Now build the list of PK attributes from the indkey definition (we
11884 : * assume a primary key cannot have expressional elements)
11885 : */
11886 944 : *attnamelist = NIL;
11887 2120 : for (i = 0; i < indexStruct->indnkeyatts; i++)
11888 : {
11889 1176 : int pkattno = indexStruct->indkey.values[i];
11890 :
11891 1176 : attnums[i] = pkattno;
11892 1176 : atttypids[i] = attnumTypeId(pkrel, pkattno);
11893 1176 : opclasses[i] = indclass->values[i];
11894 1176 : *attnamelist = lappend(*attnamelist,
11895 1176 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
11896 : }
11897 :
11898 944 : ReleaseSysCache(indexTuple);
11899 :
11900 944 : return i;
11901 : }
11902 :
11903 : /*
11904 : * transformFkeyCheckAttrs -
11905 : *
11906 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
11907 : * reference as part of a foreign key constraint.
11908 : *
11909 : * Returns the OID of the unique index supporting the constraint and
11910 : * populates the caller-provided 'opclasses' array with the opclasses
11911 : * associated with the index columns.
11912 : *
11913 : * Raises an ERROR on validation failure.
11914 : */
11915 : static Oid
11916 1088 : transformFkeyCheckAttrs(Relation pkrel,
11917 : int numattrs, int16 *attnums,
11918 : Oid *opclasses)
11919 : {
11920 1088 : Oid indexoid = InvalidOid;
11921 1088 : bool found = false;
11922 1088 : bool found_deferrable = false;
11923 : List *indexoidlist;
11924 : ListCell *indexoidscan;
11925 : int i,
11926 : j;
11927 :
11928 : /*
11929 : * Reject duplicate appearances of columns in the referenced-columns list.
11930 : * Such a case is forbidden by the SQL standard, and even if we thought it
11931 : * useful to allow it, there would be ambiguity about how to match the
11932 : * list to unique indexes (in particular, it'd be unclear which index
11933 : * opclass goes with which FK column).
11934 : */
11935 2460 : for (i = 0; i < numattrs; i++)
11936 : {
11937 1698 : for (j = i + 1; j < numattrs; j++)
11938 : {
11939 326 : if (attnums[i] == attnums[j])
11940 0 : ereport(ERROR,
11941 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11942 : errmsg("foreign key referenced-columns list must not contain duplicates")));
11943 : }
11944 : }
11945 :
11946 : /*
11947 : * Get the list of index OIDs for the table from the relcache, and look up
11948 : * each one in the pg_index syscache, and match unique indexes to the list
11949 : * of attnums we are given.
11950 : */
11951 1088 : indexoidlist = RelationGetIndexList(pkrel);
11952 :
11953 1268 : foreach(indexoidscan, indexoidlist)
11954 : {
11955 : HeapTuple indexTuple;
11956 : Form_pg_index indexStruct;
11957 :
11958 1256 : indexoid = lfirst_oid(indexoidscan);
11959 1256 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11960 1256 : if (!HeapTupleIsValid(indexTuple))
11961 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
11962 1256 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11963 :
11964 : /*
11965 : * Must have the right number of columns; must be unique and not a
11966 : * partial index; forget it if there are any expressions, too. Invalid
11967 : * indexes are out as well.
11968 : */
11969 1256 : if (indexStruct->indnkeyatts == numattrs &&
11970 1148 : indexStruct->indisunique &&
11971 2268 : indexStruct->indisvalid &&
11972 2268 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
11973 1134 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
11974 : {
11975 : Datum indclassDatum;
11976 : oidvector *indclass;
11977 :
11978 : /* Must get indclass the hard way */
11979 1134 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11980 : Anum_pg_index_indclass);
11981 1134 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
11982 :
11983 : /*
11984 : * The given attnum list may match the index columns in any order.
11985 : * Check for a match, and extract the appropriate opclasses while
11986 : * we're at it.
11987 : *
11988 : * We know that attnums[] is duplicate-free per the test at the
11989 : * start of this function, and we checked above that the number of
11990 : * index columns agrees, so if we find a match for each attnums[]
11991 : * entry then we must have a one-to-one match in some order.
11992 : */
11993 2494 : for (i = 0; i < numattrs; i++)
11994 : {
11995 1418 : found = false;
11996 1802 : for (j = 0; j < numattrs; j++)
11997 : {
11998 1744 : if (attnums[i] == indexStruct->indkey.values[j])
11999 : {
12000 1360 : opclasses[i] = indclass->values[j];
12001 1360 : found = true;
12002 1360 : break;
12003 : }
12004 : }
12005 1418 : if (!found)
12006 58 : break;
12007 : }
12008 :
12009 : /*
12010 : * Refuse to use a deferrable unique/primary key. This is per SQL
12011 : * spec, and there would be a lot of interesting semantic problems
12012 : * if we tried to allow it.
12013 : */
12014 1134 : if (found && !indexStruct->indimmediate)
12015 : {
12016 : /*
12017 : * Remember that we found an otherwise matching index, so that
12018 : * we can generate a more appropriate error message.
12019 : */
12020 0 : found_deferrable = true;
12021 0 : found = false;
12022 : }
12023 : }
12024 1256 : ReleaseSysCache(indexTuple);
12025 1256 : if (found)
12026 1076 : break;
12027 : }
12028 :
12029 1088 : if (!found)
12030 : {
12031 12 : if (found_deferrable)
12032 0 : ereport(ERROR,
12033 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12034 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12035 : RelationGetRelationName(pkrel))));
12036 : else
12037 12 : ereport(ERROR,
12038 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12039 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12040 : RelationGetRelationName(pkrel))));
12041 : }
12042 :
12043 1076 : list_free(indexoidlist);
12044 :
12045 1076 : return indexoid;
12046 : }
12047 :
12048 : /*
12049 : * findFkeyCast -
12050 : *
12051 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12052 : * Caller has equal regard for binary coercibility and for an exact match.
12053 : */
12054 : static CoercionPathType
12055 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12056 : {
12057 : CoercionPathType ret;
12058 :
12059 12 : if (targetTypeId == sourceTypeId)
12060 : {
12061 12 : ret = COERCION_PATH_RELABELTYPE;
12062 12 : *funcid = InvalidOid;
12063 : }
12064 : else
12065 : {
12066 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12067 : COERCION_IMPLICIT, funcid);
12068 0 : if (ret == COERCION_PATH_NONE)
12069 : /* A previously-relied-upon cast is now gone. */
12070 0 : elog(ERROR, "could not find cast from %u to %u",
12071 : sourceTypeId, targetTypeId);
12072 : }
12073 :
12074 12 : return ret;
12075 : }
12076 :
12077 : /*
12078 : * Permissions checks on the referenced table for ADD FOREIGN KEY
12079 : *
12080 : * Note: we have already checked that the user owns the referencing table,
12081 : * else we'd have failed much earlier; no additional checks are needed for it.
12082 : */
12083 : static void
12084 2020 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12085 : {
12086 2020 : Oid roleid = GetUserId();
12087 : AclResult aclresult;
12088 : int i;
12089 :
12090 : /* Okay if we have relation-level REFERENCES permission */
12091 2020 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12092 : ACL_REFERENCES);
12093 2020 : if (aclresult == ACLCHECK_OK)
12094 2020 : return;
12095 : /* Else we must have REFERENCES on each column */
12096 0 : for (i = 0; i < natts; i++)
12097 : {
12098 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12099 : roleid, ACL_REFERENCES);
12100 0 : if (aclresult != ACLCHECK_OK)
12101 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12102 0 : RelationGetRelationName(rel));
12103 : }
12104 : }
12105 :
12106 : /*
12107 : * Scan the existing rows in a table to verify they meet a proposed FK
12108 : * constraint.
12109 : *
12110 : * Caller must have opened and locked both relations appropriately.
12111 : */
12112 : static void
12113 944 : validateForeignKeyConstraint(char *conname,
12114 : Relation rel,
12115 : Relation pkrel,
12116 : Oid pkindOid,
12117 : Oid constraintOid)
12118 : {
12119 : TupleTableSlot *slot;
12120 : TableScanDesc scan;
12121 944 : Trigger trig = {0};
12122 : Snapshot snapshot;
12123 : MemoryContext oldcxt;
12124 : MemoryContext perTupCxt;
12125 :
12126 944 : ereport(DEBUG1,
12127 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12128 :
12129 : /*
12130 : * Build a trigger call structure; we'll need it either way.
12131 : */
12132 944 : trig.tgoid = InvalidOid;
12133 944 : trig.tgname = conname;
12134 944 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12135 944 : trig.tgisinternal = true;
12136 944 : trig.tgconstrrelid = RelationGetRelid(pkrel);
12137 944 : trig.tgconstrindid = pkindOid;
12138 944 : trig.tgconstraint = constraintOid;
12139 944 : trig.tgdeferrable = false;
12140 944 : trig.tginitdeferred = false;
12141 : /* we needn't fill in remaining fields */
12142 :
12143 : /*
12144 : * See if we can do it with a single LEFT JOIN query. A false result
12145 : * indicates we must proceed with the fire-the-trigger method.
12146 : */
12147 944 : if (RI_Initial_Check(&trig, rel, pkrel))
12148 870 : return;
12149 :
12150 : /*
12151 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12152 : * if that tuple had just been inserted. If any of those fail, it should
12153 : * ereport(ERROR) and that's that.
12154 : */
12155 12 : snapshot = RegisterSnapshot(GetLatestSnapshot());
12156 12 : slot = table_slot_create(rel, NULL);
12157 12 : scan = table_beginscan(rel, snapshot, 0, NULL);
12158 :
12159 12 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12160 : "validateForeignKeyConstraint",
12161 : ALLOCSET_SMALL_SIZES);
12162 12 : oldcxt = MemoryContextSwitchTo(perTupCxt);
12163 :
12164 72 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12165 : {
12166 66 : LOCAL_FCINFO(fcinfo, 0);
12167 66 : TriggerData trigdata = {0};
12168 :
12169 66 : CHECK_FOR_INTERRUPTS();
12170 :
12171 : /*
12172 : * Make a call to the trigger function
12173 : *
12174 : * No parameters are passed, but we do set a context
12175 : */
12176 330 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12177 :
12178 : /*
12179 : * We assume RI_FKey_check_ins won't look at flinfo...
12180 : */
12181 66 : trigdata.type = T_TriggerData;
12182 66 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12183 66 : trigdata.tg_relation = rel;
12184 66 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12185 66 : trigdata.tg_trigslot = slot;
12186 66 : trigdata.tg_trigger = &trig;
12187 :
12188 66 : fcinfo->context = (Node *) &trigdata;
12189 :
12190 66 : RI_FKey_check_ins(fcinfo);
12191 :
12192 60 : MemoryContextReset(perTupCxt);
12193 : }
12194 :
12195 6 : MemoryContextSwitchTo(oldcxt);
12196 6 : MemoryContextDelete(perTupCxt);
12197 6 : table_endscan(scan);
12198 6 : UnregisterSnapshot(snapshot);
12199 6 : ExecDropSingleTupleTableSlot(slot);
12200 : }
12201 :
12202 : /*
12203 : * CreateFKCheckTrigger
12204 : * Creates the insert (on_insert=true) or update "check" trigger that
12205 : * implements a given foreign key
12206 : *
12207 : * Returns the OID of the so created trigger.
12208 : */
12209 : static Oid
12210 5088 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12211 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12212 : bool on_insert)
12213 : {
12214 : ObjectAddress trigAddress;
12215 : CreateTrigStmt *fk_trigger;
12216 :
12217 : /*
12218 : * Note: for a self-referential FK (referencing and referenced tables are
12219 : * the same), it is important that the ON UPDATE action fires before the
12220 : * CHECK action, since both triggers will fire on the same row during an
12221 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12222 : * state of the row. Triggers fire in name order, so we ensure this by
12223 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12224 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12225 : */
12226 5088 : fk_trigger = makeNode(CreateTrigStmt);
12227 5088 : fk_trigger->replace = false;
12228 5088 : fk_trigger->isconstraint = true;
12229 5088 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
12230 5088 : fk_trigger->relation = NULL;
12231 :
12232 : /* Either ON INSERT or ON UPDATE */
12233 5088 : if (on_insert)
12234 : {
12235 2544 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12236 2544 : fk_trigger->events = TRIGGER_TYPE_INSERT;
12237 : }
12238 : else
12239 : {
12240 2544 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12241 2544 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12242 : }
12243 :
12244 5088 : fk_trigger->args = NIL;
12245 5088 : fk_trigger->row = true;
12246 5088 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12247 5088 : fk_trigger->columns = NIL;
12248 5088 : fk_trigger->whenClause = NULL;
12249 5088 : fk_trigger->transitionRels = NIL;
12250 5088 : fk_trigger->deferrable = fkconstraint->deferrable;
12251 5088 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12252 5088 : fk_trigger->constrrel = NULL;
12253 :
12254 5088 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12255 : constraintOid, indexOid, InvalidOid,
12256 : parentTrigOid, NULL, true, false);
12257 :
12258 : /* Make changes-so-far visible */
12259 5088 : CommandCounterIncrement();
12260 :
12261 5088 : return trigAddress.objectId;
12262 : }
12263 :
12264 : /*
12265 : * createForeignKeyActionTriggers
12266 : * Create the referenced-side "action" triggers that implement a foreign
12267 : * key.
12268 : *
12269 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
12270 : * *updateTrigOid.
12271 : */
12272 : static void
12273 2560 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12274 : Oid constraintOid, Oid indexOid,
12275 : Oid parentDelTrigger, Oid parentUpdTrigger,
12276 : Oid *deleteTrigOid, Oid *updateTrigOid)
12277 : {
12278 : CreateTrigStmt *fk_trigger;
12279 : ObjectAddress trigAddress;
12280 :
12281 : /*
12282 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12283 : * DELETE action on the referenced table.
12284 : */
12285 2560 : fk_trigger = makeNode(CreateTrigStmt);
12286 2560 : fk_trigger->replace = false;
12287 2560 : fk_trigger->isconstraint = true;
12288 2560 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12289 2560 : fk_trigger->relation = NULL;
12290 2560 : fk_trigger->args = NIL;
12291 2560 : fk_trigger->row = true;
12292 2560 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12293 2560 : fk_trigger->events = TRIGGER_TYPE_DELETE;
12294 2560 : fk_trigger->columns = NIL;
12295 2560 : fk_trigger->whenClause = NULL;
12296 2560 : fk_trigger->transitionRels = NIL;
12297 2560 : fk_trigger->constrrel = NULL;
12298 2560 : switch (fkconstraint->fk_del_action)
12299 : {
12300 1932 : case FKCONSTR_ACTION_NOACTION:
12301 1932 : fk_trigger->deferrable = fkconstraint->deferrable;
12302 1932 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12303 1932 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12304 1932 : break;
12305 30 : case FKCONSTR_ACTION_RESTRICT:
12306 30 : fk_trigger->deferrable = false;
12307 30 : fk_trigger->initdeferred = false;
12308 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12309 30 : break;
12310 440 : case FKCONSTR_ACTION_CASCADE:
12311 440 : fk_trigger->deferrable = false;
12312 440 : fk_trigger->initdeferred = false;
12313 440 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12314 440 : break;
12315 98 : case FKCONSTR_ACTION_SETNULL:
12316 98 : fk_trigger->deferrable = false;
12317 98 : fk_trigger->initdeferred = false;
12318 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12319 98 : break;
12320 60 : case FKCONSTR_ACTION_SETDEFAULT:
12321 60 : fk_trigger->deferrable = false;
12322 60 : fk_trigger->initdeferred = false;
12323 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12324 60 : break;
12325 0 : default:
12326 0 : elog(ERROR, "unrecognized FK action type: %d",
12327 : (int) fkconstraint->fk_del_action);
12328 : break;
12329 : }
12330 :
12331 2560 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12332 : RelationGetRelid(rel),
12333 : constraintOid, indexOid, InvalidOid,
12334 : parentDelTrigger, NULL, true, false);
12335 2560 : if (deleteTrigOid)
12336 2494 : *deleteTrigOid = trigAddress.objectId;
12337 :
12338 : /* Make changes-so-far visible */
12339 2560 : CommandCounterIncrement();
12340 :
12341 : /*
12342 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12343 : * UPDATE action on the referenced table.
12344 : */
12345 2560 : fk_trigger = makeNode(CreateTrigStmt);
12346 2560 : fk_trigger->replace = false;
12347 2560 : fk_trigger->isconstraint = true;
12348 2560 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12349 2560 : fk_trigger->relation = NULL;
12350 2560 : fk_trigger->args = NIL;
12351 2560 : fk_trigger->row = true;
12352 2560 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12353 2560 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12354 2560 : fk_trigger->columns = NIL;
12355 2560 : fk_trigger->whenClause = NULL;
12356 2560 : fk_trigger->transitionRels = NIL;
12357 2560 : fk_trigger->constrrel = NULL;
12358 2560 : switch (fkconstraint->fk_upd_action)
12359 : {
12360 2138 : case FKCONSTR_ACTION_NOACTION:
12361 2138 : fk_trigger->deferrable = fkconstraint->deferrable;
12362 2138 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12363 2138 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12364 2138 : break;
12365 30 : case FKCONSTR_ACTION_RESTRICT:
12366 30 : fk_trigger->deferrable = false;
12367 30 : fk_trigger->initdeferred = false;
12368 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12369 30 : break;
12370 288 : case FKCONSTR_ACTION_CASCADE:
12371 288 : fk_trigger->deferrable = false;
12372 288 : fk_trigger->initdeferred = false;
12373 288 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12374 288 : break;
12375 62 : case FKCONSTR_ACTION_SETNULL:
12376 62 : fk_trigger->deferrable = false;
12377 62 : fk_trigger->initdeferred = false;
12378 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12379 62 : break;
12380 42 : case FKCONSTR_ACTION_SETDEFAULT:
12381 42 : fk_trigger->deferrable = false;
12382 42 : fk_trigger->initdeferred = false;
12383 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12384 42 : break;
12385 0 : default:
12386 0 : elog(ERROR, "unrecognized FK action type: %d",
12387 : (int) fkconstraint->fk_upd_action);
12388 : break;
12389 : }
12390 :
12391 2560 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12392 : RelationGetRelid(rel),
12393 : constraintOid, indexOid, InvalidOid,
12394 : parentUpdTrigger, NULL, true, false);
12395 2560 : if (updateTrigOid)
12396 2494 : *updateTrigOid = trigAddress.objectId;
12397 2560 : }
12398 :
12399 : /*
12400 : * createForeignKeyCheckTriggers
12401 : * Create the referencing-side "check" triggers that implement a foreign
12402 : * key.
12403 : *
12404 : * Returns the OIDs of the so created triggers in *insertTrigOid and
12405 : * *updateTrigOid.
12406 : */
12407 : static void
12408 2544 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12409 : Constraint *fkconstraint, Oid constraintOid,
12410 : Oid indexOid,
12411 : Oid parentInsTrigger, Oid parentUpdTrigger,
12412 : Oid *insertTrigOid, Oid *updateTrigOid)
12413 : {
12414 2544 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12415 : constraintOid, indexOid,
12416 : parentInsTrigger, true);
12417 2544 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12418 : constraintOid, indexOid,
12419 : parentUpdTrigger, false);
12420 2544 : }
12421 :
12422 : /*
12423 : * ALTER TABLE DROP CONSTRAINT
12424 : *
12425 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12426 : */
12427 : static void
12428 698 : ATExecDropConstraint(Relation rel, const char *constrName,
12429 : DropBehavior behavior,
12430 : bool recurse, bool recursing,
12431 : bool missing_ok, LOCKMODE lockmode)
12432 : {
12433 : List *children;
12434 : Relation conrel;
12435 : Form_pg_constraint con;
12436 : SysScanDesc scan;
12437 : ScanKeyData skey[3];
12438 : HeapTuple tuple;
12439 698 : bool found = false;
12440 698 : bool is_no_inherit_constraint = false;
12441 : char contype;
12442 :
12443 : /* At top level, permission check was done in ATPrepCmd, else do it */
12444 698 : if (recursing)
12445 140 : ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12446 :
12447 698 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12448 :
12449 : /*
12450 : * Find and drop the target constraint
12451 : */
12452 698 : ScanKeyInit(&skey[0],
12453 : Anum_pg_constraint_conrelid,
12454 : BTEqualStrategyNumber, F_OIDEQ,
12455 : ObjectIdGetDatum(RelationGetRelid(rel)));
12456 698 : ScanKeyInit(&skey[1],
12457 : Anum_pg_constraint_contypid,
12458 : BTEqualStrategyNumber, F_OIDEQ,
12459 : ObjectIdGetDatum(InvalidOid));
12460 698 : ScanKeyInit(&skey[2],
12461 : Anum_pg_constraint_conname,
12462 : BTEqualStrategyNumber, F_NAMEEQ,
12463 : CStringGetDatum(constrName));
12464 698 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12465 : true, NULL, 3, skey);
12466 :
12467 : /* There can be at most one matching row */
12468 698 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12469 : {
12470 : ObjectAddress conobj;
12471 :
12472 668 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12473 :
12474 : /* Don't drop inherited constraints */
12475 668 : if (con->coninhcount > 0 && !recursing)
12476 120 : ereport(ERROR,
12477 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12478 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12479 : constrName, RelationGetRelationName(rel))));
12480 :
12481 548 : is_no_inherit_constraint = con->connoinherit;
12482 548 : contype = con->contype;
12483 :
12484 : /*
12485 : * If it's a foreign-key constraint, we'd better lock the referenced
12486 : * table and check that that's not in use, just as we've already done
12487 : * for the constrained table (else we might, eg, be dropping a trigger
12488 : * that has unfired events). But we can/must skip that in the
12489 : * self-referential case.
12490 : */
12491 548 : if (contype == CONSTRAINT_FOREIGN &&
12492 72 : con->confrelid != RelationGetRelid(rel))
12493 : {
12494 : Relation frel;
12495 :
12496 : /* Must match lock taken by RemoveTriggerById: */
12497 72 : frel = table_open(con->confrelid, AccessExclusiveLock);
12498 72 : CheckAlterTableIsSafe(frel);
12499 66 : table_close(frel, NoLock);
12500 : }
12501 :
12502 : /*
12503 : * Perform the actual constraint deletion
12504 : */
12505 542 : conobj.classId = ConstraintRelationId;
12506 542 : conobj.objectId = con->oid;
12507 542 : conobj.objectSubId = 0;
12508 :
12509 542 : performDeletion(&conobj, behavior, 0);
12510 :
12511 506 : found = true;
12512 : }
12513 :
12514 536 : systable_endscan(scan);
12515 :
12516 536 : if (!found)
12517 : {
12518 30 : if (!missing_ok)
12519 : {
12520 18 : ereport(ERROR,
12521 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12522 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12523 : constrName, RelationGetRelationName(rel))));
12524 : }
12525 : else
12526 : {
12527 12 : ereport(NOTICE,
12528 : (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12529 : constrName, RelationGetRelationName(rel))));
12530 12 : table_close(conrel, RowExclusiveLock);
12531 54 : return;
12532 : }
12533 : }
12534 :
12535 : /*
12536 : * For partitioned tables, non-CHECK inherited constraints are dropped via
12537 : * the dependency mechanism, so we're done here.
12538 : */
12539 506 : if (contype != CONSTRAINT_CHECK &&
12540 182 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12541 : {
12542 42 : table_close(conrel, RowExclusiveLock);
12543 42 : return;
12544 : }
12545 :
12546 : /*
12547 : * Propagate to children as appropriate. Unlike most other ALTER
12548 : * routines, we have to do this one level of recursion at a time; we can't
12549 : * use find_all_inheritors to do it in one pass.
12550 : */
12551 464 : if (!is_no_inherit_constraint)
12552 330 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
12553 : else
12554 134 : children = NIL;
12555 :
12556 : /*
12557 : * For a partitioned table, if partitions exist and we are told not to
12558 : * recurse, it's a user error. It doesn't make sense to have a constraint
12559 : * be defined only on the parent, especially if it's a partitioned table.
12560 : */
12561 464 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
12562 62 : children != NIL && !recurse)
12563 6 : ereport(ERROR,
12564 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12565 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12566 : errhint("Do not specify the ONLY keyword.")));
12567 :
12568 1116 : foreach_oid(childrelid, children)
12569 : {
12570 : Relation childrel;
12571 : HeapTuple copy_tuple;
12572 :
12573 : /* find_inheritance_children already got lock */
12574 200 : childrel = table_open(childrelid, NoLock);
12575 200 : CheckAlterTableIsSafe(childrel);
12576 :
12577 200 : ScanKeyInit(&skey[0],
12578 : Anum_pg_constraint_conrelid,
12579 : BTEqualStrategyNumber, F_OIDEQ,
12580 : ObjectIdGetDatum(childrelid));
12581 200 : ScanKeyInit(&skey[1],
12582 : Anum_pg_constraint_contypid,
12583 : BTEqualStrategyNumber, F_OIDEQ,
12584 : ObjectIdGetDatum(InvalidOid));
12585 200 : ScanKeyInit(&skey[2],
12586 : Anum_pg_constraint_conname,
12587 : BTEqualStrategyNumber, F_NAMEEQ,
12588 : CStringGetDatum(constrName));
12589 200 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12590 : true, NULL, 3, skey);
12591 :
12592 : /* There can be at most one matching row */
12593 200 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12594 0 : ereport(ERROR,
12595 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12596 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12597 : constrName,
12598 : RelationGetRelationName(childrel))));
12599 :
12600 200 : copy_tuple = heap_copytuple(tuple);
12601 :
12602 200 : systable_endscan(scan);
12603 :
12604 200 : con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
12605 :
12606 : /* Right now only CHECK constraints can be inherited */
12607 200 : if (con->contype != CONSTRAINT_CHECK)
12608 0 : elog(ERROR, "inherited constraint is not a CHECK constraint");
12609 :
12610 200 : if (con->coninhcount <= 0) /* shouldn't happen */
12611 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
12612 : childrelid, constrName);
12613 :
12614 200 : if (recurse)
12615 : {
12616 : /*
12617 : * If the child constraint has other definition sources, just
12618 : * decrement its inheritance count; if not, recurse to delete it.
12619 : */
12620 194 : if (con->coninhcount == 1 && !con->conislocal)
12621 : {
12622 : /* Time to delete this child constraint, too */
12623 140 : ATExecDropConstraint(childrel, constrName, behavior,
12624 : true, true,
12625 : false, lockmode);
12626 : }
12627 : else
12628 : {
12629 : /* Child constraint must survive my deletion */
12630 54 : con->coninhcount--;
12631 54 : CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
12632 :
12633 : /* Make update visible */
12634 54 : CommandCounterIncrement();
12635 : }
12636 : }
12637 : else
12638 : {
12639 : /*
12640 : * If we were told to drop ONLY in this table (no recursion), we
12641 : * need to mark the inheritors' constraints as locally defined
12642 : * rather than inherited.
12643 : */
12644 6 : con->coninhcount--;
12645 6 : con->conislocal = true;
12646 :
12647 6 : CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
12648 :
12649 : /* Make update visible */
12650 6 : CommandCounterIncrement();
12651 : }
12652 :
12653 200 : heap_freetuple(copy_tuple);
12654 :
12655 200 : table_close(childrel, NoLock);
12656 : }
12657 :
12658 458 : table_close(conrel, RowExclusiveLock);
12659 : }
12660 :
12661 : /*
12662 : * ALTER COLUMN TYPE
12663 : *
12664 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
12665 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
12666 : * transformed (and must be, because we rely on some transformed fields).
12667 : *
12668 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
12669 : * table will be done "in parallel" during phase 3, so all the USING
12670 : * expressions should be parsed assuming the original column types. Also,
12671 : * this allows a USING expression to refer to a field that will be dropped.
12672 : *
12673 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
12674 : * the first two execution steps in phase 2; they must not see the effects
12675 : * of any other subcommand types, since the USING expressions are parsed
12676 : * against the unmodified table's state.
12677 : */
12678 : static void
12679 1122 : ATPrepAlterColumnType(List **wqueue,
12680 : AlteredTableInfo *tab, Relation rel,
12681 : bool recurse, bool recursing,
12682 : AlterTableCmd *cmd, LOCKMODE lockmode,
12683 : AlterTableUtilityContext *context)
12684 : {
12685 1122 : char *colName = cmd->name;
12686 1122 : ColumnDef *def = (ColumnDef *) cmd->def;
12687 1122 : TypeName *typeName = def->typeName;
12688 1122 : Node *transform = def->cooked_default;
12689 : HeapTuple tuple;
12690 : Form_pg_attribute attTup;
12691 : AttrNumber attnum;
12692 : Oid targettype;
12693 : int32 targettypmod;
12694 : Oid targetcollid;
12695 : NewColumnValue *newval;
12696 1122 : ParseState *pstate = make_parsestate(NULL);
12697 : AclResult aclresult;
12698 : bool is_expr;
12699 :
12700 1122 : if (rel->rd_rel->reloftype && !recursing)
12701 6 : ereport(ERROR,
12702 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12703 : errmsg("cannot alter column type of typed table")));
12704 :
12705 : /* lookup the attribute so we can check inheritance status */
12706 1116 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
12707 1116 : if (!HeapTupleIsValid(tuple))
12708 0 : ereport(ERROR,
12709 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12710 : errmsg("column \"%s\" of relation \"%s\" does not exist",
12711 : colName, RelationGetRelationName(rel))));
12712 1116 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
12713 1116 : attnum = attTup->attnum;
12714 :
12715 : /* Can't alter a system attribute */
12716 1116 : if (attnum <= 0)
12717 0 : ereport(ERROR,
12718 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12719 : errmsg("cannot alter system column \"%s\"",
12720 : colName)));
12721 :
12722 : /*
12723 : * Don't alter inherited columns. At outer level, there had better not be
12724 : * any inherited definition; when recursing, we assume this was checked at
12725 : * the parent level (see below).
12726 : */
12727 1116 : if (attTup->attinhcount > 0 && !recursing)
12728 6 : ereport(ERROR,
12729 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12730 : errmsg("cannot alter inherited column \"%s\"",
12731 : colName)));
12732 :
12733 : /* Don't alter columns used in the partition key */
12734 1110 : if (has_partition_attrs(rel,
12735 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
12736 : &is_expr))
12737 18 : ereport(ERROR,
12738 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12739 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12740 : colName, RelationGetRelationName(rel))));
12741 :
12742 : /* Look up the target type */
12743 1092 : typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
12744 :
12745 1092 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
12746 1092 : if (aclresult != ACLCHECK_OK)
12747 12 : aclcheck_error_type(aclresult, targettype);
12748 :
12749 : /* And the collation */
12750 1080 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
12751 :
12752 : /* make sure datatype is legal for a column */
12753 1080 : CheckAttributeType(colName, targettype, targetcollid,
12754 1080 : list_make1_oid(rel->rd_rel->reltype),
12755 : 0);
12756 :
12757 1074 : if (tab->relkind == RELKIND_RELATION ||
12758 196 : tab->relkind == RELKIND_PARTITIONED_TABLE)
12759 : {
12760 : /*
12761 : * Set up an expression to transform the old data value to the new
12762 : * type. If a USING option was given, use the expression as
12763 : * transformed by transformAlterTableStmt, else just take the old
12764 : * value and try to coerce it. We do this first so that type
12765 : * incompatibility can be detected before we waste effort, and because
12766 : * we need the expression to be parsed against the original table row
12767 : * type.
12768 : */
12769 938 : if (!transform)
12770 : {
12771 734 : transform = (Node *) makeVar(1, attnum,
12772 : attTup->atttypid, attTup->atttypmod,
12773 : attTup->attcollation,
12774 : 0);
12775 : }
12776 :
12777 938 : transform = coerce_to_target_type(pstate,
12778 : transform, exprType(transform),
12779 : targettype, targettypmod,
12780 : COERCION_ASSIGNMENT,
12781 : COERCE_IMPLICIT_CAST,
12782 : -1);
12783 938 : if (transform == NULL)
12784 : {
12785 : /* error text depends on whether USING was specified or not */
12786 24 : if (def->cooked_default != NULL)
12787 6 : ereport(ERROR,
12788 : (errcode(ERRCODE_DATATYPE_MISMATCH),
12789 : errmsg("result of USING clause for column \"%s\""
12790 : " cannot be cast automatically to type %s",
12791 : colName, format_type_be(targettype)),
12792 : errhint("You might need to add an explicit cast.")));
12793 : else
12794 18 : ereport(ERROR,
12795 : (errcode(ERRCODE_DATATYPE_MISMATCH),
12796 : errmsg("column \"%s\" cannot be cast automatically to type %s",
12797 : colName, format_type_be(targettype)),
12798 : /* translator: USING is SQL, don't translate it */
12799 : errhint("You might need to specify \"USING %s::%s\".",
12800 : quote_identifier(colName),
12801 : format_type_with_typemod(targettype,
12802 : targettypmod))));
12803 : }
12804 :
12805 : /* Fix collations after all else */
12806 914 : assign_expr_collations(pstate, transform);
12807 :
12808 : /* Plan the expr now so we can accurately assess the need to rewrite. */
12809 914 : transform = (Node *) expression_planner((Expr *) transform);
12810 :
12811 : /*
12812 : * Add a work queue item to make ATRewriteTable update the column
12813 : * contents.
12814 : */
12815 914 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
12816 914 : newval->attnum = attnum;
12817 914 : newval->expr = (Expr *) transform;
12818 914 : newval->is_generated = false;
12819 :
12820 914 : tab->newvals = lappend(tab->newvals, newval);
12821 914 : if (ATColumnChangeRequiresRewrite(transform, attnum))
12822 732 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
12823 : }
12824 136 : else if (transform)
12825 12 : ereport(ERROR,
12826 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12827 : errmsg("\"%s\" is not a table",
12828 : RelationGetRelationName(rel))));
12829 :
12830 1038 : if (!RELKIND_HAS_STORAGE(tab->relkind))
12831 : {
12832 : /*
12833 : * For relations without storage, do this check now. Regular tables
12834 : * will check it later when the table is being rewritten.
12835 : */
12836 184 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
12837 : }
12838 :
12839 1008 : ReleaseSysCache(tuple);
12840 :
12841 : /*
12842 : * Recurse manually by queueing a new command for each child, if
12843 : * necessary. We cannot apply ATSimpleRecursion here because we need to
12844 : * remap attribute numbers in the USING expression, if any.
12845 : *
12846 : * If we are told not to recurse, there had better not be any child
12847 : * tables; else the alter would put them out of step.
12848 : */
12849 1008 : if (recurse)
12850 : {
12851 756 : Oid relid = RelationGetRelid(rel);
12852 : List *child_oids,
12853 : *child_numparents;
12854 : ListCell *lo,
12855 : *li;
12856 :
12857 756 : child_oids = find_all_inheritors(relid, lockmode,
12858 : &child_numparents);
12859 :
12860 : /*
12861 : * find_all_inheritors does the recursive search of the inheritance
12862 : * hierarchy, so all we have to do is process all of the relids in the
12863 : * list that it returns.
12864 : */
12865 1714 : forboth(lo, child_oids, li, child_numparents)
12866 : {
12867 982 : Oid childrelid = lfirst_oid(lo);
12868 982 : int numparents = lfirst_int(li);
12869 : Relation childrel;
12870 : HeapTuple childtuple;
12871 : Form_pg_attribute childattTup;
12872 :
12873 982 : if (childrelid == relid)
12874 756 : continue;
12875 :
12876 : /* find_all_inheritors already got lock */
12877 226 : childrel = relation_open(childrelid, NoLock);
12878 226 : CheckAlterTableIsSafe(childrel);
12879 :
12880 : /*
12881 : * Verify that the child doesn't have any inherited definitions of
12882 : * this column that came from outside this inheritance hierarchy.
12883 : * (renameatt makes a similar test, though in a different way
12884 : * because of its different recursion mechanism.)
12885 : */
12886 226 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
12887 : colName);
12888 226 : if (!HeapTupleIsValid(childtuple))
12889 0 : ereport(ERROR,
12890 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12891 : errmsg("column \"%s\" of relation \"%s\" does not exist",
12892 : colName, RelationGetRelationName(childrel))));
12893 226 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
12894 :
12895 226 : if (childattTup->attinhcount > numparents)
12896 6 : ereport(ERROR,
12897 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12898 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
12899 : colName, RelationGetRelationName(childrel))));
12900 :
12901 220 : ReleaseSysCache(childtuple);
12902 :
12903 : /*
12904 : * Remap the attribute numbers. If no USING expression was
12905 : * specified, there is no need for this step.
12906 : */
12907 220 : if (def->cooked_default)
12908 : {
12909 : AttrMap *attmap;
12910 : bool found_whole_row;
12911 :
12912 : /* create a copy to scribble on */
12913 72 : cmd = copyObject(cmd);
12914 :
12915 72 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
12916 : RelationGetDescr(rel),
12917 : false);
12918 144 : ((ColumnDef *) cmd->def)->cooked_default =
12919 72 : map_variable_attnos(def->cooked_default,
12920 : 1, 0,
12921 : attmap,
12922 : InvalidOid, &found_whole_row);
12923 72 : if (found_whole_row)
12924 6 : ereport(ERROR,
12925 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12926 : errmsg("cannot convert whole-row table reference"),
12927 : errdetail("USING expression contains a whole-row table reference.")));
12928 66 : pfree(attmap);
12929 : }
12930 214 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
12931 202 : relation_close(childrel, NoLock);
12932 : }
12933 : }
12934 302 : else if (!recursing &&
12935 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
12936 0 : ereport(ERROR,
12937 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12938 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
12939 : colName)));
12940 :
12941 984 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
12942 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
12943 978 : }
12944 :
12945 : /*
12946 : * When the data type of a column is changed, a rewrite might not be required
12947 : * if the new type is sufficiently identical to the old one, and the USING
12948 : * clause isn't trying to insert some other value. It's safe to skip the
12949 : * rewrite in these cases:
12950 : *
12951 : * - the old type is binary coercible to the new type
12952 : * - the new type is an unconstrained domain over the old type
12953 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
12954 : *
12955 : * In the case of a constrained domain, we could get by with scanning the
12956 : * table and checking the constraint rather than actually rewriting it, but we
12957 : * don't currently try to do that.
12958 : */
12959 : static bool
12960 1018 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
12961 : {
12962 : Assert(expr != NULL);
12963 :
12964 : for (;;)
12965 : {
12966 : /* only one varno, so no need to check that */
12967 1018 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
12968 182 : return false;
12969 836 : else if (IsA(expr, RelabelType))
12970 92 : expr = (Node *) ((RelabelType *) expr)->arg;
12971 744 : else if (IsA(expr, CoerceToDomain))
12972 : {
12973 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
12974 :
12975 0 : if (DomainHasConstraints(d->resulttype))
12976 0 : return true;
12977 0 : expr = (Node *) d->arg;
12978 : }
12979 744 : else if (IsA(expr, FuncExpr))
12980 : {
12981 544 : FuncExpr *f = (FuncExpr *) expr;
12982 :
12983 544 : switch (f->funcid)
12984 : {
12985 18 : case F_TIMESTAMPTZ_TIMESTAMP:
12986 : case F_TIMESTAMP_TIMESTAMPTZ:
12987 18 : if (TimestampTimestampTzRequiresRewrite())
12988 6 : return true;
12989 : else
12990 12 : expr = linitial(f->args);
12991 12 : break;
12992 526 : default:
12993 526 : return true;
12994 : }
12995 : }
12996 : else
12997 200 : return true;
12998 : }
12999 : }
13000 :
13001 : /*
13002 : * ALTER COLUMN .. SET DATA TYPE
13003 : *
13004 : * Return the address of the modified column.
13005 : */
13006 : static ObjectAddress
13007 948 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13008 : AlterTableCmd *cmd, LOCKMODE lockmode)
13009 : {
13010 948 : char *colName = cmd->name;
13011 948 : ColumnDef *def = (ColumnDef *) cmd->def;
13012 948 : TypeName *typeName = def->typeName;
13013 : HeapTuple heapTup;
13014 : Form_pg_attribute attTup,
13015 : attOldTup;
13016 : AttrNumber attnum;
13017 : HeapTuple typeTuple;
13018 : Form_pg_type tform;
13019 : Oid targettype;
13020 : int32 targettypmod;
13021 : Oid targetcollid;
13022 : Node *defaultexpr;
13023 : Relation attrelation;
13024 : Relation depRel;
13025 : ScanKeyData key[3];
13026 : SysScanDesc scan;
13027 : HeapTuple depTup;
13028 : ObjectAddress address;
13029 :
13030 : /*
13031 : * Clear all the missing values if we're rewriting the table, since this
13032 : * renders them pointless.
13033 : */
13034 948 : if (tab->rewrite)
13035 : {
13036 : Relation newrel;
13037 :
13038 678 : newrel = table_open(RelationGetRelid(rel), NoLock);
13039 678 : RelationClearMissing(newrel);
13040 678 : relation_close(newrel, NoLock);
13041 : /* make sure we don't conflict with later attribute modifications */
13042 678 : CommandCounterIncrement();
13043 : }
13044 :
13045 948 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13046 :
13047 : /* Look up the target column */
13048 948 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13049 948 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13050 0 : ereport(ERROR,
13051 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13052 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13053 : colName, RelationGetRelationName(rel))));
13054 948 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13055 948 : attnum = attTup->attnum;
13056 948 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13057 :
13058 : /* Check for multiple ALTER TYPE on same column --- can't cope */
13059 948 : if (attTup->atttypid != attOldTup->atttypid ||
13060 948 : attTup->atttypmod != attOldTup->atttypmod)
13061 0 : ereport(ERROR,
13062 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13063 : errmsg("cannot alter type of column \"%s\" twice",
13064 : colName)));
13065 :
13066 : /* Look up the target type (should not fail, since prep found it) */
13067 948 : typeTuple = typenameType(NULL, typeName, &targettypmod);
13068 948 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
13069 948 : targettype = tform->oid;
13070 : /* And the collation */
13071 948 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13072 :
13073 : /*
13074 : * If there is a default expression for the column, get it and ensure we
13075 : * can coerce it to the new datatype. (We must do this before changing
13076 : * the column type, because build_column_default itself will try to
13077 : * coerce, and will not issue the error message we want if it fails.)
13078 : *
13079 : * We remove any implicit coercion steps at the top level of the old
13080 : * default expression; this has been agreed to satisfy the principle of
13081 : * least surprise. (The conversion to the new column type should act like
13082 : * it started from what the user sees as the stored expression, and the
13083 : * implicit coercions aren't going to be shown.)
13084 : */
13085 948 : if (attTup->atthasdef)
13086 : {
13087 62 : defaultexpr = build_column_default(rel, attnum);
13088 : Assert(defaultexpr);
13089 62 : defaultexpr = strip_implicit_coercions(defaultexpr);
13090 62 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13091 : defaultexpr, exprType(defaultexpr),
13092 : targettype, targettypmod,
13093 : COERCION_ASSIGNMENT,
13094 : COERCE_IMPLICIT_CAST,
13095 : -1);
13096 62 : if (defaultexpr == NULL)
13097 : {
13098 12 : if (attTup->attgenerated)
13099 6 : ereport(ERROR,
13100 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13101 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13102 : colName, format_type_be(targettype))));
13103 : else
13104 6 : ereport(ERROR,
13105 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13106 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13107 : colName, format_type_be(targettype))));
13108 : }
13109 : }
13110 : else
13111 886 : defaultexpr = NULL;
13112 :
13113 : /*
13114 : * Find everything that depends on the column (constraints, indexes, etc),
13115 : * and record enough information to let us recreate the objects.
13116 : *
13117 : * The actual recreation does not happen here, but only after we have
13118 : * performed all the individual ALTER TYPE operations. We have to save
13119 : * the info before executing ALTER TYPE, though, else the deparser will
13120 : * get confused.
13121 : */
13122 936 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13123 :
13124 : /*
13125 : * Now scan for dependencies of this column on other things. The only
13126 : * things we should find are the dependency on the column datatype and
13127 : * possibly a collation dependency. Those can be removed.
13128 : */
13129 912 : depRel = table_open(DependRelationId, RowExclusiveLock);
13130 :
13131 912 : ScanKeyInit(&key[0],
13132 : Anum_pg_depend_classid,
13133 : BTEqualStrategyNumber, F_OIDEQ,
13134 : ObjectIdGetDatum(RelationRelationId));
13135 912 : ScanKeyInit(&key[1],
13136 : Anum_pg_depend_objid,
13137 : BTEqualStrategyNumber, F_OIDEQ,
13138 : ObjectIdGetDatum(RelationGetRelid(rel)));
13139 912 : ScanKeyInit(&key[2],
13140 : Anum_pg_depend_objsubid,
13141 : BTEqualStrategyNumber, F_INT4EQ,
13142 : Int32GetDatum((int32) attnum));
13143 :
13144 912 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
13145 : NULL, 3, key);
13146 :
13147 916 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13148 : {
13149 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13150 : ObjectAddress foundObject;
13151 :
13152 4 : foundObject.classId = foundDep->refclassid;
13153 4 : foundObject.objectId = foundDep->refobjid;
13154 4 : foundObject.objectSubId = foundDep->refobjsubid;
13155 :
13156 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
13157 0 : elog(ERROR, "found unexpected dependency type '%c'",
13158 : foundDep->deptype);
13159 4 : if (!(foundDep->refclassid == TypeRelationId &&
13160 4 : foundDep->refobjid == attTup->atttypid) &&
13161 0 : !(foundDep->refclassid == CollationRelationId &&
13162 0 : foundDep->refobjid == attTup->attcollation))
13163 0 : elog(ERROR, "found unexpected dependency for column: %s",
13164 : getObjectDescription(&foundObject, false));
13165 :
13166 4 : CatalogTupleDelete(depRel, &depTup->t_self);
13167 : }
13168 :
13169 912 : systable_endscan(scan);
13170 :
13171 912 : table_close(depRel, RowExclusiveLock);
13172 :
13173 : /*
13174 : * Here we go --- change the recorded column type and collation. (Note
13175 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13176 : * fix up the missing value if any.
13177 : */
13178 912 : if (attTup->atthasmissing)
13179 : {
13180 : Datum missingval;
13181 : bool missingNull;
13182 :
13183 : /* if rewrite is true the missing value should already be cleared */
13184 : Assert(tab->rewrite == 0);
13185 :
13186 : /* Get the missing value datum */
13187 6 : missingval = heap_getattr(heapTup,
13188 : Anum_pg_attribute_attmissingval,
13189 : attrelation->rd_att,
13190 : &missingNull);
13191 :
13192 : /* if it's a null array there is nothing to do */
13193 :
13194 6 : if (!missingNull)
13195 : {
13196 : /*
13197 : * Get the datum out of the array and repack it in a new array
13198 : * built with the new type data. We assume that since the table
13199 : * doesn't need rewriting, the actual Datum doesn't need to be
13200 : * changed, only the array metadata.
13201 : */
13202 :
13203 6 : int one = 1;
13204 : bool isNull;
13205 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
13206 6 : bool nullsAtt[Natts_pg_attribute] = {0};
13207 6 : bool replacesAtt[Natts_pg_attribute] = {0};
13208 : HeapTuple newTup;
13209 :
13210 12 : missingval = array_get_element(missingval,
13211 : 1,
13212 : &one,
13213 : 0,
13214 6 : attTup->attlen,
13215 6 : attTup->attbyval,
13216 6 : attTup->attalign,
13217 : &isNull);
13218 6 : missingval = PointerGetDatum(construct_array(&missingval,
13219 : 1,
13220 : targettype,
13221 6 : tform->typlen,
13222 6 : tform->typbyval,
13223 6 : tform->typalign));
13224 :
13225 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13226 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13227 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13228 :
13229 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13230 : valuesAtt, nullsAtt, replacesAtt);
13231 6 : heap_freetuple(heapTup);
13232 6 : heapTup = newTup;
13233 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13234 : }
13235 : }
13236 :
13237 912 : attTup->atttypid = targettype;
13238 912 : attTup->atttypmod = targettypmod;
13239 912 : attTup->attcollation = targetcollid;
13240 912 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13241 0 : ereport(ERROR,
13242 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13243 : errmsg("too many array dimensions"));
13244 912 : attTup->attndims = list_length(typeName->arrayBounds);
13245 912 : attTup->attlen = tform->typlen;
13246 912 : attTup->attbyval = tform->typbyval;
13247 912 : attTup->attalign = tform->typalign;
13248 912 : attTup->attstorage = tform->typstorage;
13249 912 : attTup->attcompression = InvalidCompressionMethod;
13250 :
13251 912 : ReleaseSysCache(typeTuple);
13252 :
13253 912 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13254 :
13255 912 : table_close(attrelation, RowExclusiveLock);
13256 :
13257 : /* Install dependencies on new datatype and collation */
13258 912 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13259 912 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13260 :
13261 : /*
13262 : * Drop any pg_statistic entry for the column, since it's now wrong type
13263 : */
13264 912 : RemoveStatistics(RelationGetRelid(rel), attnum);
13265 :
13266 912 : InvokeObjectPostAlterHook(RelationRelationId,
13267 : RelationGetRelid(rel), attnum);
13268 :
13269 : /*
13270 : * Update the default, if present, by brute force --- remove and re-add
13271 : * the default. Probably unsafe to take shortcuts, since the new version
13272 : * may well have additional dependencies. (It's okay to do this now,
13273 : * rather than after other ALTER TYPE commands, since the default won't
13274 : * depend on other column types.)
13275 : */
13276 912 : if (defaultexpr)
13277 : {
13278 : /*
13279 : * If it's a GENERATED default, drop its dependency records, in
13280 : * particular its INTERNAL dependency on the column, which would
13281 : * otherwise cause dependency.c to refuse to perform the deletion.
13282 : */
13283 50 : if (attTup->attgenerated)
13284 : {
13285 6 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13286 :
13287 6 : if (!OidIsValid(attrdefoid))
13288 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13289 : RelationGetRelid(rel), attnum);
13290 6 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13291 : }
13292 :
13293 : /*
13294 : * Make updates-so-far visible, particularly the new pg_attribute row
13295 : * which will be updated again.
13296 : */
13297 50 : CommandCounterIncrement();
13298 :
13299 : /*
13300 : * We use RESTRICT here for safety, but at present we do not expect
13301 : * anything to depend on the default.
13302 : */
13303 50 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13304 : true);
13305 :
13306 50 : StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13307 : }
13308 :
13309 912 : ObjectAddressSubSet(address, RelationRelationId,
13310 : RelationGetRelid(rel), attnum);
13311 :
13312 : /* Cleanup */
13313 912 : heap_freetuple(heapTup);
13314 :
13315 912 : return address;
13316 : }
13317 :
13318 : /*
13319 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13320 : * that depends on the column (constraints, indexes, etc), and record enough
13321 : * information to let us recreate the objects.
13322 : */
13323 : static void
13324 1014 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
13325 : Relation rel, AttrNumber attnum, const char *colName)
13326 : {
13327 : Relation depRel;
13328 : ScanKeyData key[3];
13329 : SysScanDesc scan;
13330 : HeapTuple depTup;
13331 :
13332 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13333 :
13334 1014 : depRel = table_open(DependRelationId, RowExclusiveLock);
13335 :
13336 1014 : ScanKeyInit(&key[0],
13337 : Anum_pg_depend_refclassid,
13338 : BTEqualStrategyNumber, F_OIDEQ,
13339 : ObjectIdGetDatum(RelationRelationId));
13340 1014 : ScanKeyInit(&key[1],
13341 : Anum_pg_depend_refobjid,
13342 : BTEqualStrategyNumber, F_OIDEQ,
13343 : ObjectIdGetDatum(RelationGetRelid(rel)));
13344 1014 : ScanKeyInit(&key[2],
13345 : Anum_pg_depend_refobjsubid,
13346 : BTEqualStrategyNumber, F_INT4EQ,
13347 : Int32GetDatum((int32) attnum));
13348 :
13349 1014 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13350 : NULL, 3, key);
13351 :
13352 1878 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13353 : {
13354 888 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13355 : ObjectAddress foundObject;
13356 :
13357 888 : foundObject.classId = foundDep->classid;
13358 888 : foundObject.objectId = foundDep->objid;
13359 888 : foundObject.objectSubId = foundDep->objsubid;
13360 :
13361 888 : switch (foundObject.classId)
13362 : {
13363 270 : case RelationRelationId:
13364 : {
13365 270 : char relKind = get_rel_relkind(foundObject.objectId);
13366 :
13367 270 : if (relKind == RELKIND_INDEX ||
13368 : relKind == RELKIND_PARTITIONED_INDEX)
13369 : {
13370 : Assert(foundObject.objectSubId == 0);
13371 232 : RememberIndexForRebuilding(foundObject.objectId, tab);
13372 : }
13373 38 : else if (relKind == RELKIND_SEQUENCE)
13374 : {
13375 : /*
13376 : * This must be a SERIAL column's sequence. We need
13377 : * not do anything to it.
13378 : */
13379 : Assert(foundObject.objectSubId == 0);
13380 : }
13381 : else
13382 : {
13383 : /* Not expecting any other direct dependencies... */
13384 0 : elog(ERROR, "unexpected object depending on column: %s",
13385 : getObjectDescription(&foundObject, false));
13386 : }
13387 270 : break;
13388 : }
13389 :
13390 452 : case ConstraintRelationId:
13391 : Assert(foundObject.objectSubId == 0);
13392 452 : RememberConstraintForRebuilding(foundObject.objectId, tab);
13393 452 : break;
13394 :
13395 0 : case ProcedureRelationId:
13396 :
13397 : /*
13398 : * A new-style SQL function can depend on a column, if that
13399 : * column is referenced in the parsed function body. Ideally
13400 : * we'd automatically update the function by deparsing and
13401 : * reparsing it, but that's risky and might well fail anyhow.
13402 : * FIXME someday.
13403 : *
13404 : * This is only a problem for AT_AlterColumnType, not
13405 : * AT_SetExpression.
13406 : */
13407 0 : if (subtype == AT_AlterColumnType)
13408 0 : ereport(ERROR,
13409 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13410 : errmsg("cannot alter type of a column used by a function or procedure"),
13411 : errdetail("%s depends on column \"%s\"",
13412 : getObjectDescription(&foundObject, false),
13413 : colName)));
13414 0 : break;
13415 :
13416 12 : case RewriteRelationId:
13417 :
13418 : /*
13419 : * View/rule bodies have pretty much the same issues as
13420 : * function bodies. FIXME someday.
13421 : */
13422 12 : if (subtype == AT_AlterColumnType)
13423 12 : ereport(ERROR,
13424 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13425 : errmsg("cannot alter type of a column used by a view or rule"),
13426 : errdetail("%s depends on column \"%s\"",
13427 : getObjectDescription(&foundObject, false),
13428 : colName)));
13429 0 : break;
13430 :
13431 0 : case TriggerRelationId:
13432 :
13433 : /*
13434 : * A trigger can depend on a column because the column is
13435 : * specified as an update target, or because the column is
13436 : * used in the trigger's WHEN condition. The first case would
13437 : * not require any extra work, but the second case would
13438 : * require updating the WHEN expression, which has the same
13439 : * issues as above. Since we can't easily tell which case
13440 : * applies, we punt for both. FIXME someday.
13441 : */
13442 0 : if (subtype == AT_AlterColumnType)
13443 0 : ereport(ERROR,
13444 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13445 : errmsg("cannot alter type of a column used in a trigger definition"),
13446 : errdetail("%s depends on column \"%s\"",
13447 : getObjectDescription(&foundObject, false),
13448 : colName)));
13449 0 : break;
13450 :
13451 0 : case PolicyRelationId:
13452 :
13453 : /*
13454 : * A policy can depend on a column because the column is
13455 : * specified in the policy's USING or WITH CHECK qual
13456 : * expressions. It might be possible to rewrite and recheck
13457 : * the policy expression, but punt for now. It's certainly
13458 : * easy enough to remove and recreate the policy; still, FIXME
13459 : * someday.
13460 : */
13461 0 : if (subtype == AT_AlterColumnType)
13462 0 : ereport(ERROR,
13463 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13464 : errmsg("cannot alter type of a column used in a policy definition"),
13465 : errdetail("%s depends on column \"%s\"",
13466 : getObjectDescription(&foundObject, false),
13467 : colName)));
13468 0 : break;
13469 :
13470 140 : case AttrDefaultRelationId:
13471 : {
13472 140 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
13473 :
13474 140 : if (col.objectId == RelationGetRelid(rel) &&
13475 140 : col.objectSubId == attnum)
13476 : {
13477 : /*
13478 : * Ignore the column's own default expression. The
13479 : * caller deals with it.
13480 : */
13481 : }
13482 : else
13483 : {
13484 : /*
13485 : * This must be a reference from the expression of a
13486 : * generated column elsewhere in the same table.
13487 : * Changing the type/generated expression of a column
13488 : * that is used by a generated column is not allowed
13489 : * by SQL standard, so just punt for now. It might be
13490 : * doable with some thinking and effort.
13491 : */
13492 12 : if (subtype == AT_AlterColumnType)
13493 12 : ereport(ERROR,
13494 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13495 : errmsg("cannot alter type of a column used by a generated column"),
13496 : errdetail("Column \"%s\" is used by generated column \"%s\".",
13497 : colName,
13498 : get_attname(col.objectId,
13499 : col.objectSubId,
13500 : false))));
13501 : }
13502 128 : break;
13503 : }
13504 :
13505 14 : case StatisticExtRelationId:
13506 :
13507 : /*
13508 : * Give the extended-stats machinery a chance to fix anything
13509 : * that this column type change would break.
13510 : */
13511 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
13512 14 : break;
13513 :
13514 0 : case PublicationRelRelationId:
13515 :
13516 : /*
13517 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
13518 : * clause. Same issues as above. FIXME someday.
13519 : */
13520 0 : if (subtype == AT_AlterColumnType)
13521 0 : ereport(ERROR,
13522 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13523 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
13524 : errdetail("%s depends on column \"%s\"",
13525 : getObjectDescription(&foundObject, false),
13526 : colName)));
13527 0 : break;
13528 :
13529 0 : default:
13530 :
13531 : /*
13532 : * We don't expect any other sorts of objects to depend on a
13533 : * column.
13534 : */
13535 0 : elog(ERROR, "unexpected object depending on column: %s",
13536 : getObjectDescription(&foundObject, false));
13537 : break;
13538 : }
13539 : }
13540 :
13541 990 : systable_endscan(scan);
13542 990 : table_close(depRel, NoLock);
13543 990 : }
13544 :
13545 : /*
13546 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
13547 : * needs to be reset.
13548 : */
13549 : static void
13550 416 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
13551 : {
13552 416 : if (!get_index_isreplident(indoid))
13553 398 : return;
13554 :
13555 18 : if (tab->replicaIdentityIndex)
13556 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
13557 :
13558 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
13559 : }
13560 :
13561 : /*
13562 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
13563 : */
13564 : static void
13565 416 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
13566 : {
13567 416 : if (!get_index_isclustered(indoid))
13568 398 : return;
13569 :
13570 18 : if (tab->clusterOnIndex)
13571 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
13572 :
13573 18 : tab->clusterOnIndex = get_rel_name(indoid);
13574 : }
13575 :
13576 : /*
13577 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
13578 : * to be rebuilt (which we might already know).
13579 : */
13580 : static void
13581 464 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
13582 : {
13583 : /*
13584 : * This de-duplication check is critical for two independent reasons: we
13585 : * mustn't try to recreate the same constraint twice, and if a constraint
13586 : * depends on more than one column whose type is to be altered, we must
13587 : * capture its definition string before applying any of the column type
13588 : * changes. ruleutils.c will get confused if we ask again later.
13589 : */
13590 464 : if (!list_member_oid(tab->changedConstraintOids, conoid))
13591 : {
13592 : /* OK, capture the constraint's existing definition string */
13593 374 : char *defstring = pg_get_constraintdef_command(conoid);
13594 : Oid indoid;
13595 :
13596 374 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
13597 : conoid);
13598 374 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
13599 : defstring);
13600 :
13601 : /*
13602 : * For the index of a constraint, if any, remember if it is used for
13603 : * the table's replica identity or if it is a clustered index, so that
13604 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
13605 : * those properties.
13606 : */
13607 374 : indoid = get_constraint_index(conoid);
13608 374 : if (OidIsValid(indoid))
13609 : {
13610 204 : RememberReplicaIdentityForRebuilding(indoid, tab);
13611 204 : RememberClusterOnForRebuilding(indoid, tab);
13612 : }
13613 : }
13614 464 : }
13615 :
13616 : /*
13617 : * Subroutine for ATExecAlterColumnType: remember that an index needs
13618 : * to be rebuilt (which we might already know).
13619 : */
13620 : static void
13621 232 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
13622 : {
13623 : /*
13624 : * This de-duplication check is critical for two independent reasons: we
13625 : * mustn't try to recreate the same index twice, and if an index depends
13626 : * on more than one column whose type is to be altered, we must capture
13627 : * its definition string before applying any of the column type changes.
13628 : * ruleutils.c will get confused if we ask again later.
13629 : */
13630 232 : if (!list_member_oid(tab->changedIndexOids, indoid))
13631 : {
13632 : /*
13633 : * Before adding it as an index-to-rebuild, we'd better see if it
13634 : * belongs to a constraint, and if so rebuild the constraint instead.
13635 : * Typically this check fails, because constraint indexes normally
13636 : * have only dependencies on their constraint. But it's possible for
13637 : * such an index to also have direct dependencies on table columns,
13638 : * for example with a partial exclusion constraint.
13639 : */
13640 224 : Oid conoid = get_index_constraint(indoid);
13641 :
13642 224 : if (OidIsValid(conoid))
13643 : {
13644 12 : RememberConstraintForRebuilding(conoid, tab);
13645 : }
13646 : else
13647 : {
13648 : /* OK, capture the index's existing definition string */
13649 212 : char *defstring = pg_get_indexdef_string(indoid);
13650 :
13651 212 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
13652 : indoid);
13653 212 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
13654 : defstring);
13655 :
13656 : /*
13657 : * Remember if this index is used for the table's replica identity
13658 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13659 : * can queue up commands necessary to restore those properties.
13660 : */
13661 212 : RememberReplicaIdentityForRebuilding(indoid, tab);
13662 212 : RememberClusterOnForRebuilding(indoid, tab);
13663 : }
13664 : }
13665 232 : }
13666 :
13667 : /*
13668 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
13669 : * needs to be rebuilt (which we might already know).
13670 : */
13671 : static void
13672 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
13673 : {
13674 : /*
13675 : * This de-duplication check is critical for two independent reasons: we
13676 : * mustn't try to recreate the same statistics object twice, and if the
13677 : * statistics object depends on more than one column whose type is to be
13678 : * altered, we must capture its definition string before applying any of
13679 : * the type changes. ruleutils.c will get confused if we ask again later.
13680 : */
13681 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
13682 : {
13683 : /* OK, capture the statistics object's existing definition string */
13684 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
13685 :
13686 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
13687 : stxoid);
13688 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
13689 : defstring);
13690 : }
13691 14 : }
13692 :
13693 : /*
13694 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
13695 : * operations for a particular relation. We have to drop and recreate all the
13696 : * indexes and constraints that depend on the altered columns. We do the
13697 : * actual dropping here, but re-creation is managed by adding work queue
13698 : * entries to do those steps later.
13699 : */
13700 : static void
13701 960 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
13702 : {
13703 : ObjectAddress obj;
13704 : ObjectAddresses *objects;
13705 : ListCell *def_item;
13706 : ListCell *oid_item;
13707 :
13708 : /*
13709 : * Collect all the constraints and indexes to drop so we can process them
13710 : * in a single call. That way we don't have to worry about dependencies
13711 : * among them.
13712 : */
13713 960 : objects = new_object_addresses();
13714 :
13715 : /*
13716 : * Re-parse the index and constraint definitions, and attach them to the
13717 : * appropriate work queue entries. We do this before dropping because in
13718 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13719 : * lock on the table the constraint is attached to, and we need to get
13720 : * that before reparsing/dropping.
13721 : *
13722 : * We can't rely on the output of deparsing to tell us which relation to
13723 : * operate on, because concurrent activity might have made the name
13724 : * resolve differently. Instead, we've got to use the OID of the
13725 : * constraint or index we're processing to figure out which relation to
13726 : * operate on.
13727 : */
13728 1334 : forboth(oid_item, tab->changedConstraintOids,
13729 : def_item, tab->changedConstraintDefs)
13730 : {
13731 374 : Oid oldId = lfirst_oid(oid_item);
13732 : HeapTuple tup;
13733 : Form_pg_constraint con;
13734 : Oid relid;
13735 : Oid confrelid;
13736 : char contype;
13737 : bool conislocal;
13738 :
13739 374 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
13740 374 : if (!HeapTupleIsValid(tup)) /* should not happen */
13741 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
13742 374 : con = (Form_pg_constraint) GETSTRUCT(tup);
13743 374 : if (OidIsValid(con->conrelid))
13744 360 : relid = con->conrelid;
13745 : else
13746 : {
13747 : /* must be a domain constraint */
13748 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
13749 14 : if (!OidIsValid(relid))
13750 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
13751 : }
13752 374 : confrelid = con->confrelid;
13753 374 : contype = con->contype;
13754 374 : conislocal = con->conislocal;
13755 374 : ReleaseSysCache(tup);
13756 :
13757 374 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
13758 374 : add_exact_object_address(&obj, objects);
13759 :
13760 : /*
13761 : * If the constraint is inherited (only), we don't want to inject a
13762 : * new definition here; it'll get recreated when ATAddCheckConstraint
13763 : * recurses from adding the parent table's constraint. But we had to
13764 : * carry the info this far so that we can drop the constraint below.
13765 : */
13766 374 : if (!conislocal)
13767 12 : continue;
13768 :
13769 : /*
13770 : * When rebuilding an FK constraint that references the table we're
13771 : * modifying, we might not yet have any lock on the FK's table, so get
13772 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13773 : * step, so there's no value in asking for anything weaker.
13774 : */
13775 362 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
13776 36 : LockRelationOid(relid, AccessExclusiveLock);
13777 :
13778 362 : ATPostAlterTypeParse(oldId, relid, confrelid,
13779 362 : (char *) lfirst(def_item),
13780 362 : wqueue, lockmode, tab->rewrite);
13781 : }
13782 1172 : forboth(oid_item, tab->changedIndexOids,
13783 : def_item, tab->changedIndexDefs)
13784 : {
13785 212 : Oid oldId = lfirst_oid(oid_item);
13786 : Oid relid;
13787 :
13788 212 : relid = IndexGetRelation(oldId, false);
13789 212 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
13790 212 : (char *) lfirst(def_item),
13791 212 : wqueue, lockmode, tab->rewrite);
13792 :
13793 212 : ObjectAddressSet(obj, RelationRelationId, oldId);
13794 212 : add_exact_object_address(&obj, objects);
13795 : }
13796 :
13797 : /* add dependencies for new statistics */
13798 974 : forboth(oid_item, tab->changedStatisticsOids,
13799 : def_item, tab->changedStatisticsDefs)
13800 : {
13801 14 : Oid oldId = lfirst_oid(oid_item);
13802 : Oid relid;
13803 :
13804 14 : relid = StatisticsGetRelation(oldId, false);
13805 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
13806 14 : (char *) lfirst(def_item),
13807 14 : wqueue, lockmode, tab->rewrite);
13808 :
13809 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
13810 14 : add_exact_object_address(&obj, objects);
13811 : }
13812 :
13813 : /*
13814 : * Queue up command to restore replica identity index marking
13815 : */
13816 960 : if (tab->replicaIdentityIndex)
13817 : {
13818 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
13819 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
13820 :
13821 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
13822 18 : subcmd->name = tab->replicaIdentityIndex;
13823 18 : cmd->subtype = AT_ReplicaIdentity;
13824 18 : cmd->def = (Node *) subcmd;
13825 :
13826 : /* do it after indexes and constraints */
13827 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
13828 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13829 : }
13830 :
13831 : /*
13832 : * Queue up command to restore marking of index used for cluster.
13833 : */
13834 960 : if (tab->clusterOnIndex)
13835 : {
13836 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
13837 :
13838 18 : cmd->subtype = AT_ClusterOn;
13839 18 : cmd->name = tab->clusterOnIndex;
13840 :
13841 : /* do it after indexes and constraints */
13842 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
13843 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13844 : }
13845 :
13846 : /*
13847 : * It should be okay to use DROP_RESTRICT here, since nothing else should
13848 : * be depending on these objects.
13849 : */
13850 960 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
13851 :
13852 960 : free_object_addresses(objects);
13853 :
13854 : /*
13855 : * The objects will get recreated during subsequent passes over the work
13856 : * queue.
13857 : */
13858 960 : }
13859 :
13860 : /*
13861 : * Parse the previously-saved definition string for a constraint, index or
13862 : * statistics object against the newly-established column data type(s), and
13863 : * queue up the resulting command parsetrees for execution.
13864 : *
13865 : * This might fail if, for example, you have a WHERE clause that uses an
13866 : * operator that's not available for the new column type.
13867 : */
13868 : static void
13869 588 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
13870 : List **wqueue, LOCKMODE lockmode, bool rewrite)
13871 : {
13872 : List *raw_parsetree_list;
13873 : List *querytree_list;
13874 : ListCell *list_item;
13875 : Relation rel;
13876 :
13877 : /*
13878 : * We expect that we will get only ALTER TABLE and CREATE INDEX
13879 : * statements. Hence, there is no need to pass them through
13880 : * parse_analyze_*() or the rewriter, but instead we need to pass them
13881 : * through parse_utilcmd.c to make them ready for execution.
13882 : */
13883 588 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
13884 588 : querytree_list = NIL;
13885 1176 : foreach(list_item, raw_parsetree_list)
13886 : {
13887 588 : RawStmt *rs = lfirst_node(RawStmt, list_item);
13888 588 : Node *stmt = rs->stmt;
13889 :
13890 588 : if (IsA(stmt, IndexStmt))
13891 212 : querytree_list = lappend(querytree_list,
13892 212 : transformIndexStmt(oldRelId,
13893 : (IndexStmt *) stmt,
13894 : cmd));
13895 376 : else if (IsA(stmt, AlterTableStmt))
13896 : {
13897 : List *beforeStmts;
13898 : List *afterStmts;
13899 :
13900 348 : stmt = (Node *) transformAlterTableStmt(oldRelId,
13901 : (AlterTableStmt *) stmt,
13902 : cmd,
13903 : &beforeStmts,
13904 : &afterStmts);
13905 348 : querytree_list = list_concat(querytree_list, beforeStmts);
13906 348 : querytree_list = lappend(querytree_list, stmt);
13907 348 : querytree_list = list_concat(querytree_list, afterStmts);
13908 : }
13909 28 : else if (IsA(stmt, CreateStatsStmt))
13910 14 : querytree_list = lappend(querytree_list,
13911 14 : transformStatsStmt(oldRelId,
13912 : (CreateStatsStmt *) stmt,
13913 : cmd));
13914 : else
13915 14 : querytree_list = lappend(querytree_list, stmt);
13916 : }
13917 :
13918 : /* Caller should already have acquired whatever lock we need. */
13919 588 : rel = relation_open(oldRelId, NoLock);
13920 :
13921 : /*
13922 : * Attach each generated command to the proper place in the work queue.
13923 : * Note this could result in creation of entirely new work-queue entries.
13924 : *
13925 : * Also note that we have to tweak the command subtypes, because it turns
13926 : * out that re-creation of indexes and constraints has to act a bit
13927 : * differently from initial creation.
13928 : */
13929 1176 : foreach(list_item, querytree_list)
13930 : {
13931 588 : Node *stm = (Node *) lfirst(list_item);
13932 : AlteredTableInfo *tab;
13933 :
13934 588 : tab = ATGetQueueEntry(wqueue, rel);
13935 :
13936 588 : if (IsA(stm, IndexStmt))
13937 : {
13938 212 : IndexStmt *stmt = (IndexStmt *) stm;
13939 : AlterTableCmd *newcmd;
13940 :
13941 212 : if (!rewrite)
13942 54 : TryReuseIndex(oldId, stmt);
13943 212 : stmt->reset_default_tblspc = true;
13944 : /* keep the index's comment */
13945 212 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
13946 :
13947 212 : newcmd = makeNode(AlterTableCmd);
13948 212 : newcmd->subtype = AT_ReAddIndex;
13949 212 : newcmd->def = (Node *) stmt;
13950 212 : tab->subcmds[AT_PASS_OLD_INDEX] =
13951 212 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
13952 : }
13953 376 : else if (IsA(stm, AlterTableStmt))
13954 : {
13955 348 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
13956 : ListCell *lcmd;
13957 :
13958 816 : foreach(lcmd, stmt->cmds)
13959 : {
13960 468 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
13961 :
13962 468 : if (cmd->subtype == AT_AddIndex)
13963 : {
13964 : IndexStmt *indstmt;
13965 : Oid indoid;
13966 :
13967 204 : indstmt = castNode(IndexStmt, cmd->def);
13968 204 : indoid = get_constraint_index(oldId);
13969 :
13970 204 : if (!rewrite)
13971 48 : TryReuseIndex(indoid, indstmt);
13972 : /* keep any comment on the index */
13973 204 : indstmt->idxcomment = GetComment(indoid,
13974 : RelationRelationId, 0);
13975 204 : indstmt->reset_default_tblspc = true;
13976 :
13977 204 : cmd->subtype = AT_ReAddIndex;
13978 204 : tab->subcmds[AT_PASS_OLD_INDEX] =
13979 204 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
13980 :
13981 : /* recreate any comment on the constraint */
13982 204 : RebuildConstraintComment(tab,
13983 : AT_PASS_OLD_INDEX,
13984 : oldId,
13985 : rel,
13986 : NIL,
13987 204 : indstmt->idxname);
13988 : }
13989 264 : else if (cmd->subtype == AT_AddConstraint)
13990 : {
13991 144 : Constraint *con = castNode(Constraint, cmd->def);
13992 :
13993 144 : con->old_pktable_oid = refRelId;
13994 : /* rewriting neither side of a FK */
13995 144 : if (con->contype == CONSTR_FOREIGN &&
13996 72 : !rewrite && tab->rewrite == 0)
13997 6 : TryReuseForeignKey(oldId, con);
13998 144 : con->reset_default_tblspc = true;
13999 144 : cmd->subtype = AT_ReAddConstraint;
14000 144 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14001 144 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14002 :
14003 : /* recreate any comment on the constraint */
14004 144 : RebuildConstraintComment(tab,
14005 : AT_PASS_OLD_CONSTR,
14006 : oldId,
14007 : rel,
14008 : NIL,
14009 144 : con->conname);
14010 : }
14011 120 : else if (cmd->subtype == AT_SetNotNull)
14012 : {
14013 : /*
14014 : * The parser will create AT_SetNotNull subcommands for
14015 : * columns of PRIMARY KEY indexes/constraints, but we need
14016 : * not do anything with them here, because the columns'
14017 : * NOT NULL marks will already have been propagated into
14018 : * the new table definition.
14019 : */
14020 : }
14021 : else
14022 0 : elog(ERROR, "unexpected statement subtype: %d",
14023 : (int) cmd->subtype);
14024 : }
14025 : }
14026 28 : else if (IsA(stm, AlterDomainStmt))
14027 : {
14028 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14029 :
14030 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14031 : {
14032 14 : Constraint *con = castNode(Constraint, stmt->def);
14033 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14034 :
14035 14 : cmd->subtype = AT_ReAddDomainConstraint;
14036 14 : cmd->def = (Node *) stmt;
14037 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14038 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14039 :
14040 : /* recreate any comment on the constraint */
14041 14 : RebuildConstraintComment(tab,
14042 : AT_PASS_OLD_CONSTR,
14043 : oldId,
14044 : NULL,
14045 : stmt->typeName,
14046 14 : con->conname);
14047 : }
14048 : else
14049 0 : elog(ERROR, "unexpected statement subtype: %d",
14050 : (int) stmt->subtype);
14051 : }
14052 14 : else if (IsA(stm, CreateStatsStmt))
14053 : {
14054 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14055 : AlterTableCmd *newcmd;
14056 :
14057 : /* keep the statistics object's comment */
14058 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14059 :
14060 14 : newcmd = makeNode(AlterTableCmd);
14061 14 : newcmd->subtype = AT_ReAddStatistics;
14062 14 : newcmd->def = (Node *) stmt;
14063 14 : tab->subcmds[AT_PASS_MISC] =
14064 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14065 : }
14066 : else
14067 0 : elog(ERROR, "unexpected statement type: %d",
14068 : (int) nodeTag(stm));
14069 : }
14070 :
14071 588 : relation_close(rel, NoLock);
14072 588 : }
14073 :
14074 : /*
14075 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14076 : * for a table or domain constraint that is being rebuilt.
14077 : *
14078 : * objid is the OID of the constraint.
14079 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14080 : * as a string list) for a domain constraint.
14081 : * (We could dig that info, as well as the conname, out of the pg_constraint
14082 : * entry; but callers already have them so might as well pass them.)
14083 : */
14084 : static void
14085 362 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14086 : Relation rel, List *domname,
14087 : const char *conname)
14088 : {
14089 : CommentStmt *cmd;
14090 : char *comment_str;
14091 : AlterTableCmd *newcmd;
14092 :
14093 : /* Look for comment for object wanted, and leave if none */
14094 362 : comment_str = GetComment(objid, ConstraintRelationId, 0);
14095 362 : if (comment_str == NULL)
14096 296 : return;
14097 :
14098 : /* Build CommentStmt node, copying all input data for safety */
14099 66 : cmd = makeNode(CommentStmt);
14100 66 : if (rel)
14101 : {
14102 54 : cmd->objtype = OBJECT_TABCONSTRAINT;
14103 54 : cmd->object = (Node *)
14104 54 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14105 : makeString(pstrdup(RelationGetRelationName(rel))),
14106 : makeString(pstrdup(conname)));
14107 : }
14108 : else
14109 : {
14110 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
14111 12 : cmd->object = (Node *)
14112 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
14113 : makeString(pstrdup(conname)));
14114 : }
14115 66 : cmd->comment = comment_str;
14116 :
14117 : /* Append it to list of commands */
14118 66 : newcmd = makeNode(AlterTableCmd);
14119 66 : newcmd->subtype = AT_ReAddComment;
14120 66 : newcmd->def = (Node *) cmd;
14121 66 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14122 : }
14123 :
14124 : /*
14125 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14126 : * for the real analysis, then mutates the IndexStmt based on that verdict.
14127 : */
14128 : static void
14129 102 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
14130 : {
14131 102 : if (CheckIndexCompatible(oldId,
14132 102 : stmt->accessMethod,
14133 102 : stmt->indexParams,
14134 102 : stmt->excludeOpNames))
14135 : {
14136 102 : Relation irel = index_open(oldId, NoLock);
14137 :
14138 : /* If it's a partitioned index, there is no storage to share. */
14139 102 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14140 : {
14141 72 : stmt->oldNumber = irel->rd_locator.relNumber;
14142 72 : stmt->oldCreateSubid = irel->rd_createSubid;
14143 72 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14144 : }
14145 102 : index_close(irel, NoLock);
14146 : }
14147 102 : }
14148 :
14149 : /*
14150 : * Subroutine for ATPostAlterTypeParse().
14151 : *
14152 : * Stash the old P-F equality operator into the Constraint node, for possible
14153 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14154 : * this constraint can be skipped.
14155 : */
14156 : static void
14157 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
14158 : {
14159 : HeapTuple tup;
14160 : Datum adatum;
14161 : ArrayType *arr;
14162 : Oid *rawarr;
14163 : int numkeys;
14164 : int i;
14165 :
14166 : Assert(con->contype == CONSTR_FOREIGN);
14167 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14168 :
14169 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14170 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
14171 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14172 :
14173 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14174 : Anum_pg_constraint_conpfeqop);
14175 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14176 6 : numkeys = ARR_DIMS(arr)[0];
14177 : /* test follows the one in ri_FetchConstraintInfo() */
14178 6 : if (ARR_NDIM(arr) != 1 ||
14179 6 : ARR_HASNULL(arr) ||
14180 6 : ARR_ELEMTYPE(arr) != OIDOID)
14181 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
14182 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
14183 :
14184 : /* stash a List of the operator Oids in our Constraint node */
14185 12 : for (i = 0; i < numkeys; i++)
14186 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14187 :
14188 6 : ReleaseSysCache(tup);
14189 6 : }
14190 :
14191 : /*
14192 : * ALTER COLUMN .. OPTIONS ( ... )
14193 : *
14194 : * Returns the address of the modified column
14195 : */
14196 : static ObjectAddress
14197 164 : ATExecAlterColumnGenericOptions(Relation rel,
14198 : const char *colName,
14199 : List *options,
14200 : LOCKMODE lockmode)
14201 : {
14202 : Relation ftrel;
14203 : Relation attrel;
14204 : ForeignServer *server;
14205 : ForeignDataWrapper *fdw;
14206 : HeapTuple tuple;
14207 : HeapTuple newtuple;
14208 : bool isnull;
14209 : Datum repl_val[Natts_pg_attribute];
14210 : bool repl_null[Natts_pg_attribute];
14211 : bool repl_repl[Natts_pg_attribute];
14212 : Datum datum;
14213 : Form_pg_foreign_table fttableform;
14214 : Form_pg_attribute atttableform;
14215 : AttrNumber attnum;
14216 : ObjectAddress address;
14217 :
14218 164 : if (options == NIL)
14219 0 : return InvalidObjectAddress;
14220 :
14221 : /* First, determine FDW validator associated to the foreign table. */
14222 164 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14223 164 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14224 164 : if (!HeapTupleIsValid(tuple))
14225 0 : ereport(ERROR,
14226 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14227 : errmsg("foreign table \"%s\" does not exist",
14228 : RelationGetRelationName(rel))));
14229 164 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14230 164 : server = GetForeignServer(fttableform->ftserver);
14231 164 : fdw = GetForeignDataWrapper(server->fdwid);
14232 :
14233 164 : table_close(ftrel, AccessShareLock);
14234 164 : ReleaseSysCache(tuple);
14235 :
14236 164 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
14237 164 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14238 164 : if (!HeapTupleIsValid(tuple))
14239 0 : ereport(ERROR,
14240 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14241 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14242 : colName, RelationGetRelationName(rel))));
14243 :
14244 : /* Prevent them from altering a system attribute */
14245 164 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14246 164 : attnum = atttableform->attnum;
14247 164 : if (attnum <= 0)
14248 6 : ereport(ERROR,
14249 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14250 : errmsg("cannot alter system column \"%s\"", colName)));
14251 :
14252 :
14253 : /* Initialize buffers for new tuple values */
14254 158 : memset(repl_val, 0, sizeof(repl_val));
14255 158 : memset(repl_null, false, sizeof(repl_null));
14256 158 : memset(repl_repl, false, sizeof(repl_repl));
14257 :
14258 : /* Extract the current options */
14259 158 : datum = SysCacheGetAttr(ATTNAME,
14260 : tuple,
14261 : Anum_pg_attribute_attfdwoptions,
14262 : &isnull);
14263 158 : if (isnull)
14264 148 : datum = PointerGetDatum(NULL);
14265 :
14266 : /* Transform the options */
14267 158 : datum = transformGenericOptions(AttributeRelationId,
14268 : datum,
14269 : options,
14270 : fdw->fdwvalidator);
14271 :
14272 158 : if (PointerIsValid(DatumGetPointer(datum)))
14273 158 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14274 : else
14275 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14276 :
14277 158 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14278 :
14279 : /* Everything looks good - update the tuple */
14280 :
14281 158 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14282 : repl_val, repl_null, repl_repl);
14283 :
14284 158 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14285 :
14286 158 : InvokeObjectPostAlterHook(RelationRelationId,
14287 : RelationGetRelid(rel),
14288 : atttableform->attnum);
14289 158 : ObjectAddressSubSet(address, RelationRelationId,
14290 : RelationGetRelid(rel), attnum);
14291 :
14292 158 : ReleaseSysCache(tuple);
14293 :
14294 158 : table_close(attrel, RowExclusiveLock);
14295 :
14296 158 : heap_freetuple(newtuple);
14297 :
14298 158 : return address;
14299 : }
14300 :
14301 : /*
14302 : * ALTER TABLE OWNER
14303 : *
14304 : * recursing is true if we are recursing from a table to its indexes,
14305 : * sequences, or toast table. We don't allow the ownership of those things to
14306 : * be changed separately from the parent table. Also, we can skip permission
14307 : * checks (this is necessary not just an optimization, else we'd fail to
14308 : * handle toast tables properly).
14309 : *
14310 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14311 : * free-standing composite type.
14312 : */
14313 : void
14314 1996 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14315 : {
14316 : Relation target_rel;
14317 : Relation class_rel;
14318 : HeapTuple tuple;
14319 : Form_pg_class tuple_class;
14320 :
14321 : /*
14322 : * Get exclusive lock till end of transaction on the target table. Use
14323 : * relation_open so that we can work on indexes and sequences.
14324 : */
14325 1996 : target_rel = relation_open(relationOid, lockmode);
14326 :
14327 : /* Get its pg_class tuple, too */
14328 1996 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
14329 :
14330 1996 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14331 1996 : if (!HeapTupleIsValid(tuple))
14332 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
14333 1996 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
14334 :
14335 : /* Can we change the ownership of this tuple? */
14336 1996 : switch (tuple_class->relkind)
14337 : {
14338 1730 : case RELKIND_RELATION:
14339 : case RELKIND_VIEW:
14340 : case RELKIND_MATVIEW:
14341 : case RELKIND_FOREIGN_TABLE:
14342 : case RELKIND_PARTITIONED_TABLE:
14343 : /* ok to change owner */
14344 1730 : break;
14345 96 : case RELKIND_INDEX:
14346 96 : if (!recursing)
14347 : {
14348 : /*
14349 : * Because ALTER INDEX OWNER used to be allowed, and in fact
14350 : * is generated by old versions of pg_dump, we give a warning
14351 : * and do nothing rather than erroring out. Also, to avoid
14352 : * unnecessary chatter while restoring those old dumps, say
14353 : * nothing at all if the command would be a no-op anyway.
14354 : */
14355 0 : if (tuple_class->relowner != newOwnerId)
14356 0 : ereport(WARNING,
14357 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14358 : errmsg("cannot change owner of index \"%s\"",
14359 : NameStr(tuple_class->relname)),
14360 : errhint("Change the ownership of the index's table instead.")));
14361 : /* quick hack to exit via the no-op path */
14362 0 : newOwnerId = tuple_class->relowner;
14363 : }
14364 96 : break;
14365 20 : case RELKIND_PARTITIONED_INDEX:
14366 20 : if (recursing)
14367 20 : break;
14368 0 : ereport(ERROR,
14369 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14370 : errmsg("cannot change owner of index \"%s\"",
14371 : NameStr(tuple_class->relname)),
14372 : errhint("Change the ownership of the index's table instead.")));
14373 : break;
14374 100 : case RELKIND_SEQUENCE:
14375 100 : if (!recursing &&
14376 52 : tuple_class->relowner != newOwnerId)
14377 : {
14378 : /* if it's an owned sequence, disallow changing it by itself */
14379 : Oid tableId;
14380 : int32 colId;
14381 :
14382 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
14383 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
14384 0 : ereport(ERROR,
14385 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14386 : errmsg("cannot change owner of sequence \"%s\"",
14387 : NameStr(tuple_class->relname)),
14388 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
14389 : NameStr(tuple_class->relname),
14390 : get_rel_name(tableId))));
14391 : }
14392 100 : break;
14393 8 : case RELKIND_COMPOSITE_TYPE:
14394 8 : if (recursing)
14395 8 : break;
14396 0 : ereport(ERROR,
14397 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14398 : errmsg("\"%s\" is a composite type",
14399 : NameStr(tuple_class->relname)),
14400 : /* translator: %s is an SQL ALTER command */
14401 : errhint("Use %s instead.",
14402 : "ALTER TYPE")));
14403 : break;
14404 42 : case RELKIND_TOASTVALUE:
14405 42 : if (recursing)
14406 42 : break;
14407 : /* FALL THRU */
14408 : default:
14409 0 : ereport(ERROR,
14410 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14411 : errmsg("cannot change owner of relation \"%s\"",
14412 : NameStr(tuple_class->relname)),
14413 : errdetail_relkind_not_supported(tuple_class->relkind)));
14414 : }
14415 :
14416 : /*
14417 : * If the new owner is the same as the existing owner, consider the
14418 : * command to have succeeded. This is for dump restoration purposes.
14419 : */
14420 1996 : if (tuple_class->relowner != newOwnerId)
14421 : {
14422 : Datum repl_val[Natts_pg_class];
14423 : bool repl_null[Natts_pg_class];
14424 : bool repl_repl[Natts_pg_class];
14425 : Acl *newAcl;
14426 : Datum aclDatum;
14427 : bool isNull;
14428 : HeapTuple newtuple;
14429 :
14430 : /* skip permission checks when recursing to index or toast table */
14431 528 : if (!recursing)
14432 : {
14433 : /* Superusers can always do it */
14434 310 : if (!superuser())
14435 : {
14436 42 : Oid namespaceOid = tuple_class->relnamespace;
14437 : AclResult aclresult;
14438 :
14439 : /* Otherwise, must be owner of the existing object */
14440 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
14441 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
14442 0 : RelationGetRelationName(target_rel));
14443 :
14444 : /* Must be able to become new owner */
14445 42 : check_can_set_role(GetUserId(), newOwnerId);
14446 :
14447 : /* New owner must have CREATE privilege on namespace */
14448 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
14449 : ACL_CREATE);
14450 30 : if (aclresult != ACLCHECK_OK)
14451 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
14452 0 : get_namespace_name(namespaceOid));
14453 : }
14454 : }
14455 :
14456 516 : memset(repl_null, false, sizeof(repl_null));
14457 516 : memset(repl_repl, false, sizeof(repl_repl));
14458 :
14459 516 : repl_repl[Anum_pg_class_relowner - 1] = true;
14460 516 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
14461 :
14462 : /*
14463 : * Determine the modified ACL for the new owner. This is only
14464 : * necessary when the ACL is non-null.
14465 : */
14466 516 : aclDatum = SysCacheGetAttr(RELOID, tuple,
14467 : Anum_pg_class_relacl,
14468 : &isNull);
14469 516 : if (!isNull)
14470 : {
14471 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
14472 : tuple_class->relowner, newOwnerId);
14473 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
14474 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
14475 : }
14476 :
14477 516 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
14478 :
14479 516 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
14480 :
14481 516 : heap_freetuple(newtuple);
14482 :
14483 : /*
14484 : * We must similarly update any per-column ACLs to reflect the new
14485 : * owner; for neatness reasons that's split out as a subroutine.
14486 : */
14487 516 : change_owner_fix_column_acls(relationOid,
14488 : tuple_class->relowner,
14489 : newOwnerId);
14490 :
14491 : /*
14492 : * Update owner dependency reference, if any. A composite type has
14493 : * none, because it's tracked for the pg_type entry instead of here;
14494 : * indexes and TOAST tables don't have their own entries either.
14495 : */
14496 516 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
14497 508 : tuple_class->relkind != RELKIND_INDEX &&
14498 412 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
14499 392 : tuple_class->relkind != RELKIND_TOASTVALUE)
14500 350 : changeDependencyOnOwner(RelationRelationId, relationOid,
14501 : newOwnerId);
14502 :
14503 : /*
14504 : * Also change the ownership of the table's row type, if it has one
14505 : */
14506 516 : if (OidIsValid(tuple_class->reltype))
14507 324 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
14508 :
14509 : /*
14510 : * If we are operating on a table or materialized view, also change
14511 : * the ownership of any indexes and sequences that belong to the
14512 : * relation, as well as its toast table (if it has one).
14513 : */
14514 516 : if (tuple_class->relkind == RELKIND_RELATION ||
14515 274 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
14516 224 : tuple_class->relkind == RELKIND_MATVIEW ||
14517 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
14518 : {
14519 : List *index_oid_list;
14520 : ListCell *i;
14521 :
14522 : /* Find all the indexes belonging to this relation */
14523 334 : index_oid_list = RelationGetIndexList(target_rel);
14524 :
14525 : /* For each index, recursively change its ownership */
14526 450 : foreach(i, index_oid_list)
14527 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
14528 :
14529 334 : list_free(index_oid_list);
14530 : }
14531 :
14532 : /* If it has a toast table, recurse to change its ownership */
14533 516 : if (tuple_class->reltoastrelid != InvalidOid)
14534 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
14535 : true, lockmode);
14536 :
14537 : /* If it has dependent sequences, recurse to change them too */
14538 516 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
14539 : }
14540 :
14541 1984 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
14542 :
14543 1984 : ReleaseSysCache(tuple);
14544 1984 : table_close(class_rel, RowExclusiveLock);
14545 1984 : relation_close(target_rel, NoLock);
14546 1984 : }
14547 :
14548 : /*
14549 : * change_owner_fix_column_acls
14550 : *
14551 : * Helper function for ATExecChangeOwner. Scan the columns of the table
14552 : * and fix any non-null column ACLs to reflect the new owner.
14553 : */
14554 : static void
14555 516 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
14556 : {
14557 : Relation attRelation;
14558 : SysScanDesc scan;
14559 : ScanKeyData key[1];
14560 : HeapTuple attributeTuple;
14561 :
14562 516 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
14563 516 : ScanKeyInit(&key[0],
14564 : Anum_pg_attribute_attrelid,
14565 : BTEqualStrategyNumber, F_OIDEQ,
14566 : ObjectIdGetDatum(relationOid));
14567 516 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
14568 : true, NULL, 1, key);
14569 3612 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
14570 : {
14571 3096 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
14572 : Datum repl_val[Natts_pg_attribute];
14573 : bool repl_null[Natts_pg_attribute];
14574 : bool repl_repl[Natts_pg_attribute];
14575 : Acl *newAcl;
14576 : Datum aclDatum;
14577 : bool isNull;
14578 : HeapTuple newtuple;
14579 :
14580 : /* Ignore dropped columns */
14581 3096 : if (att->attisdropped)
14582 3094 : continue;
14583 :
14584 3096 : aclDatum = heap_getattr(attributeTuple,
14585 : Anum_pg_attribute_attacl,
14586 : RelationGetDescr(attRelation),
14587 : &isNull);
14588 : /* Null ACLs do not require changes */
14589 3096 : if (isNull)
14590 3094 : continue;
14591 :
14592 2 : memset(repl_null, false, sizeof(repl_null));
14593 2 : memset(repl_repl, false, sizeof(repl_repl));
14594 :
14595 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
14596 : oldOwnerId, newOwnerId);
14597 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
14598 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
14599 :
14600 2 : newtuple = heap_modify_tuple(attributeTuple,
14601 : RelationGetDescr(attRelation),
14602 : repl_val, repl_null, repl_repl);
14603 :
14604 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
14605 :
14606 2 : heap_freetuple(newtuple);
14607 : }
14608 516 : systable_endscan(scan);
14609 516 : table_close(attRelation, RowExclusiveLock);
14610 516 : }
14611 :
14612 : /*
14613 : * change_owner_recurse_to_sequences
14614 : *
14615 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
14616 : * for sequences that are dependent on serial columns, and changes their
14617 : * ownership.
14618 : */
14619 : static void
14620 516 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
14621 : {
14622 : Relation depRel;
14623 : SysScanDesc scan;
14624 : ScanKeyData key[2];
14625 : HeapTuple tup;
14626 :
14627 : /*
14628 : * SERIAL sequences are those having an auto dependency on one of the
14629 : * table's columns (we don't care *which* column, exactly).
14630 : */
14631 516 : depRel = table_open(DependRelationId, AccessShareLock);
14632 :
14633 516 : ScanKeyInit(&key[0],
14634 : Anum_pg_depend_refclassid,
14635 : BTEqualStrategyNumber, F_OIDEQ,
14636 : ObjectIdGetDatum(RelationRelationId));
14637 516 : ScanKeyInit(&key[1],
14638 : Anum_pg_depend_refobjid,
14639 : BTEqualStrategyNumber, F_OIDEQ,
14640 : ObjectIdGetDatum(relationOid));
14641 : /* we leave refobjsubid unspecified */
14642 :
14643 516 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14644 : NULL, 2, key);
14645 :
14646 1414 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
14647 : {
14648 898 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
14649 : Relation seqRel;
14650 :
14651 : /* skip dependencies other than auto dependencies on columns */
14652 898 : if (depForm->refobjsubid == 0 ||
14653 302 : depForm->classid != RelationRelationId ||
14654 142 : depForm->objsubid != 0 ||
14655 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
14656 756 : continue;
14657 :
14658 : /* Use relation_open just in case it's an index */
14659 142 : seqRel = relation_open(depForm->objid, lockmode);
14660 :
14661 : /* skip non-sequence relations */
14662 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
14663 : {
14664 : /* No need to keep the lock */
14665 116 : relation_close(seqRel, lockmode);
14666 116 : continue;
14667 : }
14668 :
14669 : /* We don't need to close the sequence while we alter it. */
14670 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
14671 :
14672 : /* Now we can close it. Keep the lock till end of transaction. */
14673 26 : relation_close(seqRel, NoLock);
14674 : }
14675 :
14676 516 : systable_endscan(scan);
14677 :
14678 516 : relation_close(depRel, AccessShareLock);
14679 516 : }
14680 :
14681 : /*
14682 : * ALTER TABLE CLUSTER ON
14683 : *
14684 : * The only thing we have to do is to change the indisclustered bits.
14685 : *
14686 : * Return the address of the new clustering index.
14687 : */
14688 : static ObjectAddress
14689 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
14690 : {
14691 : Oid indexOid;
14692 : ObjectAddress address;
14693 :
14694 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
14695 :
14696 64 : if (!OidIsValid(indexOid))
14697 0 : ereport(ERROR,
14698 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14699 : errmsg("index \"%s\" for table \"%s\" does not exist",
14700 : indexName, RelationGetRelationName(rel))));
14701 :
14702 : /* Check index is valid to cluster on */
14703 64 : check_index_is_clusterable(rel, indexOid, lockmode);
14704 :
14705 : /* And do the work */
14706 64 : mark_index_clustered(rel, indexOid, false);
14707 :
14708 58 : ObjectAddressSet(address,
14709 : RelationRelationId, indexOid);
14710 :
14711 58 : return address;
14712 : }
14713 :
14714 : /*
14715 : * ALTER TABLE SET WITHOUT CLUSTER
14716 : *
14717 : * We have to find any indexes on the table that have indisclustered bit
14718 : * set and turn it off.
14719 : */
14720 : static void
14721 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
14722 : {
14723 18 : mark_index_clustered(rel, InvalidOid, false);
14724 12 : }
14725 :
14726 : /*
14727 : * Preparation phase for SET ACCESS METHOD
14728 : *
14729 : * Check that the access method exists and determine whether a change is
14730 : * actually needed.
14731 : */
14732 : static void
14733 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
14734 : {
14735 : Oid amoid;
14736 :
14737 : /*
14738 : * Look up the access method name and check that it differs from the
14739 : * table's current AM. If DEFAULT was specified for a partitioned table
14740 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
14741 : */
14742 110 : if (amname != NULL)
14743 74 : amoid = get_table_am_oid(amname, false);
14744 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14745 18 : amoid = InvalidOid;
14746 : else
14747 18 : amoid = get_table_am_oid(default_table_access_method, false);
14748 :
14749 : /* if it's a match, phase 3 doesn't need to do anything */
14750 110 : if (rel->rd_rel->relam == amoid)
14751 12 : return;
14752 :
14753 : /* Save info for Phase 3 to do the real work */
14754 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
14755 98 : tab->newAccessMethod = amoid;
14756 98 : tab->chgAccessMethod = true;
14757 : }
14758 :
14759 : /*
14760 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
14761 : * storage that have an interest in preserving AM.
14762 : *
14763 : * Since these have no storage, setting the access method is a catalog only
14764 : * operation.
14765 : */
14766 : static void
14767 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
14768 : {
14769 : Relation pg_class;
14770 : Oid oldAccessMethodId;
14771 : HeapTuple tuple;
14772 : Form_pg_class rd_rel;
14773 44 : Oid reloid = RelationGetRelid(rel);
14774 :
14775 : /*
14776 : * Shouldn't be called on relations having storage; these are processed in
14777 : * phase 3.
14778 : */
14779 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
14780 :
14781 : /* Get a modifiable copy of the relation's pg_class row. */
14782 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
14783 :
14784 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
14785 44 : if (!HeapTupleIsValid(tuple))
14786 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
14787 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
14788 :
14789 : /* Update the pg_class row. */
14790 44 : oldAccessMethodId = rd_rel->relam;
14791 44 : rd_rel->relam = newAccessMethodId;
14792 :
14793 : /* Leave if no update required */
14794 44 : if (rd_rel->relam == oldAccessMethodId)
14795 : {
14796 0 : heap_freetuple(tuple);
14797 0 : table_close(pg_class, RowExclusiveLock);
14798 0 : return;
14799 : }
14800 :
14801 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
14802 :
14803 : /*
14804 : * Update the dependency on the new access method. No dependency is added
14805 : * if the new access method is InvalidOid (default case). Be very careful
14806 : * that this has to compare the previous value stored in pg_class with the
14807 : * new one.
14808 : */
14809 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
14810 20 : {
14811 : ObjectAddress relobj,
14812 : referenced;
14813 :
14814 : /*
14815 : * New access method is defined and there was no dependency
14816 : * previously, so record a new one.
14817 : */
14818 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
14819 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
14820 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
14821 : }
14822 24 : else if (OidIsValid(oldAccessMethodId) &&
14823 24 : !OidIsValid(rd_rel->relam))
14824 : {
14825 : /*
14826 : * There was an access method defined, and no new one, so just remove
14827 : * the existing dependency.
14828 : */
14829 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
14830 : AccessMethodRelationId,
14831 : DEPENDENCY_NORMAL);
14832 : }
14833 : else
14834 : {
14835 : Assert(OidIsValid(oldAccessMethodId) &&
14836 : OidIsValid(rd_rel->relam));
14837 :
14838 : /* Both are valid, so update the dependency */
14839 12 : changeDependencyFor(RelationRelationId, reloid,
14840 : AccessMethodRelationId,
14841 : oldAccessMethodId, rd_rel->relam);
14842 : }
14843 :
14844 : /* make the relam and dependency changes visible */
14845 44 : CommandCounterIncrement();
14846 :
14847 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
14848 :
14849 44 : heap_freetuple(tuple);
14850 44 : table_close(pg_class, RowExclusiveLock);
14851 : }
14852 :
14853 : /*
14854 : * ALTER TABLE SET TABLESPACE
14855 : */
14856 : static void
14857 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
14858 : {
14859 : Oid tablespaceId;
14860 :
14861 : /* Check that the tablespace exists */
14862 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
14863 :
14864 : /* Check permissions except when moving to database's default */
14865 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
14866 : {
14867 : AclResult aclresult;
14868 :
14869 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
14870 66 : if (aclresult != ACLCHECK_OK)
14871 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
14872 : }
14873 :
14874 : /* Save info for Phase 3 to do the real work */
14875 158 : if (OidIsValid(tab->newTableSpace))
14876 0 : ereport(ERROR,
14877 : (errcode(ERRCODE_SYNTAX_ERROR),
14878 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
14879 :
14880 158 : tab->newTableSpace = tablespaceId;
14881 158 : }
14882 :
14883 : /*
14884 : * Set, reset, or replace reloptions.
14885 : */
14886 : static void
14887 946 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
14888 : LOCKMODE lockmode)
14889 : {
14890 : Oid relid;
14891 : Relation pgclass;
14892 : HeapTuple tuple;
14893 : HeapTuple newtuple;
14894 : Datum datum;
14895 : bool isnull;
14896 : Datum newOptions;
14897 : Datum repl_val[Natts_pg_class];
14898 : bool repl_null[Natts_pg_class];
14899 : bool repl_repl[Natts_pg_class];
14900 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
14901 :
14902 946 : if (defList == NIL && operation != AT_ReplaceRelOptions)
14903 0 : return; /* nothing to do */
14904 :
14905 946 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
14906 :
14907 : /* Fetch heap tuple */
14908 946 : relid = RelationGetRelid(rel);
14909 946 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14910 946 : if (!HeapTupleIsValid(tuple))
14911 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
14912 :
14913 946 : if (operation == AT_ReplaceRelOptions)
14914 : {
14915 : /*
14916 : * If we're supposed to replace the reloptions list, we just pretend
14917 : * there were none before.
14918 : */
14919 194 : datum = (Datum) 0;
14920 194 : isnull = true;
14921 : }
14922 : else
14923 : {
14924 : /* Get the old reloptions */
14925 752 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
14926 : &isnull);
14927 : }
14928 :
14929 : /* Generate new proposed reloptions (text array) */
14930 946 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
14931 : defList, NULL, validnsps, false,
14932 : operation == AT_ResetRelOptions);
14933 :
14934 : /* Validate */
14935 940 : switch (rel->rd_rel->relkind)
14936 : {
14937 524 : case RELKIND_RELATION:
14938 : case RELKIND_TOASTVALUE:
14939 : case RELKIND_MATVIEW:
14940 524 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
14941 524 : break;
14942 6 : case RELKIND_PARTITIONED_TABLE:
14943 6 : (void) partitioned_table_reloptions(newOptions, true);
14944 0 : break;
14945 296 : case RELKIND_VIEW:
14946 296 : (void) view_reloptions(newOptions, true);
14947 278 : break;
14948 114 : case RELKIND_INDEX:
14949 : case RELKIND_PARTITIONED_INDEX:
14950 114 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
14951 92 : break;
14952 0 : default:
14953 0 : ereport(ERROR,
14954 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14955 : errmsg("cannot set options for relation \"%s\"",
14956 : RelationGetRelationName(rel)),
14957 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
14958 : break;
14959 : }
14960 :
14961 : /* Special-case validation of view options */
14962 894 : if (rel->rd_rel->relkind == RELKIND_VIEW)
14963 : {
14964 278 : Query *view_query = get_view_query(rel);
14965 278 : List *view_options = untransformRelOptions(newOptions);
14966 : ListCell *cell;
14967 278 : bool check_option = false;
14968 :
14969 380 : foreach(cell, view_options)
14970 : {
14971 102 : DefElem *defel = (DefElem *) lfirst(cell);
14972 :
14973 102 : if (strcmp(defel->defname, "check_option") == 0)
14974 24 : check_option = true;
14975 : }
14976 :
14977 : /*
14978 : * If the check option is specified, look to see if the view is
14979 : * actually auto-updatable or not.
14980 : */
14981 278 : if (check_option)
14982 : {
14983 : const char *view_updatable_error =
14984 24 : view_query_is_auto_updatable(view_query, true);
14985 :
14986 24 : if (view_updatable_error)
14987 0 : ereport(ERROR,
14988 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14989 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
14990 : errhint("%s", _(view_updatable_error))));
14991 : }
14992 : }
14993 :
14994 : /*
14995 : * All we need do here is update the pg_class row; the new options will be
14996 : * propagated into relcaches during post-commit cache inval.
14997 : */
14998 894 : memset(repl_val, 0, sizeof(repl_val));
14999 894 : memset(repl_null, false, sizeof(repl_null));
15000 894 : memset(repl_repl, false, sizeof(repl_repl));
15001 :
15002 894 : if (newOptions != (Datum) 0)
15003 600 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15004 : else
15005 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
15006 :
15007 894 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15008 :
15009 894 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15010 : repl_val, repl_null, repl_repl);
15011 :
15012 894 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15013 :
15014 894 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15015 :
15016 894 : heap_freetuple(newtuple);
15017 :
15018 894 : ReleaseSysCache(tuple);
15019 :
15020 : /* repeat the whole exercise for the toast table, if there's one */
15021 894 : if (OidIsValid(rel->rd_rel->reltoastrelid))
15022 : {
15023 : Relation toastrel;
15024 256 : Oid toastid = rel->rd_rel->reltoastrelid;
15025 :
15026 256 : toastrel = table_open(toastid, lockmode);
15027 :
15028 : /* Fetch heap tuple */
15029 256 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15030 256 : if (!HeapTupleIsValid(tuple))
15031 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
15032 :
15033 256 : if (operation == AT_ReplaceRelOptions)
15034 : {
15035 : /*
15036 : * If we're supposed to replace the reloptions list, we just
15037 : * pretend there were none before.
15038 : */
15039 0 : datum = (Datum) 0;
15040 0 : isnull = true;
15041 : }
15042 : else
15043 : {
15044 : /* Get the old reloptions */
15045 256 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15046 : &isnull);
15047 : }
15048 :
15049 256 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15050 : defList, "toast", validnsps, false,
15051 : operation == AT_ResetRelOptions);
15052 :
15053 256 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15054 :
15055 256 : memset(repl_val, 0, sizeof(repl_val));
15056 256 : memset(repl_null, false, sizeof(repl_null));
15057 256 : memset(repl_repl, false, sizeof(repl_repl));
15058 :
15059 256 : if (newOptions != (Datum) 0)
15060 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15061 : else
15062 214 : repl_null[Anum_pg_class_reloptions - 1] = true;
15063 :
15064 256 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15065 :
15066 256 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15067 : repl_val, repl_null, repl_repl);
15068 :
15069 256 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15070 :
15071 256 : InvokeObjectPostAlterHookArg(RelationRelationId,
15072 : RelationGetRelid(toastrel), 0,
15073 : InvalidOid, true);
15074 :
15075 256 : heap_freetuple(newtuple);
15076 :
15077 256 : ReleaseSysCache(tuple);
15078 :
15079 256 : table_close(toastrel, NoLock);
15080 : }
15081 :
15082 894 : table_close(pgclass, RowExclusiveLock);
15083 : }
15084 :
15085 : /*
15086 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15087 : * rewriting to be done, so we just want to copy the data as fast as possible.
15088 : */
15089 : static void
15090 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15091 : {
15092 : Relation rel;
15093 : Oid reltoastrelid;
15094 : RelFileNumber newrelfilenumber;
15095 : RelFileLocator newrlocator;
15096 162 : List *reltoastidxids = NIL;
15097 : ListCell *lc;
15098 :
15099 : /*
15100 : * Need lock here in case we are recursing to toast table or index
15101 : */
15102 162 : rel = relation_open(tableOid, lockmode);
15103 :
15104 : /* Check first if relation can be moved to new tablespace */
15105 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15106 : {
15107 2 : InvokeObjectPostAlterHook(RelationRelationId,
15108 : RelationGetRelid(rel), 0);
15109 2 : relation_close(rel, NoLock);
15110 2 : return;
15111 : }
15112 :
15113 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
15114 : /* Fetch the list of indexes on toast relation if necessary */
15115 160 : if (OidIsValid(reltoastrelid))
15116 : {
15117 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
15118 :
15119 20 : reltoastidxids = RelationGetIndexList(toastRel);
15120 20 : relation_close(toastRel, lockmode);
15121 : }
15122 :
15123 : /*
15124 : * Relfilenumbers are not unique in databases across tablespaces, so we
15125 : * need to allocate a new one in the new tablespace.
15126 : */
15127 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15128 160 : rel->rd_rel->relpersistence);
15129 :
15130 : /* Open old and new relation */
15131 160 : newrlocator = rel->rd_locator;
15132 160 : newrlocator.relNumber = newrelfilenumber;
15133 160 : newrlocator.spcOid = newTableSpace;
15134 :
15135 : /* hand off to AM to actually create new rel storage and copy the data */
15136 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
15137 : {
15138 62 : index_copy_data(rel, newrlocator);
15139 : }
15140 : else
15141 : {
15142 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15143 98 : table_relation_copy_data(rel, &newrlocator);
15144 : }
15145 :
15146 : /*
15147 : * Update the pg_class row.
15148 : *
15149 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15150 : * executed on pg_class or its indexes (the above copy wouldn't contain
15151 : * the updated pg_class entry), but that's forbidden with
15152 : * CheckRelationTableSpaceMove().
15153 : */
15154 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15155 :
15156 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15157 :
15158 160 : RelationAssumeNewRelfilelocator(rel);
15159 :
15160 160 : relation_close(rel, NoLock);
15161 :
15162 : /* Make sure the reltablespace change is visible */
15163 160 : CommandCounterIncrement();
15164 :
15165 : /* Move associated toast relation and/or indexes, too */
15166 160 : if (OidIsValid(reltoastrelid))
15167 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15168 180 : foreach(lc, reltoastidxids)
15169 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15170 :
15171 : /* Clean up */
15172 160 : list_free(reltoastidxids);
15173 : }
15174 :
15175 : /*
15176 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15177 : * storage that have an interest in preserving tablespace.
15178 : *
15179 : * Since these have no storage the tablespace can be updated with a simple
15180 : * metadata only operation to update the tablespace.
15181 : */
15182 : static void
15183 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15184 : {
15185 : /*
15186 : * Shouldn't be called on relations having storage; these are processed in
15187 : * phase 3.
15188 : */
15189 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15190 :
15191 : /* check if relation can be moved to its new tablespace */
15192 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15193 : {
15194 0 : InvokeObjectPostAlterHook(RelationRelationId,
15195 : RelationGetRelid(rel),
15196 : 0);
15197 0 : return;
15198 : }
15199 :
15200 : /* Update can be done, so change reltablespace */
15201 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15202 :
15203 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15204 :
15205 : /* Make sure the reltablespace change is visible */
15206 30 : CommandCounterIncrement();
15207 : }
15208 :
15209 : /*
15210 : * Alter Table ALL ... SET TABLESPACE
15211 : *
15212 : * Allows a user to move all objects of some type in a given tablespace in the
15213 : * current database to another tablespace. Objects can be chosen based on the
15214 : * owner of the object also, to allow users to move only their objects.
15215 : * The user must have CREATE rights on the new tablespace, as usual. The main
15216 : * permissions handling is done by the lower-level table move function.
15217 : *
15218 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
15219 : * lock can't be acquired then we ereport(ERROR).
15220 : */
15221 : Oid
15222 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15223 : {
15224 30 : List *relations = NIL;
15225 : ListCell *l;
15226 : ScanKeyData key[1];
15227 : Relation rel;
15228 : TableScanDesc scan;
15229 : HeapTuple tuple;
15230 : Oid orig_tablespaceoid;
15231 : Oid new_tablespaceoid;
15232 30 : List *role_oids = roleSpecsToIds(stmt->roles);
15233 :
15234 : /* Ensure we were not asked to move something we can't */
15235 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15236 12 : stmt->objtype != OBJECT_MATVIEW)
15237 0 : ereport(ERROR,
15238 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15239 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15240 :
15241 : /* Get the orig and new tablespace OIDs */
15242 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15243 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15244 :
15245 : /* Can't move shared relations in to or out of pg_global */
15246 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15247 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15248 : new_tablespaceoid == GLOBALTABLESPACE_OID)
15249 0 : ereport(ERROR,
15250 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15251 : errmsg("cannot move relations in to or out of pg_global tablespace")));
15252 :
15253 : /*
15254 : * Must have CREATE rights on the new tablespace, unless it is the
15255 : * database default tablespace (which all users implicitly have CREATE
15256 : * rights on).
15257 : */
15258 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15259 : {
15260 : AclResult aclresult;
15261 :
15262 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15263 : ACL_CREATE);
15264 0 : if (aclresult != ACLCHECK_OK)
15265 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
15266 0 : get_tablespace_name(new_tablespaceoid));
15267 : }
15268 :
15269 : /*
15270 : * Now that the checks are done, check if we should set either to
15271 : * InvalidOid because it is our database's default tablespace.
15272 : */
15273 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
15274 0 : orig_tablespaceoid = InvalidOid;
15275 :
15276 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
15277 30 : new_tablespaceoid = InvalidOid;
15278 :
15279 : /* no-op */
15280 30 : if (orig_tablespaceoid == new_tablespaceoid)
15281 0 : return new_tablespaceoid;
15282 :
15283 : /*
15284 : * Walk the list of objects in the tablespace and move them. This will
15285 : * only find objects in our database, of course.
15286 : */
15287 30 : ScanKeyInit(&key[0],
15288 : Anum_pg_class_reltablespace,
15289 : BTEqualStrategyNumber, F_OIDEQ,
15290 : ObjectIdGetDatum(orig_tablespaceoid));
15291 :
15292 30 : rel = table_open(RelationRelationId, AccessShareLock);
15293 30 : scan = table_beginscan_catalog(rel, 1, key);
15294 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15295 : {
15296 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15297 102 : Oid relOid = relForm->oid;
15298 :
15299 : /*
15300 : * Do not move objects in pg_catalog as part of this, if an admin
15301 : * really wishes to do so, they can issue the individual ALTER
15302 : * commands directly.
15303 : *
15304 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
15305 : * (TOAST will be moved with the main table).
15306 : */
15307 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
15308 204 : relForm->relisshared ||
15309 204 : isAnyTempNamespace(relForm->relnamespace) ||
15310 102 : IsToastNamespace(relForm->relnamespace))
15311 0 : continue;
15312 :
15313 : /* Only move the object type requested */
15314 102 : if ((stmt->objtype == OBJECT_TABLE &&
15315 60 : relForm->relkind != RELKIND_RELATION &&
15316 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15317 66 : (stmt->objtype == OBJECT_INDEX &&
15318 36 : relForm->relkind != RELKIND_INDEX &&
15319 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15320 60 : (stmt->objtype == OBJECT_MATVIEW &&
15321 6 : relForm->relkind != RELKIND_MATVIEW))
15322 42 : continue;
15323 :
15324 : /* Check if we are only moving objects owned by certain roles */
15325 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15326 0 : continue;
15327 :
15328 : /*
15329 : * Handle permissions-checking here since we are locking the tables
15330 : * and also to avoid doing a bunch of work only to fail part-way. Note
15331 : * that permissions will also be checked by AlterTableInternal().
15332 : *
15333 : * Caller must be considered an owner on the table to move it.
15334 : */
15335 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
15336 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
15337 0 : NameStr(relForm->relname));
15338 :
15339 60 : if (stmt->nowait &&
15340 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
15341 0 : ereport(ERROR,
15342 : (errcode(ERRCODE_OBJECT_IN_USE),
15343 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
15344 : get_namespace_name(relForm->relnamespace),
15345 : NameStr(relForm->relname))));
15346 : else
15347 60 : LockRelationOid(relOid, AccessExclusiveLock);
15348 :
15349 : /* Add to our list of objects to move */
15350 60 : relations = lappend_oid(relations, relOid);
15351 : }
15352 :
15353 30 : table_endscan(scan);
15354 30 : table_close(rel, AccessShareLock);
15355 :
15356 30 : if (relations == NIL)
15357 12 : ereport(NOTICE,
15358 : (errcode(ERRCODE_NO_DATA_FOUND),
15359 : errmsg("no matching relations in tablespace \"%s\" found",
15360 : orig_tablespaceoid == InvalidOid ? "(database default)" :
15361 : get_tablespace_name(orig_tablespaceoid))));
15362 :
15363 : /* Everything is locked, loop through and move all of the relations. */
15364 90 : foreach(l, relations)
15365 : {
15366 60 : List *cmds = NIL;
15367 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15368 :
15369 60 : cmd->subtype = AT_SetTableSpace;
15370 60 : cmd->name = stmt->new_tablespacename;
15371 :
15372 60 : cmds = lappend(cmds, cmd);
15373 :
15374 60 : EventTriggerAlterTableStart((Node *) stmt);
15375 : /* OID is set by AlterTableInternal */
15376 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
15377 60 : EventTriggerAlterTableEnd();
15378 : }
15379 :
15380 30 : return new_tablespaceoid;
15381 : }
15382 :
15383 : static void
15384 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
15385 : {
15386 : SMgrRelation dstrel;
15387 :
15388 : /*
15389 : * Since we copy the file directly without looking at the shared buffers,
15390 : * we'd better first flush out any pages of the source relation that are
15391 : * in shared buffers. We assume no new changes will be made while we are
15392 : * holding exclusive lock on the rel.
15393 : */
15394 62 : FlushRelationBuffers(rel);
15395 :
15396 : /*
15397 : * Create and copy all forks of the relation, and schedule unlinking of
15398 : * old physical files.
15399 : *
15400 : * NOTE: any conflict in relfilenumber value will be caught in
15401 : * RelationCreateStorage().
15402 : */
15403 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
15404 :
15405 : /* copy main fork */
15406 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
15407 62 : rel->rd_rel->relpersistence);
15408 :
15409 : /* copy those extra forks that exist */
15410 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
15411 186 : forkNum <= MAX_FORKNUM; forkNum++)
15412 : {
15413 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
15414 : {
15415 0 : smgrcreate(dstrel, forkNum, false);
15416 :
15417 : /*
15418 : * WAL log creation if the relation is persistent, or this is the
15419 : * init fork of an unlogged relation.
15420 : */
15421 0 : if (RelationIsPermanent(rel) ||
15422 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
15423 : forkNum == INIT_FORKNUM))
15424 0 : log_smgrcreate(&newrlocator, forkNum);
15425 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
15426 0 : rel->rd_rel->relpersistence);
15427 : }
15428 : }
15429 :
15430 : /* drop old relation, and close new one */
15431 62 : RelationDropStorage(rel);
15432 62 : smgrclose(dstrel);
15433 62 : }
15434 :
15435 : /*
15436 : * ALTER TABLE ENABLE/DISABLE TRIGGER
15437 : *
15438 : * We just pass this off to trigger.c.
15439 : */
15440 : static void
15441 340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
15442 : char fires_when, bool skip_system, bool recurse,
15443 : LOCKMODE lockmode)
15444 : {
15445 340 : EnableDisableTrigger(rel, trigname, InvalidOid,
15446 : fires_when, skip_system, recurse,
15447 : lockmode);
15448 :
15449 340 : InvokeObjectPostAlterHook(RelationRelationId,
15450 : RelationGetRelid(rel), 0);
15451 340 : }
15452 :
15453 : /*
15454 : * ALTER TABLE ENABLE/DISABLE RULE
15455 : *
15456 : * We just pass this off to rewriteDefine.c.
15457 : */
15458 : static void
15459 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
15460 : char fires_when, LOCKMODE lockmode)
15461 : {
15462 46 : EnableDisableRule(rel, rulename, fires_when);
15463 :
15464 46 : InvokeObjectPostAlterHook(RelationRelationId,
15465 : RelationGetRelid(rel), 0);
15466 46 : }
15467 :
15468 : /*
15469 : * ALTER TABLE INHERIT
15470 : *
15471 : * Add a parent to the child's parents. This verifies that all the columns and
15472 : * check constraints of the parent appear in the child and that they have the
15473 : * same data types and expressions.
15474 : */
15475 : static void
15476 272 : ATPrepAddInherit(Relation child_rel)
15477 : {
15478 272 : if (child_rel->rd_rel->reloftype)
15479 6 : ereport(ERROR,
15480 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15481 : errmsg("cannot change inheritance of typed table")));
15482 :
15483 266 : if (child_rel->rd_rel->relispartition)
15484 6 : ereport(ERROR,
15485 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15486 : errmsg("cannot change inheritance of a partition")));
15487 :
15488 260 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15489 6 : ereport(ERROR,
15490 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15491 : errmsg("cannot change inheritance of partitioned table")));
15492 254 : }
15493 :
15494 : /*
15495 : * Return the address of the new parent relation.
15496 : */
15497 : static ObjectAddress
15498 254 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
15499 : {
15500 : Relation parent_rel;
15501 : List *children;
15502 : ObjectAddress address;
15503 : const char *trigger_name;
15504 :
15505 : /*
15506 : * A self-exclusive lock is needed here. See the similar case in
15507 : * MergeAttributes() for a full explanation.
15508 : */
15509 254 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
15510 :
15511 : /*
15512 : * Must be owner of both parent and child -- child was checked by
15513 : * ATSimplePermissions call in ATPrepCmd
15514 : */
15515 254 : ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
15516 :
15517 : /* Permanent rels cannot inherit from temporary ones */
15518 254 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15519 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
15520 0 : ereport(ERROR,
15521 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15522 : errmsg("cannot inherit from temporary relation \"%s\"",
15523 : RelationGetRelationName(parent_rel))));
15524 :
15525 : /* If parent rel is temp, it must belong to this session */
15526 254 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15527 6 : !parent_rel->rd_islocaltemp)
15528 0 : ereport(ERROR,
15529 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15530 : errmsg("cannot inherit from temporary relation of another session")));
15531 :
15532 : /* Ditto for the child */
15533 254 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15534 6 : !child_rel->rd_islocaltemp)
15535 0 : ereport(ERROR,
15536 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15537 : errmsg("cannot inherit to temporary relation of another session")));
15538 :
15539 : /* Prevent partitioned tables from becoming inheritance parents */
15540 254 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15541 6 : ereport(ERROR,
15542 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15543 : errmsg("cannot inherit from partitioned table \"%s\"",
15544 : parent->relname)));
15545 :
15546 : /* Likewise for partitions */
15547 248 : if (parent_rel->rd_rel->relispartition)
15548 6 : ereport(ERROR,
15549 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15550 : errmsg("cannot inherit from a partition")));
15551 :
15552 : /*
15553 : * Prevent circularity by seeing if proposed parent inherits from child.
15554 : * (In particular, this disallows making a rel inherit from itself.)
15555 : *
15556 : * This is not completely bulletproof because of race conditions: in
15557 : * multi-level inheritance trees, someone else could concurrently be
15558 : * making another inheritance link that closes the loop but does not join
15559 : * either of the rels we have locked. Preventing that seems to require
15560 : * exclusive locks on the entire inheritance tree, which is a cure worse
15561 : * than the disease. find_all_inheritors() will cope with circularity
15562 : * anyway, so don't sweat it too much.
15563 : *
15564 : * We use weakest lock we can on child's children, namely AccessShareLock.
15565 : */
15566 242 : children = find_all_inheritors(RelationGetRelid(child_rel),
15567 : AccessShareLock, NULL);
15568 :
15569 242 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
15570 12 : ereport(ERROR,
15571 : (errcode(ERRCODE_DUPLICATE_TABLE),
15572 : errmsg("circular inheritance not allowed"),
15573 : errdetail("\"%s\" is already a child of \"%s\".",
15574 : parent->relname,
15575 : RelationGetRelationName(child_rel))));
15576 :
15577 : /*
15578 : * If child_rel has row-level triggers with transition tables, we
15579 : * currently don't allow it to become an inheritance child. See also
15580 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
15581 : */
15582 230 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
15583 230 : if (trigger_name != NULL)
15584 6 : ereport(ERROR,
15585 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15586 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
15587 : trigger_name, RelationGetRelationName(child_rel)),
15588 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
15589 :
15590 : /* OK to create inheritance */
15591 224 : CreateInheritance(child_rel, parent_rel, false);
15592 :
15593 182 : ObjectAddressSet(address, RelationRelationId,
15594 : RelationGetRelid(parent_rel));
15595 :
15596 : /* keep our lock on the parent relation until commit */
15597 182 : table_close(parent_rel, NoLock);
15598 :
15599 182 : return address;
15600 : }
15601 :
15602 : /*
15603 : * CreateInheritance
15604 : * Catalog manipulation portion of creating inheritance between a child
15605 : * table and a parent table.
15606 : *
15607 : * Common to ATExecAddInherit() and ATExecAttachPartition().
15608 : */
15609 : static void
15610 2798 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
15611 : {
15612 : Relation catalogRelation;
15613 : SysScanDesc scan;
15614 : ScanKeyData key;
15615 : HeapTuple inheritsTuple;
15616 : int32 inhseqno;
15617 :
15618 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
15619 2798 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
15620 :
15621 : /*
15622 : * Check for duplicates in the list of parents, and determine the highest
15623 : * inhseqno already present; we'll use the next one for the new parent.
15624 : * Also, if proposed child is a partition, it cannot already be
15625 : * inheriting.
15626 : *
15627 : * Note: we do not reject the case where the child already inherits from
15628 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15629 : */
15630 2798 : ScanKeyInit(&key,
15631 : Anum_pg_inherits_inhrelid,
15632 : BTEqualStrategyNumber, F_OIDEQ,
15633 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
15634 2798 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
15635 : true, NULL, 1, &key);
15636 :
15637 : /* inhseqno sequences start at 1 */
15638 2798 : inhseqno = 0;
15639 2842 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
15640 : {
15641 50 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
15642 :
15643 50 : if (inh->inhparent == RelationGetRelid(parent_rel))
15644 6 : ereport(ERROR,
15645 : (errcode(ERRCODE_DUPLICATE_TABLE),
15646 : errmsg("relation \"%s\" would be inherited from more than once",
15647 : RelationGetRelationName(parent_rel))));
15648 :
15649 44 : if (inh->inhseqno > inhseqno)
15650 44 : inhseqno = inh->inhseqno;
15651 : }
15652 2792 : systable_endscan(scan);
15653 :
15654 : /* Match up the columns and bump attinhcount as needed */
15655 2792 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
15656 :
15657 : /* Match up the constraints and bump coninhcount as needed */
15658 2720 : MergeConstraintsIntoExisting(child_rel, parent_rel);
15659 :
15660 : /*
15661 : * OK, it looks valid. Make the catalog entries that show inheritance.
15662 : */
15663 2690 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
15664 : RelationGetRelid(parent_rel),
15665 : inhseqno + 1,
15666 : catalogRelation,
15667 2690 : parent_rel->rd_rel->relkind ==
15668 : RELKIND_PARTITIONED_TABLE);
15669 :
15670 : /* Now we're done with pg_inherits */
15671 2690 : table_close(catalogRelation, RowExclusiveLock);
15672 2690 : }
15673 :
15674 : /*
15675 : * Obtain the source-text form of the constraint expression for a check
15676 : * constraint, given its pg_constraint tuple
15677 : */
15678 : static char *
15679 180 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
15680 : {
15681 : Form_pg_constraint con;
15682 : bool isnull;
15683 : Datum attr;
15684 : Datum expr;
15685 :
15686 180 : con = (Form_pg_constraint) GETSTRUCT(contup);
15687 180 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
15688 180 : if (isnull)
15689 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
15690 :
15691 180 : expr = DirectFunctionCall2(pg_get_expr, attr,
15692 : ObjectIdGetDatum(con->conrelid));
15693 180 : return TextDatumGetCString(expr);
15694 : }
15695 :
15696 : /*
15697 : * Determine whether two check constraints are functionally equivalent
15698 : *
15699 : * The test we apply is to see whether they reverse-compile to the same
15700 : * source string. This insulates us from issues like whether attributes
15701 : * have the same physical column numbers in parent and child relations.
15702 : */
15703 : static bool
15704 90 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
15705 : {
15706 90 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
15707 90 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
15708 :
15709 90 : if (acon->condeferrable != bcon->condeferrable ||
15710 90 : acon->condeferred != bcon->condeferred ||
15711 90 : strcmp(decompile_conbin(a, tupleDesc),
15712 90 : decompile_conbin(b, tupleDesc)) != 0)
15713 6 : return false;
15714 : else
15715 84 : return true;
15716 : }
15717 :
15718 : /*
15719 : * Check columns in child table match up with columns in parent, and increment
15720 : * their attinhcount.
15721 : *
15722 : * Called by CreateInheritance
15723 : *
15724 : * Currently all parent columns must be found in child. Missing columns are an
15725 : * error. One day we might consider creating new columns like CREATE TABLE
15726 : * does. However, that is widely unpopular --- in the common use case of
15727 : * partitioned tables it's a foot-gun.
15728 : *
15729 : * The data type must match exactly. If the parent column is NOT NULL then
15730 : * the child must be as well. Defaults are not compared, however.
15731 : */
15732 : static void
15733 2792 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
15734 : {
15735 : Relation attrrel;
15736 : TupleDesc parent_desc;
15737 :
15738 2792 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
15739 2792 : parent_desc = RelationGetDescr(parent_rel);
15740 :
15741 9532 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
15742 : {
15743 6812 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
15744 6812 : char *parent_attname = NameStr(parent_att->attname);
15745 : HeapTuple tuple;
15746 :
15747 : /* Ignore dropped columns in the parent. */
15748 6812 : if (parent_att->attisdropped)
15749 296 : continue;
15750 :
15751 : /* Find same column in child (matching on column name). */
15752 6516 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
15753 6516 : if (HeapTupleIsValid(tuple))
15754 : {
15755 6504 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
15756 :
15757 6504 : if (parent_att->atttypid != child_att->atttypid ||
15758 6498 : parent_att->atttypmod != child_att->atttypmod)
15759 12 : ereport(ERROR,
15760 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15761 : errmsg("child table \"%s\" has different type for column \"%s\"",
15762 : RelationGetRelationName(child_rel), parent_attname)));
15763 :
15764 6492 : if (parent_att->attcollation != child_att->attcollation)
15765 6 : ereport(ERROR,
15766 : (errcode(ERRCODE_COLLATION_MISMATCH),
15767 : errmsg("child table \"%s\" has different collation for column \"%s\"",
15768 : RelationGetRelationName(child_rel), parent_attname)));
15769 :
15770 : /*
15771 : * Check child doesn't discard NOT NULL property. (Other
15772 : * constraints are checked elsewhere.)
15773 : */
15774 6486 : if (parent_att->attnotnull && !child_att->attnotnull)
15775 12 : ereport(ERROR,
15776 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15777 : errmsg("column \"%s\" in child table must be marked NOT NULL",
15778 : parent_attname)));
15779 :
15780 : /*
15781 : * Child column must be generated if and only if parent column is.
15782 : */
15783 6474 : if (parent_att->attgenerated && !child_att->attgenerated)
15784 18 : ereport(ERROR,
15785 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15786 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
15787 6456 : if (child_att->attgenerated && !parent_att->attgenerated)
15788 12 : ereport(ERROR,
15789 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15790 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
15791 :
15792 : /*
15793 : * Regular inheritance children are independent enough not to
15794 : * inherit identity columns. But partitions are integral part of
15795 : * a partitioned table and inherit identity column.
15796 : */
15797 6444 : if (ispartition)
15798 5984 : child_att->attidentity = parent_att->attidentity;
15799 :
15800 : /*
15801 : * OK, bump the child column's inheritance count. (If we fail
15802 : * later on, this change will just roll back.)
15803 : */
15804 6444 : child_att->attinhcount++;
15805 6444 : if (child_att->attinhcount < 0)
15806 0 : ereport(ERROR,
15807 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15808 : errmsg("too many inheritance parents"));
15809 :
15810 : /*
15811 : * In case of partitions, we must enforce that value of attislocal
15812 : * is same in all partitions. (Note: there are only inherited
15813 : * attributes in partitions)
15814 : */
15815 6444 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15816 : {
15817 : Assert(child_att->attinhcount == 1);
15818 5984 : child_att->attislocal = false;
15819 : }
15820 :
15821 6444 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
15822 6444 : heap_freetuple(tuple);
15823 : }
15824 : else
15825 : {
15826 12 : ereport(ERROR,
15827 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15828 : errmsg("child table is missing column \"%s\"", parent_attname)));
15829 : }
15830 : }
15831 :
15832 2720 : table_close(attrrel, RowExclusiveLock);
15833 2720 : }
15834 :
15835 : /*
15836 : * Check constraints in child table match up with constraints in parent,
15837 : * and increment their coninhcount.
15838 : *
15839 : * Constraints that are marked ONLY in the parent are ignored.
15840 : *
15841 : * Called by CreateInheritance
15842 : *
15843 : * Currently all constraints in parent must be present in the child. One day we
15844 : * may consider adding new constraints like CREATE TABLE does.
15845 : *
15846 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
15847 : * constraints. As long as tables have more like 10 constraints it shouldn't be
15848 : * a problem though. Even 100 constraints ought not be the end of the world.
15849 : *
15850 : * XXX See MergeWithExistingConstraint too if you change this code.
15851 : */
15852 : static void
15853 2720 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
15854 : {
15855 : Relation constraintrel;
15856 : SysScanDesc parent_scan;
15857 : ScanKeyData parent_key;
15858 : HeapTuple parent_tuple;
15859 2720 : Oid parent_relid = RelationGetRelid(parent_rel);
15860 :
15861 2720 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
15862 :
15863 : /* Outer loop scans through the parent's constraint definitions */
15864 2720 : ScanKeyInit(&parent_key,
15865 : Anum_pg_constraint_conrelid,
15866 : BTEqualStrategyNumber, F_OIDEQ,
15867 : ObjectIdGetDatum(parent_relid));
15868 2720 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
15869 : true, NULL, 1, &parent_key);
15870 :
15871 3528 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
15872 : {
15873 838 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
15874 : SysScanDesc child_scan;
15875 : ScanKeyData child_key;
15876 : HeapTuple child_tuple;
15877 838 : bool found = false;
15878 :
15879 838 : if (parent_con->contype != CONSTRAINT_CHECK)
15880 724 : continue;
15881 :
15882 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
15883 134 : if (parent_con->connoinherit)
15884 20 : continue;
15885 :
15886 : /* Search for a child constraint matching this one */
15887 114 : ScanKeyInit(&child_key,
15888 : Anum_pg_constraint_conrelid,
15889 : BTEqualStrategyNumber, F_OIDEQ,
15890 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
15891 114 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
15892 : true, NULL, 1, &child_key);
15893 :
15894 144 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
15895 : {
15896 120 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
15897 : HeapTuple child_copy;
15898 :
15899 120 : if (child_con->contype != CONSTRAINT_CHECK)
15900 0 : continue;
15901 :
15902 120 : if (strcmp(NameStr(parent_con->conname),
15903 120 : NameStr(child_con->conname)) != 0)
15904 30 : continue;
15905 :
15906 90 : if (!constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
15907 6 : ereport(ERROR,
15908 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15909 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15910 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
15911 :
15912 : /* If the child constraint is "no inherit" then cannot merge */
15913 84 : if (child_con->connoinherit)
15914 0 : ereport(ERROR,
15915 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15916 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15917 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
15918 :
15919 : /*
15920 : * If the child constraint is "not valid" then cannot merge with a
15921 : * valid parent constraint
15922 : */
15923 84 : if (parent_con->convalidated && !child_con->convalidated)
15924 0 : ereport(ERROR,
15925 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15926 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
15927 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
15928 :
15929 : /*
15930 : * OK, bump the child constraint's inheritance count. (If we fail
15931 : * later on, this change will just roll back.)
15932 : */
15933 84 : child_copy = heap_copytuple(child_tuple);
15934 84 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
15935 84 : child_con->coninhcount++;
15936 84 : if (child_con->coninhcount < 0)
15937 0 : ereport(ERROR,
15938 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15939 : errmsg("too many inheritance parents"));
15940 :
15941 : /*
15942 : * In case of partitions, an inherited constraint must be
15943 : * inherited only once since it cannot have multiple parents and
15944 : * it is never considered local.
15945 : */
15946 84 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15947 : {
15948 : Assert(child_con->coninhcount == 1);
15949 68 : child_con->conislocal = false;
15950 : }
15951 :
15952 84 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
15953 84 : heap_freetuple(child_copy);
15954 :
15955 84 : found = true;
15956 84 : break;
15957 : }
15958 :
15959 108 : systable_endscan(child_scan);
15960 :
15961 108 : if (!found)
15962 24 : ereport(ERROR,
15963 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15964 : errmsg("child table is missing constraint \"%s\"",
15965 : NameStr(parent_con->conname))));
15966 : }
15967 :
15968 2690 : systable_endscan(parent_scan);
15969 2690 : table_close(constraintrel, RowExclusiveLock);
15970 2690 : }
15971 :
15972 : /*
15973 : * ALTER TABLE NO INHERIT
15974 : *
15975 : * Return value is the address of the relation that is no longer parent.
15976 : */
15977 : static ObjectAddress
15978 38 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
15979 : {
15980 : ObjectAddress address;
15981 : Relation parent_rel;
15982 :
15983 38 : if (rel->rd_rel->relispartition)
15984 0 : ereport(ERROR,
15985 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15986 : errmsg("cannot change inheritance of a partition")));
15987 :
15988 : /*
15989 : * AccessShareLock on the parent is probably enough, seeing that DROP
15990 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
15991 : * be inspecting the parent's schema.
15992 : */
15993 38 : parent_rel = table_openrv(parent, AccessShareLock);
15994 :
15995 : /*
15996 : * We don't bother to check ownership of the parent table --- ownership of
15997 : * the child is presumed enough rights.
15998 : */
15999 :
16000 : /* Off to RemoveInheritance() where most of the work happens */
16001 38 : RemoveInheritance(rel, parent_rel, false);
16002 :
16003 32 : ObjectAddressSet(address, RelationRelationId,
16004 : RelationGetRelid(parent_rel));
16005 :
16006 : /* keep our lock on the parent relation until commit */
16007 32 : table_close(parent_rel, NoLock);
16008 :
16009 32 : return address;
16010 : }
16011 :
16012 : /*
16013 : * MarkInheritDetached
16014 : *
16015 : * Set inhdetachpending for a partition, for ATExecDetachPartition
16016 : * in concurrent mode. While at it, verify that no other partition is
16017 : * already pending detach.
16018 : */
16019 : static void
16020 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
16021 : {
16022 : Relation catalogRelation;
16023 : SysScanDesc scan;
16024 : ScanKeyData key;
16025 : HeapTuple inheritsTuple;
16026 146 : bool found = false;
16027 :
16028 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16029 :
16030 : /*
16031 : * Find pg_inherits entries by inhparent. (We need to scan them all in
16032 : * order to verify that no other partition is pending detach.)
16033 : */
16034 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16035 146 : ScanKeyInit(&key,
16036 : Anum_pg_inherits_inhparent,
16037 : BTEqualStrategyNumber, F_OIDEQ,
16038 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16039 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16040 : true, NULL, 1, &key);
16041 :
16042 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16043 : {
16044 : Form_pg_inherits inhForm;
16045 :
16046 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16047 286 : if (inhForm->inhdetachpending)
16048 2 : ereport(ERROR,
16049 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16050 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16051 : get_rel_name(inhForm->inhrelid),
16052 : get_namespace_name(parent_rel->rd_rel->relnamespace),
16053 : RelationGetRelationName(parent_rel)),
16054 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16055 :
16056 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
16057 : {
16058 : HeapTuple newtup;
16059 :
16060 144 : newtup = heap_copytuple(inheritsTuple);
16061 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16062 :
16063 144 : CatalogTupleUpdate(catalogRelation,
16064 : &inheritsTuple->t_self,
16065 : newtup);
16066 144 : found = true;
16067 144 : heap_freetuple(newtup);
16068 : /* keep looking, to ensure we catch others pending detach */
16069 : }
16070 : }
16071 :
16072 : /* Done */
16073 144 : systable_endscan(scan);
16074 144 : table_close(catalogRelation, RowExclusiveLock);
16075 :
16076 144 : if (!found)
16077 0 : ereport(ERROR,
16078 : (errcode(ERRCODE_UNDEFINED_TABLE),
16079 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16080 : RelationGetRelationName(child_rel),
16081 : RelationGetRelationName(parent_rel))));
16082 144 : }
16083 :
16084 : /*
16085 : * RemoveInheritance
16086 : *
16087 : * Drop a parent from the child's parents. This just adjusts the attinhcount
16088 : * and attislocal of the columns and removes the pg_inherit and pg_depend
16089 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16090 : *
16091 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16092 : * up attislocal stays true, which means if a child is ever removed from a
16093 : * parent then its columns will never be automatically dropped which may
16094 : * surprise. But at least we'll never surprise by dropping columns someone
16095 : * isn't expecting to be dropped which would actually mean data loss.
16096 : *
16097 : * coninhcount and conislocal for inherited constraints are adjusted in
16098 : * exactly the same way.
16099 : *
16100 : * Common to ATExecDropInherit() and ATExecDetachPartition().
16101 : */
16102 : static void
16103 932 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16104 : {
16105 : Relation catalogRelation;
16106 : SysScanDesc scan;
16107 : ScanKeyData key[3];
16108 : HeapTuple attributeTuple,
16109 : constraintTuple;
16110 : List *connames;
16111 : bool found;
16112 : bool is_partitioning;
16113 :
16114 932 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16115 :
16116 932 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16117 : RelationGetRelid(parent_rel),
16118 : expect_detached,
16119 932 : RelationGetRelationName(child_rel));
16120 932 : if (!found)
16121 : {
16122 24 : if (is_partitioning)
16123 18 : ereport(ERROR,
16124 : (errcode(ERRCODE_UNDEFINED_TABLE),
16125 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16126 : RelationGetRelationName(child_rel),
16127 : RelationGetRelationName(parent_rel))));
16128 : else
16129 6 : ereport(ERROR,
16130 : (errcode(ERRCODE_UNDEFINED_TABLE),
16131 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16132 : RelationGetRelationName(parent_rel),
16133 : RelationGetRelationName(child_rel))));
16134 : }
16135 :
16136 : /*
16137 : * Search through child columns looking for ones matching parent rel
16138 : */
16139 908 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16140 908 : ScanKeyInit(&key[0],
16141 : Anum_pg_attribute_attrelid,
16142 : BTEqualStrategyNumber, F_OIDEQ,
16143 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16144 908 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16145 : true, NULL, 1, key);
16146 8402 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16147 : {
16148 7494 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16149 :
16150 : /* Ignore if dropped or not inherited */
16151 7494 : if (att->attisdropped)
16152 36 : continue;
16153 7458 : if (att->attinhcount <= 0)
16154 5466 : continue;
16155 :
16156 1992 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16157 1992 : NameStr(att->attname)))
16158 : {
16159 : /* Decrement inhcount and possibly set islocal to true */
16160 1980 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
16161 1980 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16162 :
16163 1980 : copy_att->attinhcount--;
16164 1980 : if (copy_att->attinhcount == 0)
16165 1980 : copy_att->attislocal = true;
16166 :
16167 1980 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
16168 1980 : heap_freetuple(copyTuple);
16169 : }
16170 : }
16171 908 : systable_endscan(scan);
16172 908 : table_close(catalogRelation, RowExclusiveLock);
16173 :
16174 : /*
16175 : * Likewise, find inherited check constraints and disinherit them. To do
16176 : * this, we first need a list of the names of the parent's check
16177 : * constraints. (We cheat a bit by only checking for name matches,
16178 : * assuming that the expressions will match.)
16179 : */
16180 908 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16181 908 : ScanKeyInit(&key[0],
16182 : Anum_pg_constraint_conrelid,
16183 : BTEqualStrategyNumber, F_OIDEQ,
16184 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16185 908 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16186 : true, NULL, 1, key);
16187 :
16188 908 : connames = NIL;
16189 :
16190 1174 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16191 : {
16192 266 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16193 :
16194 266 : if (con->contype == CONSTRAINT_CHECK)
16195 18 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
16196 : }
16197 :
16198 908 : systable_endscan(scan);
16199 :
16200 : /* Now scan the child's constraints */
16201 908 : ScanKeyInit(&key[0],
16202 : Anum_pg_constraint_conrelid,
16203 : BTEqualStrategyNumber, F_OIDEQ,
16204 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16205 908 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16206 : true, NULL, 1, key);
16207 :
16208 1382 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16209 : {
16210 474 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16211 : bool match;
16212 :
16213 474 : if (con->contype != CONSTRAINT_CHECK)
16214 296 : continue;
16215 :
16216 178 : match = false;
16217 368 : foreach_ptr(char, chkname, connames)
16218 : {
16219 30 : if (strcmp(NameStr(con->conname), chkname) == 0)
16220 : {
16221 18 : match = true;
16222 18 : break;
16223 : }
16224 : }
16225 :
16226 178 : if (match)
16227 : {
16228 : /* Decrement inhcount and possibly set islocal to true */
16229 18 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
16230 18 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
16231 :
16232 18 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
16233 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
16234 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
16235 :
16236 18 : copy_con->coninhcount--;
16237 18 : if (copy_con->coninhcount == 0)
16238 18 : copy_con->conislocal = true;
16239 :
16240 18 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
16241 18 : heap_freetuple(copyTuple);
16242 : }
16243 : }
16244 :
16245 908 : systable_endscan(scan);
16246 908 : table_close(catalogRelation, RowExclusiveLock);
16247 :
16248 908 : drop_parent_dependency(RelationGetRelid(child_rel),
16249 : RelationRelationId,
16250 : RelationGetRelid(parent_rel),
16251 : child_dependency_type(is_partitioning));
16252 :
16253 : /*
16254 : * Post alter hook of this inherits. Since object_access_hook doesn't take
16255 : * multiple object identifiers, we relay oid of parent relation using
16256 : * auxiliary_id argument.
16257 : */
16258 908 : InvokeObjectPostAlterHookArg(InheritsRelationId,
16259 : RelationGetRelid(child_rel), 0,
16260 : RelationGetRelid(parent_rel), false);
16261 908 : }
16262 :
16263 : /*
16264 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16265 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16266 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16267 : * be TypeRelationId). There's no convenient way to do this, so go trawling
16268 : * through pg_depend.
16269 : */
16270 : static void
16271 920 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
16272 : DependencyType deptype)
16273 : {
16274 : Relation catalogRelation;
16275 : SysScanDesc scan;
16276 : ScanKeyData key[3];
16277 : HeapTuple depTuple;
16278 :
16279 920 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
16280 :
16281 920 : ScanKeyInit(&key[0],
16282 : Anum_pg_depend_classid,
16283 : BTEqualStrategyNumber, F_OIDEQ,
16284 : ObjectIdGetDatum(RelationRelationId));
16285 920 : ScanKeyInit(&key[1],
16286 : Anum_pg_depend_objid,
16287 : BTEqualStrategyNumber, F_OIDEQ,
16288 : ObjectIdGetDatum(relid));
16289 920 : ScanKeyInit(&key[2],
16290 : Anum_pg_depend_objsubid,
16291 : BTEqualStrategyNumber, F_INT4EQ,
16292 : Int32GetDatum(0));
16293 :
16294 920 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
16295 : NULL, 3, key);
16296 :
16297 2808 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
16298 : {
16299 1888 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
16300 :
16301 1888 : if (dep->refclassid == refclassid &&
16302 932 : dep->refobjid == refobjid &&
16303 920 : dep->refobjsubid == 0 &&
16304 920 : dep->deptype == deptype)
16305 920 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
16306 : }
16307 :
16308 920 : systable_endscan(scan);
16309 920 : table_close(catalogRelation, RowExclusiveLock);
16310 920 : }
16311 :
16312 : /*
16313 : * ALTER TABLE OF
16314 : *
16315 : * Attach a table to a composite type, as though it had been created with CREATE
16316 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
16317 : * subject table must not have inheritance parents. These restrictions ensure
16318 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
16319 : *
16320 : * The address of the type is returned.
16321 : */
16322 : static ObjectAddress
16323 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
16324 : {
16325 66 : Oid relid = RelationGetRelid(rel);
16326 : Type typetuple;
16327 : Form_pg_type typeform;
16328 : Oid typeid;
16329 : Relation inheritsRelation,
16330 : relationRelation;
16331 : SysScanDesc scan;
16332 : ScanKeyData key;
16333 : AttrNumber table_attno,
16334 : type_attno;
16335 : TupleDesc typeTupleDesc,
16336 : tableTupleDesc;
16337 : ObjectAddress tableobj,
16338 : typeobj;
16339 : HeapTuple classtuple;
16340 :
16341 : /* Validate the type. */
16342 66 : typetuple = typenameType(NULL, ofTypename, NULL);
16343 66 : check_of_type(typetuple);
16344 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
16345 66 : typeid = typeform->oid;
16346 :
16347 : /* Fail if the table has any inheritance parents. */
16348 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
16349 66 : ScanKeyInit(&key,
16350 : Anum_pg_inherits_inhrelid,
16351 : BTEqualStrategyNumber, F_OIDEQ,
16352 : ObjectIdGetDatum(relid));
16353 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
16354 : true, NULL, 1, &key);
16355 66 : if (HeapTupleIsValid(systable_getnext(scan)))
16356 6 : ereport(ERROR,
16357 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16358 : errmsg("typed tables cannot inherit")));
16359 60 : systable_endscan(scan);
16360 60 : table_close(inheritsRelation, AccessShareLock);
16361 :
16362 : /*
16363 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
16364 : * require that the order also match. However, attnotnull need not match.
16365 : */
16366 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
16367 60 : tableTupleDesc = RelationGetDescr(rel);
16368 60 : table_attno = 1;
16369 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
16370 : {
16371 : Form_pg_attribute type_attr,
16372 : table_attr;
16373 : const char *type_attname,
16374 : *table_attname;
16375 :
16376 : /* Get the next non-dropped type attribute. */
16377 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
16378 154 : if (type_attr->attisdropped)
16379 44 : continue;
16380 110 : type_attname = NameStr(type_attr->attname);
16381 :
16382 : /* Get the next non-dropped table attribute. */
16383 : do
16384 : {
16385 122 : if (table_attno > tableTupleDesc->natts)
16386 6 : ereport(ERROR,
16387 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16388 : errmsg("table is missing column \"%s\"",
16389 : type_attname)));
16390 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
16391 116 : table_attno++;
16392 116 : } while (table_attr->attisdropped);
16393 104 : table_attname = NameStr(table_attr->attname);
16394 :
16395 : /* Compare name. */
16396 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
16397 6 : ereport(ERROR,
16398 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16399 : errmsg("table has column \"%s\" where type requires \"%s\"",
16400 : table_attname, type_attname)));
16401 :
16402 : /* Compare type. */
16403 98 : if (table_attr->atttypid != type_attr->atttypid ||
16404 92 : table_attr->atttypmod != type_attr->atttypmod ||
16405 86 : table_attr->attcollation != type_attr->attcollation)
16406 12 : ereport(ERROR,
16407 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16408 : errmsg("table \"%s\" has different type for column \"%s\"",
16409 : RelationGetRelationName(rel), type_attname)));
16410 : }
16411 36 : ReleaseTupleDesc(typeTupleDesc);
16412 :
16413 : /* Any remaining columns at the end of the table had better be dropped. */
16414 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
16415 : {
16416 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
16417 : table_attno - 1);
16418 :
16419 6 : if (!table_attr->attisdropped)
16420 6 : ereport(ERROR,
16421 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16422 : errmsg("table has extra column \"%s\"",
16423 : NameStr(table_attr->attname))));
16424 : }
16425 :
16426 : /* If the table was already typed, drop the existing dependency. */
16427 30 : if (rel->rd_rel->reloftype)
16428 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16429 : DEPENDENCY_NORMAL);
16430 :
16431 : /* Record a dependency on the new type. */
16432 30 : tableobj.classId = RelationRelationId;
16433 30 : tableobj.objectId = relid;
16434 30 : tableobj.objectSubId = 0;
16435 30 : typeobj.classId = TypeRelationId;
16436 30 : typeobj.objectId = typeid;
16437 30 : typeobj.objectSubId = 0;
16438 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
16439 :
16440 : /* Update pg_class.reloftype */
16441 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
16442 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16443 30 : if (!HeapTupleIsValid(classtuple))
16444 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16445 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
16446 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
16447 :
16448 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16449 :
16450 30 : heap_freetuple(classtuple);
16451 30 : table_close(relationRelation, RowExclusiveLock);
16452 :
16453 30 : ReleaseSysCache(typetuple);
16454 :
16455 30 : return typeobj;
16456 : }
16457 :
16458 : /*
16459 : * ALTER TABLE NOT OF
16460 : *
16461 : * Detach a typed table from its originating type. Just clear reloftype and
16462 : * remove the dependency.
16463 : */
16464 : static void
16465 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
16466 : {
16467 6 : Oid relid = RelationGetRelid(rel);
16468 : Relation relationRelation;
16469 : HeapTuple tuple;
16470 :
16471 6 : if (!OidIsValid(rel->rd_rel->reloftype))
16472 0 : ereport(ERROR,
16473 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16474 : errmsg("\"%s\" is not a typed table",
16475 : RelationGetRelationName(rel))));
16476 :
16477 : /*
16478 : * We don't bother to check ownership of the type --- ownership of the
16479 : * table is presumed enough rights. No lock required on the type, either.
16480 : */
16481 :
16482 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16483 : DEPENDENCY_NORMAL);
16484 :
16485 : /* Clear pg_class.reloftype */
16486 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
16487 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16488 6 : if (!HeapTupleIsValid(tuple))
16489 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16490 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
16491 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
16492 :
16493 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16494 :
16495 6 : heap_freetuple(tuple);
16496 6 : table_close(relationRelation, RowExclusiveLock);
16497 6 : }
16498 :
16499 : /*
16500 : * relation_mark_replica_identity: Update a table's replica identity
16501 : *
16502 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
16503 : * index. Otherwise, it must be InvalidOid.
16504 : *
16505 : * Caller had better hold an exclusive lock on the relation, as the results
16506 : * of running two of these concurrently wouldn't be pretty.
16507 : */
16508 : static void
16509 388 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
16510 : bool is_internal)
16511 : {
16512 : Relation pg_index;
16513 : Relation pg_class;
16514 : HeapTuple pg_class_tuple;
16515 : HeapTuple pg_index_tuple;
16516 : Form_pg_class pg_class_form;
16517 : Form_pg_index pg_index_form;
16518 : ListCell *index;
16519 :
16520 : /*
16521 : * Check whether relreplident has changed, and update it if so.
16522 : */
16523 388 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16524 388 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
16525 : ObjectIdGetDatum(RelationGetRelid(rel)));
16526 388 : if (!HeapTupleIsValid(pg_class_tuple))
16527 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
16528 : RelationGetRelationName(rel));
16529 388 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
16530 388 : if (pg_class_form->relreplident != ri_type)
16531 : {
16532 338 : pg_class_form->relreplident = ri_type;
16533 338 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
16534 : }
16535 388 : table_close(pg_class, RowExclusiveLock);
16536 388 : heap_freetuple(pg_class_tuple);
16537 :
16538 : /*
16539 : * Update the per-index indisreplident flags correctly.
16540 : */
16541 388 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
16542 1048 : foreach(index, RelationGetIndexList(rel))
16543 : {
16544 660 : Oid thisIndexOid = lfirst_oid(index);
16545 660 : bool dirty = false;
16546 :
16547 660 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
16548 : ObjectIdGetDatum(thisIndexOid));
16549 660 : if (!HeapTupleIsValid(pg_index_tuple))
16550 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
16551 660 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
16552 :
16553 660 : if (thisIndexOid == indexOid)
16554 : {
16555 : /* Set the bit if not already set. */
16556 214 : if (!pg_index_form->indisreplident)
16557 : {
16558 196 : dirty = true;
16559 196 : pg_index_form->indisreplident = true;
16560 : }
16561 : }
16562 : else
16563 : {
16564 : /* Unset the bit if set. */
16565 446 : if (pg_index_form->indisreplident)
16566 : {
16567 40 : dirty = true;
16568 40 : pg_index_form->indisreplident = false;
16569 : }
16570 : }
16571 :
16572 660 : if (dirty)
16573 : {
16574 236 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
16575 236 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
16576 : InvalidOid, is_internal);
16577 :
16578 : /*
16579 : * Invalidate the relcache for the table, so that after we commit
16580 : * all sessions will refresh the table's replica identity index
16581 : * before attempting any UPDATE or DELETE on the table. (If we
16582 : * changed the table's pg_class row above, then a relcache inval
16583 : * is already queued due to that; but we might not have.)
16584 : */
16585 236 : CacheInvalidateRelcache(rel);
16586 : }
16587 660 : heap_freetuple(pg_index_tuple);
16588 : }
16589 :
16590 388 : table_close(pg_index, RowExclusiveLock);
16591 388 : }
16592 :
16593 : /*
16594 : * ALTER TABLE <name> REPLICA IDENTITY ...
16595 : */
16596 : static void
16597 436 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
16598 : {
16599 : Oid indexOid;
16600 : Relation indexRel;
16601 : int key;
16602 :
16603 436 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
16604 : {
16605 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16606 6 : return;
16607 : }
16608 430 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
16609 : {
16610 132 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16611 132 : return;
16612 : }
16613 298 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
16614 : {
16615 36 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16616 36 : return;
16617 : }
16618 262 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
16619 : {
16620 : /* fallthrough */ ;
16621 : }
16622 : else
16623 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
16624 :
16625 : /* Check that the index exists */
16626 262 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
16627 262 : if (!OidIsValid(indexOid))
16628 0 : ereport(ERROR,
16629 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16630 : errmsg("index \"%s\" for table \"%s\" does not exist",
16631 : stmt->name, RelationGetRelationName(rel))));
16632 :
16633 262 : indexRel = index_open(indexOid, ShareLock);
16634 :
16635 : /* Check that the index is on the relation we're altering. */
16636 262 : if (indexRel->rd_index == NULL ||
16637 262 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
16638 6 : ereport(ERROR,
16639 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16640 : errmsg("\"%s\" is not an index for table \"%s\"",
16641 : RelationGetRelationName(indexRel),
16642 : RelationGetRelationName(rel))));
16643 : /* The AM must support uniqueness, and the index must in fact be unique. */
16644 256 : if (!indexRel->rd_indam->amcanunique ||
16645 250 : !indexRel->rd_index->indisunique)
16646 12 : ereport(ERROR,
16647 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16648 : errmsg("cannot use non-unique index \"%s\" as replica identity",
16649 : RelationGetRelationName(indexRel))));
16650 : /* Deferred indexes are not guaranteed to be always unique. */
16651 244 : if (!indexRel->rd_index->indimmediate)
16652 12 : ereport(ERROR,
16653 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16654 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
16655 : RelationGetRelationName(indexRel))));
16656 : /* Expression indexes aren't supported. */
16657 232 : if (RelationGetIndexExpressions(indexRel) != NIL)
16658 6 : ereport(ERROR,
16659 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16660 : errmsg("cannot use expression index \"%s\" as replica identity",
16661 : RelationGetRelationName(indexRel))));
16662 : /* Predicate indexes aren't supported. */
16663 226 : if (RelationGetIndexPredicate(indexRel) != NIL)
16664 6 : ereport(ERROR,
16665 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16666 : errmsg("cannot use partial index \"%s\" as replica identity",
16667 : RelationGetRelationName(indexRel))));
16668 :
16669 : /* Check index for nullable columns. */
16670 480 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
16671 : {
16672 266 : int16 attno = indexRel->rd_index->indkey.values[key];
16673 : Form_pg_attribute attr;
16674 :
16675 : /*
16676 : * Reject any other system columns. (Going forward, we'll disallow
16677 : * indexes containing such columns in the first place, but they might
16678 : * exist in older branches.)
16679 : */
16680 266 : if (attno <= 0)
16681 0 : ereport(ERROR,
16682 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
16683 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16684 : RelationGetRelationName(indexRel), attno)));
16685 :
16686 266 : attr = TupleDescAttr(rel->rd_att, attno - 1);
16687 266 : if (!attr->attnotnull)
16688 6 : ereport(ERROR,
16689 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16690 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16691 : RelationGetRelationName(indexRel),
16692 : NameStr(attr->attname))));
16693 : }
16694 :
16695 : /* This index is suitable for use as a replica identity. Mark it. */
16696 214 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
16697 :
16698 214 : index_close(indexRel, NoLock);
16699 : }
16700 :
16701 : /*
16702 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
16703 : */
16704 : static void
16705 294 : ATExecSetRowSecurity(Relation rel, bool rls)
16706 : {
16707 : Relation pg_class;
16708 : Oid relid;
16709 : HeapTuple tuple;
16710 :
16711 294 : relid = RelationGetRelid(rel);
16712 :
16713 : /* Pull the record for this relation and update it */
16714 294 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16715 :
16716 294 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16717 :
16718 294 : if (!HeapTupleIsValid(tuple))
16719 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16720 :
16721 294 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
16722 294 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16723 :
16724 294 : InvokeObjectPostAlterHook(RelationRelationId,
16725 : RelationGetRelid(rel), 0);
16726 :
16727 294 : table_close(pg_class, RowExclusiveLock);
16728 294 : heap_freetuple(tuple);
16729 294 : }
16730 :
16731 : /*
16732 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
16733 : */
16734 : static void
16735 120 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
16736 : {
16737 : Relation pg_class;
16738 : Oid relid;
16739 : HeapTuple tuple;
16740 :
16741 120 : relid = RelationGetRelid(rel);
16742 :
16743 120 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16744 :
16745 120 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16746 :
16747 120 : if (!HeapTupleIsValid(tuple))
16748 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16749 :
16750 120 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
16751 120 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16752 :
16753 120 : InvokeObjectPostAlterHook(RelationRelationId,
16754 : RelationGetRelid(rel), 0);
16755 :
16756 120 : table_close(pg_class, RowExclusiveLock);
16757 120 : heap_freetuple(tuple);
16758 120 : }
16759 :
16760 : /*
16761 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
16762 : */
16763 : static void
16764 50 : ATExecGenericOptions(Relation rel, List *options)
16765 : {
16766 : Relation ftrel;
16767 : ForeignServer *server;
16768 : ForeignDataWrapper *fdw;
16769 : HeapTuple tuple;
16770 : bool isnull;
16771 : Datum repl_val[Natts_pg_foreign_table];
16772 : bool repl_null[Natts_pg_foreign_table];
16773 : bool repl_repl[Natts_pg_foreign_table];
16774 : Datum datum;
16775 : Form_pg_foreign_table tableform;
16776 :
16777 50 : if (options == NIL)
16778 0 : return;
16779 :
16780 50 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
16781 :
16782 50 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
16783 : ObjectIdGetDatum(rel->rd_id));
16784 50 : if (!HeapTupleIsValid(tuple))
16785 0 : ereport(ERROR,
16786 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16787 : errmsg("foreign table \"%s\" does not exist",
16788 : RelationGetRelationName(rel))));
16789 50 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16790 50 : server = GetForeignServer(tableform->ftserver);
16791 50 : fdw = GetForeignDataWrapper(server->fdwid);
16792 :
16793 50 : memset(repl_val, 0, sizeof(repl_val));
16794 50 : memset(repl_null, false, sizeof(repl_null));
16795 50 : memset(repl_repl, false, sizeof(repl_repl));
16796 :
16797 : /* Extract the current options */
16798 50 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
16799 : tuple,
16800 : Anum_pg_foreign_table_ftoptions,
16801 : &isnull);
16802 50 : if (isnull)
16803 4 : datum = PointerGetDatum(NULL);
16804 :
16805 : /* Transform the options */
16806 50 : datum = transformGenericOptions(ForeignTableRelationId,
16807 : datum,
16808 : options,
16809 : fdw->fdwvalidator);
16810 :
16811 48 : if (PointerIsValid(DatumGetPointer(datum)))
16812 48 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
16813 : else
16814 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
16815 :
16816 48 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
16817 :
16818 : /* Everything looks good - update the tuple */
16819 :
16820 48 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
16821 : repl_val, repl_null, repl_repl);
16822 :
16823 48 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
16824 :
16825 : /*
16826 : * Invalidate relcache so that all sessions will refresh any cached plans
16827 : * that might depend on the old options.
16828 : */
16829 48 : CacheInvalidateRelcache(rel);
16830 :
16831 48 : InvokeObjectPostAlterHook(ForeignTableRelationId,
16832 : RelationGetRelid(rel), 0);
16833 :
16834 48 : table_close(ftrel, RowExclusiveLock);
16835 :
16836 48 : heap_freetuple(tuple);
16837 : }
16838 :
16839 : /*
16840 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
16841 : *
16842 : * Return value is the address of the modified column
16843 : */
16844 : static ObjectAddress
16845 68 : ATExecSetCompression(Relation rel,
16846 : const char *column,
16847 : Node *newValue,
16848 : LOCKMODE lockmode)
16849 : {
16850 : Relation attrel;
16851 : HeapTuple tuple;
16852 : Form_pg_attribute atttableform;
16853 : AttrNumber attnum;
16854 : char *compression;
16855 : char cmethod;
16856 : ObjectAddress address;
16857 :
16858 68 : compression = strVal(newValue);
16859 :
16860 68 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
16861 :
16862 : /* copy the cache entry so we can scribble on it below */
16863 68 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
16864 68 : if (!HeapTupleIsValid(tuple))
16865 0 : ereport(ERROR,
16866 : (errcode(ERRCODE_UNDEFINED_COLUMN),
16867 : errmsg("column \"%s\" of relation \"%s\" does not exist",
16868 : column, RelationGetRelationName(rel))));
16869 :
16870 : /* prevent them from altering a system attribute */
16871 68 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16872 68 : attnum = atttableform->attnum;
16873 68 : if (attnum <= 0)
16874 0 : ereport(ERROR,
16875 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16876 : errmsg("cannot alter system column \"%s\"", column)));
16877 :
16878 : /*
16879 : * Check that column type is compressible, then get the attribute
16880 : * compression method code
16881 : */
16882 68 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
16883 :
16884 : /* update pg_attribute entry */
16885 62 : atttableform->attcompression = cmethod;
16886 62 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
16887 :
16888 62 : InvokeObjectPostAlterHook(RelationRelationId,
16889 : RelationGetRelid(rel),
16890 : attnum);
16891 :
16892 : /*
16893 : * Apply the change to indexes as well (only for simple index columns,
16894 : * matching behavior of index.c ConstructTupleDescriptor()).
16895 : */
16896 62 : SetIndexStorageProperties(rel, attrel, attnum,
16897 : false, 0,
16898 : true, cmethod,
16899 : lockmode);
16900 :
16901 62 : heap_freetuple(tuple);
16902 :
16903 62 : table_close(attrel, RowExclusiveLock);
16904 :
16905 : /* make changes visible */
16906 62 : CommandCounterIncrement();
16907 :
16908 62 : ObjectAddressSubSet(address, RelationRelationId,
16909 : RelationGetRelid(rel), attnum);
16910 62 : return address;
16911 : }
16912 :
16913 :
16914 : /*
16915 : * Preparation phase for SET LOGGED/UNLOGGED
16916 : *
16917 : * This verifies that we're not trying to change a temp table. Also,
16918 : * existing foreign key constraints are checked to avoid ending up with
16919 : * permanent tables referencing unlogged tables.
16920 : *
16921 : * Return value is false if the operation is a no-op (in which case the
16922 : * checks are skipped), otherwise true.
16923 : */
16924 : static bool
16925 88 : ATPrepChangePersistence(Relation rel, bool toLogged)
16926 : {
16927 : Relation pg_constraint;
16928 : HeapTuple tuple;
16929 : SysScanDesc scan;
16930 : ScanKeyData skey[1];
16931 :
16932 : /*
16933 : * Disallow changing status for a temp table. Also verify whether we can
16934 : * get away with doing nothing; in such cases we don't need to run the
16935 : * checks below, either.
16936 : */
16937 88 : switch (rel->rd_rel->relpersistence)
16938 : {
16939 0 : case RELPERSISTENCE_TEMP:
16940 0 : ereport(ERROR,
16941 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
16942 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
16943 : RelationGetRelationName(rel)),
16944 : errtable(rel)));
16945 : break;
16946 50 : case RELPERSISTENCE_PERMANENT:
16947 50 : if (toLogged)
16948 : /* nothing to do */
16949 6 : return false;
16950 44 : break;
16951 38 : case RELPERSISTENCE_UNLOGGED:
16952 38 : if (!toLogged)
16953 : /* nothing to do */
16954 6 : return false;
16955 32 : break;
16956 : }
16957 :
16958 : /*
16959 : * Check that the table is not part of any publication when changing to
16960 : * UNLOGGED, as UNLOGGED tables can't be published.
16961 : */
16962 120 : if (!toLogged &&
16963 44 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
16964 0 : ereport(ERROR,
16965 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16966 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
16967 : RelationGetRelationName(rel)),
16968 : errdetail("Unlogged relations cannot be replicated.")));
16969 :
16970 : /*
16971 : * Check existing foreign key constraints to preserve the invariant that
16972 : * permanent tables cannot reference unlogged ones. Self-referencing
16973 : * foreign keys can safely be ignored.
16974 : */
16975 76 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
16976 :
16977 : /*
16978 : * Scan conrelid if changing to permanent, else confrelid. This also
16979 : * determines whether a useful index exists.
16980 : */
16981 76 : ScanKeyInit(&skey[0],
16982 : toLogged ? Anum_pg_constraint_conrelid :
16983 : Anum_pg_constraint_confrelid,
16984 : BTEqualStrategyNumber, F_OIDEQ,
16985 : ObjectIdGetDatum(RelationGetRelid(rel)));
16986 76 : scan = systable_beginscan(pg_constraint,
16987 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
16988 : true, NULL, 1, skey);
16989 :
16990 106 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
16991 : {
16992 42 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
16993 :
16994 42 : if (con->contype == CONSTRAINT_FOREIGN)
16995 : {
16996 : Oid foreignrelid;
16997 : Relation foreignrel;
16998 :
16999 : /* the opposite end of what we used as scankey */
17000 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
17001 :
17002 : /* ignore if self-referencing */
17003 30 : if (RelationGetRelid(rel) == foreignrelid)
17004 12 : continue;
17005 :
17006 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
17007 :
17008 18 : if (toLogged)
17009 : {
17010 6 : if (!RelationIsPermanent(foreignrel))
17011 6 : ereport(ERROR,
17012 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17013 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17014 : RelationGetRelationName(rel),
17015 : RelationGetRelationName(foreignrel)),
17016 : errtableconstraint(rel, NameStr(con->conname))));
17017 : }
17018 : else
17019 : {
17020 12 : if (RelationIsPermanent(foreignrel))
17021 6 : ereport(ERROR,
17022 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17023 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17024 : RelationGetRelationName(rel),
17025 : RelationGetRelationName(foreignrel)),
17026 : errtableconstraint(rel, NameStr(con->conname))));
17027 : }
17028 :
17029 6 : relation_close(foreignrel, AccessShareLock);
17030 : }
17031 : }
17032 :
17033 64 : systable_endscan(scan);
17034 :
17035 64 : table_close(pg_constraint, AccessShareLock);
17036 :
17037 64 : return true;
17038 : }
17039 :
17040 : /*
17041 : * Execute ALTER TABLE SET SCHEMA
17042 : */
17043 : ObjectAddress
17044 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17045 : {
17046 : Relation rel;
17047 : Oid relid;
17048 : Oid oldNspOid;
17049 : Oid nspOid;
17050 : RangeVar *newrv;
17051 : ObjectAddresses *objsMoved;
17052 : ObjectAddress myself;
17053 :
17054 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17055 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
17056 : RangeVarCallbackForAlterRelation,
17057 : (void *) stmt);
17058 :
17059 102 : if (!OidIsValid(relid))
17060 : {
17061 12 : ereport(NOTICE,
17062 : (errmsg("relation \"%s\" does not exist, skipping",
17063 : stmt->relation->relname)));
17064 12 : return InvalidObjectAddress;
17065 : }
17066 :
17067 90 : rel = relation_open(relid, NoLock);
17068 :
17069 90 : oldNspOid = RelationGetNamespace(rel);
17070 :
17071 : /* If it's an owned sequence, disallow moving it by itself. */
17072 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17073 : {
17074 : Oid tableId;
17075 : int32 colId;
17076 :
17077 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17078 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17079 6 : ereport(ERROR,
17080 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17081 : errmsg("cannot move an owned sequence into another schema"),
17082 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
17083 : RelationGetRelationName(rel),
17084 : get_rel_name(tableId))));
17085 : }
17086 :
17087 : /* Get and lock schema OID and check its permissions. */
17088 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17089 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17090 :
17091 : /* common checks on switching namespaces */
17092 84 : CheckSetNamespace(oldNspOid, nspOid);
17093 :
17094 84 : objsMoved = new_object_addresses();
17095 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17096 84 : free_object_addresses(objsMoved);
17097 :
17098 84 : ObjectAddressSet(myself, RelationRelationId, relid);
17099 :
17100 84 : if (oldschema)
17101 84 : *oldschema = oldNspOid;
17102 :
17103 : /* close rel, but keep lock until commit */
17104 84 : relation_close(rel, NoLock);
17105 :
17106 84 : return myself;
17107 : }
17108 :
17109 : /*
17110 : * The guts of relocating a table or materialized view to another namespace:
17111 : * besides moving the relation itself, its dependent objects are relocated to
17112 : * the new schema.
17113 : */
17114 : void
17115 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17116 : ObjectAddresses *objsMoved)
17117 : {
17118 : Relation classRel;
17119 :
17120 : Assert(objsMoved != NULL);
17121 :
17122 : /* OK, modify the pg_class row and pg_depend entry */
17123 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
17124 :
17125 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17126 : nspOid, true, objsMoved);
17127 :
17128 : /* Fix the table's row type too, if it has one */
17129 86 : if (OidIsValid(rel->rd_rel->reltype))
17130 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
17131 : false, /* isImplicitArray */
17132 : false, /* ignoreDependent */
17133 : false, /* errorOnTableType */
17134 : objsMoved);
17135 :
17136 : /* Fix other dependent stuff */
17137 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17138 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17139 : objsMoved, AccessExclusiveLock);
17140 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17141 : false, objsMoved);
17142 :
17143 86 : table_close(classRel, RowExclusiveLock);
17144 86 : }
17145 :
17146 : /*
17147 : * The guts of relocating a relation to another namespace: fix the pg_class
17148 : * entry, and the pg_depend entry if any. Caller must already have
17149 : * opened and write-locked pg_class.
17150 : */
17151 : void
17152 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
17153 : Oid oldNspOid, Oid newNspOid,
17154 : bool hasDependEntry,
17155 : ObjectAddresses *objsMoved)
17156 : {
17157 : HeapTuple classTup;
17158 : Form_pg_class classForm;
17159 : ObjectAddress thisobj;
17160 188 : bool already_done = false;
17161 :
17162 188 : classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
17163 188 : if (!HeapTupleIsValid(classTup))
17164 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
17165 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
17166 :
17167 : Assert(classForm->relnamespace == oldNspOid);
17168 :
17169 188 : thisobj.classId = RelationRelationId;
17170 188 : thisobj.objectId = relOid;
17171 188 : thisobj.objectSubId = 0;
17172 :
17173 : /*
17174 : * If the object has already been moved, don't move it again. If it's
17175 : * already in the right place, don't move it, but still fire the object
17176 : * access hook.
17177 : */
17178 188 : already_done = object_address_present(&thisobj, objsMoved);
17179 188 : if (!already_done && oldNspOid != newNspOid)
17180 : {
17181 : /* check for duplicate name (more friendly than unique-index failure) */
17182 146 : if (get_relname_relid(NameStr(classForm->relname),
17183 : newNspOid) != InvalidOid)
17184 0 : ereport(ERROR,
17185 : (errcode(ERRCODE_DUPLICATE_TABLE),
17186 : errmsg("relation \"%s\" already exists in schema \"%s\"",
17187 : NameStr(classForm->relname),
17188 : get_namespace_name(newNspOid))));
17189 :
17190 : /* classTup is a copy, so OK to scribble on */
17191 146 : classForm->relnamespace = newNspOid;
17192 :
17193 146 : CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
17194 :
17195 : /* Update dependency on schema if caller said so */
17196 250 : if (hasDependEntry &&
17197 104 : changeDependencyFor(RelationRelationId,
17198 : relOid,
17199 : NamespaceRelationId,
17200 : oldNspOid,
17201 : newNspOid) != 1)
17202 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
17203 : NameStr(classForm->relname));
17204 : }
17205 188 : if (!already_done)
17206 : {
17207 188 : add_exact_object_address(&thisobj, objsMoved);
17208 :
17209 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
17210 : }
17211 :
17212 188 : heap_freetuple(classTup);
17213 188 : }
17214 :
17215 : /*
17216 : * Move all indexes for the specified relation to another namespace.
17217 : *
17218 : * Note: we assume adequate permission checking was done by the caller,
17219 : * and that the caller has a suitable lock on the owning relation.
17220 : */
17221 : static void
17222 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
17223 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
17224 : {
17225 : List *indexList;
17226 : ListCell *l;
17227 :
17228 86 : indexList = RelationGetIndexList(rel);
17229 :
17230 132 : foreach(l, indexList)
17231 : {
17232 46 : Oid indexOid = lfirst_oid(l);
17233 : ObjectAddress thisobj;
17234 :
17235 46 : thisobj.classId = RelationRelationId;
17236 46 : thisobj.objectId = indexOid;
17237 46 : thisobj.objectSubId = 0;
17238 :
17239 : /*
17240 : * Note: currently, the index will not have its own dependency on the
17241 : * namespace, so we don't need to do changeDependencyFor(). There's no
17242 : * row type in pg_type, either.
17243 : *
17244 : * XXX this objsMoved test may be pointless -- surely we have a single
17245 : * dependency link from a relation to each index?
17246 : */
17247 46 : if (!object_address_present(&thisobj, objsMoved))
17248 : {
17249 46 : AlterRelationNamespaceInternal(classRel, indexOid,
17250 : oldNspOid, newNspOid,
17251 : false, objsMoved);
17252 46 : add_exact_object_address(&thisobj, objsMoved);
17253 : }
17254 : }
17255 :
17256 86 : list_free(indexList);
17257 86 : }
17258 :
17259 : /*
17260 : * Move all identity and SERIAL-column sequences of the specified relation to another
17261 : * namespace.
17262 : *
17263 : * Note: we assume adequate permission checking was done by the caller,
17264 : * and that the caller has a suitable lock on the owning relation.
17265 : */
17266 : static void
17267 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
17268 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
17269 : LOCKMODE lockmode)
17270 : {
17271 : Relation depRel;
17272 : SysScanDesc scan;
17273 : ScanKeyData key[2];
17274 : HeapTuple tup;
17275 :
17276 : /*
17277 : * SERIAL sequences are those having an auto dependency on one of the
17278 : * table's columns (we don't care *which* column, exactly).
17279 : */
17280 86 : depRel = table_open(DependRelationId, AccessShareLock);
17281 :
17282 86 : ScanKeyInit(&key[0],
17283 : Anum_pg_depend_refclassid,
17284 : BTEqualStrategyNumber, F_OIDEQ,
17285 : ObjectIdGetDatum(RelationRelationId));
17286 86 : ScanKeyInit(&key[1],
17287 : Anum_pg_depend_refobjid,
17288 : BTEqualStrategyNumber, F_OIDEQ,
17289 : ObjectIdGetDatum(RelationGetRelid(rel)));
17290 : /* we leave refobjsubid unspecified */
17291 :
17292 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
17293 : NULL, 2, key);
17294 :
17295 554 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
17296 : {
17297 468 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
17298 : Relation seqRel;
17299 :
17300 : /* skip dependencies other than auto dependencies on columns */
17301 468 : if (depForm->refobjsubid == 0 ||
17302 320 : depForm->classid != RelationRelationId ||
17303 42 : depForm->objsubid != 0 ||
17304 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
17305 426 : continue;
17306 :
17307 : /* Use relation_open just in case it's an index */
17308 42 : seqRel = relation_open(depForm->objid, lockmode);
17309 :
17310 : /* skip non-sequence relations */
17311 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
17312 : {
17313 : /* No need to keep the lock */
17314 0 : relation_close(seqRel, lockmode);
17315 0 : continue;
17316 : }
17317 :
17318 : /* Fix the pg_class and pg_depend entries */
17319 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
17320 : oldNspOid, newNspOid,
17321 : true, objsMoved);
17322 :
17323 : /*
17324 : * Sequences used to have entries in pg_type, but no longer do. If we
17325 : * ever re-instate that, we'll need to move the pg_type entry to the
17326 : * new namespace, too (using AlterTypeNamespaceInternal).
17327 : */
17328 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
17329 :
17330 : /* Now we can close it. Keep the lock till end of transaction. */
17331 42 : relation_close(seqRel, NoLock);
17332 : }
17333 :
17334 86 : systable_endscan(scan);
17335 :
17336 86 : relation_close(depRel, AccessShareLock);
17337 86 : }
17338 :
17339 :
17340 : /*
17341 : * This code supports
17342 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
17343 : *
17344 : * Because we only support this for TEMP tables, it's sufficient to remember
17345 : * the state in a backend-local data structure.
17346 : */
17347 :
17348 : /*
17349 : * Register a newly-created relation's ON COMMIT action.
17350 : */
17351 : void
17352 166 : register_on_commit_action(Oid relid, OnCommitAction action)
17353 : {
17354 : OnCommitItem *oc;
17355 : MemoryContext oldcxt;
17356 :
17357 : /*
17358 : * We needn't bother registering the relation unless there is an ON COMMIT
17359 : * action we need to take.
17360 : */
17361 166 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
17362 24 : return;
17363 :
17364 142 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
17365 :
17366 142 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
17367 142 : oc->relid = relid;
17368 142 : oc->oncommit = action;
17369 142 : oc->creating_subid = GetCurrentSubTransactionId();
17370 142 : oc->deleting_subid = InvalidSubTransactionId;
17371 :
17372 : /*
17373 : * We use lcons() here so that ON COMMIT actions are processed in reverse
17374 : * order of registration. That might not be essential but it seems
17375 : * reasonable.
17376 : */
17377 142 : on_commits = lcons(oc, on_commits);
17378 :
17379 142 : MemoryContextSwitchTo(oldcxt);
17380 : }
17381 :
17382 : /*
17383 : * Unregister any ON COMMIT action when a relation is deleted.
17384 : *
17385 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
17386 : */
17387 : void
17388 42490 : remove_on_commit_action(Oid relid)
17389 : {
17390 : ListCell *l;
17391 :
17392 42624 : foreach(l, on_commits)
17393 : {
17394 264 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17395 :
17396 264 : if (oc->relid == relid)
17397 : {
17398 130 : oc->deleting_subid = GetCurrentSubTransactionId();
17399 130 : break;
17400 : }
17401 : }
17402 42490 : }
17403 :
17404 : /*
17405 : * Perform ON COMMIT actions.
17406 : *
17407 : * This is invoked just before actually committing, since it's possible
17408 : * to encounter errors.
17409 : */
17410 : void
17411 516338 : PreCommit_on_commit_actions(void)
17412 : {
17413 : ListCell *l;
17414 516338 : List *oids_to_truncate = NIL;
17415 516338 : List *oids_to_drop = NIL;
17416 :
17417 517056 : foreach(l, on_commits)
17418 : {
17419 718 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17420 :
17421 : /* Ignore entry if already dropped in this xact */
17422 718 : if (oc->deleting_subid != InvalidSubTransactionId)
17423 68 : continue;
17424 :
17425 650 : switch (oc->oncommit)
17426 : {
17427 0 : case ONCOMMIT_NOOP:
17428 : case ONCOMMIT_PRESERVE_ROWS:
17429 : /* Do nothing (there shouldn't be such entries, actually) */
17430 0 : break;
17431 600 : case ONCOMMIT_DELETE_ROWS:
17432 :
17433 : /*
17434 : * If this transaction hasn't accessed any temporary
17435 : * relations, we can skip truncating ON COMMIT DELETE ROWS
17436 : * tables, as they must still be empty.
17437 : */
17438 600 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
17439 400 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
17440 600 : break;
17441 50 : case ONCOMMIT_DROP:
17442 50 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
17443 50 : break;
17444 : }
17445 718 : }
17446 :
17447 : /*
17448 : * Truncate relations before dropping so that all dependencies between
17449 : * relations are removed after they are worked on. Doing it like this
17450 : * might be a waste as it is possible that a relation being truncated will
17451 : * be dropped anyway due to its parent being dropped, but this makes the
17452 : * code more robust because of not having to re-check that the relation
17453 : * exists at truncation time.
17454 : */
17455 516338 : if (oids_to_truncate != NIL)
17456 334 : heap_truncate(oids_to_truncate);
17457 :
17458 516332 : if (oids_to_drop != NIL)
17459 : {
17460 44 : ObjectAddresses *targetObjects = new_object_addresses();
17461 :
17462 94 : foreach(l, oids_to_drop)
17463 : {
17464 : ObjectAddress object;
17465 :
17466 50 : object.classId = RelationRelationId;
17467 50 : object.objectId = lfirst_oid(l);
17468 50 : object.objectSubId = 0;
17469 :
17470 : Assert(!object_address_present(&object, targetObjects));
17471 :
17472 50 : add_exact_object_address(&object, targetObjects);
17473 : }
17474 :
17475 : /*
17476 : * Object deletion might involve toast table access (to clean up
17477 : * toasted catalog entries), so ensure we have a valid snapshot.
17478 : */
17479 44 : PushActiveSnapshot(GetTransactionSnapshot());
17480 :
17481 : /*
17482 : * Since this is an automatic drop, rather than one directly initiated
17483 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
17484 : */
17485 44 : performMultipleDeletions(targetObjects, DROP_CASCADE,
17486 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
17487 :
17488 44 : PopActiveSnapshot();
17489 :
17490 : #ifdef USE_ASSERT_CHECKING
17491 :
17492 : /*
17493 : * Note that table deletion will call remove_on_commit_action, so the
17494 : * entry should get marked as deleted.
17495 : */
17496 : foreach(l, on_commits)
17497 : {
17498 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17499 :
17500 : if (oc->oncommit != ONCOMMIT_DROP)
17501 : continue;
17502 :
17503 : Assert(oc->deleting_subid != InvalidSubTransactionId);
17504 : }
17505 : #endif
17506 : }
17507 516332 : }
17508 :
17509 : /*
17510 : * Post-commit or post-abort cleanup for ON COMMIT management.
17511 : *
17512 : * All we do here is remove no-longer-needed OnCommitItem entries.
17513 : *
17514 : * During commit, remove entries that were deleted during this transaction;
17515 : * during abort, remove those created during this transaction.
17516 : */
17517 : void
17518 560902 : AtEOXact_on_commit_actions(bool isCommit)
17519 : {
17520 : ListCell *cur_item;
17521 :
17522 561650 : foreach(cur_item, on_commits)
17523 : {
17524 748 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17525 :
17526 850 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
17527 102 : oc->creating_subid != InvalidSubTransactionId)
17528 : {
17529 : /* cur_item must be removed */
17530 142 : on_commits = foreach_delete_current(on_commits, cur_item);
17531 142 : pfree(oc);
17532 : }
17533 : else
17534 : {
17535 : /* cur_item must be preserved */
17536 606 : oc->creating_subid = InvalidSubTransactionId;
17537 606 : oc->deleting_subid = InvalidSubTransactionId;
17538 : }
17539 : }
17540 560902 : }
17541 :
17542 : /*
17543 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
17544 : *
17545 : * During subabort, we can immediately remove entries created during this
17546 : * subtransaction. During subcommit, just relabel entries marked during
17547 : * this subtransaction as being the parent's responsibility.
17548 : */
17549 : void
17550 18086 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
17551 : SubTransactionId parentSubid)
17552 : {
17553 : ListCell *cur_item;
17554 :
17555 18086 : foreach(cur_item, on_commits)
17556 : {
17557 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17558 :
17559 0 : if (!isCommit && oc->creating_subid == mySubid)
17560 : {
17561 : /* cur_item must be removed */
17562 0 : on_commits = foreach_delete_current(on_commits, cur_item);
17563 0 : pfree(oc);
17564 : }
17565 : else
17566 : {
17567 : /* cur_item must be preserved */
17568 0 : if (oc->creating_subid == mySubid)
17569 0 : oc->creating_subid = parentSubid;
17570 0 : if (oc->deleting_subid == mySubid)
17571 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
17572 : }
17573 : }
17574 18086 : }
17575 :
17576 : /*
17577 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
17578 : * the relation to be locked only if (1) it's a plain or partitioned table,
17579 : * materialized view, or TOAST table and (2) the current user is the owner (or
17580 : * the superuser) or has been granted MAINTAIN. This meets the
17581 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
17582 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
17583 : */
17584 : void
17585 976 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
17586 : Oid relId, Oid oldRelId, void *arg)
17587 : {
17588 : char relkind;
17589 : AclResult aclresult;
17590 :
17591 : /* Nothing to do if the relation was not found. */
17592 976 : if (!OidIsValid(relId))
17593 6 : return;
17594 :
17595 : /*
17596 : * If the relation does exist, check whether it's an index. But note that
17597 : * the relation might have been dropped between the time we did the name
17598 : * lookup and now. In that case, there's nothing to do.
17599 : */
17600 970 : relkind = get_rel_relkind(relId);
17601 970 : if (!relkind)
17602 0 : return;
17603 970 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
17604 138 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
17605 28 : ereport(ERROR,
17606 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17607 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
17608 :
17609 : /* Check permissions */
17610 942 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
17611 942 : if (aclresult != ACLCHECK_OK)
17612 30 : aclcheck_error(aclresult,
17613 30 : get_relkind_objtype(get_rel_relkind(relId)),
17614 30 : relation->relname);
17615 : }
17616 :
17617 : /*
17618 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
17619 : */
17620 : static void
17621 1638 : RangeVarCallbackForTruncate(const RangeVar *relation,
17622 : Oid relId, Oid oldRelId, void *arg)
17623 : {
17624 : HeapTuple tuple;
17625 :
17626 : /* Nothing to do if the relation was not found. */
17627 1638 : if (!OidIsValid(relId))
17628 0 : return;
17629 :
17630 1638 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17631 1638 : if (!HeapTupleIsValid(tuple)) /* should not happen */
17632 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
17633 :
17634 1638 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
17635 1634 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
17636 :
17637 1602 : ReleaseSysCache(tuple);
17638 : }
17639 :
17640 : /*
17641 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
17642 : * the owner of the relation, or superuser.
17643 : */
17644 : void
17645 14426 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
17646 : Oid relId, Oid oldRelId, void *arg)
17647 : {
17648 : HeapTuple tuple;
17649 :
17650 : /* Nothing to do if the relation was not found. */
17651 14426 : if (!OidIsValid(relId))
17652 14 : return;
17653 :
17654 14412 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17655 14412 : if (!HeapTupleIsValid(tuple)) /* should not happen */
17656 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
17657 :
17658 14412 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
17659 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
17660 6 : relation->relname);
17661 :
17662 28692 : if (!allowSystemTableMods &&
17663 14286 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
17664 2 : ereport(ERROR,
17665 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17666 : errmsg("permission denied: \"%s\" is a system catalog",
17667 : relation->relname)));
17668 :
17669 14404 : ReleaseSysCache(tuple);
17670 : }
17671 :
17672 : /*
17673 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
17674 : * processing.
17675 : */
17676 : static void
17677 28484 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
17678 : void *arg)
17679 : {
17680 28484 : Node *stmt = (Node *) arg;
17681 : ObjectType reltype;
17682 : HeapTuple tuple;
17683 : Form_pg_class classform;
17684 : AclResult aclresult;
17685 : char relkind;
17686 :
17687 28484 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
17688 28484 : if (!HeapTupleIsValid(tuple))
17689 232 : return; /* concurrently dropped */
17690 28252 : classform = (Form_pg_class) GETSTRUCT(tuple);
17691 28252 : relkind = classform->relkind;
17692 :
17693 : /* Must own relation. */
17694 28252 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
17695 72 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
17696 :
17697 : /* No system table modifications unless explicitly allowed. */
17698 28180 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
17699 28 : ereport(ERROR,
17700 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17701 : errmsg("permission denied: \"%s\" is a system catalog",
17702 : rv->relname)));
17703 :
17704 : /*
17705 : * Extract the specified relation type from the statement parse tree.
17706 : *
17707 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
17708 : * have CREATE rights on the containing namespace.
17709 : */
17710 28152 : if (IsA(stmt, RenameStmt))
17711 : {
17712 486 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
17713 : GetUserId(), ACL_CREATE);
17714 486 : if (aclresult != ACLCHECK_OK)
17715 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
17716 0 : get_namespace_name(classform->relnamespace));
17717 486 : reltype = ((RenameStmt *) stmt)->renameType;
17718 : }
17719 27666 : else if (IsA(stmt, AlterObjectSchemaStmt))
17720 90 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
17721 :
17722 27576 : else if (IsA(stmt, AlterTableStmt))
17723 27576 : reltype = ((AlterTableStmt *) stmt)->objtype;
17724 : else
17725 : {
17726 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
17727 : reltype = OBJECT_TABLE; /* placate compiler */
17728 : }
17729 :
17730 : /*
17731 : * For compatibility with prior releases, we allow ALTER TABLE to be used
17732 : * with most other types of relations (but not composite types). We allow
17733 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
17734 : * otherwise. Otherwise, the user must select the correct form of the
17735 : * command for the relation at issue.
17736 : */
17737 28152 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
17738 0 : ereport(ERROR,
17739 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17740 : errmsg("\"%s\" is not a sequence", rv->relname)));
17741 :
17742 28152 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
17743 0 : ereport(ERROR,
17744 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17745 : errmsg("\"%s\" is not a view", rv->relname)));
17746 :
17747 28152 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
17748 0 : ereport(ERROR,
17749 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17750 : errmsg("\"%s\" is not a materialized view", rv->relname)));
17751 :
17752 28152 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
17753 0 : ereport(ERROR,
17754 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17755 : errmsg("\"%s\" is not a foreign table", rv->relname)));
17756 :
17757 28152 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
17758 0 : ereport(ERROR,
17759 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17760 : errmsg("\"%s\" is not a composite type", rv->relname)));
17761 :
17762 28152 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
17763 : relkind != RELKIND_PARTITIONED_INDEX
17764 32 : && !IsA(stmt, RenameStmt))
17765 6 : ereport(ERROR,
17766 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17767 : errmsg("\"%s\" is not an index", rv->relname)));
17768 :
17769 : /*
17770 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17771 : * TYPE for that.
17772 : */
17773 28146 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
17774 0 : ereport(ERROR,
17775 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17776 : errmsg("\"%s\" is a composite type", rv->relname),
17777 : /* translator: %s is an SQL ALTER command */
17778 : errhint("Use %s instead.",
17779 : "ALTER TYPE")));
17780 :
17781 : /*
17782 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17783 : * to a different schema, such as indexes and TOAST tables.
17784 : */
17785 28146 : if (IsA(stmt, AlterObjectSchemaStmt))
17786 : {
17787 90 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
17788 0 : ereport(ERROR,
17789 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17790 : errmsg("cannot change schema of index \"%s\"",
17791 : rv->relname),
17792 : errhint("Change the schema of the table instead.")));
17793 90 : else if (relkind == RELKIND_COMPOSITE_TYPE)
17794 0 : ereport(ERROR,
17795 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17796 : errmsg("cannot change schema of composite type \"%s\"",
17797 : rv->relname),
17798 : /* translator: %s is an SQL ALTER command */
17799 : errhint("Use %s instead.",
17800 : "ALTER TYPE")));
17801 90 : else if (relkind == RELKIND_TOASTVALUE)
17802 0 : ereport(ERROR,
17803 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17804 : errmsg("cannot change schema of TOAST table \"%s\"",
17805 : rv->relname),
17806 : errhint("Change the schema of the table instead.")));
17807 : }
17808 :
17809 28146 : ReleaseSysCache(tuple);
17810 : }
17811 :
17812 : /*
17813 : * Transform any expressions present in the partition key
17814 : *
17815 : * Returns a transformed PartitionSpec.
17816 : */
17817 : static PartitionSpec *
17818 4924 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
17819 : {
17820 : PartitionSpec *newspec;
17821 : ParseState *pstate;
17822 : ParseNamespaceItem *nsitem;
17823 : ListCell *l;
17824 :
17825 4924 : newspec = makeNode(PartitionSpec);
17826 :
17827 4924 : newspec->strategy = partspec->strategy;
17828 4924 : newspec->partParams = NIL;
17829 4924 : newspec->location = partspec->location;
17830 :
17831 : /* Check valid number of columns for strategy */
17832 7248 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
17833 2324 : list_length(partspec->partParams) != 1)
17834 6 : ereport(ERROR,
17835 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17836 : errmsg("cannot use \"list\" partition strategy with more than one column")));
17837 :
17838 : /*
17839 : * Create a dummy ParseState and insert the target relation as its sole
17840 : * rangetable entry. We need a ParseState for transformExpr.
17841 : */
17842 4918 : pstate = make_parsestate(NULL);
17843 4918 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
17844 : NULL, false, true);
17845 4918 : addNSItemToQuery(pstate, nsitem, true, true, true);
17846 :
17847 : /* take care of any partition expressions */
17848 10274 : foreach(l, partspec->partParams)
17849 : {
17850 5380 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
17851 :
17852 5380 : if (pelem->expr)
17853 : {
17854 : /* Copy, to avoid scribbling on the input */
17855 298 : pelem = copyObject(pelem);
17856 :
17857 : /* Now do parse transformation of the expression */
17858 298 : pelem->expr = transformExpr(pstate, pelem->expr,
17859 : EXPR_KIND_PARTITION_EXPRESSION);
17860 :
17861 : /* we have to fix its collations too */
17862 274 : assign_expr_collations(pstate, pelem->expr);
17863 : }
17864 :
17865 5356 : newspec->partParams = lappend(newspec->partParams, pelem);
17866 : }
17867 :
17868 4894 : return newspec;
17869 : }
17870 :
17871 : /*
17872 : * Compute per-partition-column information from a list of PartitionElems.
17873 : * Expressions in the PartitionElems must be parse-analyzed already.
17874 : */
17875 : static void
17876 4894 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
17877 : List **partexprs, Oid *partopclass, Oid *partcollation,
17878 : PartitionStrategy strategy)
17879 : {
17880 : int attn;
17881 : ListCell *lc;
17882 : Oid am_oid;
17883 :
17884 4894 : attn = 0;
17885 10166 : foreach(lc, partParams)
17886 : {
17887 5356 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
17888 : Oid atttype;
17889 : Oid attcollation;
17890 :
17891 5356 : if (pelem->name != NULL)
17892 : {
17893 : /* Simple attribute reference */
17894 : HeapTuple atttuple;
17895 : Form_pg_attribute attform;
17896 :
17897 5082 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
17898 5082 : pelem->name);
17899 5082 : if (!HeapTupleIsValid(atttuple))
17900 12 : ereport(ERROR,
17901 : (errcode(ERRCODE_UNDEFINED_COLUMN),
17902 : errmsg("column \"%s\" named in partition key does not exist",
17903 : pelem->name),
17904 : parser_errposition(pstate, pelem->location)));
17905 5070 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
17906 :
17907 5070 : if (attform->attnum <= 0)
17908 6 : ereport(ERROR,
17909 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17910 : errmsg("cannot use system column \"%s\" in partition key",
17911 : pelem->name),
17912 : parser_errposition(pstate, pelem->location)));
17913 :
17914 : /*
17915 : * Generated columns cannot work: They are computed after BEFORE
17916 : * triggers, but partition routing is done before all triggers.
17917 : */
17918 5064 : if (attform->attgenerated)
17919 6 : ereport(ERROR,
17920 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17921 : errmsg("cannot use generated column in partition key"),
17922 : errdetail("Column \"%s\" is a generated column.",
17923 : pelem->name),
17924 : parser_errposition(pstate, pelem->location)));
17925 :
17926 5058 : partattrs[attn] = attform->attnum;
17927 5058 : atttype = attform->atttypid;
17928 5058 : attcollation = attform->attcollation;
17929 5058 : ReleaseSysCache(atttuple);
17930 : }
17931 : else
17932 : {
17933 : /* Expression */
17934 274 : Node *expr = pelem->expr;
17935 : char partattname[16];
17936 :
17937 : Assert(expr != NULL);
17938 274 : atttype = exprType(expr);
17939 274 : attcollation = exprCollation(expr);
17940 :
17941 : /*
17942 : * The expression must be of a storable type (e.g., not RECORD).
17943 : * The test is the same as for whether a table column is of a safe
17944 : * type (which is why we needn't check for the non-expression
17945 : * case).
17946 : */
17947 274 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
17948 274 : CheckAttributeType(partattname,
17949 : atttype, attcollation,
17950 : NIL, CHKATYPE_IS_PARTKEY);
17951 :
17952 : /*
17953 : * Strip any top-level COLLATE clause. This ensures that we treat
17954 : * "x COLLATE y" and "(x COLLATE y)" alike.
17955 : */
17956 262 : while (IsA(expr, CollateExpr))
17957 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
17958 :
17959 262 : if (IsA(expr, Var) &&
17960 12 : ((Var *) expr)->varattno > 0)
17961 : {
17962 : /*
17963 : * User wrote "(column)" or "(column COLLATE something)".
17964 : * Treat it like simple attribute anyway.
17965 : */
17966 6 : partattrs[attn] = ((Var *) expr)->varattno;
17967 : }
17968 : else
17969 : {
17970 256 : Bitmapset *expr_attrs = NULL;
17971 : int i;
17972 :
17973 256 : partattrs[attn] = 0; /* marks the column as expression */
17974 256 : *partexprs = lappend(*partexprs, expr);
17975 :
17976 : /*
17977 : * transformPartitionSpec() should have already rejected
17978 : * subqueries, aggregates, window functions, and SRFs, based
17979 : * on the EXPR_KIND_ for partition expressions.
17980 : */
17981 :
17982 : /*
17983 : * Cannot allow system column references, since that would
17984 : * make partition routing impossible: their values won't be
17985 : * known yet when we need to do that.
17986 : */
17987 256 : pull_varattnos(expr, 1, &expr_attrs);
17988 2048 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
17989 : {
17990 1792 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
17991 : expr_attrs))
17992 0 : ereport(ERROR,
17993 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17994 : errmsg("partition key expressions cannot contain system column references")));
17995 : }
17996 :
17997 : /*
17998 : * Generated columns cannot work: They are computed after
17999 : * BEFORE triggers, but partition routing is done before all
18000 : * triggers.
18001 : */
18002 256 : i = -1;
18003 564 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
18004 : {
18005 314 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18006 :
18007 314 : if (attno > 0 &&
18008 308 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18009 6 : ereport(ERROR,
18010 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18011 : errmsg("cannot use generated column in partition key"),
18012 : errdetail("Column \"%s\" is a generated column.",
18013 : get_attname(RelationGetRelid(rel), attno, false)),
18014 : parser_errposition(pstate, pelem->location)));
18015 : }
18016 :
18017 : /*
18018 : * Preprocess the expression before checking for mutability.
18019 : * This is essential for the reasons described in
18020 : * contain_mutable_functions_after_planning. However, we call
18021 : * expression_planner for ourselves rather than using that
18022 : * function, because if constant-folding reduces the
18023 : * expression to a constant, we'd like to know that so we can
18024 : * complain below.
18025 : *
18026 : * Like contain_mutable_functions_after_planning, assume that
18027 : * expression_planner won't scribble on its input, so this
18028 : * won't affect the partexprs entry we saved above.
18029 : */
18030 250 : expr = (Node *) expression_planner((Expr *) expr);
18031 :
18032 : /*
18033 : * Partition expressions cannot contain mutable functions,
18034 : * because a given row must always map to the same partition
18035 : * as long as there is no change in the partition boundary
18036 : * structure.
18037 : */
18038 250 : if (contain_mutable_functions(expr))
18039 6 : ereport(ERROR,
18040 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18041 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
18042 :
18043 : /*
18044 : * While it is not exactly *wrong* for a partition expression
18045 : * to be a constant, it seems better to reject such keys.
18046 : */
18047 244 : if (IsA(expr, Const))
18048 12 : ereport(ERROR,
18049 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18050 : errmsg("cannot use constant expression as partition key")));
18051 : }
18052 : }
18053 :
18054 : /*
18055 : * Apply collation override if any
18056 : */
18057 5296 : if (pelem->collation)
18058 30 : attcollation = get_collation_oid(pelem->collation, false);
18059 :
18060 : /*
18061 : * Check we have a collation iff it's a collatable type. The only
18062 : * expected failures here are (1) COLLATE applied to a noncollatable
18063 : * type, or (2) partition expression had an unresolved collation. But
18064 : * we might as well code this to be a complete consistency check.
18065 : */
18066 5296 : if (type_is_collatable(atttype))
18067 : {
18068 626 : if (!OidIsValid(attcollation))
18069 0 : ereport(ERROR,
18070 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
18071 : errmsg("could not determine which collation to use for partition expression"),
18072 : errhint("Use the COLLATE clause to set the collation explicitly.")));
18073 : }
18074 : else
18075 : {
18076 4670 : if (OidIsValid(attcollation))
18077 0 : ereport(ERROR,
18078 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18079 : errmsg("collations are not supported by type %s",
18080 : format_type_be(atttype))));
18081 : }
18082 :
18083 5296 : partcollation[attn] = attcollation;
18084 :
18085 : /*
18086 : * Identify the appropriate operator class. For list and range
18087 : * partitioning, we use a btree operator class; hash partitioning uses
18088 : * a hash operator class.
18089 : */
18090 5296 : if (strategy == PARTITION_STRATEGY_HASH)
18091 288 : am_oid = HASH_AM_OID;
18092 : else
18093 5008 : am_oid = BTREE_AM_OID;
18094 :
18095 5296 : if (!pelem->opclass)
18096 : {
18097 5164 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18098 :
18099 5164 : if (!OidIsValid(partopclass[attn]))
18100 : {
18101 12 : if (strategy == PARTITION_STRATEGY_HASH)
18102 0 : ereport(ERROR,
18103 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18104 : errmsg("data type %s has no default operator class for access method \"%s\"",
18105 : format_type_be(atttype), "hash"),
18106 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18107 : else
18108 12 : ereport(ERROR,
18109 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18110 : errmsg("data type %s has no default operator class for access method \"%s\"",
18111 : format_type_be(atttype), "btree"),
18112 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18113 : }
18114 : }
18115 : else
18116 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
18117 : atttype,
18118 : am_oid == HASH_AM_OID ? "hash" : "btree",
18119 : am_oid);
18120 :
18121 5272 : attn++;
18122 : }
18123 4810 : }
18124 :
18125 : /*
18126 : * PartConstraintImpliedByRelConstraint
18127 : * Do scanrel's existing constraints imply the partition constraint?
18128 : *
18129 : * "Existing constraints" include its check constraints and column-level
18130 : * not-null constraints. partConstraint describes the partition constraint,
18131 : * in implicit-AND form.
18132 : */
18133 : bool
18134 3060 : PartConstraintImpliedByRelConstraint(Relation scanrel,
18135 : List *partConstraint)
18136 : {
18137 3060 : List *existConstraint = NIL;
18138 3060 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18139 : int i;
18140 :
18141 3060 : if (constr && constr->has_not_null)
18142 : {
18143 740 : int natts = scanrel->rd_att->natts;
18144 :
18145 2374 : for (i = 1; i <= natts; i++)
18146 : {
18147 1634 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18148 :
18149 1634 : if (att->attnotnull && !att->attisdropped)
18150 : {
18151 964 : NullTest *ntest = makeNode(NullTest);
18152 :
18153 964 : ntest->arg = (Expr *) makeVar(1,
18154 : i,
18155 : att->atttypid,
18156 : att->atttypmod,
18157 : att->attcollation,
18158 : 0);
18159 964 : ntest->nulltesttype = IS_NOT_NULL;
18160 :
18161 : /*
18162 : * argisrow=false is correct even for a composite column,
18163 : * because attnotnull does not represent a SQL-spec IS NOT
18164 : * NULL test in such a case, just IS DISTINCT FROM NULL.
18165 : */
18166 964 : ntest->argisrow = false;
18167 964 : ntest->location = -1;
18168 964 : existConstraint = lappend(existConstraint, ntest);
18169 : }
18170 : }
18171 : }
18172 :
18173 3060 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18174 : }
18175 :
18176 : /*
18177 : * ConstraintImpliedByRelConstraint
18178 : * Do scanrel's existing constraints imply the given constraint?
18179 : *
18180 : * testConstraint is the constraint to validate. provenConstraint is a
18181 : * caller-provided list of conditions which this function may assume
18182 : * to be true. Both provenConstraint and testConstraint must be in
18183 : * implicit-AND form, must only contain immutable clauses, and must
18184 : * contain only Vars with varno = 1.
18185 : */
18186 : bool
18187 3896 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
18188 : {
18189 3896 : List *existConstraint = list_copy(provenConstraint);
18190 3896 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18191 : int num_check,
18192 : i;
18193 :
18194 3896 : num_check = (constr != NULL) ? constr->num_check : 0;
18195 4384 : for (i = 0; i < num_check; i++)
18196 : {
18197 : Node *cexpr;
18198 :
18199 : /*
18200 : * If this constraint hasn't been fully validated yet, we must ignore
18201 : * it here.
18202 : */
18203 488 : if (!constr->check[i].ccvalid)
18204 6 : continue;
18205 :
18206 482 : cexpr = stringToNode(constr->check[i].ccbin);
18207 :
18208 : /*
18209 : * Run each expression through const-simplification and
18210 : * canonicalization. It is necessary, because we will be comparing it
18211 : * to similarly-processed partition constraint expressions, and may
18212 : * fail to detect valid matches without this.
18213 : */
18214 482 : cexpr = eval_const_expressions(NULL, cexpr);
18215 482 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
18216 :
18217 482 : existConstraint = list_concat(existConstraint,
18218 482 : make_ands_implicit((Expr *) cexpr));
18219 : }
18220 :
18221 : /*
18222 : * Try to make the proof. Since we are comparing CHECK constraints, we
18223 : * need to use weak implication, i.e., we assume existConstraint is
18224 : * not-false and try to prove the same for testConstraint.
18225 : *
18226 : * Note that predicate_implied_by assumes its first argument is known
18227 : * immutable. That should always be true for both NOT NULL and partition
18228 : * constraints, so we don't test it here.
18229 : */
18230 3896 : return predicate_implied_by(testConstraint, existConstraint, true);
18231 : }
18232 :
18233 : /*
18234 : * QueuePartitionConstraintValidation
18235 : *
18236 : * Add an entry to wqueue to have the given partition constraint validated by
18237 : * Phase 3, for the given relation, and all its children.
18238 : *
18239 : * We first verify whether the given constraint is implied by pre-existing
18240 : * relation constraints; if it is, there's no need to scan the table to
18241 : * validate, so don't queue in that case.
18242 : */
18243 : static void
18244 2430 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
18245 : List *partConstraint,
18246 : bool validate_default)
18247 : {
18248 : /*
18249 : * Based on the table's existing constraints, determine whether or not we
18250 : * may skip scanning the table.
18251 : */
18252 2430 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
18253 : {
18254 90 : if (!validate_default)
18255 68 : ereport(DEBUG1,
18256 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18257 : RelationGetRelationName(scanrel))));
18258 : else
18259 22 : ereport(DEBUG1,
18260 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18261 : RelationGetRelationName(scanrel))));
18262 90 : return;
18263 : }
18264 :
18265 : /*
18266 : * Constraints proved insufficient. For plain relations, queue a
18267 : * validation item now; for partitioned tables, recurse to process each
18268 : * partition.
18269 : */
18270 2340 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
18271 : {
18272 : AlteredTableInfo *tab;
18273 :
18274 : /* Grab a work queue entry. */
18275 1946 : tab = ATGetQueueEntry(wqueue, scanrel);
18276 : Assert(tab->partition_constraint == NULL);
18277 1946 : tab->partition_constraint = (Expr *) linitial(partConstraint);
18278 1946 : tab->validate_default = validate_default;
18279 : }
18280 394 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18281 : {
18282 346 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
18283 : int i;
18284 :
18285 740 : for (i = 0; i < partdesc->nparts; i++)
18286 : {
18287 : Relation part_rel;
18288 : List *thisPartConstraint;
18289 :
18290 : /*
18291 : * This is the minimum lock we need to prevent deadlocks.
18292 : */
18293 394 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
18294 :
18295 : /*
18296 : * Adjust the constraint for scanrel so that it matches this
18297 : * partition's attribute numbers.
18298 : */
18299 : thisPartConstraint =
18300 394 : map_partition_varattnos(partConstraint, 1,
18301 : part_rel, scanrel);
18302 :
18303 394 : QueuePartitionConstraintValidation(wqueue, part_rel,
18304 : thisPartConstraint,
18305 : validate_default);
18306 394 : table_close(part_rel, NoLock); /* keep lock till commit */
18307 : }
18308 : }
18309 : }
18310 :
18311 : /*
18312 : * attachPartitionTable: attach a new partition to the partitioned table
18313 : *
18314 : * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
18315 : * of an ALTER TABLE sequence.
18316 : * rel: partitioned relation;
18317 : * attachrel: relation of attached partition;
18318 : * bound: bounds of attached relation.
18319 : */
18320 : static void
18321 2574 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
18322 : {
18323 : /* OK to create inheritance. Rest of the checks performed there */
18324 2574 : CreateInheritance(attachrel, rel, true);
18325 :
18326 : /* Update the pg_class entry. */
18327 2508 : StorePartitionBound(attachrel, rel, bound);
18328 :
18329 : /* Ensure there exists a correct set of indexes in the partition. */
18330 2508 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
18331 :
18332 : /* and triggers */
18333 2478 : CloneRowTriggersToPartition(rel, attachrel);
18334 :
18335 : /*
18336 : * Clone foreign key constraints. Callee is responsible for setting up
18337 : * for phase 3 constraint verification.
18338 : */
18339 2472 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
18340 2472 : }
18341 :
18342 : /*
18343 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
18344 : *
18345 : * Return the address of the newly attached partition.
18346 : */
18347 : static ObjectAddress
18348 2190 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
18349 : AlterTableUtilityContext *context)
18350 : {
18351 : Relation attachrel,
18352 : catalog;
18353 : List *attachrel_children;
18354 : List *partConstraint;
18355 : SysScanDesc scan;
18356 : ScanKeyData skey;
18357 : AttrNumber attno;
18358 : int natts;
18359 : TupleDesc tupleDesc;
18360 : ObjectAddress address;
18361 : const char *trigger_name;
18362 : Oid defaultPartOid;
18363 : List *partBoundConstraint;
18364 2190 : ParseState *pstate = make_parsestate(NULL);
18365 :
18366 2190 : pstate->p_sourcetext = context->queryString;
18367 :
18368 : /*
18369 : * We must lock the default partition if one exists, because attaching a
18370 : * new partition will change its partition constraint.
18371 : */
18372 : defaultPartOid =
18373 2190 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
18374 2190 : if (OidIsValid(defaultPartOid))
18375 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
18376 :
18377 2190 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
18378 :
18379 : /*
18380 : * XXX I think it'd be a good idea to grab locks on all tables referenced
18381 : * by FKs at this point also.
18382 : */
18383 :
18384 : /*
18385 : * Must be owner of both parent and source table -- parent was checked by
18386 : * ATSimplePermissions call in ATPrepCmd
18387 : */
18388 2184 : ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
18389 :
18390 : /* A partition can only have one parent */
18391 2178 : if (attachrel->rd_rel->relispartition)
18392 6 : ereport(ERROR,
18393 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18394 : errmsg("\"%s\" is already a partition",
18395 : RelationGetRelationName(attachrel))));
18396 :
18397 2172 : if (OidIsValid(attachrel->rd_rel->reloftype))
18398 6 : ereport(ERROR,
18399 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18400 : errmsg("cannot attach a typed table as partition")));
18401 :
18402 : /*
18403 : * Table being attached should not already be part of inheritance; either
18404 : * as a child table...
18405 : */
18406 2166 : catalog = table_open(InheritsRelationId, AccessShareLock);
18407 2166 : ScanKeyInit(&skey,
18408 : Anum_pg_inherits_inhrelid,
18409 : BTEqualStrategyNumber, F_OIDEQ,
18410 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
18411 2166 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
18412 : NULL, 1, &skey);
18413 2166 : if (HeapTupleIsValid(systable_getnext(scan)))
18414 6 : ereport(ERROR,
18415 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18416 : errmsg("cannot attach inheritance child as partition")));
18417 2160 : systable_endscan(scan);
18418 :
18419 : /* ...or as a parent table (except the case when it is partitioned) */
18420 2160 : ScanKeyInit(&skey,
18421 : Anum_pg_inherits_inhparent,
18422 : BTEqualStrategyNumber, F_OIDEQ,
18423 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
18424 2160 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
18425 : 1, &skey);
18426 2160 : if (HeapTupleIsValid(systable_getnext(scan)) &&
18427 248 : attachrel->rd_rel->relkind == RELKIND_RELATION)
18428 6 : ereport(ERROR,
18429 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18430 : errmsg("cannot attach inheritance parent as partition")));
18431 2154 : systable_endscan(scan);
18432 2154 : table_close(catalog, AccessShareLock);
18433 :
18434 : /*
18435 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
18436 : * particular, this disallows making a rel a partition of itself.)
18437 : *
18438 : * We do that by checking if rel is a member of the list of attachrel's
18439 : * partitions provided the latter is partitioned at all. We want to avoid
18440 : * having to construct this list again, so we request the strongest lock
18441 : * on all partitions. We need the strongest lock, because we may decide
18442 : * to scan them if we find out that the table being attached (or its leaf
18443 : * partitions) may contain rows that violate the partition constraint. If
18444 : * the table has a constraint that would prevent such rows, which by
18445 : * definition is present in all the partitions, we need not scan the
18446 : * table, nor its partitions. But we cannot risk a deadlock by taking a
18447 : * weaker lock now and the stronger one only when needed.
18448 : */
18449 2154 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
18450 : AccessExclusiveLock, NULL);
18451 2154 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
18452 12 : ereport(ERROR,
18453 : (errcode(ERRCODE_DUPLICATE_TABLE),
18454 : errmsg("circular inheritance not allowed"),
18455 : errdetail("\"%s\" is already a child of \"%s\".",
18456 : RelationGetRelationName(rel),
18457 : RelationGetRelationName(attachrel))));
18458 :
18459 : /* If the parent is permanent, so must be all of its partitions. */
18460 2142 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
18461 2100 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
18462 6 : ereport(ERROR,
18463 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18464 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
18465 : RelationGetRelationName(rel))));
18466 :
18467 : /* Temp parent cannot have a partition that is itself not a temp */
18468 2136 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18469 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
18470 18 : ereport(ERROR,
18471 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18472 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
18473 : RelationGetRelationName(rel))));
18474 :
18475 : /* If the parent is temp, it must belong to this session */
18476 2118 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18477 24 : !rel->rd_islocaltemp)
18478 0 : ereport(ERROR,
18479 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18480 : errmsg("cannot attach as partition of temporary relation of another session")));
18481 :
18482 : /* Ditto for the partition */
18483 2118 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18484 24 : !attachrel->rd_islocaltemp)
18485 0 : ereport(ERROR,
18486 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18487 : errmsg("cannot attach temporary relation of another session as partition")));
18488 :
18489 : /*
18490 : * Check if attachrel has any identity columns or any columns that aren't
18491 : * in the parent.
18492 : */
18493 2118 : tupleDesc = RelationGetDescr(attachrel);
18494 2118 : natts = tupleDesc->natts;
18495 7284 : for (attno = 1; attno <= natts; attno++)
18496 : {
18497 5202 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
18498 5202 : char *attributeName = NameStr(attribute->attname);
18499 :
18500 : /* Ignore dropped */
18501 5202 : if (attribute->attisdropped)
18502 592 : continue;
18503 :
18504 4610 : if (attribute->attidentity)
18505 18 : ereport(ERROR,
18506 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18507 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
18508 : RelationGetRelationName(attachrel), attributeName),
18509 : errdetail("The new partition may not contain an identity column."));
18510 :
18511 : /* Try to find the column in parent (matching on column name) */
18512 4592 : if (!SearchSysCacheExists2(ATTNAME,
18513 : ObjectIdGetDatum(RelationGetRelid(rel)),
18514 : CStringGetDatum(attributeName)))
18515 18 : ereport(ERROR,
18516 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18517 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
18518 : RelationGetRelationName(attachrel), attributeName,
18519 : RelationGetRelationName(rel)),
18520 : errdetail("The new partition may contain only the columns present in parent.")));
18521 : }
18522 :
18523 : /*
18524 : * If child_rel has row-level triggers with transition tables, we
18525 : * currently don't allow it to become a partition. See also prohibitions
18526 : * in ATExecAddInherit() and CreateTrigger().
18527 : */
18528 2082 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
18529 2082 : if (trigger_name != NULL)
18530 6 : ereport(ERROR,
18531 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18532 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
18533 : trigger_name, RelationGetRelationName(attachrel)),
18534 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
18535 :
18536 : /*
18537 : * Check that the new partition's bound is valid and does not overlap any
18538 : * of existing partitions of the parent - note that it does not return on
18539 : * error.
18540 : */
18541 2076 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
18542 : cmd->bound, pstate);
18543 :
18544 : /* Attach a new partition to the partitioned table. */
18545 2040 : attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
18546 :
18547 : /*
18548 : * Generate partition constraint from the partition bound specification.
18549 : * If the parent itself is a partition, make sure to include its
18550 : * constraint as well.
18551 : */
18552 1938 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
18553 1938 : partConstraint = list_concat(partBoundConstraint,
18554 1938 : RelationGetPartitionQual(rel));
18555 :
18556 : /* Skip validation if there are no constraints to validate. */
18557 1938 : if (partConstraint)
18558 : {
18559 : /*
18560 : * Run the partition quals through const-simplification similar to
18561 : * check constraints. We skip canonicalize_qual, though, because
18562 : * partition quals should be in canonical form already.
18563 : */
18564 : partConstraint =
18565 1890 : (List *) eval_const_expressions(NULL,
18566 : (Node *) partConstraint);
18567 :
18568 : /* XXX this sure looks wrong */
18569 1890 : partConstraint = list_make1(make_ands_explicit(partConstraint));
18570 :
18571 : /*
18572 : * Adjust the generated constraint to match this partition's attribute
18573 : * numbers.
18574 : */
18575 1890 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
18576 : rel);
18577 :
18578 : /* Validate partition constraints against the table being attached. */
18579 1890 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
18580 : false);
18581 : }
18582 :
18583 : /*
18584 : * If we're attaching a partition other than the default partition and a
18585 : * default one exists, then that partition's partition constraint changes,
18586 : * so add an entry to the work queue to validate it, too. (We must not do
18587 : * this when the partition being attached is the default one; we already
18588 : * did it above!)
18589 : */
18590 1938 : if (OidIsValid(defaultPartOid))
18591 : {
18592 : Relation defaultrel;
18593 : List *defPartConstraint;
18594 :
18595 : Assert(!cmd->bound->is_default);
18596 :
18597 : /* we already hold a lock on the default partition */
18598 146 : defaultrel = table_open(defaultPartOid, NoLock);
18599 : defPartConstraint =
18600 146 : get_proposed_default_constraint(partBoundConstraint);
18601 :
18602 : /*
18603 : * Map the Vars in the constraint expression from rel's attnos to
18604 : * defaultrel's.
18605 : */
18606 : defPartConstraint =
18607 146 : map_partition_varattnos(defPartConstraint,
18608 : 1, defaultrel, rel);
18609 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
18610 : defPartConstraint, true);
18611 :
18612 : /* keep our lock until commit. */
18613 146 : table_close(defaultrel, NoLock);
18614 : }
18615 :
18616 1938 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
18617 :
18618 : /*
18619 : * If the partition we just attached is partitioned itself, invalidate
18620 : * relcache for all descendent partitions too to ensure that their
18621 : * rd_partcheck expression trees are rebuilt; partitions already locked at
18622 : * the beginning of this function.
18623 : */
18624 1938 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18625 : {
18626 : ListCell *l;
18627 :
18628 996 : foreach(l, attachrel_children)
18629 : {
18630 674 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
18631 : }
18632 : }
18633 :
18634 : /* keep our lock until commit */
18635 1938 : table_close(attachrel, NoLock);
18636 :
18637 1938 : return address;
18638 : }
18639 :
18640 : /*
18641 : * AttachPartitionEnsureIndexes
18642 : * subroutine for ATExecAttachPartition to create/match indexes
18643 : *
18644 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
18645 : * PARTITION: every partition must have an index attached to each index on the
18646 : * partitioned table.
18647 : */
18648 : static void
18649 2508 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
18650 : {
18651 : List *idxes;
18652 : List *attachRelIdxs;
18653 : Relation *attachrelIdxRels;
18654 : IndexInfo **attachInfos;
18655 : ListCell *cell;
18656 : MemoryContext cxt;
18657 : MemoryContext oldcxt;
18658 :
18659 2508 : cxt = AllocSetContextCreate(CurrentMemoryContext,
18660 : "AttachPartitionEnsureIndexes",
18661 : ALLOCSET_DEFAULT_SIZES);
18662 2508 : oldcxt = MemoryContextSwitchTo(cxt);
18663 :
18664 2508 : idxes = RelationGetIndexList(rel);
18665 2508 : attachRelIdxs = RelationGetIndexList(attachrel);
18666 2508 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
18667 2508 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
18668 :
18669 : /* Build arrays of all existing indexes and their IndexInfos */
18670 5368 : foreach_oid(cldIdxId, attachRelIdxs)
18671 : {
18672 352 : int i = foreach_current_index(cldIdxId);
18673 :
18674 352 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
18675 352 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
18676 : }
18677 :
18678 : /*
18679 : * If we're attaching a foreign table, we must fail if any of the indexes
18680 : * is a constraint index; otherwise, there's nothing to do here. Do this
18681 : * before starting work, to avoid wasting the effort of building a few
18682 : * non-unique indexes before coming across a unique one.
18683 : */
18684 2508 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
18685 : {
18686 86 : foreach(cell, idxes)
18687 : {
18688 36 : Oid idx = lfirst_oid(cell);
18689 36 : Relation idxRel = index_open(idx, AccessShareLock);
18690 :
18691 36 : if (idxRel->rd_index->indisunique ||
18692 24 : idxRel->rd_index->indisprimary)
18693 12 : ereport(ERROR,
18694 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18695 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18696 : RelationGetRelationName(attachrel),
18697 : RelationGetRelationName(rel)),
18698 : errdetail("Partitioned table \"%s\" contains unique indexes.",
18699 : RelationGetRelationName(rel))));
18700 24 : index_close(idxRel, AccessShareLock);
18701 : }
18702 :
18703 50 : goto out;
18704 : }
18705 :
18706 : /*
18707 : * For each index on the partitioned table, find a matching one in the
18708 : * partition-to-be; if one is not found, create one.
18709 : */
18710 3084 : foreach(cell, idxes)
18711 : {
18712 656 : Oid idx = lfirst_oid(cell);
18713 656 : Relation idxRel = index_open(idx, AccessShareLock);
18714 : IndexInfo *info;
18715 : AttrMap *attmap;
18716 656 : bool found = false;
18717 : Oid constraintOid;
18718 :
18719 : /*
18720 : * Ignore indexes in the partitioned table other than partitioned
18721 : * indexes.
18722 : */
18723 656 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
18724 : {
18725 0 : index_close(idxRel, AccessShareLock);
18726 0 : continue;
18727 : }
18728 :
18729 : /* construct an indexinfo to compare existing indexes against */
18730 656 : info = BuildIndexInfo(idxRel);
18731 656 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
18732 : RelationGetDescr(rel),
18733 : false);
18734 656 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
18735 :
18736 : /*
18737 : * Scan the list of existing indexes in the partition-to-be, and mark
18738 : * the first matching, valid, unattached one we find, if any, as
18739 : * partition of the parent index. If we find one, we're done.
18740 : */
18741 716 : for (int i = 0; i < list_length(attachRelIdxs); i++)
18742 : {
18743 262 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
18744 262 : Oid cldConstrOid = InvalidOid;
18745 :
18746 : /* does this index have a parent? if so, can't use it */
18747 262 : if (attachrelIdxRels[i]->rd_rel->relispartition)
18748 12 : continue;
18749 :
18750 : /* If this index is invalid, can't use it */
18751 250 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
18752 6 : continue;
18753 :
18754 244 : if (CompareIndexInfo(attachInfos[i], info,
18755 244 : attachrelIdxRels[i]->rd_indcollation,
18756 244 : idxRel->rd_indcollation,
18757 244 : attachrelIdxRels[i]->rd_opfamily,
18758 244 : idxRel->rd_opfamily,
18759 : attmap))
18760 : {
18761 : /*
18762 : * If this index is being created in the parent because of a
18763 : * constraint, then the child needs to have a constraint also,
18764 : * so look for one. If there is no such constraint, this
18765 : * index is no good, so keep looking.
18766 : */
18767 208 : if (OidIsValid(constraintOid))
18768 : {
18769 : cldConstrOid =
18770 110 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
18771 : cldIdxId);
18772 : /* no dice */
18773 110 : if (!OidIsValid(cldConstrOid))
18774 6 : continue;
18775 :
18776 : /* Ensure they're both the same type of constraint */
18777 208 : if (get_constraint_type(constraintOid) !=
18778 104 : get_constraint_type(cldConstrOid))
18779 0 : continue;
18780 : }
18781 :
18782 : /* bingo. */
18783 202 : IndexSetParentIndex(attachrelIdxRels[i], idx);
18784 202 : if (OidIsValid(constraintOid))
18785 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
18786 : RelationGetRelid(attachrel));
18787 202 : found = true;
18788 :
18789 202 : CommandCounterIncrement();
18790 202 : break;
18791 : }
18792 : }
18793 :
18794 : /*
18795 : * If no suitable index was found in the partition-to-be, create one
18796 : * now.
18797 : */
18798 656 : if (!found)
18799 : {
18800 : IndexStmt *stmt;
18801 : Oid conOid;
18802 :
18803 454 : stmt = generateClonedIndexStmt(NULL,
18804 : idxRel, attmap,
18805 : &conOid);
18806 454 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
18807 : RelationGetRelid(idxRel),
18808 : conOid,
18809 : -1,
18810 : true, false, false, false, false);
18811 : }
18812 :
18813 638 : index_close(idxRel, AccessShareLock);
18814 : }
18815 :
18816 2478 : out:
18817 : /* Clean up. */
18818 2818 : for (int i = 0; i < list_length(attachRelIdxs); i++)
18819 340 : index_close(attachrelIdxRels[i], AccessShareLock);
18820 2478 : MemoryContextSwitchTo(oldcxt);
18821 2478 : MemoryContextDelete(cxt);
18822 2478 : }
18823 :
18824 : /*
18825 : * CloneRowTriggersToPartition
18826 : * subroutine for ATExecAttachPartition/DefineRelation to create row
18827 : * triggers on partitions
18828 : */
18829 : static void
18830 2892 : CloneRowTriggersToPartition(Relation parent, Relation partition)
18831 : {
18832 : Relation pg_trigger;
18833 : ScanKeyData key;
18834 : SysScanDesc scan;
18835 : HeapTuple tuple;
18836 : MemoryContext perTupCxt;
18837 :
18838 2892 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
18839 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
18840 2892 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
18841 2892 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
18842 : true, NULL, 1, &key);
18843 :
18844 2892 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
18845 : "clone trig", ALLOCSET_SMALL_SIZES);
18846 :
18847 4448 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18848 : {
18849 1562 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
18850 : CreateTrigStmt *trigStmt;
18851 1562 : Node *qual = NULL;
18852 : Datum value;
18853 : bool isnull;
18854 1562 : List *cols = NIL;
18855 1562 : List *trigargs = NIL;
18856 : MemoryContext oldcxt;
18857 :
18858 : /*
18859 : * Ignore statement-level triggers; those are not cloned.
18860 : */
18861 1562 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
18862 1382 : continue;
18863 :
18864 : /*
18865 : * Don't clone internal triggers, because the constraint cloning code
18866 : * will.
18867 : */
18868 1538 : if (trigForm->tgisinternal)
18869 1358 : continue;
18870 :
18871 : /*
18872 : * Complain if we find an unexpected trigger type.
18873 : */
18874 180 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
18875 162 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
18876 0 : elog(ERROR, "unexpected trigger \"%s\" found",
18877 : NameStr(trigForm->tgname));
18878 :
18879 : /* Use short-lived context for CREATE TRIGGER */
18880 180 : oldcxt = MemoryContextSwitchTo(perTupCxt);
18881 :
18882 : /*
18883 : * If there is a WHEN clause, generate a 'cooked' version of it that's
18884 : * appropriate for the partition.
18885 : */
18886 180 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
18887 : RelationGetDescr(pg_trigger), &isnull);
18888 180 : if (!isnull)
18889 : {
18890 6 : qual = stringToNode(TextDatumGetCString(value));
18891 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
18892 : partition, parent);
18893 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
18894 : partition, parent);
18895 : }
18896 :
18897 : /*
18898 : * If there is a column list, transform it to a list of column names.
18899 : * Note we don't need to map this list in any way ...
18900 : */
18901 180 : if (trigForm->tgattr.dim1 > 0)
18902 : {
18903 : int i;
18904 :
18905 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
18906 : {
18907 : Form_pg_attribute col;
18908 :
18909 6 : col = TupleDescAttr(parent->rd_att,
18910 : trigForm->tgattr.values[i] - 1);
18911 6 : cols = lappend(cols,
18912 6 : makeString(pstrdup(NameStr(col->attname))));
18913 : }
18914 : }
18915 :
18916 : /* Reconstruct trigger arguments list. */
18917 180 : if (trigForm->tgnargs > 0)
18918 : {
18919 : char *p;
18920 :
18921 36 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
18922 : RelationGetDescr(pg_trigger), &isnull);
18923 36 : if (isnull)
18924 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
18925 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
18926 :
18927 36 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
18928 :
18929 84 : for (int i = 0; i < trigForm->tgnargs; i++)
18930 : {
18931 48 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
18932 48 : p += strlen(p) + 1;
18933 : }
18934 : }
18935 :
18936 180 : trigStmt = makeNode(CreateTrigStmt);
18937 180 : trigStmt->replace = false;
18938 180 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
18939 180 : trigStmt->trigname = NameStr(trigForm->tgname);
18940 180 : trigStmt->relation = NULL;
18941 180 : trigStmt->funcname = NULL; /* passed separately */
18942 180 : trigStmt->args = trigargs;
18943 180 : trigStmt->row = true;
18944 180 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
18945 180 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
18946 180 : trigStmt->columns = cols;
18947 180 : trigStmt->whenClause = NULL; /* passed separately */
18948 180 : trigStmt->transitionRels = NIL; /* not supported at present */
18949 180 : trigStmt->deferrable = trigForm->tgdeferrable;
18950 180 : trigStmt->initdeferred = trigForm->tginitdeferred;
18951 180 : trigStmt->constrrel = NULL; /* passed separately */
18952 :
18953 180 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
18954 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
18955 : trigForm->tgfoid, trigForm->oid, qual,
18956 180 : false, true, trigForm->tgenabled);
18957 :
18958 174 : MemoryContextSwitchTo(oldcxt);
18959 174 : MemoryContextReset(perTupCxt);
18960 : }
18961 :
18962 2886 : MemoryContextDelete(perTupCxt);
18963 :
18964 2886 : systable_endscan(scan);
18965 2886 : table_close(pg_trigger, RowExclusiveLock);
18966 2886 : }
18967 :
18968 : /*
18969 : * ALTER TABLE DETACH PARTITION
18970 : *
18971 : * Return the address of the relation that is no longer a partition of rel.
18972 : *
18973 : * If concurrent mode is requested, we run in two transactions. A side-
18974 : * effect is that this command cannot run in a multi-part ALTER TABLE.
18975 : * Currently, that's enforced by the grammar.
18976 : *
18977 : * The strategy for concurrency is to first modify the partition's
18978 : * pg_inherit catalog row to make it visible to everyone that the
18979 : * partition is detached, lock the partition against writes, and commit
18980 : * the transaction; anyone who requests the partition descriptor from
18981 : * that point onwards has to ignore such a partition. In a second
18982 : * transaction, we wait until all transactions that could have seen the
18983 : * partition as attached are gone, then we remove the rest of partition
18984 : * metadata (pg_inherits and pg_class.relpartbounds).
18985 : */
18986 : static ObjectAddress
18987 540 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
18988 : RangeVar *name, bool concurrent)
18989 : {
18990 : Relation partRel;
18991 : ObjectAddress address;
18992 : Oid defaultPartOid;
18993 :
18994 : /*
18995 : * We must lock the default partition, because detaching this partition
18996 : * will change its partition constraint.
18997 : */
18998 : defaultPartOid =
18999 540 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19000 540 : if (OidIsValid(defaultPartOid))
19001 : {
19002 : /*
19003 : * Concurrent detaching when a default partition exists is not
19004 : * supported. The main problem is that the default partition
19005 : * constraint would change. And there's a definitional problem: what
19006 : * should happen to the tuples that are being inserted that belong to
19007 : * the partition being detached? Putting them on the partition being
19008 : * detached would be wrong, since they'd become "lost" after the
19009 : * detaching completes but we cannot put them in the default partition
19010 : * either until we alter its partition constraint.
19011 : *
19012 : * I think we could solve this problem if we effected the constraint
19013 : * change before committing the first transaction. But the lock would
19014 : * have to remain AEL and it would cause concurrent query planning to
19015 : * be blocked, so changing it that way would be even worse.
19016 : */
19017 112 : if (concurrent)
19018 12 : ereport(ERROR,
19019 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19020 : errmsg("cannot detach partitions concurrently when a default partition exists")));
19021 100 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19022 : }
19023 :
19024 : /*
19025 : * In concurrent mode, the partition is locked with share-update-exclusive
19026 : * in the first transaction. This allows concurrent transactions to be
19027 : * doing DML to the partition.
19028 : */
19029 528 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19030 : AccessExclusiveLock);
19031 :
19032 : /*
19033 : * Check inheritance conditions and either delete the pg_inherits row (in
19034 : * non-concurrent mode) or just set the inhdetachpending flag.
19035 : */
19036 516 : if (!concurrent)
19037 370 : RemoveInheritance(partRel, rel, false);
19038 : else
19039 146 : MarkInheritDetached(partRel, rel);
19040 :
19041 : /*
19042 : * Ensure that foreign keys still hold after this detach. This keeps
19043 : * locks on the referencing tables, which prevents concurrent transactions
19044 : * from adding rows that we wouldn't see. For this to work in concurrent
19045 : * mode, it is critical that the partition appears as no longer attached
19046 : * for the RI queries as soon as the first transaction commits.
19047 : */
19048 496 : ATDetachCheckNoForeignKeyRefs(partRel);
19049 :
19050 : /*
19051 : * Concurrent mode has to work harder; first we add a new constraint to
19052 : * the partition that matches the partition constraint. Then we close our
19053 : * existing transaction, and in a new one wait for all processes to catch
19054 : * up on the catalog updates we've done so far; at that point we can
19055 : * complete the operation.
19056 : */
19057 462 : if (concurrent)
19058 : {
19059 : Oid partrelid,
19060 : parentrelid;
19061 : LOCKTAG tag;
19062 : char *parentrelname;
19063 : char *partrelname;
19064 :
19065 : /*
19066 : * Add a new constraint to the partition being detached, which
19067 : * supplants the partition constraint (unless there is one already).
19068 : */
19069 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
19070 :
19071 : /*
19072 : * We're almost done now; the only traces that remain are the
19073 : * pg_inherits tuple and the partition's relpartbounds. Before we can
19074 : * remove those, we need to wait until all transactions that know that
19075 : * this is a partition are gone.
19076 : */
19077 :
19078 : /*
19079 : * Remember relation OIDs to re-acquire them later; and relation names
19080 : * too, for error messages if something is dropped in between.
19081 : */
19082 140 : partrelid = RelationGetRelid(partRel);
19083 140 : parentrelid = RelationGetRelid(rel);
19084 140 : parentrelname = MemoryContextStrdup(PortalContext,
19085 140 : RelationGetRelationName(rel));
19086 140 : partrelname = MemoryContextStrdup(PortalContext,
19087 140 : RelationGetRelationName(partRel));
19088 :
19089 : /* Invalidate relcache entries for the parent -- must be before close */
19090 140 : CacheInvalidateRelcache(rel);
19091 :
19092 140 : table_close(partRel, NoLock);
19093 140 : table_close(rel, NoLock);
19094 140 : tab->rel = NULL;
19095 :
19096 : /* Make updated catalog entry visible */
19097 140 : PopActiveSnapshot();
19098 140 : CommitTransactionCommand();
19099 :
19100 140 : StartTransactionCommand();
19101 :
19102 : /*
19103 : * Now wait. This ensures that all queries that were planned
19104 : * including the partition are finished before we remove the rest of
19105 : * catalog entries. We don't need or indeed want to acquire this
19106 : * lock, though -- that would block later queries.
19107 : *
19108 : * We don't need to concern ourselves with waiting for a lock on the
19109 : * partition itself, since we will acquire AccessExclusiveLock below.
19110 : */
19111 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19112 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19113 :
19114 : /*
19115 : * Now acquire locks in both relations again. Note they may have been
19116 : * removed in the meantime, so care is required.
19117 : */
19118 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19119 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
19120 :
19121 : /* If the relations aren't there, something bad happened; bail out */
19122 90 : if (rel == NULL)
19123 : {
19124 0 : if (partRel != NULL) /* shouldn't happen */
19125 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19126 : partrelname);
19127 0 : ereport(ERROR,
19128 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19129 : errmsg("partitioned table \"%s\" was removed concurrently",
19130 : parentrelname)));
19131 : }
19132 90 : if (partRel == NULL)
19133 0 : ereport(ERROR,
19134 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19135 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
19136 :
19137 90 : tab->rel = rel;
19138 : }
19139 :
19140 : /* Do the final part of detaching */
19141 412 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19142 :
19143 410 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19144 :
19145 : /* keep our lock until commit */
19146 410 : table_close(partRel, NoLock);
19147 :
19148 410 : return address;
19149 : }
19150 :
19151 : /*
19152 : * Second part of ALTER TABLE .. DETACH.
19153 : *
19154 : * This is separate so that it can be run independently when the second
19155 : * transaction of the concurrent algorithm fails (crash or abort).
19156 : */
19157 : static void
19158 846 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
19159 : Oid defaultPartOid)
19160 : {
19161 : Relation classRel;
19162 : List *fks;
19163 : ListCell *cell;
19164 : List *indexes;
19165 : Datum new_val[Natts_pg_class];
19166 : bool new_null[Natts_pg_class],
19167 : new_repl[Natts_pg_class];
19168 : HeapTuple tuple,
19169 : newtuple;
19170 846 : Relation trigrel = NULL;
19171 :
19172 846 : if (concurrent)
19173 : {
19174 : /*
19175 : * We can remove the pg_inherits row now. (In the non-concurrent case,
19176 : * this was already done).
19177 : */
19178 104 : RemoveInheritance(partRel, rel, true);
19179 : }
19180 :
19181 : /* Drop any triggers that were cloned on creation/attach. */
19182 846 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
19183 :
19184 : /*
19185 : * Detach any foreign keys that are inherited. This includes creating
19186 : * additional action triggers.
19187 : */
19188 846 : fks = copyObject(RelationGetFKeyList(partRel));
19189 846 : if (fks != NIL)
19190 54 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
19191 930 : foreach(cell, fks)
19192 : {
19193 84 : ForeignKeyCacheInfo *fk = lfirst(cell);
19194 : HeapTuple contup;
19195 : Form_pg_constraint conform;
19196 : Constraint *fkconstraint;
19197 : Oid insertTriggerOid,
19198 : updateTriggerOid;
19199 :
19200 84 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
19201 84 : if (!HeapTupleIsValid(contup))
19202 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
19203 84 : conform = (Form_pg_constraint) GETSTRUCT(contup);
19204 :
19205 : /* consider only the inherited foreign keys */
19206 84 : if (conform->contype != CONSTRAINT_FOREIGN ||
19207 84 : !OidIsValid(conform->conparentid))
19208 : {
19209 18 : ReleaseSysCache(contup);
19210 18 : continue;
19211 : }
19212 :
19213 : /* unset conparentid and adjust conislocal, coninhcount, etc. */
19214 66 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
19215 :
19216 : /*
19217 : * Also, look up the partition's "check" triggers corresponding to the
19218 : * constraint being detached and detach them from the parent triggers.
19219 : */
19220 66 : GetForeignKeyCheckTriggers(trigrel,
19221 : fk->conoid, fk->confrelid, fk->conrelid,
19222 : &insertTriggerOid, &updateTriggerOid);
19223 : Assert(OidIsValid(insertTriggerOid));
19224 66 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
19225 : RelationGetRelid(partRel));
19226 : Assert(OidIsValid(updateTriggerOid));
19227 66 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
19228 : RelationGetRelid(partRel));
19229 :
19230 : /*
19231 : * Make the action triggers on the referenced relation. When this was
19232 : * a partition the action triggers pointed to the parent rel (they
19233 : * still do), but now we need separate ones of our own.
19234 : */
19235 66 : fkconstraint = makeNode(Constraint);
19236 66 : fkconstraint->contype = CONSTRAINT_FOREIGN;
19237 66 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
19238 66 : fkconstraint->deferrable = conform->condeferrable;
19239 66 : fkconstraint->initdeferred = conform->condeferred;
19240 66 : fkconstraint->location = -1;
19241 66 : fkconstraint->pktable = NULL;
19242 66 : fkconstraint->fk_attrs = NIL;
19243 66 : fkconstraint->pk_attrs = NIL;
19244 66 : fkconstraint->fk_matchtype = conform->confmatchtype;
19245 66 : fkconstraint->fk_upd_action = conform->confupdtype;
19246 66 : fkconstraint->fk_del_action = conform->confdeltype;
19247 66 : fkconstraint->fk_del_set_cols = NIL;
19248 66 : fkconstraint->old_conpfeqop = NIL;
19249 66 : fkconstraint->old_pktable_oid = InvalidOid;
19250 66 : fkconstraint->skip_validation = false;
19251 66 : fkconstraint->initially_valid = true;
19252 :
19253 66 : createForeignKeyActionTriggers(partRel, conform->confrelid,
19254 : fkconstraint, fk->conoid,
19255 : conform->conindid,
19256 : InvalidOid, InvalidOid,
19257 : NULL, NULL);
19258 :
19259 66 : ReleaseSysCache(contup);
19260 : }
19261 846 : list_free_deep(fks);
19262 846 : if (trigrel)
19263 54 : table_close(trigrel, RowExclusiveLock);
19264 :
19265 : /*
19266 : * Any sub-constraints that are in the referenced-side of a larger
19267 : * constraint have to be removed. This partition is no longer part of the
19268 : * key space of the constraint.
19269 : */
19270 888 : foreach(cell, GetParentedForeignKeyRefs(partRel))
19271 : {
19272 44 : Oid constrOid = lfirst_oid(cell);
19273 : ObjectAddress constraint;
19274 :
19275 44 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
19276 44 : deleteDependencyRecordsForClass(ConstraintRelationId,
19277 : constrOid,
19278 : ConstraintRelationId,
19279 : DEPENDENCY_INTERNAL);
19280 44 : CommandCounterIncrement();
19281 :
19282 44 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
19283 44 : performDeletion(&constraint, DROP_RESTRICT, 0);
19284 : }
19285 :
19286 : /* Now we can detach indexes */
19287 844 : indexes = RelationGetIndexList(partRel);
19288 1230 : foreach(cell, indexes)
19289 : {
19290 386 : Oid idxid = lfirst_oid(cell);
19291 : Oid parentidx;
19292 : Relation idx;
19293 : Oid constrOid;
19294 : Oid parentConstrOid;
19295 :
19296 386 : if (!has_superclass(idxid))
19297 12 : continue;
19298 :
19299 374 : parentidx = get_partition_parent(idxid, false);
19300 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
19301 :
19302 374 : idx = index_open(idxid, AccessExclusiveLock);
19303 374 : IndexSetParentIndex(idx, InvalidOid);
19304 :
19305 : /*
19306 : * If there's a constraint associated with the index, detach it too.
19307 : * Careful: it is possible for a constraint index in a partition to be
19308 : * the child of a non-constraint index, so verify whether the parent
19309 : * index does actually have a constraint.
19310 : */
19311 374 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
19312 : idxid);
19313 374 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
19314 : parentidx);
19315 374 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
19316 150 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
19317 :
19318 374 : index_close(idx, NoLock);
19319 : }
19320 :
19321 : /* Update pg_class tuple */
19322 844 : classRel = table_open(RelationRelationId, RowExclusiveLock);
19323 844 : tuple = SearchSysCacheCopy1(RELOID,
19324 : ObjectIdGetDatum(RelationGetRelid(partRel)));
19325 844 : if (!HeapTupleIsValid(tuple))
19326 0 : elog(ERROR, "cache lookup failed for relation %u",
19327 : RelationGetRelid(partRel));
19328 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
19329 :
19330 : /* Clear relpartbound and reset relispartition */
19331 844 : memset(new_val, 0, sizeof(new_val));
19332 844 : memset(new_null, false, sizeof(new_null));
19333 844 : memset(new_repl, false, sizeof(new_repl));
19334 844 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
19335 844 : new_null[Anum_pg_class_relpartbound - 1] = true;
19336 844 : new_repl[Anum_pg_class_relpartbound - 1] = true;
19337 844 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
19338 : new_val, new_null, new_repl);
19339 :
19340 844 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
19341 844 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
19342 844 : heap_freetuple(newtuple);
19343 844 : table_close(classRel, RowExclusiveLock);
19344 :
19345 : /*
19346 : * Drop identity property from all identity columns of partition.
19347 : */
19348 2788 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
19349 : {
19350 1944 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
19351 :
19352 1944 : if (!attr->attisdropped && attr->attidentity)
19353 30 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
19354 : AccessExclusiveLock, true, true);
19355 : }
19356 :
19357 844 : if (OidIsValid(defaultPartOid))
19358 : {
19359 : /*
19360 : * If the relation being detached is the default partition itself,
19361 : * remove it from the parent's pg_partitioned_table entry.
19362 : *
19363 : * If not, we must invalidate default partition's relcache entry, as
19364 : * in StorePartitionBound: its partition constraint depends on every
19365 : * other partition's partition constraint.
19366 : */
19367 232 : if (RelationGetRelid(partRel) == defaultPartOid)
19368 38 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
19369 : else
19370 194 : CacheInvalidateRelcacheByRelid(defaultPartOid);
19371 : }
19372 :
19373 : /*
19374 : * Invalidate the parent's relcache so that the partition is no longer
19375 : * included in its partition descriptor.
19376 : */
19377 844 : CacheInvalidateRelcache(rel);
19378 :
19379 : /*
19380 : * If the partition we just detached is partitioned itself, invalidate
19381 : * relcache for all descendent partitions too to ensure that their
19382 : * rd_partcheck expression trees are rebuilt; must lock partitions before
19383 : * doing so, using the same lockmode as what partRel has been locked with
19384 : * by the caller.
19385 : */
19386 844 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19387 : {
19388 : List *children;
19389 :
19390 50 : children = find_all_inheritors(RelationGetRelid(partRel),
19391 : AccessExclusiveLock, NULL);
19392 162 : foreach(cell, children)
19393 : {
19394 112 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
19395 : }
19396 : }
19397 844 : }
19398 :
19399 : /*
19400 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
19401 : *
19402 : * To use when a DETACH PARTITION command previously did not run to
19403 : * completion; this completes the detaching process.
19404 : */
19405 : static ObjectAddress
19406 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
19407 : {
19408 : Relation partRel;
19409 : ObjectAddress address;
19410 14 : Snapshot snap = GetActiveSnapshot();
19411 :
19412 14 : partRel = table_openrv(name, AccessExclusiveLock);
19413 :
19414 : /*
19415 : * Wait until existing snapshots are gone. This is important if the
19416 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
19417 : * user could immediately run DETACH FINALIZE without actually waiting for
19418 : * existing transactions. We must not complete the detach action until
19419 : * all such queries are complete (otherwise we would present them with an
19420 : * inconsistent view of catalogs).
19421 : */
19422 14 : WaitForOlderSnapshots(snap->xmin, false);
19423 :
19424 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
19425 :
19426 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19427 :
19428 14 : table_close(partRel, NoLock);
19429 :
19430 14 : return address;
19431 : }
19432 :
19433 : /*
19434 : * DetachAddConstraintIfNeeded
19435 : * Subroutine for ATExecDetachPartition. Create a constraint that
19436 : * takes the place of the partition constraint, but avoid creating
19437 : * a dupe if an constraint already exists which implies the needed
19438 : * constraint.
19439 : */
19440 : static void
19441 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
19442 : {
19443 : List *constraintExpr;
19444 :
19445 140 : constraintExpr = RelationGetPartitionQual(partRel);
19446 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
19447 :
19448 : /*
19449 : * Avoid adding a new constraint if the needed constraint is implied by an
19450 : * existing constraint
19451 : */
19452 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
19453 : {
19454 : AlteredTableInfo *tab;
19455 : Constraint *n;
19456 :
19457 134 : tab = ATGetQueueEntry(wqueue, partRel);
19458 :
19459 : /* Add constraint on partition, equivalent to the partition constraint */
19460 134 : n = makeNode(Constraint);
19461 134 : n->contype = CONSTR_CHECK;
19462 134 : n->conname = NULL;
19463 134 : n->location = -1;
19464 134 : n->is_no_inherit = false;
19465 134 : n->raw_expr = NULL;
19466 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
19467 134 : n->initially_valid = true;
19468 134 : n->skip_validation = true;
19469 : /* It's a re-add, since it nominally already exists */
19470 134 : ATAddCheckConstraint(wqueue, tab, partRel, n,
19471 : true, false, true, ShareUpdateExclusiveLock);
19472 : }
19473 140 : }
19474 :
19475 : /*
19476 : * DropClonedTriggersFromPartition
19477 : * subroutine for ATExecDetachPartition to remove any triggers that were
19478 : * cloned to the partition when it was created-as-partition or attached.
19479 : * This undoes what CloneRowTriggersToPartition did.
19480 : */
19481 : static void
19482 846 : DropClonedTriggersFromPartition(Oid partitionId)
19483 : {
19484 : ScanKeyData skey;
19485 : SysScanDesc scan;
19486 : HeapTuple trigtup;
19487 : Relation tgrel;
19488 : ObjectAddresses *objects;
19489 :
19490 846 : objects = new_object_addresses();
19491 :
19492 : /*
19493 : * Scan pg_trigger to search for all triggers on this rel.
19494 : */
19495 846 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19496 : F_OIDEQ, ObjectIdGetDatum(partitionId));
19497 846 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
19498 846 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
19499 : true, NULL, 1, &skey);
19500 1144 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
19501 : {
19502 298 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
19503 : ObjectAddress trig;
19504 :
19505 : /* Ignore triggers that weren't cloned */
19506 298 : if (!OidIsValid(pg_trigger->tgparentid))
19507 256 : continue;
19508 :
19509 : /*
19510 : * Ignore internal triggers that are implementation objects of foreign
19511 : * keys, because these will be detached when the foreign keys
19512 : * themselves are.
19513 : */
19514 262 : if (OidIsValid(pg_trigger->tgconstrrelid))
19515 220 : continue;
19516 :
19517 : /*
19518 : * This is ugly, but necessary: remove the dependency markings on the
19519 : * trigger so that it can be removed.
19520 : */
19521 42 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19522 : TriggerRelationId,
19523 : DEPENDENCY_PARTITION_PRI);
19524 42 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19525 : RelationRelationId,
19526 : DEPENDENCY_PARTITION_SEC);
19527 :
19528 : /* remember this trigger to remove it below */
19529 42 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
19530 42 : add_exact_object_address(&trig, objects);
19531 : }
19532 :
19533 : /* make the dependency removal visible to the deletion below */
19534 846 : CommandCounterIncrement();
19535 846 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
19536 :
19537 : /* done */
19538 846 : free_object_addresses(objects);
19539 846 : systable_endscan(scan);
19540 846 : table_close(tgrel, RowExclusiveLock);
19541 846 : }
19542 :
19543 : /*
19544 : * Before acquiring lock on an index, acquire the same lock on the owning
19545 : * table.
19546 : */
19547 : struct AttachIndexCallbackState
19548 : {
19549 : Oid partitionOid;
19550 : Oid parentTblOid;
19551 : bool lockedParentTbl;
19552 : };
19553 :
19554 : static void
19555 402 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
19556 : void *arg)
19557 : {
19558 : struct AttachIndexCallbackState *state;
19559 : Form_pg_class classform;
19560 : HeapTuple tuple;
19561 :
19562 402 : state = (struct AttachIndexCallbackState *) arg;
19563 :
19564 402 : if (!state->lockedParentTbl)
19565 : {
19566 392 : LockRelationOid(state->parentTblOid, AccessShareLock);
19567 392 : state->lockedParentTbl = true;
19568 : }
19569 :
19570 : /*
19571 : * If we previously locked some other heap, and the name we're looking up
19572 : * no longer refers to an index on that relation, release the now-useless
19573 : * lock. XXX maybe we should do *after* we verify whether the index does
19574 : * not actually belong to the same relation ...
19575 : */
19576 402 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
19577 : {
19578 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
19579 0 : state->partitionOid = InvalidOid;
19580 : }
19581 :
19582 : /* Didn't find a relation, so no need for locking or permission checks. */
19583 402 : if (!OidIsValid(relOid))
19584 6 : return;
19585 :
19586 396 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
19587 396 : if (!HeapTupleIsValid(tuple))
19588 0 : return; /* concurrently dropped, so nothing to do */
19589 396 : classform = (Form_pg_class) GETSTRUCT(tuple);
19590 396 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
19591 306 : classform->relkind != RELKIND_INDEX)
19592 6 : ereport(ERROR,
19593 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19594 : errmsg("\"%s\" is not an index", rv->relname)));
19595 390 : ReleaseSysCache(tuple);
19596 :
19597 : /*
19598 : * Since we need only examine the heap's tupledesc, an access share lock
19599 : * on it (preventing any DDL) is sufficient.
19600 : */
19601 390 : state->partitionOid = IndexGetRelation(relOid, false);
19602 390 : LockRelationOid(state->partitionOid, AccessShareLock);
19603 : }
19604 :
19605 : /*
19606 : * ALTER INDEX i1 ATTACH PARTITION i2
19607 : */
19608 : static ObjectAddress
19609 392 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
19610 : {
19611 : Relation partIdx;
19612 : Relation partTbl;
19613 : Relation parentTbl;
19614 : ObjectAddress address;
19615 : Oid partIdxId;
19616 : Oid currParent;
19617 : struct AttachIndexCallbackState state;
19618 :
19619 : /*
19620 : * We need to obtain lock on the index 'name' to modify it, but we also
19621 : * need to read its owning table's tuple descriptor -- so we need to lock
19622 : * both. To avoid deadlocks, obtain lock on the table before doing so on
19623 : * the index. Furthermore, we need to examine the parent table of the
19624 : * partition, so lock that one too.
19625 : */
19626 392 : state.partitionOid = InvalidOid;
19627 392 : state.parentTblOid = parentIdx->rd_index->indrelid;
19628 392 : state.lockedParentTbl = false;
19629 : partIdxId =
19630 392 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
19631 : RangeVarCallbackForAttachIndex,
19632 : (void *) &state);
19633 : /* Not there? */
19634 380 : if (!OidIsValid(partIdxId))
19635 0 : ereport(ERROR,
19636 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19637 : errmsg("index \"%s\" does not exist", name->relname)));
19638 :
19639 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
19640 380 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
19641 :
19642 : /* we already hold locks on both tables, so this is safe: */
19643 380 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
19644 380 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
19645 :
19646 380 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
19647 :
19648 : /* Silently do nothing if already in the right state */
19649 760 : currParent = partIdx->rd_rel->relispartition ?
19650 380 : get_partition_parent(partIdxId, false) : InvalidOid;
19651 380 : if (currParent != RelationGetRelid(parentIdx))
19652 : {
19653 : IndexInfo *childInfo;
19654 : IndexInfo *parentInfo;
19655 : AttrMap *attmap;
19656 : bool found;
19657 : int i;
19658 : PartitionDesc partDesc;
19659 : Oid constraintOid,
19660 356 : cldConstrId = InvalidOid;
19661 :
19662 : /*
19663 : * If this partition already has an index attached, refuse the
19664 : * operation.
19665 : */
19666 356 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
19667 :
19668 350 : if (OidIsValid(currParent))
19669 0 : ereport(ERROR,
19670 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19671 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19672 : RelationGetRelationName(partIdx),
19673 : RelationGetRelationName(parentIdx)),
19674 : errdetail("Index \"%s\" is already attached to another index.",
19675 : RelationGetRelationName(partIdx))));
19676 :
19677 : /* Make sure it indexes a partition of the other index's table */
19678 350 : partDesc = RelationGetPartitionDesc(parentTbl, true);
19679 350 : found = false;
19680 552 : for (i = 0; i < partDesc->nparts; i++)
19681 : {
19682 546 : if (partDesc->oids[i] == state.partitionOid)
19683 : {
19684 344 : found = true;
19685 344 : break;
19686 : }
19687 : }
19688 350 : if (!found)
19689 6 : ereport(ERROR,
19690 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19691 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19692 : RelationGetRelationName(partIdx),
19693 : RelationGetRelationName(parentIdx)),
19694 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19695 : RelationGetRelationName(partIdx),
19696 : RelationGetRelationName(parentTbl))));
19697 :
19698 : /* Ensure the indexes are compatible */
19699 344 : childInfo = BuildIndexInfo(partIdx);
19700 344 : parentInfo = BuildIndexInfo(parentIdx);
19701 344 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
19702 : RelationGetDescr(parentTbl),
19703 : false);
19704 344 : if (!CompareIndexInfo(childInfo, parentInfo,
19705 344 : partIdx->rd_indcollation,
19706 344 : parentIdx->rd_indcollation,
19707 344 : partIdx->rd_opfamily,
19708 344 : parentIdx->rd_opfamily,
19709 : attmap))
19710 42 : ereport(ERROR,
19711 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19712 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19713 : RelationGetRelationName(partIdx),
19714 : RelationGetRelationName(parentIdx)),
19715 : errdetail("The index definitions do not match.")));
19716 :
19717 : /*
19718 : * If there is a constraint in the parent, make sure there is one in
19719 : * the child too.
19720 : */
19721 302 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
19722 : RelationGetRelid(parentIdx));
19723 :
19724 302 : if (OidIsValid(constraintOid))
19725 : {
19726 122 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
19727 : partIdxId);
19728 122 : if (!OidIsValid(cldConstrId))
19729 6 : ereport(ERROR,
19730 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19731 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19732 : RelationGetRelationName(partIdx),
19733 : RelationGetRelationName(parentIdx)),
19734 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19735 : RelationGetRelationName(parentIdx),
19736 : RelationGetRelationName(parentTbl),
19737 : RelationGetRelationName(partIdx))));
19738 : }
19739 :
19740 : /* All good -- do it */
19741 296 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
19742 296 : if (OidIsValid(constraintOid))
19743 116 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
19744 : RelationGetRelid(partTbl));
19745 :
19746 296 : free_attrmap(attmap);
19747 :
19748 296 : validatePartitionedIndex(parentIdx, parentTbl);
19749 : }
19750 :
19751 320 : relation_close(parentTbl, AccessShareLock);
19752 : /* keep these locks till commit */
19753 320 : relation_close(partTbl, NoLock);
19754 320 : relation_close(partIdx, NoLock);
19755 :
19756 320 : return address;
19757 : }
19758 :
19759 : /*
19760 : * Verify whether the given partition already contains an index attached
19761 : * to the given partitioned index. If so, raise an error.
19762 : */
19763 : static void
19764 356 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
19765 : {
19766 : Oid existingIdx;
19767 :
19768 356 : existingIdx = index_get_partition(partitionTbl,
19769 : RelationGetRelid(parentIdx));
19770 356 : if (OidIsValid(existingIdx))
19771 6 : ereport(ERROR,
19772 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19773 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19774 : RelationGetRelationName(partIdx),
19775 : RelationGetRelationName(parentIdx)),
19776 : errdetail("Another index is already attached for partition \"%s\".",
19777 : RelationGetRelationName(partitionTbl))));
19778 350 : }
19779 :
19780 : /*
19781 : * Verify whether the set of attached partition indexes to a parent index on
19782 : * a partitioned table is complete. If it is, mark the parent index valid.
19783 : *
19784 : * This should be called each time a partition index is attached.
19785 : */
19786 : static void
19787 338 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
19788 : {
19789 : Relation inheritsRel;
19790 : SysScanDesc scan;
19791 : ScanKeyData key;
19792 338 : int tuples = 0;
19793 : HeapTuple inhTup;
19794 338 : bool updated = false;
19795 :
19796 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
19797 :
19798 : /*
19799 : * Scan pg_inherits for this parent index. Count each valid index we find
19800 : * (verifying the pg_index entry for each), and if we reach the total
19801 : * amount we expect, we can mark this parent index as valid.
19802 : */
19803 338 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
19804 338 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
19805 : BTEqualStrategyNumber, F_OIDEQ,
19806 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19807 338 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
19808 : NULL, 1, &key);
19809 880 : while ((inhTup = systable_getnext(scan)) != NULL)
19810 : {
19811 542 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
19812 : HeapTuple indTup;
19813 : Form_pg_index indexForm;
19814 :
19815 542 : indTup = SearchSysCache1(INDEXRELID,
19816 : ObjectIdGetDatum(inhForm->inhrelid));
19817 542 : if (!HeapTupleIsValid(indTup))
19818 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
19819 542 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
19820 542 : if (indexForm->indisvalid)
19821 484 : tuples += 1;
19822 542 : ReleaseSysCache(indTup);
19823 : }
19824 :
19825 : /* Done with pg_inherits */
19826 338 : systable_endscan(scan);
19827 338 : table_close(inheritsRel, AccessShareLock);
19828 :
19829 : /*
19830 : * If we found as many inherited indexes as the partitioned table has
19831 : * partitions, we're good; update pg_index to set indisvalid.
19832 : */
19833 338 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
19834 : {
19835 : Relation idxRel;
19836 : HeapTuple indTup;
19837 : Form_pg_index indexForm;
19838 :
19839 168 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
19840 168 : indTup = SearchSysCacheCopy1(INDEXRELID,
19841 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19842 168 : if (!HeapTupleIsValid(indTup))
19843 0 : elog(ERROR, "cache lookup failed for index %u",
19844 : RelationGetRelid(partedIdx));
19845 168 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
19846 :
19847 168 : indexForm->indisvalid = true;
19848 168 : updated = true;
19849 :
19850 168 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
19851 :
19852 168 : table_close(idxRel, RowExclusiveLock);
19853 168 : heap_freetuple(indTup);
19854 : }
19855 :
19856 : /*
19857 : * If this index is in turn a partition of a larger index, validating it
19858 : * might cause the parent to become valid also. Try that.
19859 : */
19860 338 : if (updated && partedIdx->rd_rel->relispartition)
19861 : {
19862 : Oid parentIdxId,
19863 : parentTblId;
19864 : Relation parentIdx,
19865 : parentTbl;
19866 :
19867 : /* make sure we see the validation we just did */
19868 42 : CommandCounterIncrement();
19869 :
19870 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
19871 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
19872 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
19873 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
19874 : Assert(!parentIdx->rd_index->indisvalid);
19875 :
19876 42 : validatePartitionedIndex(parentIdx, parentTbl);
19877 :
19878 42 : relation_close(parentIdx, AccessExclusiveLock);
19879 42 : relation_close(parentTbl, AccessExclusiveLock);
19880 : }
19881 338 : }
19882 :
19883 : /*
19884 : * Return an OID list of constraints that reference the given relation
19885 : * that are marked as having a parent constraints.
19886 : */
19887 : static List *
19888 1342 : GetParentedForeignKeyRefs(Relation partition)
19889 : {
19890 : Relation pg_constraint;
19891 : HeapTuple tuple;
19892 : SysScanDesc scan;
19893 : ScanKeyData key[2];
19894 1342 : List *constraints = NIL;
19895 :
19896 : /*
19897 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
19898 : * scan.
19899 : */
19900 1902 : if (RelationGetIndexList(partition) == NIL ||
19901 560 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
19902 : INDEX_ATTR_BITMAP_KEY)))
19903 1028 : return NIL;
19904 :
19905 : /* Search for constraints referencing this table */
19906 314 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
19907 314 : ScanKeyInit(&key[0],
19908 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
19909 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
19910 314 : ScanKeyInit(&key[1],
19911 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
19912 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
19913 :
19914 : /* XXX This is a seqscan, as we don't have a usable index */
19915 314 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
19916 444 : while ((tuple = systable_getnext(scan)) != NULL)
19917 : {
19918 130 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19919 :
19920 : /*
19921 : * We only need to process constraints that are part of larger ones.
19922 : */
19923 130 : if (!OidIsValid(constrForm->conparentid))
19924 0 : continue;
19925 :
19926 130 : constraints = lappend_oid(constraints, constrForm->oid);
19927 : }
19928 :
19929 314 : systable_endscan(scan);
19930 314 : table_close(pg_constraint, AccessShareLock);
19931 :
19932 314 : return constraints;
19933 : }
19934 :
19935 : /*
19936 : * During DETACH PARTITION, verify that any foreign keys pointing to the
19937 : * partitioned table would not become invalid. An error is raised if any
19938 : * referenced values exist.
19939 : */
19940 : static void
19941 496 : ATDetachCheckNoForeignKeyRefs(Relation partition)
19942 : {
19943 : List *constraints;
19944 : ListCell *cell;
19945 :
19946 496 : constraints = GetParentedForeignKeyRefs(partition);
19947 :
19948 548 : foreach(cell, constraints)
19949 : {
19950 86 : Oid constrOid = lfirst_oid(cell);
19951 : HeapTuple tuple;
19952 : Form_pg_constraint constrForm;
19953 : Relation rel;
19954 86 : Trigger trig = {0};
19955 :
19956 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
19957 86 : if (!HeapTupleIsValid(tuple))
19958 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
19959 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19960 :
19961 : Assert(OidIsValid(constrForm->conparentid));
19962 : Assert(constrForm->confrelid == RelationGetRelid(partition));
19963 :
19964 : /* prevent data changes into the referencing table until commit */
19965 86 : rel = table_open(constrForm->conrelid, ShareLock);
19966 :
19967 86 : trig.tgoid = InvalidOid;
19968 86 : trig.tgname = NameStr(constrForm->conname);
19969 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
19970 86 : trig.tgisinternal = true;
19971 86 : trig.tgconstrrelid = RelationGetRelid(partition);
19972 86 : trig.tgconstrindid = constrForm->conindid;
19973 86 : trig.tgconstraint = constrForm->oid;
19974 86 : trig.tgdeferrable = false;
19975 86 : trig.tginitdeferred = false;
19976 : /* we needn't fill in remaining fields */
19977 :
19978 86 : RI_PartitionRemove_Check(&trig, rel, partition);
19979 :
19980 52 : ReleaseSysCache(tuple);
19981 :
19982 52 : table_close(rel, NoLock);
19983 : }
19984 462 : }
19985 :
19986 : /*
19987 : * resolve column compression specification to compression method.
19988 : */
19989 : static char
19990 216654 : GetAttributeCompression(Oid atttypid, const char *compression)
19991 : {
19992 : char cmethod;
19993 :
19994 216654 : if (compression == NULL || strcmp(compression, "default") == 0)
19995 216504 : return InvalidCompressionMethod;
19996 :
19997 : /*
19998 : * To specify a nondefault method, the column data type must be toastable.
19999 : * Note this says nothing about whether the column's attstorage setting
20000 : * permits compression; we intentionally allow attstorage and
20001 : * attcompression to be independent. But with a non-toastable type,
20002 : * attstorage could not be set to a value that would permit compression.
20003 : *
20004 : * We don't actually need to enforce this, since nothing bad would happen
20005 : * if attcompression were non-default; it would never be consulted. But
20006 : * it seems more user-friendly to complain about a certainly-useless
20007 : * attempt to set the property.
20008 : */
20009 150 : if (!TypeIsToastable(atttypid))
20010 6 : ereport(ERROR,
20011 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20012 : errmsg("column data type %s does not support compression",
20013 : format_type_be(atttypid))));
20014 :
20015 144 : cmethod = CompressionNameToMethod(compression);
20016 144 : if (!CompressionMethodIsValid(cmethod))
20017 12 : ereport(ERROR,
20018 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20019 : errmsg("invalid compression method \"%s\"", compression)));
20020 :
20021 132 : return cmethod;
20022 : }
20023 :
20024 : /*
20025 : * resolve column storage specification
20026 : */
20027 : static char
20028 242 : GetAttributeStorage(Oid atttypid, const char *storagemode)
20029 : {
20030 242 : char cstorage = 0;
20031 :
20032 242 : if (pg_strcasecmp(storagemode, "plain") == 0)
20033 50 : cstorage = TYPSTORAGE_PLAIN;
20034 192 : else if (pg_strcasecmp(storagemode, "external") == 0)
20035 156 : cstorage = TYPSTORAGE_EXTERNAL;
20036 36 : else if (pg_strcasecmp(storagemode, "extended") == 0)
20037 16 : cstorage = TYPSTORAGE_EXTENDED;
20038 20 : else if (pg_strcasecmp(storagemode, "main") == 0)
20039 14 : cstorage = TYPSTORAGE_MAIN;
20040 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
20041 6 : cstorage = get_typstorage(atttypid);
20042 : else
20043 0 : ereport(ERROR,
20044 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20045 : errmsg("invalid storage type \"%s\"",
20046 : storagemode)));
20047 :
20048 : /*
20049 : * safety check: do not allow toasted storage modes unless column datatype
20050 : * is TOAST-aware.
20051 : */
20052 242 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20053 6 : ereport(ERROR,
20054 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20055 : errmsg("column data type %s can only have storage PLAIN",
20056 : format_type_be(atttypid))));
20057 :
20058 236 : return cstorage;
20059 : }
20060 :
20061 : /*
20062 : * Struct with context of new partition for inserting rows from split partition
20063 : */
20064 : typedef struct SplitPartitionContext
20065 : {
20066 : ExprState *partqualstate; /* expression for checking slot for partition
20067 : * (NULL for DEFAULT partition) */
20068 : BulkInsertState bistate; /* state of bulk inserts for partition */
20069 : TupleTableSlot *dstslot; /* slot for inserting row into partition */
20070 : Relation partRel; /* relation for partition */
20071 : } SplitPartitionContext;
20072 :
20073 :
20074 : /*
20075 : * createSplitPartitionContext: create context for partition and fill it
20076 : */
20077 : static SplitPartitionContext *
20078 480 : createSplitPartitionContext(Relation partRel)
20079 : {
20080 : SplitPartitionContext *pc;
20081 :
20082 480 : pc = (SplitPartitionContext *) palloc0(sizeof(SplitPartitionContext));
20083 480 : pc->partRel = partRel;
20084 :
20085 : /*
20086 : * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
20087 : * don't bother using it.
20088 : */
20089 480 : pc->bistate = GetBulkInsertState();
20090 :
20091 : /* Create tuple slot for new partition. */
20092 480 : pc->dstslot = MakeSingleTupleTableSlot(RelationGetDescr(pc->partRel),
20093 : table_slot_callbacks(pc->partRel));
20094 480 : ExecStoreAllNullTuple(pc->dstslot);
20095 :
20096 480 : return pc;
20097 : }
20098 :
20099 : /*
20100 : * deleteSplitPartitionContext: delete context for partition
20101 : */
20102 : static void
20103 480 : deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
20104 : {
20105 480 : ExecDropSingleTupleTableSlot(pc->dstslot);
20106 480 : FreeBulkInsertState(pc->bistate);
20107 :
20108 480 : table_finish_bulk_insert(pc->partRel, ti_options);
20109 :
20110 480 : pfree(pc);
20111 480 : }
20112 :
20113 : /*
20114 : * moveSplitTableRows: scan split partition (splitRel) of partitioned table
20115 : * (rel) and move rows into new partitions.
20116 : *
20117 : * New partitions description:
20118 : * partlist: list of pointers to SinglePartitionSpec structures.
20119 : * newPartRels: list of Relations.
20120 : * defaultPartOid: oid of DEFAULT partition, for table rel.
20121 : */
20122 : static void
20123 150 : moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
20124 : {
20125 : /* The FSM is empty, so don't bother using it. */
20126 150 : int ti_options = TABLE_INSERT_SKIP_FSM;
20127 : CommandId mycid;
20128 : EState *estate;
20129 : ListCell *listptr,
20130 : *listptr2;
20131 : TupleTableSlot *srcslot;
20132 : ExprContext *econtext;
20133 : TableScanDesc scan;
20134 : Snapshot snapshot;
20135 : MemoryContext oldCxt;
20136 150 : List *partContexts = NIL;
20137 : TupleConversionMap *tuple_map;
20138 150 : SplitPartitionContext *defaultPartCtx = NULL,
20139 : *pc;
20140 150 : bool isOldDefaultPart = false;
20141 :
20142 150 : mycid = GetCurrentCommandId(true);
20143 :
20144 150 : estate = CreateExecutorState();
20145 :
20146 582 : forboth(listptr, partlist, listptr2, newPartRels)
20147 : {
20148 432 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20149 :
20150 432 : pc = createSplitPartitionContext((Relation) lfirst(listptr2));
20151 :
20152 432 : if (sps->bound->is_default)
20153 : {
20154 : /* We should not create constraint for detached DEFAULT partition. */
20155 30 : defaultPartCtx = pc;
20156 : }
20157 : else
20158 : {
20159 : List *partConstraint;
20160 :
20161 : /* Build expression execution states for partition check quals. */
20162 402 : partConstraint = get_qual_from_partbound(rel, sps->bound);
20163 : partConstraint =
20164 402 : (List *) eval_const_expressions(NULL,
20165 : (Node *) partConstraint);
20166 : /* Make boolean expression for ExecCheck(). */
20167 402 : partConstraint = list_make1(make_ands_explicit(partConstraint));
20168 :
20169 : /*
20170 : * Map the vars in the constraint expression from rel's attnos to
20171 : * splitRel's.
20172 : */
20173 402 : partConstraint = map_partition_varattnos(partConstraint,
20174 : 1, splitRel, rel);
20175 :
20176 402 : pc->partqualstate =
20177 402 : ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
20178 : Assert(pc->partqualstate != NULL);
20179 : }
20180 :
20181 : /* Store partition context into list. */
20182 432 : partContexts = lappend(partContexts, pc);
20183 : }
20184 :
20185 : /*
20186 : * Create partition context for DEFAULT partition. We can insert values
20187 : * into this partition in case spaces with values between new partitions.
20188 : */
20189 150 : if (!defaultPartCtx && OidIsValid(defaultPartOid))
20190 : {
20191 : /* Indicate that we allocate context for old DEFAULT partition */
20192 48 : isOldDefaultPart = true;
20193 48 : defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
20194 : }
20195 :
20196 150 : econtext = GetPerTupleExprContext(estate);
20197 :
20198 : /* Create necessary tuple slot. */
20199 150 : srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
20200 : table_slot_callbacks(splitRel));
20201 :
20202 : /*
20203 : * Map computing for moving attributes of split partition to new partition
20204 : * (for first new partition, but other new partitions can use the same
20205 : * map).
20206 : */
20207 150 : pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
20208 150 : tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
20209 150 : RelationGetDescr(pc->partRel));
20210 :
20211 : /* Scan through the rows. */
20212 150 : snapshot = RegisterSnapshot(GetLatestSnapshot());
20213 150 : scan = table_beginscan(splitRel, snapshot, 0, NULL);
20214 :
20215 : /*
20216 : * Switch to per-tuple memory context and reset it for each tuple
20217 : * produced, so we don't leak memory.
20218 : */
20219 150 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
20220 :
20221 690 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
20222 : {
20223 540 : bool found = false;
20224 : TupleTableSlot *insertslot;
20225 :
20226 : /* Extract data from old tuple. */
20227 540 : slot_getallattrs(srcslot);
20228 :
20229 540 : econtext->ecxt_scantuple = srcslot;
20230 :
20231 : /* Search partition for current slot srcslot. */
20232 1488 : foreach(listptr, partContexts)
20233 : {
20234 1374 : pc = (SplitPartitionContext *) lfirst(listptr);
20235 :
20236 2640 : if (pc->partqualstate /* skip DEFAULT partition */ &&
20237 1266 : ExecCheck(pc->partqualstate, econtext))
20238 : {
20239 426 : found = true;
20240 426 : break;
20241 : }
20242 948 : ResetExprContext(econtext);
20243 : }
20244 540 : if (!found)
20245 : {
20246 : /* Use DEFAULT partition if it exists. */
20247 114 : if (defaultPartCtx)
20248 114 : pc = defaultPartCtx;
20249 : else
20250 0 : ereport(ERROR,
20251 : (errcode(ERRCODE_CHECK_VIOLATION),
20252 : errmsg("can not find partition for split partition row"),
20253 : errtable(splitRel)));
20254 : }
20255 :
20256 540 : if (tuple_map)
20257 : {
20258 : /* Need to use map to copy attributes. */
20259 24 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
20260 : }
20261 : else
20262 : {
20263 : /* Copy attributes directly. */
20264 516 : insertslot = pc->dstslot;
20265 :
20266 516 : ExecClearTuple(insertslot);
20267 :
20268 516 : memcpy(insertslot->tts_values, srcslot->tts_values,
20269 516 : sizeof(Datum) * srcslot->tts_nvalid);
20270 516 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
20271 516 : sizeof(bool) * srcslot->tts_nvalid);
20272 :
20273 516 : ExecStoreVirtualTuple(insertslot);
20274 : }
20275 :
20276 : /* Write the tuple out to the new relation. */
20277 540 : table_tuple_insert(pc->partRel, insertslot, mycid,
20278 : ti_options, pc->bistate);
20279 :
20280 540 : ResetExprContext(econtext);
20281 :
20282 540 : CHECK_FOR_INTERRUPTS();
20283 : }
20284 :
20285 150 : MemoryContextSwitchTo(oldCxt);
20286 :
20287 150 : table_endscan(scan);
20288 150 : UnregisterSnapshot(snapshot);
20289 :
20290 150 : if (tuple_map)
20291 6 : free_conversion_map(tuple_map);
20292 :
20293 150 : ExecDropSingleTupleTableSlot(srcslot);
20294 :
20295 150 : FreeExecutorState(estate);
20296 :
20297 582 : foreach(listptr, partContexts)
20298 432 : deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
20299 :
20300 : /* Need to close table and free buffers for DEFAULT partition. */
20301 150 : if (isOldDefaultPart)
20302 : {
20303 48 : Relation defaultPartRel = defaultPartCtx->partRel;
20304 :
20305 48 : deleteSplitPartitionContext(defaultPartCtx, ti_options);
20306 : /* Keep the lock until commit. */
20307 48 : table_close(defaultPartRel, NoLock);
20308 : }
20309 150 : }
20310 :
20311 : /*
20312 : * createPartitionTable: create table for a new partition with given name
20313 : * (newPartName) like table (modelRel)
20314 : *
20315 : * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name>
20316 : * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY EXCLUDING STATISTICS)
20317 : *
20318 : * Also, this function sets the new partition access method same as parent
20319 : * table access methods (similarly to CREATE TABLE ... PARTITION OF). It
20320 : * checks that parent and child tables have compatible persistence.
20321 : *
20322 : * Function returns the created relation (locked in AccessExclusiveLock mode).
20323 : */
20324 : static Relation
20325 546 : createPartitionTable(RangeVar *newPartName, Relation modelRel,
20326 : AlterTableUtilityContext *context)
20327 : {
20328 : CreateStmt *createStmt;
20329 : TableLikeClause *tlc;
20330 : PlannedStmt *wrapper;
20331 : Relation newRel;
20332 :
20333 : /* If existing rel is temp, it must belong to this session */
20334 546 : if (modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20335 36 : !modelRel->rd_islocaltemp)
20336 0 : ereport(ERROR,
20337 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20338 : errmsg("cannot create as partition of temporary relation of another session")));
20339 :
20340 : /* New partition should have the same persistence as modelRel */
20341 546 : newPartName->relpersistence = modelRel->rd_rel->relpersistence;
20342 :
20343 546 : createStmt = makeNode(CreateStmt);
20344 546 : createStmt->relation = newPartName;
20345 546 : createStmt->tableElts = NIL;
20346 546 : createStmt->inhRelations = NIL;
20347 546 : createStmt->constraints = NIL;
20348 546 : createStmt->options = NIL;
20349 546 : createStmt->oncommit = ONCOMMIT_NOOP;
20350 546 : createStmt->tablespacename = get_tablespace_name(modelRel->rd_rel->reltablespace);
20351 546 : createStmt->if_not_exists = false;
20352 546 : createStmt->accessMethod = get_am_name(modelRel->rd_rel->relam);
20353 :
20354 546 : tlc = makeNode(TableLikeClause);
20355 546 : tlc->relation = makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel)),
20356 546 : RelationGetRelationName(modelRel), -1);
20357 :
20358 : /*
20359 : * Indexes will be inherited on "attach new partitions" stage, after data
20360 : * moving. We also don't copy the extended statistics for consistency
20361 : * with CREATE TABLE PARTITION OF.
20362 : */
20363 546 : tlc->options = CREATE_TABLE_LIKE_ALL &
20364 : ~(CREATE_TABLE_LIKE_INDEXES | CREATE_TABLE_LIKE_IDENTITY | CREATE_TABLE_LIKE_STATISTICS);
20365 546 : tlc->relationOid = InvalidOid;
20366 546 : createStmt->tableElts = lappend(createStmt->tableElts, tlc);
20367 :
20368 : /* Need to make a wrapper PlannedStmt. */
20369 546 : wrapper = makeNode(PlannedStmt);
20370 546 : wrapper->commandType = CMD_UTILITY;
20371 546 : wrapper->canSetTag = false;
20372 546 : wrapper->utilityStmt = (Node *) createStmt;
20373 546 : wrapper->stmt_location = context->pstmt->stmt_location;
20374 546 : wrapper->stmt_len = context->pstmt->stmt_len;
20375 :
20376 546 : ProcessUtility(wrapper,
20377 : context->queryString,
20378 : false,
20379 : PROCESS_UTILITY_SUBCOMMAND,
20380 : NULL,
20381 : NULL,
20382 : None_Receiver,
20383 : NULL);
20384 :
20385 : /*
20386 : * Open the new partition with no lock, because we already have
20387 : * AccessExclusiveLock placed there after creation.
20388 : */
20389 546 : newRel = table_openrv(newPartName, NoLock);
20390 :
20391 : /*
20392 : * We intended to create the partition with the same persistence as the
20393 : * parent table, but we still need to recheck because that might be
20394 : * affected by the search_path. If the parent is permanent, so must be
20395 : * all of its partitions.
20396 : */
20397 546 : if (modelRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20398 510 : newRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20399 12 : ereport(ERROR,
20400 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20401 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
20402 : RelationGetRelationName(modelRel))));
20403 :
20404 : /* Permanent rels cannot be partitions belonging to temporary parent */
20405 534 : if (newRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20406 498 : modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20407 0 : ereport(ERROR,
20408 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20409 : errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
20410 : RelationGetRelationName(modelRel))));
20411 :
20412 534 : return newRel;
20413 : }
20414 :
20415 : /*
20416 : * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
20417 : */
20418 : static void
20419 156 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20420 : PartitionCmd *cmd, AlterTableUtilityContext *context)
20421 : {
20422 : Relation splitRel;
20423 : Oid splitRelOid;
20424 : char relname[NAMEDATALEN];
20425 : Oid namespaceId;
20426 : ListCell *listptr,
20427 : *listptr2;
20428 156 : bool isSameName = false;
20429 : char tmpRelName[NAMEDATALEN];
20430 156 : List *newPartRels = NIL;
20431 : ObjectAddress object;
20432 : Oid defaultPartOid;
20433 :
20434 156 : defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20435 :
20436 : /*
20437 : * We are going to detach and remove this partition: need to use exclusive
20438 : * lock for preventing DML-queries to the partition.
20439 : */
20440 156 : splitRel = table_openrv(cmd->name, AccessExclusiveLock);
20441 :
20442 156 : splitRelOid = RelationGetRelid(splitRel);
20443 :
20444 : /* Check descriptions of new partitions. */
20445 588 : foreach(listptr, cmd->partlist)
20446 : {
20447 : Oid existing_relid;
20448 438 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20449 :
20450 438 : strlcpy(relname, sps->name->relname, NAMEDATALEN);
20451 :
20452 : /*
20453 : * Look up the namespace in which we are supposed to create the
20454 : * partition, check we have permission to create there, lock it
20455 : * against concurrent drop, and mark stmt->relation as
20456 : * RELPERSISTENCE_TEMP if a temporary namespace is selected.
20457 : */
20458 438 : sps->name->relpersistence = rel->rd_rel->relpersistence;
20459 : namespaceId =
20460 438 : RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL);
20461 :
20462 : /*
20463 : * This would fail later on anyway if the relation already exists. But
20464 : * by catching it here we can emit a nicer error message.
20465 : */
20466 438 : existing_relid = get_relname_relid(relname, namespaceId);
20467 438 : if (existing_relid == splitRelOid && !isSameName)
20468 : /* One new partition can have the same name as split partition. */
20469 30 : isSameName = true;
20470 408 : else if (existing_relid != InvalidOid)
20471 6 : ereport(ERROR,
20472 : (errcode(ERRCODE_DUPLICATE_TABLE),
20473 : errmsg("relation \"%s\" already exists", relname)));
20474 : }
20475 :
20476 : /* Detach split partition. */
20477 150 : RemoveInheritance(splitRel, rel, false);
20478 : /* Do the final part of detaching. */
20479 150 : DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
20480 :
20481 : /*
20482 : * If new partition has the same name as split partition then we should
20483 : * rename split partition for reusing name.
20484 : */
20485 150 : if (isSameName)
20486 : {
20487 : /*
20488 : * We must bump the command counter to make the split partition tuple
20489 : * visible for renaming.
20490 : */
20491 30 : CommandCounterIncrement();
20492 : /* Rename partition. */
20493 30 : sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
20494 30 : RenameRelationInternal(splitRelOid, tmpRelName, false, false);
20495 :
20496 : /*
20497 : * We must bump the command counter to make the split partition tuple
20498 : * visible after renaming.
20499 : */
20500 30 : CommandCounterIncrement();
20501 : }
20502 :
20503 : /* Create new partitions (like split partition), without indexes. */
20504 582 : foreach(listptr, cmd->partlist)
20505 : {
20506 432 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20507 : Relation newPartRel;
20508 :
20509 432 : newPartRel = createPartitionTable(sps->name, rel, context);
20510 432 : newPartRels = lappend(newPartRels, newPartRel);
20511 : }
20512 :
20513 : /* Copy data from split partition to new partitions. */
20514 150 : moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
20515 : /* Keep the lock until commit. */
20516 150 : table_close(splitRel, NoLock);
20517 :
20518 : /* Attach new partitions to partitioned table. */
20519 582 : forboth(listptr, cmd->partlist, listptr2, newPartRels)
20520 : {
20521 432 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20522 432 : Relation newPartRel = (Relation) lfirst(listptr2);
20523 :
20524 : /*
20525 : * wqueue = NULL: verification for each cloned constraint is not
20526 : * needed.
20527 : */
20528 432 : attachPartitionTable(NULL, rel, newPartRel, sps->bound);
20529 : /* Keep the lock until commit. */
20530 432 : table_close(newPartRel, NoLock);
20531 : }
20532 :
20533 : /* Drop split partition. */
20534 150 : object.classId = RelationRelationId;
20535 150 : object.objectId = splitRelOid;
20536 150 : object.objectSubId = 0;
20537 : /* Probably DROP_CASCADE is not needed. */
20538 150 : performDeletion(&object, DROP_RESTRICT, 0);
20539 150 : }
20540 :
20541 : /*
20542 : * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
20543 : * of the partitioned table (rel) and move rows into the new partition
20544 : * (newPartRel).
20545 : */
20546 : static void
20547 102 : moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
20548 : Relation newPartRel)
20549 : {
20550 : CommandId mycid;
20551 :
20552 : /* The FSM is empty, so don't bother using it. */
20553 102 : int ti_options = TABLE_INSERT_SKIP_FSM;
20554 : ListCell *listptr;
20555 : BulkInsertState bistate; /* state of bulk inserts for partition */
20556 : TupleTableSlot *dstslot;
20557 :
20558 102 : mycid = GetCurrentCommandId(true);
20559 :
20560 : /* Prepare a BulkInsertState for table_tuple_insert. */
20561 102 : bistate = GetBulkInsertState();
20562 :
20563 : /* Create necessary tuple slot. */
20564 102 : dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
20565 : table_slot_callbacks(newPartRel));
20566 102 : ExecStoreAllNullTuple(dstslot);
20567 :
20568 348 : foreach(listptr, mergingPartitionsList)
20569 : {
20570 246 : Relation mergingPartition = (Relation) lfirst(listptr);
20571 : TupleTableSlot *srcslot;
20572 : TupleConversionMap *tuple_map;
20573 : TableScanDesc scan;
20574 : Snapshot snapshot;
20575 :
20576 : /* Create tuple slot for new partition. */
20577 246 : srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
20578 : table_slot_callbacks(mergingPartition));
20579 :
20580 : /*
20581 : * Map computing for moving attributes of merged partition to new
20582 : * partition.
20583 : */
20584 246 : tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
20585 : RelationGetDescr(newPartRel));
20586 :
20587 : /* Scan through the rows. */
20588 246 : snapshot = RegisterSnapshot(GetLatestSnapshot());
20589 246 : scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
20590 :
20591 552 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
20592 : {
20593 : TupleTableSlot *insertslot;
20594 :
20595 : /* Extract data from old tuple. */
20596 306 : slot_getallattrs(srcslot);
20597 :
20598 306 : if (tuple_map)
20599 : {
20600 : /* Need to use map to copy attributes. */
20601 30 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
20602 : }
20603 : else
20604 : {
20605 : /* Copy attributes directly. */
20606 276 : insertslot = dstslot;
20607 :
20608 276 : ExecClearTuple(insertslot);
20609 :
20610 276 : memcpy(insertslot->tts_values, srcslot->tts_values,
20611 276 : sizeof(Datum) * srcslot->tts_nvalid);
20612 276 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
20613 276 : sizeof(bool) * srcslot->tts_nvalid);
20614 :
20615 276 : ExecStoreVirtualTuple(insertslot);
20616 : }
20617 :
20618 : /* Write the tuple out to the new relation. */
20619 306 : table_tuple_insert(newPartRel, insertslot, mycid,
20620 : ti_options, bistate);
20621 :
20622 306 : CHECK_FOR_INTERRUPTS();
20623 : }
20624 :
20625 246 : table_endscan(scan);
20626 246 : UnregisterSnapshot(snapshot);
20627 :
20628 246 : if (tuple_map)
20629 18 : free_conversion_map(tuple_map);
20630 :
20631 246 : ExecDropSingleTupleTableSlot(srcslot);
20632 : }
20633 :
20634 102 : ExecDropSingleTupleTableSlot(dstslot);
20635 102 : FreeBulkInsertState(bistate);
20636 :
20637 102 : table_finish_bulk_insert(newPartRel, ti_options);
20638 102 : }
20639 :
20640 : /*
20641 : * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
20642 : */
20643 : static void
20644 114 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
20645 : PartitionCmd *cmd, AlterTableUtilityContext *context)
20646 : {
20647 : Relation newPartRel;
20648 : ListCell *listptr;
20649 114 : List *mergingPartitionsList = NIL;
20650 : Oid defaultPartOid;
20651 : Oid namespaceId;
20652 : Oid existingRelid;
20653 :
20654 : /*
20655 : * Lock all merged partitions, check them and create list with partitions
20656 : * contexts.
20657 : */
20658 384 : foreach(listptr, cmd->partlist)
20659 : {
20660 270 : RangeVar *name = (RangeVar *) lfirst(listptr);
20661 : Relation mergingPartition;
20662 :
20663 : /*
20664 : * We are going to detach and remove this partition: need to use
20665 : * exclusive lock for preventing DML-queries to the partition.
20666 : */
20667 270 : mergingPartition = table_openrv(name, AccessExclusiveLock);
20668 :
20669 : /* Store a next merging partition into the list. */
20670 270 : mergingPartitionsList = lappend(mergingPartitionsList,
20671 : mergingPartition);
20672 : }
20673 :
20674 : /*
20675 : * Look up the namespace in which we are supposed to create the partition,
20676 : * check we have permission to create there, lock it against concurrent
20677 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
20678 : * namespace is selected.
20679 : */
20680 114 : cmd->name->relpersistence = rel->rd_rel->relpersistence;
20681 : namespaceId =
20682 114 : RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, NULL);
20683 :
20684 : /*
20685 : * Check if this name is already taken. This helps us to detect the
20686 : * situation when one of the merging partitions has the same name as the
20687 : * new partition. Otherwise, this would fail later on anyway but catching
20688 : * this here allows us to emit a nicer error message.
20689 : */
20690 114 : existingRelid = get_relname_relid(cmd->name->relname, namespaceId);
20691 :
20692 114 : if (OidIsValid(existingRelid))
20693 : {
20694 12 : Relation sameNamePartition = NULL;
20695 :
20696 36 : foreach_ptr(RelationData, mergingPartition, mergingPartitionsList)
20697 : {
20698 24 : if (RelationGetRelid(mergingPartition) == existingRelid)
20699 : {
20700 12 : sameNamePartition = mergingPartition;
20701 12 : break;
20702 : }
20703 : }
20704 :
20705 12 : if (sameNamePartition)
20706 : {
20707 : /*
20708 : * The new partition has the same name as one of merging
20709 : * partitions.
20710 : */
20711 : char tmpRelName[NAMEDATALEN];
20712 :
20713 : /* Generate temporary name. */
20714 12 : sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
20715 :
20716 : /*
20717 : * Rename the existing partition with a temporary name, leaving it
20718 : * free for the new partition. We don't need to care about this
20719 : * in the future because we're going to eventually drop the
20720 : * existing partition anyway.
20721 : */
20722 12 : RenameRelationInternal(RelationGetRelid(sameNamePartition),
20723 : tmpRelName, false, false);
20724 :
20725 : /*
20726 : * We must bump the command counter to make the new partition
20727 : * tuple visible for rename.
20728 : */
20729 12 : CommandCounterIncrement();
20730 : }
20731 : else
20732 : {
20733 0 : ereport(ERROR,
20734 : (errcode(ERRCODE_DUPLICATE_TABLE),
20735 : errmsg("relation \"%s\" already exists", cmd->name->relname)));
20736 : }
20737 : }
20738 :
20739 : /* Detach all merged partitions. */
20740 : defaultPartOid =
20741 114 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20742 384 : foreach(listptr, mergingPartitionsList)
20743 : {
20744 270 : Relation mergingPartition = (Relation) lfirst(listptr);
20745 :
20746 : /* Remove the pg_inherits row first. */
20747 270 : RemoveInheritance(mergingPartition, rel, false);
20748 : /* Do the final part of detaching. */
20749 270 : DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
20750 : }
20751 :
20752 : /* Create table for new partition, use partitioned table as model. */
20753 114 : newPartRel = createPartitionTable(cmd->name, rel, context);
20754 :
20755 : /* Copy data from merged partitions to new partition. */
20756 102 : moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
20757 :
20758 : /* Drop the current partitions before attaching the new one. */
20759 348 : foreach(listptr, mergingPartitionsList)
20760 : {
20761 : ObjectAddress object;
20762 246 : Relation mergingPartition = (Relation) lfirst(listptr);
20763 :
20764 : /* Get relation id before table_close() call. */
20765 246 : object.objectId = RelationGetRelid(mergingPartition);
20766 246 : object.classId = RelationRelationId;
20767 246 : object.objectSubId = 0;
20768 :
20769 : /* Keep the lock until commit. */
20770 246 : table_close(mergingPartition, NoLock);
20771 :
20772 246 : performDeletion(&object, DROP_RESTRICT, 0);
20773 : }
20774 102 : list_free(mergingPartitionsList);
20775 :
20776 : /*
20777 : * Attach a new partition to the partitioned table. wqueue = NULL:
20778 : * verification for each cloned constraint is not needed.
20779 : */
20780 102 : attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
20781 :
20782 : /* Keep the lock until commit. */
20783 102 : table_close(newPartRel, NoLock);
20784 102 : }
|