1 /*-------------------------------------------------------------------------
2 *
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
5 *
6 * Portions Copyright (c) 1996-2021, 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 "catalog/catalog.h"
30 #include "catalog/heap.h"
31 #include "catalog/index.h"
32 #include "catalog/namespace.h"
33 #include "catalog/objectaccess.h"
34 #include "catalog/partition.h"
35 #include "catalog/pg_am.h"
36 #include "catalog/pg_collation.h"
37 #include "catalog/pg_constraint.h"
38 #include "catalog/pg_depend.h"
39 #include "catalog/pg_foreign_table.h"
40 #include "catalog/pg_inherits.h"
41 #include "catalog/pg_namespace.h"
42 #include "catalog/pg_opclass.h"
43 #include "catalog/pg_tablespace.h"
44 #include "catalog/pg_statistic_ext.h"
45 #include "catalog/pg_trigger.h"
46 #include "catalog/pg_type.h"
47 #include "catalog/storage.h"
48 #include "catalog/storage_xlog.h"
49 #include "catalog/toasting.h"
50 #include "commands/cluster.h"
51 #include "commands/comment.h"
52 #include "commands/defrem.h"
53 #include "commands/event_trigger.h"
54 #include "commands/policy.h"
55 #include "commands/sequence.h"
56 #include "commands/tablecmds.h"
57 #include "commands/tablespace.h"
58 #include "commands/trigger.h"
59 #include "commands/typecmds.h"
60 #include "commands/user.h"
61 #include "executor/executor.h"
62 #include "foreign/fdwapi.h"
63 #include "foreign/foreign.h"
64 #include "miscadmin.h"
65 #include "nodes/makefuncs.h"
66 #include "nodes/nodeFuncs.h"
67 #include "nodes/parsenodes.h"
68 #include "optimizer/optimizer.h"
69 #include "parser/parse_clause.h"
70 #include "parser/parse_coerce.h"
71 #include "parser/parse_collate.h"
72 #include "parser/parse_expr.h"
73 #include "parser/parse_oper.h"
74 #include "parser/parse_relation.h"
75 #include "parser/parse_type.h"
76 #include "parser/parse_utilcmd.h"
77 #include "parser/parser.h"
78 #include "partitioning/partbounds.h"
79 #include "partitioning/partdesc.h"
80 #include "pgstat.h"
81 #include "rewrite/rewriteDefine.h"
82 #include "rewrite/rewriteHandler.h"
83 #include "rewrite/rewriteManip.h"
84 #include "storage/bufmgr.h"
85 #include "storage/lmgr.h"
86 #include "storage/lock.h"
87 #include "storage/predicate.h"
88 #include "storage/smgr.h"
89 #include "tcop/utility.h"
90 #include "utils/acl.h"
91 #include "utils/builtins.h"
92 #include "utils/fmgroids.h"
93 #include "utils/inval.h"
94 #include "utils/lsyscache.h"
95 #include "utils/memutils.h"
96 #include "utils/partcache.h"
97 #include "utils/relcache.h"
98 #include "utils/ruleutils.h"
99 #include "utils/snapmgr.h"
100 #include "utils/syscache.h"
101 #include "utils/timestamp.h"
102 #include "utils/typcache.h"
103
104 /*
105 * ON COMMIT action list
106 */
107 typedef struct OnCommitItem
108 {
109 Oid relid; /* relid of relation */
110 OnCommitAction oncommit; /* what to do at end of xact */
111
112 /*
113 * If this entry was created during the current transaction,
114 * creating_subid is the ID of the creating subxact; if created in a prior
115 * transaction, creating_subid is zero. If deleted during the current
116 * transaction, deleting_subid is the ID of the deleting subxact; if no
117 * deletion request is pending, deleting_subid is zero.
118 */
119 SubTransactionId creating_subid;
120 SubTransactionId deleting_subid;
121 } OnCommitItem;
122
123 static List *on_commits = NIL;
124
125
126 /*
127 * State information for ALTER TABLE
128 *
129 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
130 * structs, one for each table modified by the operation (the named table
131 * plus any child tables that are affected). We save lists of subcommands
132 * to apply to this table (possibly modified by parse transformation steps);
133 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
134 * necessary information is stored in the constraints and newvals lists.
135 *
136 * Phase 2 is divided into multiple passes; subcommands are executed in
137 * a pass determined by subcommand type.
138 */
139
140 #define AT_PASS_UNSET -1 /* UNSET will cause ERROR */
141 #define AT_PASS_DROP 0 /* DROP (all flavors) */
142 #define AT_PASS_ALTER_TYPE 1 /* ALTER COLUMN TYPE */
143 #define AT_PASS_OLD_INDEX 2 /* re-add existing indexes */
144 #define AT_PASS_OLD_CONSTR 3 /* re-add existing constraints */
145 /* We could support a RENAME COLUMN pass here, but not currently used */
146 #define AT_PASS_ADD_COL 4 /* ADD COLUMN */
147 #define AT_PASS_ADD_CONSTR 5 /* ADD constraints (initial examination) */
148 #define AT_PASS_COL_ATTRS 6 /* set column attributes, eg NOT NULL */
149 #define AT_PASS_ADD_INDEXCONSTR 7 /* ADD index-based constraints */
150 #define AT_PASS_ADD_INDEX 8 /* ADD indexes */
151 #define AT_PASS_ADD_OTHERCONSTR 9 /* ADD other constraints, defaults */
152 #define AT_PASS_MISC 10 /* other stuff */
153 #define AT_NUM_PASSES 11
154
155 typedef struct AlteredTableInfo
156 {
157 /* Information saved before any work commences: */
158 Oid relid; /* Relation to work on */
159 char relkind; /* Its relkind */
160 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
161
162 /*
163 * Transiently set during Phase 2, normally set to NULL.
164 *
165 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
166 * returns control. This can be exploited by ATExecCmd subroutines to
167 * close/reopen across transaction boundaries.
168 */
169 Relation rel;
170
171 /* Information saved by Phase 1 for Phase 2: */
172 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
173 /* Information saved by Phases 1/2 for Phase 3: */
174 List *constraints; /* List of NewConstraint */
175 List *newvals; /* List of NewColumnValue */
176 List *afterStmts; /* List of utility command parsetrees */
177 bool verify_new_notnull; /* T if we should recheck NOT NULL */
178 int rewrite; /* Reason for forced rewrite, if any */
179 Oid newTableSpace; /* new tablespace; 0 means no change */
180 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
181 char newrelpersistence; /* if above is true */
182 Expr *partition_constraint; /* for attach partition validation */
183 /* true, if validating default due to some other attach/detach */
184 bool validate_default;
185 /* Objects to rebuild after completing ALTER TYPE operations */
186 List *changedConstraintOids; /* OIDs of constraints to rebuild */
187 List *changedConstraintDefs; /* string definitions of same */
188 List *changedIndexOids; /* OIDs of indexes to rebuild */
189 List *changedIndexDefs; /* string definitions of same */
190 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
191 char *clusterOnIndex; /* index to use for CLUSTER */
192 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
193 List *changedStatisticsDefs; /* string definitions of same */
194 } AlteredTableInfo;
195
196 /* Struct describing one new constraint to check in Phase 3 scan */
197 /* Note: new NOT NULL constraints are handled elsewhere */
198 typedef struct NewConstraint
199 {
200 char *name; /* Constraint name, or NULL if none */
201 ConstrType contype; /* CHECK or FOREIGN */
202 Oid refrelid; /* PK rel, if FOREIGN */
203 Oid refindid; /* OID of PK's index, if FOREIGN */
204 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
205 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
206 ExprState *qualstate; /* Execution state for CHECK expr */
207 } NewConstraint;
208
209 /*
210 * Struct describing one new column value that needs to be computed during
211 * Phase 3 copy (this could be either a new column with a non-null default, or
212 * a column that we're changing the type of). Columns without such an entry
213 * are just copied from the old table during ATRewriteTable. Note that the
214 * expr is an expression over *old* table values, except when is_generated
215 * is true; then it is an expression over columns of the *new* tuple.
216 */
217 typedef struct NewColumnValue
218 {
219 AttrNumber attnum; /* which column */
220 Expr *expr; /* expression to compute */
221 ExprState *exprstate; /* execution state */
222 bool is_generated; /* is it a GENERATED expression? */
223 } NewColumnValue;
224
225 /*
226 * Error-reporting support for RemoveRelations
227 */
228 struct dropmsgstrings
229 {
230 char kind;
231 int nonexistent_code;
232 const char *nonexistent_msg;
233 const char *skipping_msg;
234 const char *nota_msg;
235 const char *drophint_msg;
236 };
237
238 static const struct dropmsgstrings dropmsgstringarray[] = {
239 {RELKIND_RELATION,
240 ERRCODE_UNDEFINED_TABLE,
241 gettext_noop("table \"%s\" does not exist"),
242 gettext_noop("table \"%s\" does not exist, skipping"),
243 gettext_noop("\"%s\" is not a table"),
244 gettext_noop("Use DROP TABLE to remove a table.")},
245 {RELKIND_SEQUENCE,
246 ERRCODE_UNDEFINED_TABLE,
247 gettext_noop("sequence \"%s\" does not exist"),
248 gettext_noop("sequence \"%s\" does not exist, skipping"),
249 gettext_noop("\"%s\" is not a sequence"),
250 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
251 {RELKIND_VIEW,
252 ERRCODE_UNDEFINED_TABLE,
253 gettext_noop("view \"%s\" does not exist"),
254 gettext_noop("view \"%s\" does not exist, skipping"),
255 gettext_noop("\"%s\" is not a view"),
256 gettext_noop("Use DROP VIEW to remove a view.")},
257 {RELKIND_MATVIEW,
258 ERRCODE_UNDEFINED_TABLE,
259 gettext_noop("materialized view \"%s\" does not exist"),
260 gettext_noop("materialized view \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a materialized view"),
262 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
263 {RELKIND_INDEX,
264 ERRCODE_UNDEFINED_OBJECT,
265 gettext_noop("index \"%s\" does not exist"),
266 gettext_noop("index \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not an index"),
268 gettext_noop("Use DROP INDEX to remove an index.")},
269 {RELKIND_COMPOSITE_TYPE,
270 ERRCODE_UNDEFINED_OBJECT,
271 gettext_noop("type \"%s\" does not exist"),
272 gettext_noop("type \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not a type"),
274 gettext_noop("Use DROP TYPE to remove a type.")},
275 {RELKIND_FOREIGN_TABLE,
276 ERRCODE_UNDEFINED_OBJECT,
277 gettext_noop("foreign table \"%s\" does not exist"),
278 gettext_noop("foreign table \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not a foreign table"),
280 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
281 {RELKIND_PARTITIONED_TABLE,
282 ERRCODE_UNDEFINED_TABLE,
283 gettext_noop("table \"%s\" does not exist"),
284 gettext_noop("table \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not a table"),
286 gettext_noop("Use DROP TABLE to remove a table.")},
287 {RELKIND_PARTITIONED_INDEX,
288 ERRCODE_UNDEFINED_OBJECT,
289 gettext_noop("index \"%s\" does not exist"),
290 gettext_noop("index \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not an index"),
292 gettext_noop("Use DROP INDEX to remove an index.")},
293 {'\0', 0, NULL, NULL, NULL, NULL}
294 };
295
296 struct DropRelationCallbackState
297 {
298 char relkind;
299 Oid heapOid;
300 Oid partParentOid;
301 bool concurrent;
302 };
303
304 /* Alter table target-type flags for ATSimplePermissions */
305 #define ATT_TABLE 0x0001
306 #define ATT_VIEW 0x0002
307 #define ATT_MATVIEW 0x0004
308 #define ATT_INDEX 0x0008
309 #define ATT_COMPOSITE_TYPE 0x0010
310 #define ATT_FOREIGN_TABLE 0x0020
311 #define ATT_PARTITIONED_INDEX 0x0040
312
313 /*
314 * ForeignTruncateInfo
315 *
316 * Information related to truncation of foreign tables. This is used for
317 * the elements in a hash table. It uses the server OID as lookup key,
318 * and includes a per-server list of all foreign tables involved in the
319 * truncation.
320 */
321 typedef struct ForeignTruncateInfo
322 {
323 Oid serverid;
324 List *rels;
325 } ForeignTruncateInfo;
326
327 /*
328 * Partition tables are expected to be dropped when the parent partitioned
329 * table gets dropped. Hence for partitioning we use AUTO dependency.
330 * Otherwise, for regular inheritance use NORMAL dependency.
331 */
332 #define child_dependency_type(child_is_partition) \
333 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
334
335 static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
336 static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
337 static void truncate_check_activity(Relation rel);
338 static void RangeVarCallbackForTruncate(const RangeVar *relation,
339 Oid relId, Oid oldRelId, void *arg);
340 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
341 bool is_partition, List **supconstr);
342 static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
343 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
344 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
345 static void StoreCatalogInheritance(Oid relationId, List *supers,
346 bool child_is_partition);
347 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
348 int32 seqNumber, Relation inhRelation,
349 bool child_is_partition);
350 static int findAttrByName(const char *attributeName, List *schema);
351 static void AlterIndexNamespaces(Relation classRel, Relation rel,
352 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
353 static void AlterSeqNamespaces(Relation classRel, Relation rel,
354 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
355 LOCKMODE lockmode);
356 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
357 bool recurse, bool recursing, LOCKMODE lockmode);
358 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
359 Relation rel, HeapTuple contuple, List **otherrelids,
360 LOCKMODE lockmode);
361 static ObjectAddress ATExecValidateConstraint(List **wqueue,
362 Relation rel, char *constrName,
363 bool recurse, bool recursing, LOCKMODE lockmode);
364 static int transformColumnNameList(Oid relId, List *colList,
365 int16 *attnums, Oid *atttypids);
366 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
367 List **attnamelist,
368 int16 *attnums, Oid *atttypids,
369 Oid *opclasses);
370 static Oid transformFkeyCheckAttrs(Relation pkrel,
371 int numattrs, int16 *attnums,
372 Oid *opclasses);
373 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
374 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
375 Oid *funcid);
376 static void validateForeignKeyConstraint(char *conname,
377 Relation rel, Relation pkrel,
378 Oid pkindOid, Oid constraintOid);
379 static void ATController(AlterTableStmt *parsetree,
380 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
381 AlterTableUtilityContext *context);
382 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
383 bool recurse, bool recursing, LOCKMODE lockmode,
384 AlterTableUtilityContext *context);
385 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
386 AlterTableUtilityContext *context);
387 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
388 AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
389 AlterTableUtilityContext *context);
390 static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
391 Relation rel, AlterTableCmd *cmd,
392 bool recurse, LOCKMODE lockmode,
393 int cur_pass,
394 AlterTableUtilityContext *context);
395 static void ATRewriteTables(AlterTableStmt *parsetree,
396 List **wqueue, LOCKMODE lockmode,
397 AlterTableUtilityContext *context);
398 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
399 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
400 static void ATSimplePermissions(Relation rel, int allowed_targets);
401 static void ATWrongRelkindError(Relation rel, int allowed_targets);
402 static void ATSimpleRecursion(List **wqueue, Relation rel,
403 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
404 AlterTableUtilityContext *context);
405 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
406 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
407 LOCKMODE lockmode,
408 AlterTableUtilityContext *context);
409 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
410 DropBehavior behavior);
411 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
412 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
413 AlterTableUtilityContext *context);
414 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
415 Relation rel, AlterTableCmd **cmd,
416 bool recurse, bool recursing,
417 LOCKMODE lockmode, int cur_pass,
418 AlterTableUtilityContext *context);
419 static bool check_for_column_name_collision(Relation rel, const char *colname,
420 bool if_not_exists);
421 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
422 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
423 static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
424 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
425 static void ATPrepSetNotNull(List **wqueue, Relation rel,
426 AlterTableCmd *cmd, bool recurse, bool recursing,
427 LOCKMODE lockmode,
428 AlterTableUtilityContext *context);
429 static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
430 const char *colName, LOCKMODE lockmode);
431 static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
432 const char *colName, LOCKMODE lockmode);
433 static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
434 static bool ConstraintImpliedByRelConstraint(Relation scanrel,
435 List *testConstraint, List *provenConstraint);
436 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
437 Node *newDefault, LOCKMODE lockmode);
438 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
439 Node *newDefault);
440 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
441 Node *def, LOCKMODE lockmode);
442 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
443 Node *def, LOCKMODE lockmode);
444 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
445 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
446 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
447 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
448 Node *newValue, LOCKMODE lockmode);
449 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
450 Node *options, bool isReset, LOCKMODE lockmode);
451 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
452 Node *newValue, LOCKMODE lockmode);
453 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
454 AlterTableCmd *cmd, LOCKMODE lockmode,
455 AlterTableUtilityContext *context);
456 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
457 DropBehavior behavior,
458 bool recurse, bool recursing,
459 bool missing_ok, LOCKMODE lockmode,
460 ObjectAddresses *addrs);
461 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
462 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
463 static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
464 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
465 static ObjectAddress ATExecAddConstraint(List **wqueue,
466 AlteredTableInfo *tab, Relation rel,
467 Constraint *newConstraint, bool recurse, bool is_readd,
468 LOCKMODE lockmode);
469 static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
470 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
471 IndexStmt *stmt, LOCKMODE lockmode);
472 static ObjectAddress ATAddCheckConstraint(List **wqueue,
473 AlteredTableInfo *tab, Relation rel,
474 Constraint *constr,
475 bool recurse, bool recursing, bool is_readd,
476 LOCKMODE lockmode);
477 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
478 Relation rel, Constraint *fkconstraint,
479 bool recurse, bool recursing,
480 LOCKMODE lockmode);
481 static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
482 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
483 int numfks, int16 *pkattnum, int16 *fkattnum,
484 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
485 bool old_check_ok);
486 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
487 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
488 int numfks, int16 *pkattnum, int16 *fkattnum,
489 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
490 bool old_check_ok, LOCKMODE lockmode);
491 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
492 Relation partitionRel);
493 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
494 static void CloneFkReferencing(List **wqueue, Relation parentRel,
495 Relation partRel);
496 static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
497 Constraint *fkconstraint, Oid constraintOid,
498 Oid indexOid);
499 static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
500 Constraint *fkconstraint, Oid constraintOid,
501 Oid indexOid);
502 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
503 Oid partRelid,
504 Oid parentConstrOid, int numfks,
505 AttrNumber *mapped_conkey, AttrNumber *confkey,
506 Oid *conpfeqop);
507 static void ATExecDropConstraint(Relation rel, const char *constrName,
508 DropBehavior behavior,
509 bool recurse, bool recursing,
510 bool missing_ok, LOCKMODE lockmode);
511 static void ATPrepAlterColumnType(List **wqueue,
512 AlteredTableInfo *tab, Relation rel,
513 bool recurse, bool recursing,
514 AlterTableCmd *cmd, LOCKMODE lockmode,
515 AlterTableUtilityContext *context);
516 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
517 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
518 AlterTableCmd *cmd, LOCKMODE lockmode);
519 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
520 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
521 static void RememberStatisticsForRebuilding(Oid indoid, AlteredTableInfo *tab);
522 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
523 LOCKMODE lockmode);
524 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
525 char *cmd, List **wqueue, LOCKMODE lockmode,
526 bool rewrite);
527 static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
528 Oid objid, Relation rel, List *domname,
529 const char *conname);
530 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
531 static void TryReuseForeignKey(Oid oldId, Constraint *con);
532 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
533 List *options, LOCKMODE lockmode);
534 static void change_owner_fix_column_acls(Oid relationOid,
535 Oid oldOwnerId, Oid newOwnerId);
536 static void change_owner_recurse_to_sequences(Oid relationOid,
537 Oid newOwnerId, LOCKMODE lockmode);
538 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
539 LOCKMODE lockmode);
540 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
541 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
542 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
543 const char *tablespacename, LOCKMODE lockmode);
544 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
545 static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
546 static void ATExecSetRelOptions(Relation rel, List *defList,
547 AlterTableType operation,
548 LOCKMODE lockmode);
549 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
550 char fires_when, bool skip_system, LOCKMODE lockmode);
551 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
552 char fires_when, LOCKMODE lockmode);
553 static void ATPrepAddInherit(Relation child_rel);
554 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
555 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
556 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
557 DependencyType deptype);
558 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
559 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
560 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
561 static void ATExecGenericOptions(Relation rel, List *options);
562 static void ATExecSetRowSecurity(Relation rel, bool rls);
563 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
564 static ObjectAddress ATExecSetCompression(AlteredTableInfo *tab, Relation rel,
565 const char *column, Node *newValue, LOCKMODE lockmode);
566
567 static void index_copy_data(Relation rel, RelFileNode newrnode);
568 static const char *storage_name(char c);
569
570 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
571 Oid oldRelOid, void *arg);
572 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
573 Oid oldrelid, void *arg);
574 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
575 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
576 List **partexprs, Oid *partopclass, Oid *partcollation, char strategy);
577 static void CreateInheritance(Relation child_rel, Relation parent_rel);
578 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
579 bool allow_detached);
580 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
581 PartitionCmd *cmd,
582 AlterTableUtilityContext *context);
583 static void AttachPartitionEnsureIndexes(Relation rel, Relation attachrel);
584 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
585 List *partConstraint,
586 bool validate_default);
587 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
588 static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
589 static void DropClonedTriggersFromPartition(Oid partitionId);
590 static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
591 Relation rel, RangeVar *name,
592 bool concurrent);
593 static void DetachPartitionFinalize(Relation rel, Relation partRel,
594 bool concurrent, Oid defaultPartOid);
595 static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
596 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation rel,
597 RangeVar *name);
598 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
599 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
600 Relation partitionTbl);
601 static List *GetParentedForeignKeyRefs(Relation partition);
602 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
603 static char GetAttributeCompression(Oid atttypid, char *compression);
604
605
606 /* ----------------------------------------------------------------
607 * DefineRelation
608 * Creates a new relation.
609 *
610 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
611 * The other arguments are used to extend the behavior for other cases:
612 * relkind: relkind to assign to the new relation
613 * ownerId: if not InvalidOid, use this as the new relation's owner.
614 * typaddress: if not null, it's set to the pg_type entry's address.
615 * queryString: for error reporting
616 *
617 * Note that permissions checks are done against current user regardless of
618 * ownerId. A nonzero ownerId is used when someone is creating a relation
619 * "on behalf of" someone else, so we still want to see that the current user
620 * has permissions to do it.
621 *
622 * If successful, returns the address of the new relation.
623 * ----------------------------------------------------------------
624 */
625 ObjectAddress
DefineRelation(CreateStmt * stmt,char relkind,Oid ownerId,ObjectAddress * typaddress,const char * queryString)626 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
627 ObjectAddress *typaddress, const char *queryString)
628 {
629 char relname[NAMEDATALEN];
630 Oid namespaceId;
631 Oid relationId;
632 Oid tablespaceId;
633 Relation rel;
634 TupleDesc descriptor;
635 List *inheritOids;
636 List *old_constraints;
637 List *rawDefaults;
638 List *cookedDefaults;
639 Datum reloptions;
640 ListCell *listptr;
641 AttrNumber attnum;
642 bool partitioned;
643 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
644 Oid ofTypeId;
645 ObjectAddress address;
646 LOCKMODE parentLockmode;
647 const char *accessMethod = NULL;
648 Oid accessMethodId = InvalidOid;
649
650 /*
651 * Truncate relname to appropriate length (probably a waste of time, as
652 * parser should have done this already).
653 */
654 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
655
656 /*
657 * Check consistency of arguments
658 */
659 if (stmt->oncommit != ONCOMMIT_NOOP
660 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
661 ereport(ERROR,
662 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
663 errmsg("ON COMMIT can only be used on temporary tables")));
664
665 if (stmt->partspec != NULL)
666 {
667 if (relkind != RELKIND_RELATION)
668 elog(ERROR, "unexpected relkind: %d", (int) relkind);
669
670 relkind = RELKIND_PARTITIONED_TABLE;
671 partitioned = true;
672 }
673 else
674 partitioned = false;
675
676 /*
677 * Look up the namespace in which we are supposed to create the relation,
678 * check we have permission to create there, lock it against concurrent
679 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
680 * namespace is selected.
681 */
682 namespaceId =
683 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
684
685 /*
686 * Security check: disallow creating temp tables from security-restricted
687 * code. This is needed because calling code might not expect untrusted
688 * tables to appear in pg_temp at the front of its search path.
689 */
690 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
691 && InSecurityRestrictedOperation())
692 ereport(ERROR,
693 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
694 errmsg("cannot create temporary table within security-restricted operation")));
695
696 /*
697 * Determine the lockmode to use when scanning parents. A self-exclusive
698 * lock is needed here.
699 *
700 * For regular inheritance, if two backends attempt to add children to the
701 * same parent simultaneously, and that parent has no pre-existing
702 * children, then both will attempt to update the parent's relhassubclass
703 * field, leading to a "tuple concurrently updated" error. Also, this
704 * interlocks against a concurrent ANALYZE on the parent table, which
705 * might otherwise be attempting to clear the parent's relhassubclass
706 * field, if its previous children were recently dropped.
707 *
708 * If the child table is a partition, then we instead grab an exclusive
709 * lock on the parent because its partition descriptor will be changed by
710 * addition of the new partition.
711 */
712 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
713 ShareUpdateExclusiveLock);
714
715 /* Determine the list of OIDs of the parents. */
716 inheritOids = NIL;
717 foreach(listptr, stmt->inhRelations)
718 {
719 RangeVar *rv = (RangeVar *) lfirst(listptr);
720 Oid parentOid;
721
722 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
723
724 /*
725 * Reject duplications in the list of parents.
726 */
727 if (list_member_oid(inheritOids, parentOid))
728 ereport(ERROR,
729 (errcode(ERRCODE_DUPLICATE_TABLE),
730 errmsg("relation \"%s\" would be inherited from more than once",
731 get_rel_name(parentOid))));
732
733 inheritOids = lappend_oid(inheritOids, parentOid);
734 }
735
736 /*
737 * Select tablespace to use: an explicitly indicated one, or (in the case
738 * of a partitioned table) the parent's, if it has one.
739 */
740 if (stmt->tablespacename)
741 {
742 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
743
744 if (partitioned && tablespaceId == MyDatabaseTableSpace)
745 ereport(ERROR,
746 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
747 errmsg("cannot specify default tablespace for partitioned relations")));
748 }
749 else if (stmt->partbound)
750 {
751 /*
752 * For partitions, when no other tablespace is specified, we default
753 * the tablespace to the parent partitioned table's.
754 */
755 Assert(list_length(inheritOids) == 1);
756 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
757 }
758 else
759 tablespaceId = InvalidOid;
760
761 /* still nothing? use the default */
762 if (!OidIsValid(tablespaceId))
763 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
764 partitioned);
765
766 /* Check permissions except when using database's default */
767 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
768 {
769 AclResult aclresult;
770
771 aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
772 ACL_CREATE);
773 if (aclresult != ACLCHECK_OK)
774 aclcheck_error(aclresult, OBJECT_TABLESPACE,
775 get_tablespace_name(tablespaceId));
776 }
777
778 /* In all cases disallow placing user relations in pg_global */
779 if (tablespaceId == GLOBALTABLESPACE_OID)
780 ereport(ERROR,
781 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
782 errmsg("only shared relations can be placed in pg_global tablespace")));
783
784 /* Identify user ID that will own the table */
785 if (!OidIsValid(ownerId))
786 ownerId = GetUserId();
787
788 /*
789 * Parse and validate reloptions, if any.
790 */
791 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
792 true, false);
793
794 switch (relkind)
795 {
796 case RELKIND_VIEW:
797 (void) view_reloptions(reloptions, true);
798 break;
799 case RELKIND_PARTITIONED_TABLE:
800 (void) partitioned_table_reloptions(reloptions, true);
801 break;
802 default:
803 (void) heap_reloptions(relkind, reloptions, true);
804 }
805
806 if (stmt->ofTypename)
807 {
808 AclResult aclresult;
809
810 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
811
812 aclresult = pg_type_aclcheck(ofTypeId, GetUserId(), ACL_USAGE);
813 if (aclresult != ACLCHECK_OK)
814 aclcheck_error_type(aclresult, ofTypeId);
815 }
816 else
817 ofTypeId = InvalidOid;
818
819 /*
820 * Look up inheritance ancestors and generate relation schema, including
821 * inherited attributes. (Note that stmt->tableElts is destructively
822 * modified by MergeAttributes.)
823 */
824 stmt->tableElts =
825 MergeAttributes(stmt->tableElts, inheritOids,
826 stmt->relation->relpersistence,
827 stmt->partbound != NULL,
828 &old_constraints);
829
830 /*
831 * Create a tuple descriptor from the relation schema. Note that this
832 * deals with column names, types, and NOT NULL constraints, but not
833 * default values or CHECK constraints; we handle those below.
834 */
835 descriptor = BuildDescForRelation(stmt->tableElts);
836
837 /*
838 * Find columns with default values and prepare for insertion of the
839 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
840 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
841 * while raw defaults go into a list of RawColumnDefault structs that will
842 * be processed by AddRelationNewConstraints. (We can't deal with raw
843 * expressions until we can do transformExpr.)
844 *
845 * We can set the atthasdef flags now in the tuple descriptor; this just
846 * saves StoreAttrDefault from having to do an immediate update of the
847 * pg_attribute rows.
848 */
849 rawDefaults = NIL;
850 cookedDefaults = NIL;
851 attnum = 0;
852
853 foreach(listptr, stmt->tableElts)
854 {
855 ColumnDef *colDef = lfirst(listptr);
856 Form_pg_attribute attr;
857
858 attnum++;
859 attr = TupleDescAttr(descriptor, attnum - 1);
860
861 if (colDef->raw_default != NULL)
862 {
863 RawColumnDefault *rawEnt;
864
865 Assert(colDef->cooked_default == NULL);
866
867 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
868 rawEnt->attnum = attnum;
869 rawEnt->raw_default = colDef->raw_default;
870 rawEnt->missingMode = false;
871 rawEnt->generated = colDef->generated;
872 rawDefaults = lappend(rawDefaults, rawEnt);
873 attr->atthasdef = true;
874 }
875 else if (colDef->cooked_default != NULL)
876 {
877 CookedConstraint *cooked;
878
879 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
880 cooked->contype = CONSTR_DEFAULT;
881 cooked->conoid = InvalidOid; /* until created */
882 cooked->name = NULL;
883 cooked->attnum = attnum;
884 cooked->expr = colDef->cooked_default;
885 cooked->skip_validation = false;
886 cooked->is_local = true; /* not used for defaults */
887 cooked->inhcount = 0; /* ditto */
888 cooked->is_no_inherit = false;
889 cookedDefaults = lappend(cookedDefaults, cooked);
890 attr->atthasdef = true;
891 }
892
893 if (colDef->identity)
894 attr->attidentity = colDef->identity;
895
896 if (colDef->generated)
897 attr->attgenerated = colDef->generated;
898
899 if (colDef->compression)
900 attr->attcompression = GetAttributeCompression(attr->atttypid,
901 colDef->compression);
902 }
903
904 /*
905 * If the statement hasn't specified an access method, but we're defining
906 * a type of relation that needs one, use the default.
907 */
908 if (stmt->accessMethod != NULL)
909 {
910 accessMethod = stmt->accessMethod;
911
912 if (partitioned)
913 ereport(ERROR,
914 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
915 errmsg("specifying a table access method is not supported on a partitioned table")));
916
917 }
918 else if (relkind == RELKIND_RELATION ||
919 relkind == RELKIND_TOASTVALUE ||
920 relkind == RELKIND_MATVIEW)
921 accessMethod = default_table_access_method;
922
923 /* look up the access method, verify it is for a table */
924 if (accessMethod != NULL)
925 accessMethodId = get_table_am_oid(accessMethod, false);
926
927 /*
928 * Create the relation. Inherited defaults and constraints are passed in
929 * for immediate handling --- since they don't need parsing, they can be
930 * stored immediately.
931 */
932 relationId = heap_create_with_catalog(relname,
933 namespaceId,
934 tablespaceId,
935 InvalidOid,
936 InvalidOid,
937 ofTypeId,
938 ownerId,
939 accessMethodId,
940 descriptor,
941 list_concat(cookedDefaults,
942 old_constraints),
943 relkind,
944 stmt->relation->relpersistence,
945 false,
946 false,
947 stmt->oncommit,
948 reloptions,
949 true,
950 allowSystemTableMods,
951 false,
952 InvalidOid,
953 typaddress);
954
955 /*
956 * We must bump the command counter to make the newly-created relation
957 * tuple visible for opening.
958 */
959 CommandCounterIncrement();
960
961 /*
962 * Open the new relation and acquire exclusive lock on it. This isn't
963 * really necessary for locking out other backends (since they can't see
964 * the new rel anyway until we commit), but it keeps the lock manager from
965 * complaining about deadlock risks.
966 */
967 rel = relation_open(relationId, AccessExclusiveLock);
968
969 /*
970 * Now add any newly specified column default and generation expressions
971 * to the new relation. These are passed to us in the form of raw
972 * parsetrees; we need to transform them to executable expression trees
973 * before they can be added. The most convenient way to do that is to
974 * apply the parser's transformExpr routine, but transformExpr doesn't
975 * work unless we have a pre-existing relation. So, the transformation has
976 * to be postponed to this final step of CREATE TABLE.
977 *
978 * This needs to be before processing the partitioning clauses because
979 * those could refer to generated columns.
980 */
981 if (rawDefaults)
982 AddRelationNewConstraints(rel, rawDefaults, NIL,
983 true, true, false, queryString);
984
985 /*
986 * Make column generation expressions visible for use by partitioning.
987 */
988 CommandCounterIncrement();
989
990 /* Process and store partition bound, if any. */
991 if (stmt->partbound)
992 {
993 PartitionBoundSpec *bound;
994 ParseState *pstate;
995 Oid parentId = linitial_oid(inheritOids),
996 defaultPartOid;
997 Relation parent,
998 defaultRel = NULL;
999 ParseNamespaceItem *nsitem;
1000
1001 /* Already have strong enough lock on the parent */
1002 parent = table_open(parentId, NoLock);
1003
1004 /*
1005 * We are going to try to validate the partition bound specification
1006 * against the partition key of parentRel, so it better have one.
1007 */
1008 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1009 ereport(ERROR,
1010 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1011 errmsg("\"%s\" is not partitioned",
1012 RelationGetRelationName(parent))));
1013
1014 /*
1015 * The partition constraint of the default partition depends on the
1016 * partition bounds of every other partition. It is possible that
1017 * another backend might be about to execute a query on the default
1018 * partition table, and that the query relies on previously cached
1019 * default partition constraints. We must therefore take a table lock
1020 * strong enough to prevent all queries on the default partition from
1021 * proceeding until we commit and send out a shared-cache-inval notice
1022 * that will make them update their index lists.
1023 *
1024 * Order of locking: The relation being added won't be visible to
1025 * other backends until it is committed, hence here in
1026 * DefineRelation() the order of locking the default partition and the
1027 * relation being added does not matter. But at all other places we
1028 * need to lock the default relation before we lock the relation being
1029 * added or removed i.e. we should take the lock in same order at all
1030 * the places such that lock parent, lock default partition and then
1031 * lock the partition so as to avoid a deadlock.
1032 */
1033 defaultPartOid =
1034 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1035 true));
1036 if (OidIsValid(defaultPartOid))
1037 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1038
1039 /* Transform the bound values */
1040 pstate = make_parsestate(NULL);
1041 pstate->p_sourcetext = queryString;
1042
1043 /*
1044 * Add an nsitem containing this relation, so that transformExpr
1045 * called on partition bound expressions is able to report errors
1046 * using a proper context.
1047 */
1048 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1049 NULL, false, false);
1050 addNSItemToQuery(pstate, nsitem, false, true, true);
1051
1052 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1053
1054 /*
1055 * Check first that the new partition's bound is valid and does not
1056 * overlap with any of existing partitions of the parent.
1057 */
1058 check_new_partition_bound(relname, parent, bound, pstate);
1059
1060 /*
1061 * If the default partition exists, its partition constraints will
1062 * change after the addition of this new partition such that it won't
1063 * allow any row that qualifies for this new partition. So, check that
1064 * the existing data in the default partition satisfies the constraint
1065 * as it will exist after adding this partition.
1066 */
1067 if (OidIsValid(defaultPartOid))
1068 {
1069 check_default_partition_contents(parent, defaultRel, bound);
1070 /* Keep the lock until commit. */
1071 table_close(defaultRel, NoLock);
1072 }
1073
1074 /* Update the pg_class entry. */
1075 StorePartitionBound(rel, parent, bound);
1076
1077 table_close(parent, NoLock);
1078 }
1079
1080 /* Store inheritance information for new rel. */
1081 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1082
1083 /*
1084 * Process the partitioning specification (if any) and store the partition
1085 * key information into the catalog.
1086 */
1087 if (partitioned)
1088 {
1089 ParseState *pstate;
1090 char strategy;
1091 int partnatts;
1092 AttrNumber partattrs[PARTITION_MAX_KEYS];
1093 Oid partopclass[PARTITION_MAX_KEYS];
1094 Oid partcollation[PARTITION_MAX_KEYS];
1095 List *partexprs = NIL;
1096
1097 pstate = make_parsestate(NULL);
1098 pstate->p_sourcetext = queryString;
1099
1100 partnatts = list_length(stmt->partspec->partParams);
1101
1102 /* Protect fixed-size arrays here and in executor */
1103 if (partnatts > PARTITION_MAX_KEYS)
1104 ereport(ERROR,
1105 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1106 errmsg("cannot partition using more than %d columns",
1107 PARTITION_MAX_KEYS)));
1108
1109 /*
1110 * We need to transform the raw parsetrees corresponding to partition
1111 * expressions into executable expression trees. Like column defaults
1112 * and CHECK constraints, we could not have done the transformation
1113 * earlier.
1114 */
1115 stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
1116 &strategy);
1117
1118 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1119 partattrs, &partexprs, partopclass,
1120 partcollation, strategy);
1121
1122 StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
1123 partopclass, partcollation);
1124
1125 /* make it all visible */
1126 CommandCounterIncrement();
1127 }
1128
1129 /*
1130 * If we're creating a partition, create now all the indexes, triggers,
1131 * FKs defined in the parent.
1132 *
1133 * We can't do it earlier, because DefineIndex wants to know the partition
1134 * key which we just stored.
1135 */
1136 if (stmt->partbound)
1137 {
1138 Oid parentId = linitial_oid(inheritOids);
1139 Relation parent;
1140 List *idxlist;
1141 ListCell *cell;
1142
1143 /* Already have strong enough lock on the parent */
1144 parent = table_open(parentId, NoLock);
1145 idxlist = RelationGetIndexList(parent);
1146
1147 /*
1148 * For each index in the parent table, create one in the partition
1149 */
1150 foreach(cell, idxlist)
1151 {
1152 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1153 AttrMap *attmap;
1154 IndexStmt *idxstmt;
1155 Oid constraintOid;
1156
1157 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1158 {
1159 if (idxRel->rd_index->indisunique)
1160 ereport(ERROR,
1161 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1162 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1163 RelationGetRelationName(parent)),
1164 errdetail("Table \"%s\" contains indexes that are unique.",
1165 RelationGetRelationName(parent))));
1166 else
1167 {
1168 index_close(idxRel, AccessShareLock);
1169 continue;
1170 }
1171 }
1172
1173 attmap = build_attrmap_by_name(RelationGetDescr(rel),
1174 RelationGetDescr(parent));
1175 idxstmt =
1176 generateClonedIndexStmt(NULL, idxRel,
1177 attmap, &constraintOid);
1178 DefineIndex(RelationGetRelid(rel),
1179 idxstmt,
1180 InvalidOid,
1181 RelationGetRelid(idxRel),
1182 constraintOid,
1183 false, false, false, false, false);
1184
1185 index_close(idxRel, AccessShareLock);
1186 }
1187
1188 list_free(idxlist);
1189
1190 /*
1191 * If there are any row-level triggers, clone them to the new
1192 * partition.
1193 */
1194 if (parent->trigdesc != NULL)
1195 CloneRowTriggersToPartition(parent, rel);
1196
1197 /*
1198 * And foreign keys too. Note that because we're freshly creating the
1199 * table, there is no need to verify these new constraints.
1200 */
1201 CloneForeignKeyConstraints(NULL, parent, rel);
1202
1203 table_close(parent, NoLock);
1204 }
1205
1206 /*
1207 * Now add any newly specified CHECK constraints to the new relation. Same
1208 * as for defaults above, but these need to come after partitioning is set
1209 * up.
1210 */
1211 if (stmt->constraints)
1212 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1213 true, true, false, queryString);
1214
1215 ObjectAddressSet(address, RelationRelationId, relationId);
1216
1217 /*
1218 * Clean up. We keep lock on new relation (although it shouldn't be
1219 * visible to anyone else anyway, until commit).
1220 */
1221 relation_close(rel, NoLock);
1222
1223 return address;
1224 }
1225
1226 /*
1227 * Emit the right error or warning message for a "DROP" command issued on a
1228 * non-existent relation
1229 */
1230 static void
DropErrorMsgNonExistent(RangeVar * rel,char rightkind,bool missing_ok)1231 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1232 {
1233 const struct dropmsgstrings *rentry;
1234
1235 if (rel->schemaname != NULL &&
1236 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1237 {
1238 if (!missing_ok)
1239 {
1240 ereport(ERROR,
1241 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1242 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1243 }
1244 else
1245 {
1246 ereport(NOTICE,
1247 (errmsg("schema \"%s\" does not exist, skipping",
1248 rel->schemaname)));
1249 }
1250 return;
1251 }
1252
1253 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1254 {
1255 if (rentry->kind == rightkind)
1256 {
1257 if (!missing_ok)
1258 {
1259 ereport(ERROR,
1260 (errcode(rentry->nonexistent_code),
1261 errmsg(rentry->nonexistent_msg, rel->relname)));
1262 }
1263 else
1264 {
1265 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1266 break;
1267 }
1268 }
1269 }
1270
1271 Assert(rentry->kind != '\0'); /* Should be impossible */
1272 }
1273
1274 /*
1275 * Emit the right error message for a "DROP" command issued on a
1276 * relation of the wrong type
1277 */
1278 static void
DropErrorMsgWrongType(const char * relname,char wrongkind,char rightkind)1279 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1280 {
1281 const struct dropmsgstrings *rentry;
1282 const struct dropmsgstrings *wentry;
1283
1284 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1285 if (rentry->kind == rightkind)
1286 break;
1287 Assert(rentry->kind != '\0');
1288
1289 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1290 if (wentry->kind == wrongkind)
1291 break;
1292 /* wrongkind could be something we don't have in our table... */
1293
1294 ereport(ERROR,
1295 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1296 errmsg(rentry->nota_msg, relname),
1297 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1298 }
1299
1300 /*
1301 * RemoveRelations
1302 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1303 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1304 */
1305 void
RemoveRelations(DropStmt * drop)1306 RemoveRelations(DropStmt *drop)
1307 {
1308 ObjectAddresses *objects;
1309 char relkind;
1310 ListCell *cell;
1311 int flags = 0;
1312 LOCKMODE lockmode = AccessExclusiveLock;
1313
1314 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1315 if (drop->concurrent)
1316 {
1317 /*
1318 * Note that for temporary relations this lock may get upgraded later
1319 * on, but as no other session can access a temporary relation, this
1320 * is actually fine.
1321 */
1322 lockmode = ShareUpdateExclusiveLock;
1323 Assert(drop->removeType == OBJECT_INDEX);
1324 if (list_length(drop->objects) != 1)
1325 ereport(ERROR,
1326 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1327 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1328 if (drop->behavior == DROP_CASCADE)
1329 ereport(ERROR,
1330 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1331 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1332 }
1333
1334 /*
1335 * First we identify all the relations, then we delete them in a single
1336 * performMultipleDeletions() call. This is to avoid unwanted DROP
1337 * RESTRICT errors if one of the relations depends on another.
1338 */
1339
1340 /* Determine required relkind */
1341 switch (drop->removeType)
1342 {
1343 case OBJECT_TABLE:
1344 relkind = RELKIND_RELATION;
1345 break;
1346
1347 case OBJECT_INDEX:
1348 relkind = RELKIND_INDEX;
1349 break;
1350
1351 case OBJECT_SEQUENCE:
1352 relkind = RELKIND_SEQUENCE;
1353 break;
1354
1355 case OBJECT_VIEW:
1356 relkind = RELKIND_VIEW;
1357 break;
1358
1359 case OBJECT_MATVIEW:
1360 relkind = RELKIND_MATVIEW;
1361 break;
1362
1363 case OBJECT_FOREIGN_TABLE:
1364 relkind = RELKIND_FOREIGN_TABLE;
1365 break;
1366
1367 default:
1368 elog(ERROR, "unrecognized drop object type: %d",
1369 (int) drop->removeType);
1370 relkind = 0; /* keep compiler quiet */
1371 break;
1372 }
1373
1374 /* Lock and validate each relation; build a list of object addresses */
1375 objects = new_object_addresses();
1376
1377 foreach(cell, drop->objects)
1378 {
1379 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1380 Oid relOid;
1381 ObjectAddress obj;
1382 struct DropRelationCallbackState state;
1383
1384 /*
1385 * These next few steps are a great deal like relation_openrv, but we
1386 * don't bother building a relcache entry since we don't need it.
1387 *
1388 * Check for shared-cache-inval messages before trying to access the
1389 * relation. This is needed to cover the case where the name
1390 * identifies a rel that has been dropped and recreated since the
1391 * start of our transaction: if we don't flush the old syscache entry,
1392 * then we'll latch onto that entry and suffer an error later.
1393 */
1394 AcceptInvalidationMessages();
1395
1396 /* Look up the appropriate relation using namespace search. */
1397 state.relkind = relkind;
1398 state.heapOid = InvalidOid;
1399 state.partParentOid = InvalidOid;
1400 state.concurrent = drop->concurrent;
1401 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1402 RangeVarCallbackForDropRelation,
1403 (void *) &state);
1404
1405 /* Not there? */
1406 if (!OidIsValid(relOid))
1407 {
1408 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1409 continue;
1410 }
1411
1412 /*
1413 * Decide if concurrent mode needs to be used here or not. The
1414 * relation persistence cannot be known without its OID.
1415 */
1416 if (drop->concurrent &&
1417 get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
1418 {
1419 Assert(list_length(drop->objects) == 1 &&
1420 drop->removeType == OBJECT_INDEX);
1421 flags |= PERFORM_DELETION_CONCURRENTLY;
1422 }
1423
1424 /*
1425 * Concurrent index drop cannot be used with partitioned indexes,
1426 * either.
1427 */
1428 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1429 get_rel_relkind(relOid) == RELKIND_PARTITIONED_INDEX)
1430 ereport(ERROR,
1431 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1432 errmsg("cannot drop partitioned index \"%s\" concurrently",
1433 rel->relname)));
1434
1435 /* OK, we're ready to delete this one */
1436 obj.classId = RelationRelationId;
1437 obj.objectId = relOid;
1438 obj.objectSubId = 0;
1439
1440 add_exact_object_address(&obj, objects);
1441 }
1442
1443 performMultipleDeletions(objects, drop->behavior, flags);
1444
1445 free_object_addresses(objects);
1446 }
1447
1448 /*
1449 * Before acquiring a table lock, check whether we have sufficient rights.
1450 * In the case of DROP INDEX, also try to lock the table before the index.
1451 * Also, if the table to be dropped is a partition, we try to lock the parent
1452 * first.
1453 */
1454 static void
RangeVarCallbackForDropRelation(const RangeVar * rel,Oid relOid,Oid oldRelOid,void * arg)1455 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1456 void *arg)
1457 {
1458 HeapTuple tuple;
1459 struct DropRelationCallbackState *state;
1460 char relkind;
1461 char expected_relkind;
1462 bool is_partition;
1463 Form_pg_class classform;
1464 LOCKMODE heap_lockmode;
1465 bool invalid_system_index = false;
1466
1467 state = (struct DropRelationCallbackState *) arg;
1468 relkind = state->relkind;
1469 heap_lockmode = state->concurrent ?
1470 ShareUpdateExclusiveLock : AccessExclusiveLock;
1471
1472 /*
1473 * If we previously locked some other index's heap, and the name we're
1474 * looking up no longer refers to that relation, release the now-useless
1475 * lock.
1476 */
1477 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1478 {
1479 UnlockRelationOid(state->heapOid, heap_lockmode);
1480 state->heapOid = InvalidOid;
1481 }
1482
1483 /*
1484 * Similarly, if we previously locked some other partition's heap, and the
1485 * name we're looking up no longer refers to that relation, release the
1486 * now-useless lock.
1487 */
1488 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1489 {
1490 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1491 state->partParentOid = InvalidOid;
1492 }
1493
1494 /* Didn't find a relation, so no need for locking or permission checks. */
1495 if (!OidIsValid(relOid))
1496 return;
1497
1498 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1499 if (!HeapTupleIsValid(tuple))
1500 return; /* concurrently dropped, so nothing to do */
1501 classform = (Form_pg_class) GETSTRUCT(tuple);
1502 is_partition = classform->relispartition;
1503
1504 /*
1505 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1506 * but RemoveRelations() can only pass one relkind for a given relation.
1507 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1508 * That means we must be careful before giving the wrong type error when
1509 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1510 * exists with indexes.
1511 */
1512 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1513 expected_relkind = RELKIND_RELATION;
1514 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1515 expected_relkind = RELKIND_INDEX;
1516 else
1517 expected_relkind = classform->relkind;
1518
1519 if (relkind != expected_relkind)
1520 DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
1521
1522 /* Allow DROP to either table owner or schema owner */
1523 if (!pg_class_ownercheck(relOid, GetUserId()) &&
1524 !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
1525 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
1526 rel->relname);
1527
1528 /*
1529 * Check the case of a system index that might have been invalidated by a
1530 * failed concurrent process and allow its drop. For the time being, this
1531 * only concerns indexes of toast relations that became invalid during a
1532 * REINDEX CONCURRENTLY process.
1533 */
1534 if (IsSystemClass(relOid, classform) && relkind == RELKIND_INDEX)
1535 {
1536 HeapTuple locTuple;
1537 Form_pg_index indexform;
1538 bool indisvalid;
1539
1540 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1541 if (!HeapTupleIsValid(locTuple))
1542 {
1543 ReleaseSysCache(tuple);
1544 return;
1545 }
1546
1547 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1548 indisvalid = indexform->indisvalid;
1549 ReleaseSysCache(locTuple);
1550
1551 /* Mark object as being an invalid index of system catalogs */
1552 if (!indisvalid)
1553 invalid_system_index = true;
1554 }
1555
1556 /* In the case of an invalid index, it is fine to bypass this check */
1557 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1558 ereport(ERROR,
1559 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1560 errmsg("permission denied: \"%s\" is a system catalog",
1561 rel->relname)));
1562
1563 ReleaseSysCache(tuple);
1564
1565 /*
1566 * In DROP INDEX, attempt to acquire lock on the parent table before
1567 * locking the index. index_drop() will need this anyway, and since
1568 * regular queries lock tables before their indexes, we risk deadlock if
1569 * we do it the other way around. No error if we don't find a pg_index
1570 * entry, though --- the relation may have been dropped.
1571 */
1572 if ((relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX) &&
1573 relOid != oldRelOid)
1574 {
1575 state->heapOid = IndexGetRelation(relOid, true);
1576 if (OidIsValid(state->heapOid))
1577 LockRelationOid(state->heapOid, heap_lockmode);
1578 }
1579
1580 /*
1581 * Similarly, if the relation is a partition, we must acquire lock on its
1582 * parent before locking the partition. That's because queries lock the
1583 * parent before its partitions, so we risk deadlock it we do it the other
1584 * way around.
1585 */
1586 if (is_partition && relOid != oldRelOid)
1587 {
1588 state->partParentOid = get_partition_parent(relOid, true);
1589 if (OidIsValid(state->partParentOid))
1590 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1591 }
1592 }
1593
1594 /*
1595 * ExecuteTruncate
1596 * Executes a TRUNCATE command.
1597 *
1598 * This is a multi-relation truncate. We first open and grab exclusive
1599 * lock on all relations involved, checking permissions and otherwise
1600 * verifying that the relation is OK for truncation. Note that if relations
1601 * are foreign tables, at this stage, we have not yet checked that their
1602 * foreign data in external data sources are OK for truncation. These are
1603 * checked when foreign data are actually truncated later. In CASCADE mode,
1604 * relations having FK references to the targeted relations are automatically
1605 * added to the group; in RESTRICT mode, we check that all FK references are
1606 * internal to the group that's being truncated. Finally all the relations
1607 * are truncated and reindexed.
1608 */
1609 void
ExecuteTruncate(TruncateStmt * stmt)1610 ExecuteTruncate(TruncateStmt *stmt)
1611 {
1612 List *rels = NIL;
1613 List *relids = NIL;
1614 List *relids_logged = NIL;
1615 ListCell *cell;
1616
1617 /*
1618 * Open, exclusive-lock, and check all the explicitly-specified relations
1619 */
1620 foreach(cell, stmt->relations)
1621 {
1622 RangeVar *rv = lfirst(cell);
1623 Relation rel;
1624 bool recurse = rv->inh;
1625 Oid myrelid;
1626 LOCKMODE lockmode = AccessExclusiveLock;
1627
1628 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1629 0, RangeVarCallbackForTruncate,
1630 NULL);
1631
1632 /* don't throw error for "TRUNCATE foo, foo" */
1633 if (list_member_oid(relids, myrelid))
1634 continue;
1635
1636 /* open the relation, we already hold a lock on it */
1637 rel = table_open(myrelid, NoLock);
1638
1639 /*
1640 * RangeVarGetRelidExtended() has done most checks with its callback,
1641 * but other checks with the now-opened Relation remain.
1642 */
1643 truncate_check_activity(rel);
1644
1645 rels = lappend(rels, rel);
1646 relids = lappend_oid(relids, myrelid);
1647
1648 /* Log this relation only if needed for logical decoding */
1649 if (RelationIsLogicallyLogged(rel))
1650 relids_logged = lappend_oid(relids_logged, myrelid);
1651
1652 if (recurse)
1653 {
1654 ListCell *child;
1655 List *children;
1656
1657 children = find_all_inheritors(myrelid, lockmode, NULL);
1658
1659 foreach(child, children)
1660 {
1661 Oid childrelid = lfirst_oid(child);
1662
1663 if (list_member_oid(relids, childrelid))
1664 continue;
1665
1666 /* find_all_inheritors already got lock */
1667 rel = table_open(childrelid, NoLock);
1668
1669 /*
1670 * It is possible that the parent table has children that are
1671 * temp tables of other backends. We cannot safely access
1672 * such tables (because of buffering issues), and the best
1673 * thing to do is to silently ignore them. Note that this
1674 * check is the same as one of the checks done in
1675 * truncate_check_activity() called below, still it is kept
1676 * here for simplicity.
1677 */
1678 if (RELATION_IS_OTHER_TEMP(rel))
1679 {
1680 table_close(rel, lockmode);
1681 continue;
1682 }
1683
1684 /*
1685 * Inherited TRUNCATE commands perform access permission
1686 * checks on the parent table only. So we skip checking the
1687 * children's permissions and don't call
1688 * truncate_check_perms() here.
1689 */
1690 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1691 truncate_check_activity(rel);
1692
1693 rels = lappend(rels, rel);
1694 relids = lappend_oid(relids, childrelid);
1695
1696 /* Log this relation only if needed for logical decoding */
1697 if (RelationIsLogicallyLogged(rel))
1698 relids_logged = lappend_oid(relids_logged, childrelid);
1699 }
1700 }
1701 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1702 ereport(ERROR,
1703 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1704 errmsg("cannot truncate only a partitioned table"),
1705 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1706 }
1707
1708 ExecuteTruncateGuts(rels, relids, relids_logged,
1709 stmt->behavior, stmt->restart_seqs);
1710
1711 /* And close the rels */
1712 foreach(cell, rels)
1713 {
1714 Relation rel = (Relation) lfirst(cell);
1715
1716 table_close(rel, NoLock);
1717 }
1718 }
1719
1720 /*
1721 * ExecuteTruncateGuts
1722 *
1723 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1724 * command (see above) as well as replication subscribers that execute a
1725 * replicated TRUNCATE action.
1726 *
1727 * explicit_rels is the list of Relations to truncate that the command
1728 * specified. relids is the list of Oids corresponding to explicit_rels.
1729 * relids_logged is the list of Oids (a subset of relids) that require
1730 * WAL-logging. This is all a bit redundant, but the existing callers have
1731 * this information handy in this form.
1732 */
1733 void
ExecuteTruncateGuts(List * explicit_rels,List * relids,List * relids_logged,DropBehavior behavior,bool restart_seqs)1734 ExecuteTruncateGuts(List *explicit_rels,
1735 List *relids,
1736 List *relids_logged,
1737 DropBehavior behavior, bool restart_seqs)
1738 {
1739 List *rels;
1740 List *seq_relids = NIL;
1741 HTAB *ft_htab = NULL;
1742 EState *estate;
1743 ResultRelInfo *resultRelInfos;
1744 ResultRelInfo *resultRelInfo;
1745 SubTransactionId mySubid;
1746 ListCell *cell;
1747 Oid *logrelids;
1748
1749 /*
1750 * Check the explicitly-specified relations.
1751 *
1752 * In CASCADE mode, suck in all referencing relations as well. This
1753 * requires multiple iterations to find indirectly-dependent relations. At
1754 * each phase, we need to exclusive-lock new rels before looking for their
1755 * dependencies, else we might miss something. Also, we check each rel as
1756 * soon as we open it, to avoid a faux pas such as holding lock for a long
1757 * time on a rel we have no permissions for.
1758 */
1759 rels = list_copy(explicit_rels);
1760 if (behavior == DROP_CASCADE)
1761 {
1762 for (;;)
1763 {
1764 List *newrelids;
1765
1766 newrelids = heap_truncate_find_FKs(relids);
1767 if (newrelids == NIL)
1768 break; /* nothing else to add */
1769
1770 foreach(cell, newrelids)
1771 {
1772 Oid relid = lfirst_oid(cell);
1773 Relation rel;
1774
1775 rel = table_open(relid, AccessExclusiveLock);
1776 ereport(NOTICE,
1777 (errmsg("truncate cascades to table \"%s\"",
1778 RelationGetRelationName(rel))));
1779 truncate_check_rel(relid, rel->rd_rel);
1780 truncate_check_perms(relid, rel->rd_rel);
1781 truncate_check_activity(rel);
1782 rels = lappend(rels, rel);
1783 relids = lappend_oid(relids, relid);
1784
1785 /* Log this relation only if needed for logical decoding */
1786 if (RelationIsLogicallyLogged(rel))
1787 relids_logged = lappend_oid(relids_logged, relid);
1788 }
1789 }
1790 }
1791
1792 /*
1793 * Check foreign key references. In CASCADE mode, this should be
1794 * unnecessary since we just pulled in all the references; but as a
1795 * cross-check, do it anyway if in an Assert-enabled build.
1796 */
1797 #ifdef USE_ASSERT_CHECKING
1798 heap_truncate_check_FKs(rels, false);
1799 #else
1800 if (behavior == DROP_RESTRICT)
1801 heap_truncate_check_FKs(rels, false);
1802 #endif
1803
1804 /*
1805 * If we are asked to restart sequences, find all the sequences, lock them
1806 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1807 * We want to do this early since it's pointless to do all the truncation
1808 * work only to fail on sequence permissions.
1809 */
1810 if (restart_seqs)
1811 {
1812 foreach(cell, rels)
1813 {
1814 Relation rel = (Relation) lfirst(cell);
1815 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
1816 ListCell *seqcell;
1817
1818 foreach(seqcell, seqlist)
1819 {
1820 Oid seq_relid = lfirst_oid(seqcell);
1821 Relation seq_rel;
1822
1823 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1824
1825 /* This check must match AlterSequence! */
1826 if (!pg_class_ownercheck(seq_relid, GetUserId()))
1827 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
1828 RelationGetRelationName(seq_rel));
1829
1830 seq_relids = lappend_oid(seq_relids, seq_relid);
1831
1832 relation_close(seq_rel, NoLock);
1833 }
1834 }
1835 }
1836
1837 /* Prepare to catch AFTER triggers. */
1838 AfterTriggerBeginQuery();
1839
1840 /*
1841 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1842 * each relation. We don't need to call ExecOpenIndices, though.
1843 *
1844 * We put the ResultRelInfos in the es_opened_result_relations list, even
1845 * though we don't have a range table and don't populate the
1846 * es_result_relations array. That's a bit bogus, but it's enough to make
1847 * ExecGetTriggerResultRel() find them.
1848 */
1849 estate = CreateExecutorState();
1850 resultRelInfos = (ResultRelInfo *)
1851 palloc(list_length(rels) * sizeof(ResultRelInfo));
1852 resultRelInfo = resultRelInfos;
1853 foreach(cell, rels)
1854 {
1855 Relation rel = (Relation) lfirst(cell);
1856
1857 InitResultRelInfo(resultRelInfo,
1858 rel,
1859 0, /* dummy rangetable index */
1860 NULL,
1861 0);
1862 estate->es_opened_result_relations =
1863 lappend(estate->es_opened_result_relations, resultRelInfo);
1864 resultRelInfo++;
1865 }
1866
1867 /*
1868 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
1869 * truncating (this is because one of them might throw an error). Also, if
1870 * we were to allow them to prevent statement execution, that would need
1871 * to be handled here.
1872 */
1873 resultRelInfo = resultRelInfos;
1874 foreach(cell, rels)
1875 {
1876 ExecBSTruncateTriggers(estate, resultRelInfo);
1877 resultRelInfo++;
1878 }
1879
1880 /*
1881 * OK, truncate each table.
1882 */
1883 mySubid = GetCurrentSubTransactionId();
1884
1885 foreach(cell, rels)
1886 {
1887 Relation rel = (Relation) lfirst(cell);
1888
1889 /* Skip partitioned tables as there is nothing to do */
1890 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1891 continue;
1892
1893 /*
1894 * Build the lists of foreign tables belonging to each foreign server
1895 * and pass each list to the foreign data wrapper's callback function,
1896 * so that each server can truncate its all foreign tables in bulk.
1897 * Each list is saved as a single entry in a hash table that uses the
1898 * server OID as lookup key.
1899 */
1900 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1901 {
1902 Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
1903 bool found;
1904 ForeignTruncateInfo *ft_info;
1905
1906 /* First time through, initialize hashtable for foreign tables */
1907 if (!ft_htab)
1908 {
1909 HASHCTL hctl;
1910
1911 memset(&hctl, 0, sizeof(HASHCTL));
1912 hctl.keysize = sizeof(Oid);
1913 hctl.entrysize = sizeof(ForeignTruncateInfo);
1914 hctl.hcxt = CurrentMemoryContext;
1915
1916 ft_htab = hash_create("TRUNCATE for Foreign Tables",
1917 32, /* start small and extend */
1918 &hctl,
1919 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
1920 }
1921
1922 /* Find or create cached entry for the foreign table */
1923 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
1924 if (!found)
1925 {
1926 ft_info->serverid = serverid;
1927 ft_info->rels = NIL;
1928 }
1929
1930 /*
1931 * Save the foreign table in the entry of the server that the
1932 * foreign table belongs to.
1933 */
1934 ft_info->rels = lappend(ft_info->rels, rel);
1935 continue;
1936 }
1937
1938 /*
1939 * Normally, we need a transaction-safe truncation here. However, if
1940 * the table was either created in the current (sub)transaction or has
1941 * a new relfilenode in the current (sub)transaction, then we can just
1942 * truncate it in-place, because a rollback would cause the whole
1943 * table or the current physical file to be thrown away anyway.
1944 */
1945 if (rel->rd_createSubid == mySubid ||
1946 rel->rd_newRelfilenodeSubid == mySubid)
1947 {
1948 /* Immediate, non-rollbackable truncation is OK */
1949 heap_truncate_one_rel(rel);
1950 }
1951 else
1952 {
1953 Oid heap_relid;
1954 Oid toast_relid;
1955 ReindexParams reindex_params = {0};
1956
1957 /*
1958 * This effectively deletes all rows in the table, and may be done
1959 * in a serializable transaction. In that case we must record a
1960 * rw-conflict in to this transaction from each transaction
1961 * holding a predicate lock on the table.
1962 */
1963 CheckTableForSerializableConflictIn(rel);
1964
1965 /*
1966 * Need the full transaction-safe pushups.
1967 *
1968 * Create a new empty storage file for the relation, and assign it
1969 * as the relfilenode value. The old storage file is scheduled for
1970 * deletion at commit.
1971 */
1972 RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence);
1973
1974 heap_relid = RelationGetRelid(rel);
1975
1976 /*
1977 * The same for the toast table, if any.
1978 */
1979 toast_relid = rel->rd_rel->reltoastrelid;
1980 if (OidIsValid(toast_relid))
1981 {
1982 Relation toastrel = relation_open(toast_relid,
1983 AccessExclusiveLock);
1984
1985 RelationSetNewRelfilenode(toastrel,
1986 toastrel->rd_rel->relpersistence);
1987 table_close(toastrel, NoLock);
1988 }
1989
1990 /*
1991 * Reconstruct the indexes to match, and we're done.
1992 */
1993 reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,
1994 &reindex_params);
1995 }
1996
1997 pgstat_count_truncate(rel);
1998 }
1999
2000 /* Now go through the hash table, and truncate foreign tables */
2001 if (ft_htab)
2002 {
2003 ForeignTruncateInfo *ft_info;
2004 HASH_SEQ_STATUS seq;
2005
2006 hash_seq_init(&seq, ft_htab);
2007
2008 PG_TRY();
2009 {
2010 while ((ft_info = hash_seq_search(&seq)) != NULL)
2011 {
2012 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2013
2014 /* truncate_check_rel() has checked that already */
2015 Assert(routine->ExecForeignTruncate != NULL);
2016
2017 routine->ExecForeignTruncate(ft_info->rels,
2018 behavior,
2019 restart_seqs);
2020 }
2021 }
2022 PG_FINALLY();
2023 {
2024 hash_destroy(ft_htab);
2025 }
2026 PG_END_TRY();
2027 }
2028
2029 /*
2030 * Restart owned sequences if we were asked to.
2031 */
2032 foreach(cell, seq_relids)
2033 {
2034 Oid seq_relid = lfirst_oid(cell);
2035
2036 ResetSequence(seq_relid);
2037 }
2038
2039 /*
2040 * Write a WAL record to allow this set of actions to be logically
2041 * decoded.
2042 *
2043 * Assemble an array of relids so we can write a single WAL record for the
2044 * whole action.
2045 */
2046 if (list_length(relids_logged) > 0)
2047 {
2048 xl_heap_truncate xlrec;
2049 int i = 0;
2050
2051 /* should only get here if wal_level >= logical */
2052 Assert(XLogLogicalInfoActive());
2053
2054 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2055 foreach(cell, relids_logged)
2056 logrelids[i++] = lfirst_oid(cell);
2057
2058 xlrec.dbId = MyDatabaseId;
2059 xlrec.nrelids = list_length(relids_logged);
2060 xlrec.flags = 0;
2061 if (behavior == DROP_CASCADE)
2062 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2063 if (restart_seqs)
2064 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2065
2066 XLogBeginInsert();
2067 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2068 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2069
2070 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2071
2072 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2073 }
2074
2075 /*
2076 * Process all AFTER STATEMENT TRUNCATE triggers.
2077 */
2078 resultRelInfo = resultRelInfos;
2079 foreach(cell, rels)
2080 {
2081 ExecASTruncateTriggers(estate, resultRelInfo);
2082 resultRelInfo++;
2083 }
2084
2085 /* Handle queued AFTER triggers */
2086 AfterTriggerEndQuery(estate);
2087
2088 /* We can clean up the EState now */
2089 FreeExecutorState(estate);
2090
2091 /*
2092 * Close any rels opened by CASCADE (can't do this while EState still
2093 * holds refs)
2094 */
2095 rels = list_difference_ptr(rels, explicit_rels);
2096 foreach(cell, rels)
2097 {
2098 Relation rel = (Relation) lfirst(cell);
2099
2100 table_close(rel, NoLock);
2101 }
2102 }
2103
2104 /*
2105 * Check that a given relation is safe to truncate. Subroutine for
2106 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2107 */
2108 static void
truncate_check_rel(Oid relid,Form_pg_class reltuple)2109 truncate_check_rel(Oid relid, Form_pg_class reltuple)
2110 {
2111 char *relname = NameStr(reltuple->relname);
2112
2113 /*
2114 * Only allow truncate on regular tables, foreign tables using foreign
2115 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2116 * latter are only being included here for the following checks; no
2117 * physical truncation will occur in their case.).
2118 */
2119 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2120 {
2121 Oid serverid = GetForeignServerIdByRelId(relid);
2122 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2123
2124 if (!fdwroutine->ExecForeignTruncate)
2125 ereport(ERROR,
2126 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2127 errmsg("cannot truncate foreign table \"%s\"",
2128 relname)));
2129 }
2130 else if (reltuple->relkind != RELKIND_RELATION &&
2131 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2132 ereport(ERROR,
2133 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2134 errmsg("\"%s\" is not a table", relname)));
2135
2136 if (!allowSystemTableMods && IsSystemClass(relid, reltuple))
2137 ereport(ERROR,
2138 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2139 errmsg("permission denied: \"%s\" is a system catalog",
2140 relname)));
2141
2142 InvokeObjectTruncateHook(relid);
2143 }
2144
2145 /*
2146 * Check that current user has the permission to truncate given relation.
2147 */
2148 static void
truncate_check_perms(Oid relid,Form_pg_class reltuple)2149 truncate_check_perms(Oid relid, Form_pg_class reltuple)
2150 {
2151 char *relname = NameStr(reltuple->relname);
2152 AclResult aclresult;
2153
2154 /* Permissions checks */
2155 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2156 if (aclresult != ACLCHECK_OK)
2157 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2158 relname);
2159 }
2160
2161 /*
2162 * Set of extra sanity checks to check if a given relation is safe to
2163 * truncate. This is split with truncate_check_rel() as
2164 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2165 */
2166 static void
truncate_check_activity(Relation rel)2167 truncate_check_activity(Relation rel)
2168 {
2169 /*
2170 * Don't allow truncate on temp tables of other backends ... their local
2171 * buffer manager is not going to cope.
2172 */
2173 if (RELATION_IS_OTHER_TEMP(rel))
2174 ereport(ERROR,
2175 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2176 errmsg("cannot truncate temporary tables of other sessions")));
2177
2178 /*
2179 * Also check for active uses of the relation in the current transaction,
2180 * including open scans and pending AFTER trigger events.
2181 */
2182 CheckTableNotInUse(rel, "TRUNCATE");
2183 }
2184
2185 /*
2186 * storage_name
2187 * returns the name corresponding to a typstorage/attstorage enum value
2188 */
2189 static const char *
storage_name(char c)2190 storage_name(char c)
2191 {
2192 switch (c)
2193 {
2194 case TYPSTORAGE_PLAIN:
2195 return "PLAIN";
2196 case TYPSTORAGE_EXTERNAL:
2197 return "EXTERNAL";
2198 case TYPSTORAGE_EXTENDED:
2199 return "EXTENDED";
2200 case TYPSTORAGE_MAIN:
2201 return "MAIN";
2202 default:
2203 return "???";
2204 }
2205 }
2206
2207 /*----------
2208 * MergeAttributes
2209 * Returns new schema given initial schema and superclasses.
2210 *
2211 * Input arguments:
2212 * 'schema' is the column/attribute definition for the table. (It's a list
2213 * of ColumnDef's.) It is destructively changed.
2214 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2215 * 'relpersistence' is the persistence type of the table.
2216 * 'is_partition' tells if the table is a partition.
2217 *
2218 * Output arguments:
2219 * 'supconstr' receives a list of constraints belonging to the parents,
2220 * updated as necessary to be valid for the child.
2221 *
2222 * Return value:
2223 * Completed schema list.
2224 *
2225 * Notes:
2226 * The order in which the attributes are inherited is very important.
2227 * Intuitively, the inherited attributes should come first. If a table
2228 * inherits from multiple parents, the order of those attributes are
2229 * according to the order of the parents specified in CREATE TABLE.
2230 *
2231 * Here's an example:
2232 *
2233 * create table person (name text, age int4, location point);
2234 * create table emp (salary int4, manager text) inherits(person);
2235 * create table student (gpa float8) inherits (person);
2236 * create table stud_emp (percent int4) inherits (emp, student);
2237 *
2238 * The order of the attributes of stud_emp is:
2239 *
2240 * person {1:name, 2:age, 3:location}
2241 * / \
2242 * {6:gpa} student emp {4:salary, 5:manager}
2243 * \ /
2244 * stud_emp {7:percent}
2245 *
2246 * If the same attribute name appears multiple times, then it appears
2247 * in the result table in the proper location for its first appearance.
2248 *
2249 * Constraints (including NOT NULL constraints) for the child table
2250 * are the union of all relevant constraints, from both the child schema
2251 * and parent tables.
2252 *
2253 * The default value for a child column is defined as:
2254 * (1) If the child schema specifies a default, that value is used.
2255 * (2) If neither the child nor any parent specifies a default, then
2256 * the column will not have a default.
2257 * (3) If conflicting defaults are inherited from different parents
2258 * (and not overridden by the child), an error is raised.
2259 * (4) Otherwise the inherited default is used.
2260 * Rule (3) is new in Postgres 7.1; in earlier releases you got a
2261 * rather arbitrary choice of which parent default to use.
2262 *----------
2263 */
2264 static List *
MergeAttributes(List * schema,List * supers,char relpersistence,bool is_partition,List ** supconstr)2265 MergeAttributes(List *schema, List *supers, char relpersistence,
2266 bool is_partition, List **supconstr)
2267 {
2268 List *inhSchema = NIL;
2269 List *constraints = NIL;
2270 bool have_bogus_defaults = false;
2271 int child_attno;
2272 static Node bogus_marker = {0}; /* marks conflicting defaults */
2273 List *saved_schema = NIL;
2274 ListCell *entry;
2275
2276 /*
2277 * Check for and reject tables with too many columns. We perform this
2278 * check relatively early for two reasons: (a) we don't run the risk of
2279 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2280 * okay if we're processing <= 1600 columns, but could take minutes to
2281 * execute if the user attempts to create a table with hundreds of
2282 * thousands of columns.
2283 *
2284 * Note that we also need to check that we do not exceed this figure after
2285 * including columns from inherited relations.
2286 */
2287 if (list_length(schema) > MaxHeapAttributeNumber)
2288 ereport(ERROR,
2289 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2290 errmsg("tables can have at most %d columns",
2291 MaxHeapAttributeNumber)));
2292
2293 /*
2294 * Check for duplicate names in the explicit list of attributes.
2295 *
2296 * Although we might consider merging such entries in the same way that we
2297 * handle name conflicts for inherited attributes, it seems to make more
2298 * sense to assume such conflicts are errors.
2299 *
2300 * We don't use foreach() here because we have two nested loops over the
2301 * schema list, with possible element deletions in the inner one. If we
2302 * used foreach_delete_current() it could only fix up the state of one of
2303 * the loops, so it seems cleaner to use looping over list indexes for
2304 * both loops. Note that any deletion will happen beyond where the outer
2305 * loop is, so its index never needs adjustment.
2306 */
2307 for (int coldefpos = 0; coldefpos < list_length(schema); coldefpos++)
2308 {
2309 ColumnDef *coldef = list_nth_node(ColumnDef, schema, coldefpos);
2310
2311 if (!is_partition && coldef->typeName == NULL)
2312 {
2313 /*
2314 * Typed table column option that does not belong to a column from
2315 * the type. This works because the columns from the type come
2316 * first in the list. (We omit this check for partition column
2317 * lists; those are processed separately below.)
2318 */
2319 ereport(ERROR,
2320 (errcode(ERRCODE_UNDEFINED_COLUMN),
2321 errmsg("column \"%s\" does not exist",
2322 coldef->colname)));
2323 }
2324
2325 /* restpos scans all entries beyond coldef; incr is in loop body */
2326 for (int restpos = coldefpos + 1; restpos < list_length(schema);)
2327 {
2328 ColumnDef *restdef = list_nth_node(ColumnDef, schema, restpos);
2329
2330 if (strcmp(coldef->colname, restdef->colname) == 0)
2331 {
2332 if (coldef->is_from_type)
2333 {
2334 /*
2335 * merge the column options into the column from the type
2336 */
2337 coldef->is_not_null = restdef->is_not_null;
2338 coldef->raw_default = restdef->raw_default;
2339 coldef->cooked_default = restdef->cooked_default;
2340 coldef->constraints = restdef->constraints;
2341 coldef->is_from_type = false;
2342 schema = list_delete_nth_cell(schema, restpos);
2343 }
2344 else
2345 ereport(ERROR,
2346 (errcode(ERRCODE_DUPLICATE_COLUMN),
2347 errmsg("column \"%s\" specified more than once",
2348 coldef->colname)));
2349 }
2350 else
2351 restpos++;
2352 }
2353 }
2354
2355 /*
2356 * In case of a partition, there are no new column definitions, only dummy
2357 * ColumnDefs created for column constraints. Set them aside for now and
2358 * process them at the end.
2359 */
2360 if (is_partition)
2361 {
2362 saved_schema = schema;
2363 schema = NIL;
2364 }
2365
2366 /*
2367 * Scan the parents left-to-right, and merge their attributes to form a
2368 * list of inherited attributes (inhSchema). Also check to see if we need
2369 * to inherit an OID column.
2370 */
2371 child_attno = 0;
2372 foreach(entry, supers)
2373 {
2374 Oid parent = lfirst_oid(entry);
2375 Relation relation;
2376 TupleDesc tupleDesc;
2377 TupleConstr *constr;
2378 AttrMap *newattmap;
2379 List *inherited_defaults;
2380 List *cols_with_defaults;
2381 AttrNumber parent_attno;
2382 ListCell *lc1;
2383 ListCell *lc2;
2384
2385 /* caller already got lock */
2386 relation = table_open(parent, NoLock);
2387
2388 /*
2389 * Check for active uses of the parent partitioned table in the
2390 * current transaction, such as being used in some manner by an
2391 * enclosing command.
2392 */
2393 if (is_partition)
2394 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2395
2396 /*
2397 * We do not allow partitioned tables and partitions to participate in
2398 * regular inheritance.
2399 */
2400 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2401 !is_partition)
2402 ereport(ERROR,
2403 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2404 errmsg("cannot inherit from partitioned table \"%s\"",
2405 RelationGetRelationName(relation))));
2406 if (relation->rd_rel->relispartition && !is_partition)
2407 ereport(ERROR,
2408 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2409 errmsg("cannot inherit from partition \"%s\"",
2410 RelationGetRelationName(relation))));
2411
2412 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2413 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2414 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2415 ereport(ERROR,
2416 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2417 errmsg("inherited relation \"%s\" is not a table or foreign table",
2418 RelationGetRelationName(relation))));
2419
2420 /*
2421 * If the parent is permanent, so must be all of its partitions. Note
2422 * that inheritance allows that case.
2423 */
2424 if (is_partition &&
2425 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2426 relpersistence == RELPERSISTENCE_TEMP)
2427 ereport(ERROR,
2428 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2429 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2430 RelationGetRelationName(relation))));
2431
2432 /* Permanent rels cannot inherit from temporary ones */
2433 if (relpersistence != RELPERSISTENCE_TEMP &&
2434 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2435 ereport(ERROR,
2436 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2437 errmsg(!is_partition
2438 ? "cannot inherit from temporary relation \"%s\""
2439 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2440 RelationGetRelationName(relation))));
2441
2442 /* If existing rel is temp, it must belong to this session */
2443 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2444 !relation->rd_islocaltemp)
2445 ereport(ERROR,
2446 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2447 errmsg(!is_partition
2448 ? "cannot inherit from temporary relation of another session"
2449 : "cannot create as partition of temporary relation of another session")));
2450
2451 /*
2452 * We should have an UNDER permission flag for this, but for now,
2453 * demand that creator of a child table own the parent.
2454 */
2455 if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
2456 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2457 RelationGetRelationName(relation));
2458
2459 tupleDesc = RelationGetDescr(relation);
2460 constr = tupleDesc->constr;
2461
2462 /*
2463 * newattmap->attnums[] will contain the child-table attribute numbers
2464 * for the attributes of this parent table. (They are not the same
2465 * for parents after the first one, nor if we have dropped columns.)
2466 */
2467 newattmap = make_attrmap(tupleDesc->natts);
2468
2469 /* We can't process inherited defaults until newattmap is complete. */
2470 inherited_defaults = cols_with_defaults = NIL;
2471
2472 for (parent_attno = 1; parent_attno <= tupleDesc->natts;
2473 parent_attno++)
2474 {
2475 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2476 parent_attno - 1);
2477 char *attributeName = NameStr(attribute->attname);
2478 int exist_attno;
2479 ColumnDef *def;
2480
2481 /*
2482 * Ignore dropped columns in the parent.
2483 */
2484 if (attribute->attisdropped)
2485 continue; /* leave newattmap->attnums entry as zero */
2486
2487 /*
2488 * Does it conflict with some previously inherited column?
2489 */
2490 exist_attno = findAttrByName(attributeName, inhSchema);
2491 if (exist_attno > 0)
2492 {
2493 Oid defTypeId;
2494 int32 deftypmod;
2495 Oid defCollId;
2496
2497 /*
2498 * Yes, try to merge the two column definitions. They must
2499 * have the same type, typmod, and collation.
2500 */
2501 ereport(NOTICE,
2502 (errmsg("merging multiple inherited definitions of column \"%s\"",
2503 attributeName)));
2504 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2505 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2506 if (defTypeId != attribute->atttypid ||
2507 deftypmod != attribute->atttypmod)
2508 ereport(ERROR,
2509 (errcode(ERRCODE_DATATYPE_MISMATCH),
2510 errmsg("inherited column \"%s\" has a type conflict",
2511 attributeName),
2512 errdetail("%s versus %s",
2513 format_type_with_typemod(defTypeId,
2514 deftypmod),
2515 format_type_with_typemod(attribute->atttypid,
2516 attribute->atttypmod))));
2517 defCollId = GetColumnDefCollation(NULL, def, defTypeId);
2518 if (defCollId != attribute->attcollation)
2519 ereport(ERROR,
2520 (errcode(ERRCODE_COLLATION_MISMATCH),
2521 errmsg("inherited column \"%s\" has a collation conflict",
2522 attributeName),
2523 errdetail("\"%s\" versus \"%s\"",
2524 get_collation_name(defCollId),
2525 get_collation_name(attribute->attcollation))));
2526
2527 /* Copy/check storage parameter */
2528 if (def->storage == 0)
2529 def->storage = attribute->attstorage;
2530 else if (def->storage != attribute->attstorage)
2531 ereport(ERROR,
2532 (errcode(ERRCODE_DATATYPE_MISMATCH),
2533 errmsg("inherited column \"%s\" has a storage parameter conflict",
2534 attributeName),
2535 errdetail("%s versus %s",
2536 storage_name(def->storage),
2537 storage_name(attribute->attstorage))));
2538
2539 /* Copy/check compression parameter */
2540 if (CompressionMethodIsValid(attribute->attcompression))
2541 {
2542 const char *compression =
2543 GetCompressionMethodName(attribute->attcompression);
2544
2545 if (def->compression == NULL)
2546 def->compression = pstrdup(compression);
2547 else if (strcmp(def->compression, compression) != 0)
2548 ereport(ERROR,
2549 (errcode(ERRCODE_DATATYPE_MISMATCH),
2550 errmsg("column \"%s\" has a compression method conflict",
2551 attributeName),
2552 errdetail("%s versus %s", def->compression, compression)));
2553 }
2554
2555 def->inhcount++;
2556 /* Merge of NOT NULL constraints = OR 'em together */
2557 def->is_not_null |= attribute->attnotnull;
2558 /* Default and other constraints are handled below */
2559 newattmap->attnums[parent_attno - 1] = exist_attno;
2560
2561 /* Check for GENERATED conflicts */
2562 if (def->generated != attribute->attgenerated)
2563 ereport(ERROR,
2564 (errcode(ERRCODE_DATATYPE_MISMATCH),
2565 errmsg("inherited column \"%s\" has a generation conflict",
2566 attributeName)));
2567 }
2568 else
2569 {
2570 /*
2571 * No, create a new inherited column
2572 */
2573 def = makeNode(ColumnDef);
2574 def->colname = pstrdup(attributeName);
2575 def->typeName = makeTypeNameFromOid(attribute->atttypid,
2576 attribute->atttypmod);
2577 def->inhcount = 1;
2578 def->is_local = false;
2579 def->is_not_null = attribute->attnotnull;
2580 def->is_from_type = false;
2581 def->storage = attribute->attstorage;
2582 def->raw_default = NULL;
2583 def->cooked_default = NULL;
2584 def->generated = attribute->attgenerated;
2585 def->collClause = NULL;
2586 def->collOid = attribute->attcollation;
2587 def->constraints = NIL;
2588 def->location = -1;
2589 if (CompressionMethodIsValid(attribute->attcompression))
2590 def->compression =
2591 pstrdup(GetCompressionMethodName(attribute->attcompression));
2592 else
2593 def->compression = NULL;
2594 inhSchema = lappend(inhSchema, def);
2595 newattmap->attnums[parent_attno - 1] = ++child_attno;
2596 }
2597
2598 /*
2599 * Locate default if any
2600 */
2601 if (attribute->atthasdef)
2602 {
2603 Node *this_default = NULL;
2604
2605 /* Find default in constraint structure */
2606 if (constr != NULL)
2607 {
2608 AttrDefault *attrdef = constr->defval;
2609
2610 for (int i = 0; i < constr->num_defval; i++)
2611 {
2612 if (attrdef[i].adnum == parent_attno)
2613 {
2614 this_default = stringToNode(attrdef[i].adbin);
2615 break;
2616 }
2617 }
2618 }
2619 if (this_default == NULL)
2620 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2621 parent_attno, RelationGetRelationName(relation));
2622
2623 /*
2624 * If it's a GENERATED default, it might contain Vars that
2625 * need to be mapped to the inherited column(s)' new numbers.
2626 * We can't do that till newattmap is ready, so just remember
2627 * all the inherited default expressions for the moment.
2628 */
2629 inherited_defaults = lappend(inherited_defaults, this_default);
2630 cols_with_defaults = lappend(cols_with_defaults, def);
2631 }
2632 }
2633
2634 /*
2635 * Now process any inherited default expressions, adjusting attnos
2636 * using the completed newattmap map.
2637 */
2638 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2639 {
2640 Node *this_default = (Node *) lfirst(lc1);
2641 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2642 bool found_whole_row;
2643
2644 /* Adjust Vars to match new table's column numbering */
2645 this_default = map_variable_attnos(this_default,
2646 1, 0,
2647 newattmap,
2648 InvalidOid, &found_whole_row);
2649
2650 /*
2651 * For the moment we have to reject whole-row variables. We could
2652 * convert them, if we knew the new table's rowtype OID, but that
2653 * hasn't been assigned yet. (A variable could only appear in a
2654 * generation expression, so the error message is correct.)
2655 */
2656 if (found_whole_row)
2657 ereport(ERROR,
2658 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2659 errmsg("cannot convert whole-row table reference"),
2660 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2661 def->colname,
2662 RelationGetRelationName(relation))));
2663
2664 /*
2665 * If we already had a default from some prior parent, check to
2666 * see if they are the same. If so, no problem; if not, mark the
2667 * column as having a bogus default. Below, we will complain if
2668 * the bogus default isn't overridden by the child schema.
2669 */
2670 Assert(def->raw_default == NULL);
2671 if (def->cooked_default == NULL)
2672 def->cooked_default = this_default;
2673 else if (!equal(def->cooked_default, this_default))
2674 {
2675 def->cooked_default = &bogus_marker;
2676 have_bogus_defaults = true;
2677 }
2678 }
2679
2680 /*
2681 * Now copy the CHECK constraints of this parent, adjusting attnos
2682 * using the completed newattmap map. Identically named constraints
2683 * are merged if possible, else we throw error.
2684 */
2685 if (constr && constr->num_check > 0)
2686 {
2687 ConstrCheck *check = constr->check;
2688 int i;
2689
2690 for (i = 0; i < constr->num_check; i++)
2691 {
2692 char *name = check[i].ccname;
2693 Node *expr;
2694 bool found_whole_row;
2695
2696 /* ignore if the constraint is non-inheritable */
2697 if (check[i].ccnoinherit)
2698 continue;
2699
2700 /* Adjust Vars to match new table's column numbering */
2701 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2702 1, 0,
2703 newattmap,
2704 InvalidOid, &found_whole_row);
2705
2706 /*
2707 * For the moment we have to reject whole-row variables. We
2708 * could convert them, if we knew the new table's rowtype OID,
2709 * but that hasn't been assigned yet.
2710 */
2711 if (found_whole_row)
2712 ereport(ERROR,
2713 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2714 errmsg("cannot convert whole-row table reference"),
2715 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2716 name,
2717 RelationGetRelationName(relation))));
2718
2719 /* check for duplicate */
2720 if (!MergeCheckConstraint(constraints, name, expr))
2721 {
2722 /* nope, this is a new one */
2723 CookedConstraint *cooked;
2724
2725 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
2726 cooked->contype = CONSTR_CHECK;
2727 cooked->conoid = InvalidOid; /* until created */
2728 cooked->name = pstrdup(name);
2729 cooked->attnum = 0; /* not used for constraints */
2730 cooked->expr = expr;
2731 cooked->skip_validation = false;
2732 cooked->is_local = false;
2733 cooked->inhcount = 1;
2734 cooked->is_no_inherit = false;
2735 constraints = lappend(constraints, cooked);
2736 }
2737 }
2738 }
2739
2740 free_attrmap(newattmap);
2741
2742 /*
2743 * Close the parent rel, but keep our lock on it until xact commit.
2744 * That will prevent someone else from deleting or ALTERing the parent
2745 * before the child is committed.
2746 */
2747 table_close(relation, NoLock);
2748 }
2749
2750 /*
2751 * If we had no inherited attributes, the result schema is just the
2752 * explicitly declared columns. Otherwise, we need to merge the declared
2753 * columns into the inherited schema list. Although, we never have any
2754 * explicitly declared columns if the table is a partition.
2755 */
2756 if (inhSchema != NIL)
2757 {
2758 int schema_attno = 0;
2759
2760 foreach(entry, schema)
2761 {
2762 ColumnDef *newdef = lfirst(entry);
2763 char *attributeName = newdef->colname;
2764 int exist_attno;
2765
2766 schema_attno++;
2767
2768 /*
2769 * Does it conflict with some previously inherited column?
2770 */
2771 exist_attno = findAttrByName(attributeName, inhSchema);
2772 if (exist_attno > 0)
2773 {
2774 ColumnDef *def;
2775 Oid defTypeId,
2776 newTypeId;
2777 int32 deftypmod,
2778 newtypmod;
2779 Oid defcollid,
2780 newcollid;
2781
2782 /*
2783 * Partitions have only one parent and have no column
2784 * definitions of their own, so conflict should never occur.
2785 */
2786 Assert(!is_partition);
2787
2788 /*
2789 * Yes, try to merge the two column definitions. They must
2790 * have the same type, typmod, and collation.
2791 */
2792 if (exist_attno == schema_attno)
2793 ereport(NOTICE,
2794 (errmsg("merging column \"%s\" with inherited definition",
2795 attributeName)));
2796 else
2797 ereport(NOTICE,
2798 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
2799 errdetail("User-specified column moved to the position of the inherited column.")));
2800 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2801 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2802 typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
2803 if (defTypeId != newTypeId || deftypmod != newtypmod)
2804 ereport(ERROR,
2805 (errcode(ERRCODE_DATATYPE_MISMATCH),
2806 errmsg("column \"%s\" has a type conflict",
2807 attributeName),
2808 errdetail("%s versus %s",
2809 format_type_with_typemod(defTypeId,
2810 deftypmod),
2811 format_type_with_typemod(newTypeId,
2812 newtypmod))));
2813 defcollid = GetColumnDefCollation(NULL, def, defTypeId);
2814 newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
2815 if (defcollid != newcollid)
2816 ereport(ERROR,
2817 (errcode(ERRCODE_COLLATION_MISMATCH),
2818 errmsg("column \"%s\" has a collation conflict",
2819 attributeName),
2820 errdetail("\"%s\" versus \"%s\"",
2821 get_collation_name(defcollid),
2822 get_collation_name(newcollid))));
2823
2824 /*
2825 * Identity is never inherited. The new column can have an
2826 * identity definition, so we always just take that one.
2827 */
2828 def->identity = newdef->identity;
2829
2830 /* Copy storage parameter */
2831 if (def->storage == 0)
2832 def->storage = newdef->storage;
2833 else if (newdef->storage != 0 && def->storage != newdef->storage)
2834 ereport(ERROR,
2835 (errcode(ERRCODE_DATATYPE_MISMATCH),
2836 errmsg("column \"%s\" has a storage parameter conflict",
2837 attributeName),
2838 errdetail("%s versus %s",
2839 storage_name(def->storage),
2840 storage_name(newdef->storage))));
2841
2842 /* Copy compression parameter */
2843 if (def->compression == NULL)
2844 def->compression = newdef->compression;
2845 else if (newdef->compression != NULL)
2846 {
2847 if (strcmp(def->compression, newdef->compression) != 0)
2848 ereport(ERROR,
2849 (errcode(ERRCODE_DATATYPE_MISMATCH),
2850 errmsg("column \"%s\" has a compression method conflict",
2851 attributeName),
2852 errdetail("%s versus %s", def->compression, newdef->compression)));
2853 }
2854
2855 /* Mark the column as locally defined */
2856 def->is_local = true;
2857 /* Merge of NOT NULL constraints = OR 'em together */
2858 def->is_not_null |= newdef->is_not_null;
2859
2860 /*
2861 * Check for conflicts related to generated columns.
2862 *
2863 * If the parent column is generated, the child column must be
2864 * unadorned and will be made a generated column. (We could
2865 * in theory allow the child column definition specifying the
2866 * exact same generation expression, but that's a bit
2867 * complicated to implement and doesn't seem very useful.) We
2868 * also check that the child column doesn't specify a default
2869 * value or identity, which matches the rules for a single
2870 * column in parse_util.c.
2871 */
2872 if (def->generated)
2873 {
2874 if (newdef->generated)
2875 ereport(ERROR,
2876 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2877 errmsg("child column \"%s\" specifies generation expression",
2878 def->colname),
2879 errhint("Omit the generation expression in the definition of the child table column to inherit the generation expression from the parent table.")));
2880 if (newdef->raw_default && !newdef->generated)
2881 ereport(ERROR,
2882 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2883 errmsg("column \"%s\" inherits from generated column but specifies default",
2884 def->colname)));
2885 if (newdef->identity)
2886 ereport(ERROR,
2887 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2888 errmsg("column \"%s\" inherits from generated column but specifies identity",
2889 def->colname)));
2890 }
2891
2892 /*
2893 * If the parent column is not generated, then take whatever
2894 * the child column definition says.
2895 */
2896 else
2897 {
2898 if (newdef->generated)
2899 def->generated = newdef->generated;
2900 }
2901
2902 /* If new def has a default, override previous default */
2903 if (newdef->raw_default != NULL)
2904 {
2905 def->raw_default = newdef->raw_default;
2906 def->cooked_default = newdef->cooked_default;
2907 }
2908 }
2909 else
2910 {
2911 /*
2912 * No, attach new column to result schema
2913 */
2914 inhSchema = lappend(inhSchema, newdef);
2915 }
2916 }
2917
2918 schema = inhSchema;
2919
2920 /*
2921 * Check that we haven't exceeded the legal # of columns after merging
2922 * in inherited columns.
2923 */
2924 if (list_length(schema) > MaxHeapAttributeNumber)
2925 ereport(ERROR,
2926 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2927 errmsg("tables can have at most %d columns",
2928 MaxHeapAttributeNumber)));
2929 }
2930
2931 /*
2932 * Now that we have the column definition list for a partition, we can
2933 * check whether the columns referenced in the column constraint specs
2934 * actually exist. Also, we merge NOT NULL and defaults into each
2935 * corresponding column definition.
2936 */
2937 if (is_partition)
2938 {
2939 foreach(entry, saved_schema)
2940 {
2941 ColumnDef *restdef = lfirst(entry);
2942 bool found = false;
2943 ListCell *l;
2944
2945 foreach(l, schema)
2946 {
2947 ColumnDef *coldef = lfirst(l);
2948
2949 if (strcmp(coldef->colname, restdef->colname) == 0)
2950 {
2951 found = true;
2952 coldef->is_not_null |= restdef->is_not_null;
2953
2954 /*
2955 * Override the parent's default value for this column
2956 * (coldef->cooked_default) with the partition's local
2957 * definition (restdef->raw_default), if there's one. It
2958 * should be physically impossible to get a cooked default
2959 * in the local definition or a raw default in the
2960 * inherited definition, but make sure they're nulls, for
2961 * future-proofing.
2962 */
2963 Assert(restdef->cooked_default == NULL);
2964 Assert(coldef->raw_default == NULL);
2965 if (restdef->raw_default)
2966 {
2967 coldef->raw_default = restdef->raw_default;
2968 coldef->cooked_default = NULL;
2969 }
2970 }
2971 }
2972
2973 /* complain for constraints on columns not in parent */
2974 if (!found)
2975 ereport(ERROR,
2976 (errcode(ERRCODE_UNDEFINED_COLUMN),
2977 errmsg("column \"%s\" does not exist",
2978 restdef->colname)));
2979 }
2980 }
2981
2982 /*
2983 * If we found any conflicting parent default values, check to make sure
2984 * they were overridden by the child.
2985 */
2986 if (have_bogus_defaults)
2987 {
2988 foreach(entry, schema)
2989 {
2990 ColumnDef *def = lfirst(entry);
2991
2992 if (def->cooked_default == &bogus_marker)
2993 {
2994 if (def->generated)
2995 ereport(ERROR,
2996 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2997 errmsg("column \"%s\" inherits conflicting generation expressions",
2998 def->colname)));
2999 else
3000 ereport(ERROR,
3001 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3002 errmsg("column \"%s\" inherits conflicting default values",
3003 def->colname),
3004 errhint("To resolve the conflict, specify a default explicitly.")));
3005 }
3006 }
3007 }
3008
3009 *supconstr = constraints;
3010 return schema;
3011 }
3012
3013
3014 /*
3015 * MergeCheckConstraint
3016 * Try to merge an inherited CHECK constraint with previous ones
3017 *
3018 * If we inherit identically-named constraints from multiple parents, we must
3019 * merge them, or throw an error if they don't have identical definitions.
3020 *
3021 * constraints is a list of CookedConstraint structs for previous constraints.
3022 *
3023 * Returns true if merged (constraint is a duplicate), or false if it's
3024 * got a so-far-unique name, or throws error if conflict.
3025 */
3026 static bool
MergeCheckConstraint(List * constraints,char * name,Node * expr)3027 MergeCheckConstraint(List *constraints, char *name, Node *expr)
3028 {
3029 ListCell *lc;
3030
3031 foreach(lc, constraints)
3032 {
3033 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3034
3035 Assert(ccon->contype == CONSTR_CHECK);
3036
3037 /* Non-matching names never conflict */
3038 if (strcmp(ccon->name, name) != 0)
3039 continue;
3040
3041 if (equal(expr, ccon->expr))
3042 {
3043 /* OK to merge */
3044 ccon->inhcount++;
3045 return true;
3046 }
3047
3048 ereport(ERROR,
3049 (errcode(ERRCODE_DUPLICATE_OBJECT),
3050 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3051 name)));
3052 }
3053
3054 return false;
3055 }
3056
3057
3058 /*
3059 * StoreCatalogInheritance
3060 * Updates the system catalogs with proper inheritance information.
3061 *
3062 * supers is a list of the OIDs of the new relation's direct ancestors.
3063 */
3064 static void
StoreCatalogInheritance(Oid relationId,List * supers,bool child_is_partition)3065 StoreCatalogInheritance(Oid relationId, List *supers,
3066 bool child_is_partition)
3067 {
3068 Relation relation;
3069 int32 seqNumber;
3070 ListCell *entry;
3071
3072 /*
3073 * sanity checks
3074 */
3075 AssertArg(OidIsValid(relationId));
3076
3077 if (supers == NIL)
3078 return;
3079
3080 /*
3081 * Store INHERITS information in pg_inherits using direct ancestors only.
3082 * Also enter dependencies on the direct ancestors, and make sure they are
3083 * marked with relhassubclass = true.
3084 *
3085 * (Once upon a time, both direct and indirect ancestors were found here
3086 * and then entered into pg_ipl. Since that catalog doesn't exist
3087 * anymore, there's no need to look for indirect ancestors.)
3088 */
3089 relation = table_open(InheritsRelationId, RowExclusiveLock);
3090
3091 seqNumber = 1;
3092 foreach(entry, supers)
3093 {
3094 Oid parentOid = lfirst_oid(entry);
3095
3096 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3097 child_is_partition);
3098 seqNumber++;
3099 }
3100
3101 table_close(relation, RowExclusiveLock);
3102 }
3103
3104 /*
3105 * Make catalog entries showing relationId as being an inheritance child
3106 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3107 */
3108 static void
StoreCatalogInheritance1(Oid relationId,Oid parentOid,int32 seqNumber,Relation inhRelation,bool child_is_partition)3109 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3110 int32 seqNumber, Relation inhRelation,
3111 bool child_is_partition)
3112 {
3113 ObjectAddress childobject,
3114 parentobject;
3115
3116 /* store the pg_inherits row */
3117 StoreSingleInheritance(relationId, parentOid, seqNumber);
3118
3119 /*
3120 * Store a dependency too
3121 */
3122 parentobject.classId = RelationRelationId;
3123 parentobject.objectId = parentOid;
3124 parentobject.objectSubId = 0;
3125 childobject.classId = RelationRelationId;
3126 childobject.objectId = relationId;
3127 childobject.objectSubId = 0;
3128
3129 recordDependencyOn(&childobject, &parentobject,
3130 child_dependency_type(child_is_partition));
3131
3132 /*
3133 * Post creation hook of this inheritance. Since object_access_hook
3134 * doesn't take multiple object identifiers, we relay oid of parent
3135 * relation using auxiliary_id argument.
3136 */
3137 InvokeObjectPostAlterHookArg(InheritsRelationId,
3138 relationId, 0,
3139 parentOid, false);
3140
3141 /*
3142 * Mark the parent as having subclasses.
3143 */
3144 SetRelationHasSubclass(parentOid, true);
3145 }
3146
3147 /*
3148 * Look for an existing schema entry with the given name.
3149 *
3150 * Returns the index (starting with 1) if attribute already exists in schema,
3151 * 0 if it doesn't.
3152 */
3153 static int
findAttrByName(const char * attributeName,List * schema)3154 findAttrByName(const char *attributeName, List *schema)
3155 {
3156 ListCell *s;
3157 int i = 1;
3158
3159 foreach(s, schema)
3160 {
3161 ColumnDef *def = lfirst(s);
3162
3163 if (strcmp(attributeName, def->colname) == 0)
3164 return i;
3165
3166 i++;
3167 }
3168 return 0;
3169 }
3170
3171
3172 /*
3173 * SetRelationHasSubclass
3174 * Set the value of the relation's relhassubclass field in pg_class.
3175 *
3176 * NOTE: caller must be holding an appropriate lock on the relation.
3177 * ShareUpdateExclusiveLock is sufficient.
3178 *
3179 * NOTE: an important side-effect of this operation is that an SI invalidation
3180 * message is sent out to all backends --- including me --- causing plans
3181 * referencing the relation to be rebuilt with the new list of children.
3182 * This must happen even if we find that no change is needed in the pg_class
3183 * row.
3184 */
3185 void
SetRelationHasSubclass(Oid relationId,bool relhassubclass)3186 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3187 {
3188 Relation relationRelation;
3189 HeapTuple tuple;
3190 Form_pg_class classtuple;
3191
3192 /*
3193 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3194 */
3195 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3196 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3197 if (!HeapTupleIsValid(tuple))
3198 elog(ERROR, "cache lookup failed for relation %u", relationId);
3199 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3200
3201 if (classtuple->relhassubclass != relhassubclass)
3202 {
3203 classtuple->relhassubclass = relhassubclass;
3204 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3205 }
3206 else
3207 {
3208 /* no need to change tuple, but force relcache rebuild anyway */
3209 CacheInvalidateRelcacheByTuple(tuple);
3210 }
3211
3212 heap_freetuple(tuple);
3213 table_close(relationRelation, RowExclusiveLock);
3214 }
3215
3216 /*
3217 * CheckRelationTableSpaceMove
3218 * Check if relation can be moved to new tablespace.
3219 *
3220 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3221 *
3222 * Returns true if the relation can be moved to the new tablespace; raises
3223 * an error if it is not possible to do the move; returns false if the move
3224 * would have no effect.
3225 */
3226 bool
CheckRelationTableSpaceMove(Relation rel,Oid newTableSpaceId)3227 CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3228 {
3229 Oid oldTableSpaceId;
3230
3231 /*
3232 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3233 * stored as 0.
3234 */
3235 oldTableSpaceId = rel->rd_rel->reltablespace;
3236 if (newTableSpaceId == oldTableSpaceId ||
3237 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3238 return false;
3239
3240 /*
3241 * We cannot support moving mapped relations into different tablespaces.
3242 * (In particular this eliminates all shared catalogs.)
3243 */
3244 if (RelationIsMapped(rel))
3245 ereport(ERROR,
3246 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3247 errmsg("cannot move system relation \"%s\"",
3248 RelationGetRelationName(rel))));
3249
3250 /* Cannot move a non-shared relation into pg_global */
3251 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3252 ereport(ERROR,
3253 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3254 errmsg("only shared relations can be placed in pg_global tablespace")));
3255
3256 /*
3257 * Do not allow moving temp tables of other backends ... their local
3258 * buffer manager is not going to cope.
3259 */
3260 if (RELATION_IS_OTHER_TEMP(rel))
3261 ereport(ERROR,
3262 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3263 errmsg("cannot move temporary tables of other sessions")));
3264
3265 return true;
3266 }
3267
3268 /*
3269 * SetRelationTableSpace
3270 * Set new reltablespace and relfilenode in pg_class entry.
3271 *
3272 * newTableSpaceId is the new tablespace for the relation, and
3273 * newRelFileNode its new filenode. If newRelFileNode is InvalidOid,
3274 * this field is not updated.
3275 *
3276 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3277 *
3278 * The caller of this routine had better check if a relation can be
3279 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3280 * first, and is responsible for making the change visible with
3281 * CommandCounterIncrement().
3282 */
3283 void
SetRelationTableSpace(Relation rel,Oid newTableSpaceId,Oid newRelFileNode)3284 SetRelationTableSpace(Relation rel,
3285 Oid newTableSpaceId,
3286 Oid newRelFileNode)
3287 {
3288 Relation pg_class;
3289 HeapTuple tuple;
3290 Form_pg_class rd_rel;
3291 Oid reloid = RelationGetRelid(rel);
3292
3293 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3294
3295 /* Get a modifiable copy of the relation's pg_class row. */
3296 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3297
3298 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3299 if (!HeapTupleIsValid(tuple))
3300 elog(ERROR, "cache lookup failed for relation %u", reloid);
3301 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3302
3303 /* Update the pg_class row. */
3304 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3305 InvalidOid : newTableSpaceId;
3306 if (OidIsValid(newRelFileNode))
3307 rd_rel->relfilenode = newRelFileNode;
3308 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3309
3310 /*
3311 * Record dependency on tablespace. This is only required for relations
3312 * that have no physical storage.
3313 */
3314 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3315 changeDependencyOnTablespace(RelationRelationId, reloid,
3316 rd_rel->reltablespace);
3317
3318 heap_freetuple(tuple);
3319 table_close(pg_class, RowExclusiveLock);
3320 }
3321
3322 /*
3323 * renameatt_check - basic sanity checks before attribute rename
3324 */
3325 static void
renameatt_check(Oid myrelid,Form_pg_class classform,bool recursing)3326 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3327 {
3328 char relkind = classform->relkind;
3329
3330 if (classform->reloftype && !recursing)
3331 ereport(ERROR,
3332 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3333 errmsg("cannot rename column of typed table")));
3334
3335 /*
3336 * Renaming the columns of sequences or toast tables doesn't actually
3337 * break anything from the system's point of view, since internal
3338 * references are by attnum. But it doesn't seem right to allow users to
3339 * change names that are hardcoded into the system, hence the following
3340 * restriction.
3341 */
3342 if (relkind != RELKIND_RELATION &&
3343 relkind != RELKIND_VIEW &&
3344 relkind != RELKIND_MATVIEW &&
3345 relkind != RELKIND_COMPOSITE_TYPE &&
3346 relkind != RELKIND_INDEX &&
3347 relkind != RELKIND_PARTITIONED_INDEX &&
3348 relkind != RELKIND_FOREIGN_TABLE &&
3349 relkind != RELKIND_PARTITIONED_TABLE)
3350 ereport(ERROR,
3351 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3352 errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table",
3353 NameStr(classform->relname))));
3354
3355 /*
3356 * permissions checking. only the owner of a class can change its schema.
3357 */
3358 if (!pg_class_ownercheck(myrelid, GetUserId()))
3359 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3360 NameStr(classform->relname));
3361 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3362 ereport(ERROR,
3363 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3364 errmsg("permission denied: \"%s\" is a system catalog",
3365 NameStr(classform->relname))));
3366 }
3367
3368 /*
3369 * renameatt_internal - workhorse for renameatt
3370 *
3371 * Return value is the attribute number in the 'myrelid' relation.
3372 */
3373 static AttrNumber
renameatt_internal(Oid myrelid,const char * oldattname,const char * newattname,bool recurse,bool recursing,int expected_parents,DropBehavior behavior)3374 renameatt_internal(Oid myrelid,
3375 const char *oldattname,
3376 const char *newattname,
3377 bool recurse,
3378 bool recursing,
3379 int expected_parents,
3380 DropBehavior behavior)
3381 {
3382 Relation targetrelation;
3383 Relation attrelation;
3384 HeapTuple atttup;
3385 Form_pg_attribute attform;
3386 AttrNumber attnum;
3387
3388 /*
3389 * Grab an exclusive lock on the target table, which we will NOT release
3390 * until end of transaction.
3391 */
3392 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3393 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3394
3395 /*
3396 * if the 'recurse' flag is set then we are supposed to rename this
3397 * attribute in all classes that inherit from 'relname' (as well as in
3398 * 'relname').
3399 *
3400 * any permissions or problems with duplicate attributes will cause the
3401 * whole transaction to abort, which is what we want -- all or nothing.
3402 */
3403 if (recurse)
3404 {
3405 List *child_oids,
3406 *child_numparents;
3407 ListCell *lo,
3408 *li;
3409
3410 /*
3411 * we need the number of parents for each child so that the recursive
3412 * calls to renameatt() can determine whether there are any parents
3413 * outside the inheritance hierarchy being processed.
3414 */
3415 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3416 &child_numparents);
3417
3418 /*
3419 * find_all_inheritors does the recursive search of the inheritance
3420 * hierarchy, so all we have to do is process all of the relids in the
3421 * list that it returns.
3422 */
3423 forboth(lo, child_oids, li, child_numparents)
3424 {
3425 Oid childrelid = lfirst_oid(lo);
3426 int numparents = lfirst_int(li);
3427
3428 if (childrelid == myrelid)
3429 continue;
3430 /* note we need not recurse again */
3431 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3432 }
3433 }
3434 else
3435 {
3436 /*
3437 * If we are told not to recurse, there had better not be any child
3438 * tables; else the rename would put them out of step.
3439 *
3440 * expected_parents will only be 0 if we are not already recursing.
3441 */
3442 if (expected_parents == 0 &&
3443 find_inheritance_children(myrelid, NoLock) != NIL)
3444 ereport(ERROR,
3445 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3446 errmsg("inherited column \"%s\" must be renamed in child tables too",
3447 oldattname)));
3448 }
3449
3450 /* rename attributes in typed tables of composite type */
3451 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3452 {
3453 List *child_oids;
3454 ListCell *lo;
3455
3456 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3457 RelationGetRelationName(targetrelation),
3458 behavior);
3459
3460 foreach(lo, child_oids)
3461 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3462 }
3463
3464 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3465
3466 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3467 if (!HeapTupleIsValid(atttup))
3468 ereport(ERROR,
3469 (errcode(ERRCODE_UNDEFINED_COLUMN),
3470 errmsg("column \"%s\" does not exist",
3471 oldattname)));
3472 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3473
3474 attnum = attform->attnum;
3475 if (attnum <= 0)
3476 ereport(ERROR,
3477 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3478 errmsg("cannot rename system column \"%s\"",
3479 oldattname)));
3480
3481 /*
3482 * if the attribute is inherited, forbid the renaming. if this is a
3483 * top-level call to renameatt(), then expected_parents will be 0, so the
3484 * effect of this code will be to prohibit the renaming if the attribute
3485 * is inherited at all. if this is a recursive call to renameatt(),
3486 * expected_parents will be the number of parents the current relation has
3487 * within the inheritance hierarchy being processed, so we'll prohibit the
3488 * renaming only if there are additional parents from elsewhere.
3489 */
3490 if (attform->attinhcount > expected_parents)
3491 ereport(ERROR,
3492 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3493 errmsg("cannot rename inherited column \"%s\"",
3494 oldattname)));
3495
3496 /* new name should not already exist */
3497 (void) check_for_column_name_collision(targetrelation, newattname, false);
3498
3499 /* apply the update */
3500 namestrcpy(&(attform->attname), newattname);
3501
3502 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3503
3504 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3505
3506 heap_freetuple(atttup);
3507
3508 table_close(attrelation, RowExclusiveLock);
3509
3510 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3511
3512 return attnum;
3513 }
3514
3515 /*
3516 * Perform permissions and integrity checks before acquiring a relation lock.
3517 */
3518 static void
RangeVarCallbackForRenameAttribute(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)3519 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3520 void *arg)
3521 {
3522 HeapTuple tuple;
3523 Form_pg_class form;
3524
3525 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3526 if (!HeapTupleIsValid(tuple))
3527 return; /* concurrently dropped */
3528 form = (Form_pg_class) GETSTRUCT(tuple);
3529 renameatt_check(relid, form, false);
3530 ReleaseSysCache(tuple);
3531 }
3532
3533 /*
3534 * renameatt - changes the name of an attribute in a relation
3535 *
3536 * The returned ObjectAddress is that of the renamed column.
3537 */
3538 ObjectAddress
renameatt(RenameStmt * stmt)3539 renameatt(RenameStmt *stmt)
3540 {
3541 Oid relid;
3542 AttrNumber attnum;
3543 ObjectAddress address;
3544
3545 /* lock level taken here should match renameatt_internal */
3546 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3547 stmt->missing_ok ? RVR_MISSING_OK : 0,
3548 RangeVarCallbackForRenameAttribute,
3549 NULL);
3550
3551 if (!OidIsValid(relid))
3552 {
3553 ereport(NOTICE,
3554 (errmsg("relation \"%s\" does not exist, skipping",
3555 stmt->relation->relname)));
3556 return InvalidObjectAddress;
3557 }
3558
3559 attnum =
3560 renameatt_internal(relid,
3561 stmt->subname, /* old att name */
3562 stmt->newname, /* new att name */
3563 stmt->relation->inh, /* recursive? */
3564 false, /* recursing? */
3565 0, /* expected inhcount */
3566 stmt->behavior);
3567
3568 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3569
3570 return address;
3571 }
3572
3573 /*
3574 * same logic as renameatt_internal
3575 */
3576 static ObjectAddress
rename_constraint_internal(Oid myrelid,Oid mytypid,const char * oldconname,const char * newconname,bool recurse,bool recursing,int expected_parents)3577 rename_constraint_internal(Oid myrelid,
3578 Oid mytypid,
3579 const char *oldconname,
3580 const char *newconname,
3581 bool recurse,
3582 bool recursing,
3583 int expected_parents)
3584 {
3585 Relation targetrelation = NULL;
3586 Oid constraintOid;
3587 HeapTuple tuple;
3588 Form_pg_constraint con;
3589 ObjectAddress address;
3590
3591 AssertArg(!myrelid || !mytypid);
3592
3593 if (mytypid)
3594 {
3595 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3596 }
3597 else
3598 {
3599 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3600
3601 /*
3602 * don't tell it whether we're recursing; we allow changing typed
3603 * tables here
3604 */
3605 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3606
3607 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3608 }
3609
3610 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3611 if (!HeapTupleIsValid(tuple))
3612 elog(ERROR, "cache lookup failed for constraint %u",
3613 constraintOid);
3614 con = (Form_pg_constraint) GETSTRUCT(tuple);
3615
3616 if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
3617 {
3618 if (recurse)
3619 {
3620 List *child_oids,
3621 *child_numparents;
3622 ListCell *lo,
3623 *li;
3624
3625 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3626 &child_numparents);
3627
3628 forboth(lo, child_oids, li, child_numparents)
3629 {
3630 Oid childrelid = lfirst_oid(lo);
3631 int numparents = lfirst_int(li);
3632
3633 if (childrelid == myrelid)
3634 continue;
3635
3636 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3637 }
3638 }
3639 else
3640 {
3641 if (expected_parents == 0 &&
3642 find_inheritance_children(myrelid, NoLock) != NIL)
3643 ereport(ERROR,
3644 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3645 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3646 oldconname)));
3647 }
3648
3649 if (con->coninhcount > expected_parents)
3650 ereport(ERROR,
3651 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3652 errmsg("cannot rename inherited constraint \"%s\"",
3653 oldconname)));
3654 }
3655
3656 if (con->conindid
3657 && (con->contype == CONSTRAINT_PRIMARY
3658 || con->contype == CONSTRAINT_UNIQUE
3659 || con->contype == CONSTRAINT_EXCLUSION))
3660 /* rename the index; this renames the constraint as well */
3661 RenameRelationInternal(con->conindid, newconname, false, true);
3662 else
3663 RenameConstraintById(constraintOid, newconname);
3664
3665 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
3666
3667 ReleaseSysCache(tuple);
3668
3669 if (targetrelation)
3670 {
3671 /*
3672 * Invalidate relcache so as others can see the new constraint name.
3673 */
3674 CacheInvalidateRelcache(targetrelation);
3675
3676 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3677 }
3678
3679 return address;
3680 }
3681
3682 ObjectAddress
RenameConstraint(RenameStmt * stmt)3683 RenameConstraint(RenameStmt *stmt)
3684 {
3685 Oid relid = InvalidOid;
3686 Oid typid = InvalidOid;
3687
3688 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
3689 {
3690 Relation rel;
3691 HeapTuple tup;
3692
3693 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
3694 rel = table_open(TypeRelationId, RowExclusiveLock);
3695 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
3696 if (!HeapTupleIsValid(tup))
3697 elog(ERROR, "cache lookup failed for type %u", typid);
3698 checkDomainOwner(tup);
3699 ReleaseSysCache(tup);
3700 table_close(rel, NoLock);
3701 }
3702 else
3703 {
3704 /* lock level taken here should match rename_constraint_internal */
3705 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3706 stmt->missing_ok ? RVR_MISSING_OK : 0,
3707 RangeVarCallbackForRenameAttribute,
3708 NULL);
3709 if (!OidIsValid(relid))
3710 {
3711 ereport(NOTICE,
3712 (errmsg("relation \"%s\" does not exist, skipping",
3713 stmt->relation->relname)));
3714 return InvalidObjectAddress;
3715 }
3716 }
3717
3718 return
3719 rename_constraint_internal(relid, typid,
3720 stmt->subname,
3721 stmt->newname,
3722 (stmt->relation &&
3723 stmt->relation->inh), /* recursive? */
3724 false, /* recursing? */
3725 0 /* expected inhcount */ );
3726
3727 }
3728
3729 /*
3730 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
3731 * RENAME
3732 */
3733 ObjectAddress
RenameRelation(RenameStmt * stmt)3734 RenameRelation(RenameStmt *stmt)
3735 {
3736 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
3737 Oid relid;
3738 ObjectAddress address;
3739
3740 /*
3741 * Grab an exclusive lock on the target table, index, sequence, view,
3742 * materialized view, or foreign table, which we will NOT release until
3743 * end of transaction.
3744 *
3745 * Lock level used here should match RenameRelationInternal, to avoid lock
3746 * escalation. However, because ALTER INDEX can be used with any relation
3747 * type, we mustn't believe without verification.
3748 */
3749 for (;;)
3750 {
3751 LOCKMODE lockmode;
3752 char relkind;
3753 bool obj_is_index;
3754
3755 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
3756
3757 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
3758 stmt->missing_ok ? RVR_MISSING_OK : 0,
3759 RangeVarCallbackForAlterRelation,
3760 (void *) stmt);
3761
3762 if (!OidIsValid(relid))
3763 {
3764 ereport(NOTICE,
3765 (errmsg("relation \"%s\" does not exist, skipping",
3766 stmt->relation->relname)));
3767 return InvalidObjectAddress;
3768 }
3769
3770 /*
3771 * We allow mismatched statement and object types (e.g., ALTER INDEX
3772 * to rename a table), but we might've used the wrong lock level. If
3773 * that happens, retry with the correct lock level. We don't bother
3774 * if we already acquired AccessExclusiveLock with an index, however.
3775 */
3776 relkind = get_rel_relkind(relid);
3777 obj_is_index = (relkind == RELKIND_INDEX ||
3778 relkind == RELKIND_PARTITIONED_INDEX);
3779 if (obj_is_index || is_index_stmt == obj_is_index)
3780 break;
3781
3782 UnlockRelationOid(relid, lockmode);
3783 is_index_stmt = obj_is_index;
3784 }
3785
3786 /* Do the work */
3787 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
3788
3789 ObjectAddressSet(address, RelationRelationId, relid);
3790
3791 return address;
3792 }
3793
3794 /*
3795 * RenameRelationInternal - change the name of a relation
3796 */
3797 void
RenameRelationInternal(Oid myrelid,const char * newrelname,bool is_internal,bool is_index)3798 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
3799 {
3800 Relation targetrelation;
3801 Relation relrelation; /* for RELATION relation */
3802 HeapTuple reltup;
3803 Form_pg_class relform;
3804 Oid namespaceId;
3805
3806 /*
3807 * Grab a lock on the target relation, which we will NOT release until end
3808 * of transaction. We need at least a self-exclusive lock so that
3809 * concurrent DDL doesn't overwrite the rename if they start updating
3810 * while still seeing the old version. The lock also guards against
3811 * triggering relcache reloads in concurrent sessions, which might not
3812 * handle this information changing under them. For indexes, we can use a
3813 * reduced lock level because RelationReloadIndexInfo() handles indexes
3814 * specially.
3815 */
3816 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
3817 namespaceId = RelationGetNamespace(targetrelation);
3818
3819 /*
3820 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
3821 */
3822 relrelation = table_open(RelationRelationId, RowExclusiveLock);
3823
3824 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
3825 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
3826 elog(ERROR, "cache lookup failed for relation %u", myrelid);
3827 relform = (Form_pg_class) GETSTRUCT(reltup);
3828
3829 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
3830 ereport(ERROR,
3831 (errcode(ERRCODE_DUPLICATE_TABLE),
3832 errmsg("relation \"%s\" already exists",
3833 newrelname)));
3834
3835 /*
3836 * RenameRelation is careful not to believe the caller's idea of the
3837 * relation kind being handled. We don't have to worry about this, but
3838 * let's not be totally oblivious to it. We can process an index as
3839 * not-an-index, but not the other way around.
3840 */
3841 Assert(!is_index ||
3842 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
3843 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
3844
3845 /*
3846 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
3847 * because it's a copy...)
3848 */
3849 namestrcpy(&(relform->relname), newrelname);
3850
3851 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
3852
3853 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
3854 InvalidOid, is_internal);
3855
3856 heap_freetuple(reltup);
3857 table_close(relrelation, RowExclusiveLock);
3858
3859 /*
3860 * Also rename the associated type, if any.
3861 */
3862 if (OidIsValid(targetrelation->rd_rel->reltype))
3863 RenameTypeInternal(targetrelation->rd_rel->reltype,
3864 newrelname, namespaceId);
3865
3866 /*
3867 * Also rename the associated constraint, if any.
3868 */
3869 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
3870 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
3871 {
3872 Oid constraintId = get_index_constraint(myrelid);
3873
3874 if (OidIsValid(constraintId))
3875 RenameConstraintById(constraintId, newrelname);
3876 }
3877
3878 /*
3879 * Close rel, but keep lock!
3880 */
3881 relation_close(targetrelation, NoLock);
3882 }
3883
3884 /*
3885 * ResetRelRewrite - reset relrewrite
3886 */
3887 void
ResetRelRewrite(Oid myrelid)3888 ResetRelRewrite(Oid myrelid)
3889 {
3890 Relation relrelation; /* for RELATION relation */
3891 HeapTuple reltup;
3892 Form_pg_class relform;
3893
3894 /*
3895 * Find relation's pg_class tuple.
3896 */
3897 relrelation = table_open(RelationRelationId, RowExclusiveLock);
3898
3899 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
3900 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
3901 elog(ERROR, "cache lookup failed for relation %u", myrelid);
3902 relform = (Form_pg_class) GETSTRUCT(reltup);
3903
3904 /*
3905 * Update pg_class tuple.
3906 */
3907 relform->relrewrite = InvalidOid;
3908
3909 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
3910
3911 heap_freetuple(reltup);
3912 table_close(relrelation, RowExclusiveLock);
3913 }
3914
3915 /*
3916 * Disallow ALTER TABLE (and similar commands) when the current backend has
3917 * any open reference to the target table besides the one just acquired by
3918 * the calling command; this implies there's an open cursor or active plan.
3919 * We need this check because our lock doesn't protect us against stomping
3920 * on our own foot, only other people's feet!
3921 *
3922 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
3923 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
3924 * possibly be relaxed to only error out for certain types of alterations.
3925 * But the use-case for allowing any of these things is not obvious, so we
3926 * won't work hard at it for now.
3927 *
3928 * We also reject these commands if there are any pending AFTER trigger events
3929 * for the rel. This is certainly necessary for the rewriting variants of
3930 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
3931 * events would try to fetch the wrong tuples. It might be overly cautious
3932 * in other cases, but again it seems better to err on the side of paranoia.
3933 *
3934 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
3935 * we are worried about active indexscans on the index. The trigger-event
3936 * check can be skipped, since we are doing no damage to the parent table.
3937 *
3938 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
3939 */
3940 void
CheckTableNotInUse(Relation rel,const char * stmt)3941 CheckTableNotInUse(Relation rel, const char *stmt)
3942 {
3943 int expected_refcnt;
3944
3945 expected_refcnt = rel->rd_isnailed ? 2 : 1;
3946 if (rel->rd_refcnt != expected_refcnt)
3947 ereport(ERROR,
3948 (errcode(ERRCODE_OBJECT_IN_USE),
3949 /* translator: first %s is a SQL command, eg ALTER TABLE */
3950 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
3951 stmt, RelationGetRelationName(rel))));
3952
3953 if (rel->rd_rel->relkind != RELKIND_INDEX &&
3954 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
3955 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
3956 ereport(ERROR,
3957 (errcode(ERRCODE_OBJECT_IN_USE),
3958 /* translator: first %s is a SQL command, eg ALTER TABLE */
3959 errmsg("cannot %s \"%s\" because it has pending trigger events",
3960 stmt, RelationGetRelationName(rel))));
3961 }
3962
3963 /*
3964 * AlterTableLookupRelation
3965 * Look up, and lock, the OID for the relation named by an alter table
3966 * statement.
3967 */
3968 Oid
AlterTableLookupRelation(AlterTableStmt * stmt,LOCKMODE lockmode)3969 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
3970 {
3971 return RangeVarGetRelidExtended(stmt->relation, lockmode,
3972 stmt->missing_ok ? RVR_MISSING_OK : 0,
3973 RangeVarCallbackForAlterRelation,
3974 (void *) stmt);
3975 }
3976
3977 /*
3978 * AlterTable
3979 * Execute ALTER TABLE, which can be a list of subcommands
3980 *
3981 * ALTER TABLE is performed in three phases:
3982 * 1. Examine subcommands and perform pre-transformation checking.
3983 * 2. Validate and transform subcommands, and update system catalogs.
3984 * 3. Scan table(s) to check new constraints, and optionally recopy
3985 * the data into new table(s).
3986 * Phase 3 is not performed unless one or more of the subcommands requires
3987 * it. The intention of this design is to allow multiple independent
3988 * updates of the table schema to be performed with only one pass over the
3989 * data.
3990 *
3991 * ATPrepCmd performs phase 1. A "work queue" entry is created for
3992 * each table to be affected (there may be multiple affected tables if the
3993 * commands traverse a table inheritance hierarchy). Also we do preliminary
3994 * validation of the subcommands. Because earlier subcommands may change
3995 * the catalog state seen by later commands, there are limits to what can
3996 * be done in this phase. Generally, this phase acquires table locks,
3997 * checks permissions and relkind, and recurses to find child tables.
3998 *
3999 * ATRewriteCatalogs performs phase 2 for each affected table. (Note that
4000 * phases 2 and 3 normally do no explicit recursion, since phase 1 already
4001 * did it --- although some subcommands have to recurse in phase 2 instead.)
4002 * Certain subcommands need to be performed before others to avoid
4003 * unnecessary conflicts; for example, DROP COLUMN should come before
4004 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4005 * lists, one for each logical "pass" of phase 2.
4006 *
4007 * ATRewriteTables performs phase 3 for those tables that need it.
4008 *
4009 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4010 * the whole operation; we don't have to do anything special to clean up.
4011 *
4012 * The caller must lock the relation, with an appropriate lock level
4013 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4014 * or higher. We pass the lock level down
4015 * so that we can apply it recursively to inherited tables. Note that the
4016 * lock level we want as we recurse might well be higher than required for
4017 * that specific subcommand. So we pass down the overall lock requirement,
4018 * rather than reassess it at lower levels.
4019 *
4020 * The caller also provides a "context" which is to be passed back to
4021 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4022 * Some of the fields therein, such as the relid, are used here as well.
4023 */
4024 void
AlterTable(AlterTableStmt * stmt,LOCKMODE lockmode,AlterTableUtilityContext * context)4025 AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4026 AlterTableUtilityContext *context)
4027 {
4028 Relation rel;
4029
4030 /* Caller is required to provide an adequate lock. */
4031 rel = relation_open(context->relid, NoLock);
4032
4033 CheckTableNotInUse(rel, "ALTER TABLE");
4034
4035 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4036 }
4037
4038 /*
4039 * AlterTableInternal
4040 *
4041 * ALTER TABLE with target specified by OID
4042 *
4043 * We do not reject if the relation is already open, because it's quite
4044 * likely that one or more layers of caller have it open. That means it
4045 * is unsafe to use this entry point for alterations that could break
4046 * existing query plans. On the assumption it's not used for such, we
4047 * don't have to reject pending AFTER triggers, either.
4048 *
4049 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4050 * used for any subcommand types that require parse transformation or
4051 * could generate subcommands that have to be passed to ProcessUtility.
4052 */
4053 void
AlterTableInternal(Oid relid,List * cmds,bool recurse)4054 AlterTableInternal(Oid relid, List *cmds, bool recurse)
4055 {
4056 Relation rel;
4057 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4058
4059 rel = relation_open(relid, lockmode);
4060
4061 EventTriggerAlterTableRelid(relid);
4062
4063 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4064 }
4065
4066 /*
4067 * AlterTableGetLockLevel
4068 *
4069 * Sets the overall lock level required for the supplied list of subcommands.
4070 * Policy for doing this set according to needs of AlterTable(), see
4071 * comments there for overall explanation.
4072 *
4073 * Function is called before and after parsing, so it must give same
4074 * answer each time it is called. Some subcommands are transformed
4075 * into other subcommand types, so the transform must never be made to a
4076 * lower lock level than previously assigned. All transforms are noted below.
4077 *
4078 * Since this is called before we lock the table we cannot use table metadata
4079 * to influence the type of lock we acquire.
4080 *
4081 * There should be no lockmodes hardcoded into the subcommand functions. All
4082 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4083 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4084 * and does not travel through this section of code and cannot be combined with
4085 * any of the subcommands given here.
4086 *
4087 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4088 * so any changes that might affect SELECTs running on standbys need to use
4089 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4090 * have a solution for that also.
4091 *
4092 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4093 * that takes a lock less than AccessExclusiveLock can change object definitions
4094 * while pg_dump is running. Be careful to check that the appropriate data is
4095 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4096 * otherwise we might end up with an inconsistent dump that can't restore.
4097 */
4098 LOCKMODE
AlterTableGetLockLevel(List * cmds)4099 AlterTableGetLockLevel(List *cmds)
4100 {
4101 /*
4102 * This only works if we read catalog tables using MVCC snapshots.
4103 */
4104 ListCell *lcmd;
4105 LOCKMODE lockmode = ShareUpdateExclusiveLock;
4106
4107 foreach(lcmd, cmds)
4108 {
4109 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4110 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4111
4112 switch (cmd->subtype)
4113 {
4114 /*
4115 * These subcommands rewrite the heap, so require full locks.
4116 */
4117 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4118 * to SELECT */
4119 case AT_SetTableSpace: /* must rewrite heap */
4120 case AT_AlterColumnType: /* must rewrite heap */
4121 cmd_lockmode = AccessExclusiveLock;
4122 break;
4123
4124 /*
4125 * These subcommands may require addition of toast tables. If
4126 * we add a toast table to a table currently being scanned, we
4127 * might miss data added to the new toast table by concurrent
4128 * insert transactions.
4129 */
4130 case AT_SetStorage: /* may add toast tables, see
4131 * ATRewriteCatalogs() */
4132 cmd_lockmode = AccessExclusiveLock;
4133 break;
4134
4135 /*
4136 * Removing constraints can affect SELECTs that have been
4137 * optimized assuming the constraint holds true. See also
4138 * CloneFkReferenced.
4139 */
4140 case AT_DropConstraint: /* as DROP INDEX */
4141 case AT_DropNotNull: /* may change some SQL plans */
4142 cmd_lockmode = AccessExclusiveLock;
4143 break;
4144
4145 /*
4146 * Subcommands that may be visible to concurrent SELECTs
4147 */
4148 case AT_DropColumn: /* change visible to SELECT */
4149 case AT_AddColumnToView: /* CREATE VIEW */
4150 case AT_DropOids: /* used to equiv to DropColumn */
4151 case AT_EnableAlwaysRule: /* may change SELECT rules */
4152 case AT_EnableReplicaRule: /* may change SELECT rules */
4153 case AT_EnableRule: /* may change SELECT rules */
4154 case AT_DisableRule: /* may change SELECT rules */
4155 cmd_lockmode = AccessExclusiveLock;
4156 break;
4157
4158 /*
4159 * Changing owner may remove implicit SELECT privileges
4160 */
4161 case AT_ChangeOwner: /* change visible to SELECT */
4162 cmd_lockmode = AccessExclusiveLock;
4163 break;
4164
4165 /*
4166 * Changing foreign table options may affect optimization.
4167 */
4168 case AT_GenericOptions:
4169 case AT_AlterColumnGenericOptions:
4170 cmd_lockmode = AccessExclusiveLock;
4171 break;
4172
4173 /*
4174 * These subcommands affect write operations only.
4175 */
4176 case AT_EnableTrig:
4177 case AT_EnableAlwaysTrig:
4178 case AT_EnableReplicaTrig:
4179 case AT_EnableTrigAll:
4180 case AT_EnableTrigUser:
4181 case AT_DisableTrig:
4182 case AT_DisableTrigAll:
4183 case AT_DisableTrigUser:
4184 cmd_lockmode = ShareRowExclusiveLock;
4185 break;
4186
4187 /*
4188 * These subcommands affect write operations only. XXX
4189 * Theoretically, these could be ShareRowExclusiveLock.
4190 */
4191 case AT_ColumnDefault:
4192 case AT_CookedColumnDefault:
4193 case AT_AlterConstraint:
4194 case AT_AddIndex: /* from ADD CONSTRAINT */
4195 case AT_AddIndexConstraint:
4196 case AT_ReplicaIdentity:
4197 case AT_SetNotNull:
4198 case AT_EnableRowSecurity:
4199 case AT_DisableRowSecurity:
4200 case AT_ForceRowSecurity:
4201 case AT_NoForceRowSecurity:
4202 case AT_AddIdentity:
4203 case AT_DropIdentity:
4204 case AT_SetIdentity:
4205 case AT_DropExpression:
4206 case AT_SetCompression:
4207 cmd_lockmode = AccessExclusiveLock;
4208 break;
4209
4210 case AT_AddConstraint:
4211 case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
4212 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4213 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4214 if (IsA(cmd->def, Constraint))
4215 {
4216 Constraint *con = (Constraint *) cmd->def;
4217
4218 switch (con->contype)
4219 {
4220 case CONSTR_EXCLUSION:
4221 case CONSTR_PRIMARY:
4222 case CONSTR_UNIQUE:
4223
4224 /*
4225 * Cases essentially the same as CREATE INDEX. We
4226 * could reduce the lock strength to ShareLock if
4227 * we can work out how to allow concurrent catalog
4228 * updates. XXX Might be set down to
4229 * ShareRowExclusiveLock but requires further
4230 * analysis.
4231 */
4232 cmd_lockmode = AccessExclusiveLock;
4233 break;
4234 case CONSTR_FOREIGN:
4235
4236 /*
4237 * We add triggers to both tables when we add a
4238 * Foreign Key, so the lock level must be at least
4239 * as strong as CREATE TRIGGER.
4240 */
4241 cmd_lockmode = ShareRowExclusiveLock;
4242 break;
4243
4244 default:
4245 cmd_lockmode = AccessExclusiveLock;
4246 }
4247 }
4248 break;
4249
4250 /*
4251 * These subcommands affect inheritance behaviour. Queries
4252 * started before us will continue to see the old inheritance
4253 * behaviour, while queries started after we commit will see
4254 * new behaviour. No need to prevent reads or writes to the
4255 * subtable while we hook it up though. Changing the TupDesc
4256 * may be a problem, so keep highest lock.
4257 */
4258 case AT_AddInherit:
4259 case AT_DropInherit:
4260 cmd_lockmode = AccessExclusiveLock;
4261 break;
4262
4263 /*
4264 * These subcommands affect implicit row type conversion. They
4265 * have affects similar to CREATE/DROP CAST on queries. don't
4266 * provide for invalidating parse trees as a result of such
4267 * changes, so we keep these at AccessExclusiveLock.
4268 */
4269 case AT_AddOf:
4270 case AT_DropOf:
4271 cmd_lockmode = AccessExclusiveLock;
4272 break;
4273
4274 /*
4275 * Only used by CREATE OR REPLACE VIEW which must conflict
4276 * with an SELECTs currently using the view.
4277 */
4278 case AT_ReplaceRelOptions:
4279 cmd_lockmode = AccessExclusiveLock;
4280 break;
4281
4282 /*
4283 * These subcommands affect general strategies for performance
4284 * and maintenance, though don't change the semantic results
4285 * from normal data reads and writes. Delaying an ALTER TABLE
4286 * behind currently active writes only delays the point where
4287 * the new strategy begins to take effect, so there is no
4288 * benefit in waiting. In this case the minimum restriction
4289 * applies: we don't currently allow concurrent catalog
4290 * updates.
4291 */
4292 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4293 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4294 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4295 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4296 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4297 cmd_lockmode = ShareUpdateExclusiveLock;
4298 break;
4299
4300 case AT_SetLogged:
4301 case AT_SetUnLogged:
4302 cmd_lockmode = AccessExclusiveLock;
4303 break;
4304
4305 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4306 cmd_lockmode = ShareUpdateExclusiveLock;
4307 break;
4308
4309 /*
4310 * Rel options are more complex than first appears. Options
4311 * are set here for tables, views and indexes; for historical
4312 * reasons these can all be used with ALTER TABLE, so we can't
4313 * decide between them using the basic grammar.
4314 */
4315 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4316 * getTables() */
4317 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4318 * getTables() */
4319 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4320 break;
4321
4322 case AT_AttachPartition:
4323 cmd_lockmode = ShareUpdateExclusiveLock;
4324 break;
4325
4326 case AT_DetachPartition:
4327 if (((PartitionCmd *) cmd->def)->concurrent)
4328 cmd_lockmode = ShareUpdateExclusiveLock;
4329 else
4330 cmd_lockmode = AccessExclusiveLock;
4331 break;
4332
4333 case AT_DetachPartitionFinalize:
4334 cmd_lockmode = ShareUpdateExclusiveLock;
4335 break;
4336
4337 case AT_CheckNotNull:
4338
4339 /*
4340 * This only examines the table's schema; but lock must be
4341 * strong enough to prevent concurrent DROP NOT NULL.
4342 */
4343 cmd_lockmode = AccessShareLock;
4344 break;
4345
4346 default: /* oops */
4347 elog(ERROR, "unrecognized alter table type: %d",
4348 (int) cmd->subtype);
4349 break;
4350 }
4351
4352 /*
4353 * Take the greatest lockmode from any subcommand
4354 */
4355 if (cmd_lockmode > lockmode)
4356 lockmode = cmd_lockmode;
4357 }
4358
4359 return lockmode;
4360 }
4361
4362 /*
4363 * ATController provides top level control over the phases.
4364 *
4365 * parsetree is passed in to allow it to be passed to event triggers
4366 * when requested.
4367 */
4368 static void
ATController(AlterTableStmt * parsetree,Relation rel,List * cmds,bool recurse,LOCKMODE lockmode,AlterTableUtilityContext * context)4369 ATController(AlterTableStmt *parsetree,
4370 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4371 AlterTableUtilityContext *context)
4372 {
4373 List *wqueue = NIL;
4374 ListCell *lcmd;
4375
4376 /* Phase 1: preliminary examination of commands, create work queue */
4377 foreach(lcmd, cmds)
4378 {
4379 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4380
4381 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4382 }
4383
4384 /* Close the relation, but keep lock until commit */
4385 relation_close(rel, NoLock);
4386
4387 /* Phase 2: update system catalogs */
4388 ATRewriteCatalogs(&wqueue, lockmode, context);
4389
4390 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4391 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4392 }
4393
4394 /*
4395 * ATPrepCmd
4396 *
4397 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4398 * recursion and permission checks.
4399 *
4400 * Caller must have acquired appropriate lock type on relation already.
4401 * This lock should be held until commit.
4402 */
4403 static void
ATPrepCmd(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode,AlterTableUtilityContext * context)4404 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4405 bool recurse, bool recursing, LOCKMODE lockmode,
4406 AlterTableUtilityContext *context)
4407 {
4408 AlteredTableInfo *tab;
4409 int pass = AT_PASS_UNSET;
4410
4411 /* Find or create work queue entry for this table */
4412 tab = ATGetQueueEntry(wqueue, rel);
4413
4414 /*
4415 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4416 * partitions that are pending detach.
4417 */
4418 if (rel->rd_rel->relispartition &&
4419 cmd->subtype != AT_DetachPartitionFinalize &&
4420 PartitionHasPendingDetach(RelationGetRelid(rel)))
4421 ereport(ERROR,
4422 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4423 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4424 RelationGetRelationName(rel)),
4425 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4426
4427 /*
4428 * Copy the original subcommand for each table. This avoids conflicts
4429 * when different child tables need to make different parse
4430 * transformations (for example, the same column may have different column
4431 * numbers in different children).
4432 */
4433 cmd = copyObject(cmd);
4434
4435 /*
4436 * Do permissions and relkind checking, recursion to child tables if
4437 * needed, and any additional phase-1 processing needed. (But beware of
4438 * adding any processing that looks at table details that another
4439 * subcommand could change. In some cases we reject multiple subcommands
4440 * that could try to change the same state in contrary ways.)
4441 */
4442 switch (cmd->subtype)
4443 {
4444 case AT_AddColumn: /* ADD COLUMN */
4445 ATSimplePermissions(rel,
4446 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4447 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4448 lockmode, context);
4449 /* Recursion occurs during execution phase */
4450 pass = AT_PASS_ADD_COL;
4451 break;
4452 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4453 ATSimplePermissions(rel, ATT_VIEW);
4454 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4455 lockmode, context);
4456 /* Recursion occurs during execution phase */
4457 pass = AT_PASS_ADD_COL;
4458 break;
4459 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4460
4461 /*
4462 * We allow defaults on views so that INSERT into a view can have
4463 * default-ish behavior. This works because the rewriter
4464 * substitutes default values into INSERTs before it expands
4465 * rules.
4466 */
4467 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4468 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4469 /* No command-specific prep needed */
4470 pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4471 break;
4472 case AT_CookedColumnDefault: /* add a pre-cooked default */
4473 /* This is currently used only in CREATE TABLE */
4474 /* (so the permission check really isn't necessary) */
4475 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4476 /* This command never recurses */
4477 pass = AT_PASS_ADD_OTHERCONSTR;
4478 break;
4479 case AT_AddIdentity:
4480 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4481 /* This command never recurses */
4482 pass = AT_PASS_ADD_OTHERCONSTR;
4483 break;
4484 case AT_SetIdentity:
4485 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4486 /* This command never recurses */
4487 /* This should run after AddIdentity, so do it in MISC pass */
4488 pass = AT_PASS_MISC;
4489 break;
4490 case AT_DropIdentity:
4491 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4492 /* This command never recurses */
4493 pass = AT_PASS_DROP;
4494 break;
4495 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4496 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4497 ATPrepDropNotNull(rel, recurse, recursing);
4498 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4499 pass = AT_PASS_DROP;
4500 break;
4501 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4502 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4503 /* Need command-specific recursion decision */
4504 ATPrepSetNotNull(wqueue, rel, cmd, recurse, recursing,
4505 lockmode, context);
4506 pass = AT_PASS_COL_ATTRS;
4507 break;
4508 case AT_CheckNotNull: /* check column is already marked NOT NULL */
4509 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4510 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4511 /* No command-specific prep needed */
4512 pass = AT_PASS_COL_ATTRS;
4513 break;
4514 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4515 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4516 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4517 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4518 pass = AT_PASS_DROP;
4519 break;
4520 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4521 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4522 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4523 /* No command-specific prep needed */
4524 pass = AT_PASS_MISC;
4525 break;
4526 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4527 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4528 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4529 /* This command never recurses */
4530 pass = AT_PASS_MISC;
4531 break;
4532 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4533 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4534 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4535 /* No command-specific prep needed */
4536 pass = AT_PASS_MISC;
4537 break;
4538 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4539 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
4540 /* This command never recurses */
4541 /* No command-specific prep needed */
4542 pass = AT_PASS_MISC;
4543 break;
4544 case AT_DropColumn: /* DROP COLUMN */
4545 ATSimplePermissions(rel,
4546 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4547 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4548 lockmode, context);
4549 /* Recursion occurs during execution phase */
4550 pass = AT_PASS_DROP;
4551 break;
4552 case AT_AddIndex: /* ADD INDEX */
4553 ATSimplePermissions(rel, ATT_TABLE);
4554 /* This command never recurses */
4555 /* No command-specific prep needed */
4556 pass = AT_PASS_ADD_INDEX;
4557 break;
4558 case AT_AddConstraint: /* ADD CONSTRAINT */
4559 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4560 /* Recursion occurs during execution phase */
4561 /* No command-specific prep needed except saving recurse flag */
4562 if (recurse)
4563 cmd->subtype = AT_AddConstraintRecurse;
4564 pass = AT_PASS_ADD_CONSTR;
4565 break;
4566 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4567 ATSimplePermissions(rel, ATT_TABLE);
4568 /* This command never recurses */
4569 /* No command-specific prep needed */
4570 pass = AT_PASS_ADD_INDEXCONSTR;
4571 break;
4572 case AT_DropConstraint: /* DROP CONSTRAINT */
4573 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4574 ATCheckPartitionsNotInUse(rel, lockmode);
4575 /* Other recursion occurs during execution phase */
4576 /* No command-specific prep needed except saving recurse flag */
4577 if (recurse)
4578 cmd->subtype = AT_DropConstraintRecurse;
4579 pass = AT_PASS_DROP;
4580 break;
4581 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4582 ATSimplePermissions(rel,
4583 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4584 /* See comments for ATPrepAlterColumnType */
4585 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4586 AT_PASS_UNSET, context);
4587 Assert(cmd != NULL);
4588 /* Performs own recursion */
4589 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4590 lockmode, context);
4591 pass = AT_PASS_ALTER_TYPE;
4592 break;
4593 case AT_AlterColumnGenericOptions:
4594 ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
4595 /* This command never recurses */
4596 /* No command-specific prep needed */
4597 pass = AT_PASS_MISC;
4598 break;
4599 case AT_ChangeOwner: /* ALTER OWNER */
4600 /* This command never recurses */
4601 /* No command-specific prep needed */
4602 pass = AT_PASS_MISC;
4603 break;
4604 case AT_ClusterOn: /* CLUSTER ON */
4605 case AT_DropCluster: /* SET WITHOUT CLUSTER */
4606 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
4607 /* These commands never recurse */
4608 /* No command-specific prep needed */
4609 pass = AT_PASS_MISC;
4610 break;
4611 case AT_SetLogged: /* SET LOGGED */
4612 ATSimplePermissions(rel, ATT_TABLE);
4613 if (tab->chgPersistence)
4614 ereport(ERROR,
4615 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4616 errmsg("cannot change persistence setting twice")));
4617 tab->chgPersistence = ATPrepChangePersistence(rel, true);
4618 /* force rewrite if necessary; see comment in ATRewriteTables */
4619 if (tab->chgPersistence)
4620 {
4621 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4622 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4623 }
4624 pass = AT_PASS_MISC;
4625 break;
4626 case AT_SetUnLogged: /* SET UNLOGGED */
4627 ATSimplePermissions(rel, ATT_TABLE);
4628 if (tab->chgPersistence)
4629 ereport(ERROR,
4630 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4631 errmsg("cannot change persistence setting twice")));
4632 tab->chgPersistence = ATPrepChangePersistence(rel, false);
4633 /* force rewrite if necessary; see comment in ATRewriteTables */
4634 if (tab->chgPersistence)
4635 {
4636 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4637 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
4638 }
4639 pass = AT_PASS_MISC;
4640 break;
4641 case AT_DropOids: /* SET WITHOUT OIDS */
4642 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4643 pass = AT_PASS_DROP;
4644 break;
4645 case AT_SetTableSpace: /* SET TABLESPACE */
4646 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
4647 ATT_PARTITIONED_INDEX);
4648 /* This command never recurses */
4649 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
4650 pass = AT_PASS_MISC; /* doesn't actually matter */
4651 break;
4652 case AT_SetRelOptions: /* SET (...) */
4653 case AT_ResetRelOptions: /* RESET (...) */
4654 case AT_ReplaceRelOptions: /* reset them all, then set just these */
4655 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
4656 /* This command never recurses */
4657 /* No command-specific prep needed */
4658 pass = AT_PASS_MISC;
4659 break;
4660 case AT_AddInherit: /* INHERIT */
4661 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4662 /* This command never recurses */
4663 ATPrepAddInherit(rel);
4664 pass = AT_PASS_MISC;
4665 break;
4666 case AT_DropInherit: /* NO INHERIT */
4667 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4668 /* This command never recurses */
4669 /* No command-specific prep needed */
4670 pass = AT_PASS_MISC;
4671 break;
4672 case AT_AlterConstraint: /* ALTER CONSTRAINT */
4673 ATSimplePermissions(rel, ATT_TABLE);
4674 /* Recursion occurs during execution phase */
4675 pass = AT_PASS_MISC;
4676 break;
4677 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
4678 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4679 /* Recursion occurs during execution phase */
4680 /* No command-specific prep needed except saving recurse flag */
4681 if (recurse)
4682 cmd->subtype = AT_ValidateConstraintRecurse;
4683 pass = AT_PASS_MISC;
4684 break;
4685 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
4686 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
4687 pass = AT_PASS_MISC;
4688 /* This command never recurses */
4689 /* No command-specific prep needed */
4690 break;
4691 case AT_EnableTrig: /* ENABLE TRIGGER variants */
4692 case AT_EnableAlwaysTrig:
4693 case AT_EnableReplicaTrig:
4694 case AT_EnableTrigAll:
4695 case AT_EnableTrigUser:
4696 case AT_DisableTrig: /* DISABLE TRIGGER variants */
4697 case AT_DisableTrigAll:
4698 case AT_DisableTrigUser:
4699 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4700 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4701 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4702 pass = AT_PASS_MISC;
4703 break;
4704 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
4705 case AT_EnableAlwaysRule:
4706 case AT_EnableReplicaRule:
4707 case AT_DisableRule:
4708 case AT_AddOf: /* OF */
4709 case AT_DropOf: /* NOT OF */
4710 case AT_EnableRowSecurity:
4711 case AT_DisableRowSecurity:
4712 case AT_ForceRowSecurity:
4713 case AT_NoForceRowSecurity:
4714 ATSimplePermissions(rel, ATT_TABLE);
4715 /* These commands never recurse */
4716 /* No command-specific prep needed */
4717 pass = AT_PASS_MISC;
4718 break;
4719 case AT_GenericOptions:
4720 ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
4721 /* No command-specific prep needed */
4722 pass = AT_PASS_MISC;
4723 break;
4724 case AT_AttachPartition:
4725 ATSimplePermissions(rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
4726 /* No command-specific prep needed */
4727 pass = AT_PASS_MISC;
4728 break;
4729 case AT_DetachPartition:
4730 ATSimplePermissions(rel, ATT_TABLE);
4731 /* No command-specific prep needed */
4732 pass = AT_PASS_MISC;
4733 break;
4734 case AT_DetachPartitionFinalize:
4735 ATSimplePermissions(rel, ATT_TABLE);
4736 /* No command-specific prep needed */
4737 pass = AT_PASS_MISC;
4738 break;
4739 default: /* oops */
4740 elog(ERROR, "unrecognized alter table type: %d",
4741 (int) cmd->subtype);
4742 pass = AT_PASS_UNSET; /* keep compiler quiet */
4743 break;
4744 }
4745 Assert(pass > AT_PASS_UNSET);
4746
4747 /* Add the subcommand to the appropriate list for phase 2 */
4748 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
4749 }
4750
4751 /*
4752 * ATRewriteCatalogs
4753 *
4754 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
4755 * dispatched in a "safe" execution order (designed to avoid unnecessary
4756 * conflicts).
4757 */
4758 static void
ATRewriteCatalogs(List ** wqueue,LOCKMODE lockmode,AlterTableUtilityContext * context)4759 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
4760 AlterTableUtilityContext *context)
4761 {
4762 int pass;
4763 ListCell *ltab;
4764
4765 /*
4766 * We process all the tables "in parallel", one pass at a time. This is
4767 * needed because we may have to propagate work from one table to another
4768 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
4769 * re-adding of the foreign key constraint to the other table). Work can
4770 * only be propagated into later passes, however.
4771 */
4772 for (pass = 0; pass < AT_NUM_PASSES; pass++)
4773 {
4774 /* Go through each table that needs to be processed */
4775 foreach(ltab, *wqueue)
4776 {
4777 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4778 List *subcmds = tab->subcmds[pass];
4779 ListCell *lcmd;
4780
4781 if (subcmds == NIL)
4782 continue;
4783
4784 /*
4785 * Open the relation and store it in tab. This allows subroutines
4786 * close and reopen, if necessary. Appropriate lock was obtained
4787 * by phase 1, needn't get it again.
4788 */
4789 tab->rel = relation_open(tab->relid, NoLock);
4790
4791 foreach(lcmd, subcmds)
4792 ATExecCmd(wqueue, tab,
4793 castNode(AlterTableCmd, lfirst(lcmd)),
4794 lockmode, pass, context);
4795
4796 /*
4797 * After the ALTER TYPE pass, do cleanup work (this is not done in
4798 * ATExecAlterColumnType since it should be done only once if
4799 * multiple columns of a table are altered).
4800 */
4801 if (pass == AT_PASS_ALTER_TYPE)
4802 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
4803
4804 if (tab->rel)
4805 {
4806 relation_close(tab->rel, NoLock);
4807 tab->rel = NULL;
4808 }
4809 }
4810 }
4811
4812 /* Check to see if a toast table must be added. */
4813 foreach(ltab, *wqueue)
4814 {
4815 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4816
4817 /*
4818 * If the table is source table of ATTACH PARTITION command, we did
4819 * not modify anything about it that will change its toasting
4820 * requirement, so no need to check.
4821 */
4822 if (((tab->relkind == RELKIND_RELATION ||
4823 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
4824 tab->partition_constraint == NULL) ||
4825 tab->relkind == RELKIND_MATVIEW)
4826 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
4827 }
4828 }
4829
4830 /*
4831 * ATExecCmd: dispatch a subcommand to appropriate execution routine
4832 */
4833 static void
ATExecCmd(List ** wqueue,AlteredTableInfo * tab,AlterTableCmd * cmd,LOCKMODE lockmode,int cur_pass,AlterTableUtilityContext * context)4834 ATExecCmd(List **wqueue, AlteredTableInfo *tab,
4835 AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
4836 AlterTableUtilityContext *context)
4837 {
4838 ObjectAddress address = InvalidObjectAddress;
4839 Relation rel = tab->rel;
4840
4841 switch (cmd->subtype)
4842 {
4843 case AT_AddColumn: /* ADD COLUMN */
4844 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4845 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
4846 false, false,
4847 lockmode, cur_pass, context);
4848 break;
4849 case AT_AddColumnRecurse:
4850 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
4851 true, false,
4852 lockmode, cur_pass, context);
4853 break;
4854 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4855 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
4856 break;
4857 case AT_CookedColumnDefault: /* add a pre-cooked default */
4858 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
4859 break;
4860 case AT_AddIdentity:
4861 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
4862 cur_pass, context);
4863 Assert(cmd != NULL);
4864 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
4865 break;
4866 case AT_SetIdentity:
4867 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
4868 cur_pass, context);
4869 Assert(cmd != NULL);
4870 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
4871 break;
4872 case AT_DropIdentity:
4873 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
4874 break;
4875 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4876 address = ATExecDropNotNull(rel, cmd->name, lockmode);
4877 break;
4878 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4879 address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
4880 break;
4881 case AT_CheckNotNull: /* check column is already marked NOT NULL */
4882 ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
4883 break;
4884 case AT_DropExpression:
4885 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
4886 break;
4887 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4888 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
4889 break;
4890 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4891 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
4892 break;
4893 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4894 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
4895 break;
4896 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4897 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
4898 break;
4899 case AT_SetCompression:
4900 address = ATExecSetCompression(tab, rel, cmd->name, cmd->def,
4901 lockmode);
4902 break;
4903 case AT_DropColumn: /* DROP COLUMN */
4904 address = ATExecDropColumn(wqueue, rel, cmd->name,
4905 cmd->behavior, false, false,
4906 cmd->missing_ok, lockmode,
4907 NULL);
4908 break;
4909 case AT_DropColumnRecurse: /* DROP COLUMN with recursion */
4910 address = ATExecDropColumn(wqueue, rel, cmd->name,
4911 cmd->behavior, true, false,
4912 cmd->missing_ok, lockmode,
4913 NULL);
4914 break;
4915 case AT_AddIndex: /* ADD INDEX */
4916 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
4917 lockmode);
4918 break;
4919 case AT_ReAddIndex: /* ADD INDEX */
4920 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
4921 lockmode);
4922 break;
4923 case AT_ReAddStatistics: /* ADD STATISTICS */
4924 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
4925 true, lockmode);
4926 break;
4927 case AT_AddConstraint: /* ADD CONSTRAINT */
4928 /* Transform the command only during initial examination */
4929 if (cur_pass == AT_PASS_ADD_CONSTR)
4930 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
4931 false, lockmode,
4932 cur_pass, context);
4933 /* Depending on constraint type, might be no more work to do now */
4934 if (cmd != NULL)
4935 address =
4936 ATExecAddConstraint(wqueue, tab, rel,
4937 (Constraint *) cmd->def,
4938 false, false, lockmode);
4939 break;
4940 case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
4941 /* Transform the command only during initial examination */
4942 if (cur_pass == AT_PASS_ADD_CONSTR)
4943 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
4944 true, lockmode,
4945 cur_pass, context);
4946 /* Depending on constraint type, might be no more work to do now */
4947 if (cmd != NULL)
4948 address =
4949 ATExecAddConstraint(wqueue, tab, rel,
4950 (Constraint *) cmd->def,
4951 true, false, lockmode);
4952 break;
4953 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
4954 address =
4955 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
4956 true, true, lockmode);
4957 break;
4958 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
4959 * constraint */
4960 address =
4961 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
4962 ((AlterDomainStmt *) cmd->def)->def,
4963 NULL);
4964 break;
4965 case AT_ReAddComment: /* Re-add existing comment */
4966 address = CommentObject((CommentStmt *) cmd->def);
4967 break;
4968 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4969 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
4970 lockmode);
4971 break;
4972 case AT_AlterConstraint: /* ALTER CONSTRAINT */
4973 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
4974 break;
4975 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
4976 address = ATExecValidateConstraint(wqueue, rel, cmd->name, false,
4977 false, lockmode);
4978 break;
4979 case AT_ValidateConstraintRecurse: /* VALIDATE CONSTRAINT with
4980 * recursion */
4981 address = ATExecValidateConstraint(wqueue, rel, cmd->name, true,
4982 false, lockmode);
4983 break;
4984 case AT_DropConstraint: /* DROP CONSTRAINT */
4985 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
4986 false, false,
4987 cmd->missing_ok, lockmode);
4988 break;
4989 case AT_DropConstraintRecurse: /* DROP CONSTRAINT with recursion */
4990 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
4991 true, false,
4992 cmd->missing_ok, lockmode);
4993 break;
4994 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4995 /* parse transformation was done earlier */
4996 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
4997 break;
4998 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
4999 address =
5000 ATExecAlterColumnGenericOptions(rel, cmd->name,
5001 (List *) cmd->def, lockmode);
5002 break;
5003 case AT_ChangeOwner: /* ALTER OWNER */
5004 ATExecChangeOwner(RelationGetRelid(rel),
5005 get_rolespec_oid(cmd->newowner, false),
5006 false, lockmode);
5007 break;
5008 case AT_ClusterOn: /* CLUSTER ON */
5009 address = ATExecClusterOn(rel, cmd->name, lockmode);
5010 break;
5011 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5012 ATExecDropCluster(rel, lockmode);
5013 break;
5014 case AT_SetLogged: /* SET LOGGED */
5015 case AT_SetUnLogged: /* SET UNLOGGED */
5016 break;
5017 case AT_DropOids: /* SET WITHOUT OIDS */
5018 /* nothing to do here, oid columns don't exist anymore */
5019 break;
5020 case AT_SetTableSpace: /* SET TABLESPACE */
5021
5022 /*
5023 * Only do this for partitioned tables and indexes, for which this
5024 * is just a catalog change. Other relation types which have
5025 * storage are handled by Phase 3.
5026 */
5027 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5028 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5029 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5030
5031 break;
5032 case AT_SetRelOptions: /* SET (...) */
5033 case AT_ResetRelOptions: /* RESET (...) */
5034 case AT_ReplaceRelOptions: /* replace entire option list */
5035 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5036 break;
5037 case AT_EnableTrig: /* ENABLE TRIGGER name */
5038 ATExecEnableDisableTrigger(rel, cmd->name,
5039 TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
5040 break;
5041 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5042 ATExecEnableDisableTrigger(rel, cmd->name,
5043 TRIGGER_FIRES_ALWAYS, false, lockmode);
5044 break;
5045 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5046 ATExecEnableDisableTrigger(rel, cmd->name,
5047 TRIGGER_FIRES_ON_REPLICA, false, lockmode);
5048 break;
5049 case AT_DisableTrig: /* DISABLE TRIGGER name */
5050 ATExecEnableDisableTrigger(rel, cmd->name,
5051 TRIGGER_DISABLED, false, lockmode);
5052 break;
5053 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5054 ATExecEnableDisableTrigger(rel, NULL,
5055 TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
5056 break;
5057 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5058 ATExecEnableDisableTrigger(rel, NULL,
5059 TRIGGER_DISABLED, false, lockmode);
5060 break;
5061 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5062 ATExecEnableDisableTrigger(rel, NULL,
5063 TRIGGER_FIRES_ON_ORIGIN, true, lockmode);
5064 break;
5065 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5066 ATExecEnableDisableTrigger(rel, NULL,
5067 TRIGGER_DISABLED, true, lockmode);
5068 break;
5069
5070 case AT_EnableRule: /* ENABLE RULE name */
5071 ATExecEnableDisableRule(rel, cmd->name,
5072 RULE_FIRES_ON_ORIGIN, lockmode);
5073 break;
5074 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5075 ATExecEnableDisableRule(rel, cmd->name,
5076 RULE_FIRES_ALWAYS, lockmode);
5077 break;
5078 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5079 ATExecEnableDisableRule(rel, cmd->name,
5080 RULE_FIRES_ON_REPLICA, lockmode);
5081 break;
5082 case AT_DisableRule: /* DISABLE RULE name */
5083 ATExecEnableDisableRule(rel, cmd->name,
5084 RULE_DISABLED, lockmode);
5085 break;
5086
5087 case AT_AddInherit:
5088 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5089 break;
5090 case AT_DropInherit:
5091 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5092 break;
5093 case AT_AddOf:
5094 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5095 break;
5096 case AT_DropOf:
5097 ATExecDropOf(rel, lockmode);
5098 break;
5099 case AT_ReplicaIdentity:
5100 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5101 break;
5102 case AT_EnableRowSecurity:
5103 ATExecSetRowSecurity(rel, true);
5104 break;
5105 case AT_DisableRowSecurity:
5106 ATExecSetRowSecurity(rel, false);
5107 break;
5108 case AT_ForceRowSecurity:
5109 ATExecForceNoForceRowSecurity(rel, true);
5110 break;
5111 case AT_NoForceRowSecurity:
5112 ATExecForceNoForceRowSecurity(rel, false);
5113 break;
5114 case AT_GenericOptions:
5115 ATExecGenericOptions(rel, (List *) cmd->def);
5116 break;
5117 case AT_AttachPartition:
5118 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5119 cur_pass, context);
5120 Assert(cmd != NULL);
5121 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5122 ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5123 context);
5124 else
5125 ATExecAttachPartitionIdx(wqueue, rel,
5126 ((PartitionCmd *) cmd->def)->name);
5127 break;
5128 case AT_DetachPartition:
5129 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5130 cur_pass, context);
5131 Assert(cmd != NULL);
5132 /* ATPrepCmd ensures it must be a table */
5133 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5134 ATExecDetachPartition(wqueue, tab, rel,
5135 ((PartitionCmd *) cmd->def)->name,
5136 ((PartitionCmd *) cmd->def)->concurrent);
5137 break;
5138 case AT_DetachPartitionFinalize:
5139 ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5140 break;
5141 default: /* oops */
5142 elog(ERROR, "unrecognized alter table type: %d",
5143 (int) cmd->subtype);
5144 break;
5145 }
5146
5147 /*
5148 * Report the subcommand to interested event triggers.
5149 */
5150 if (cmd)
5151 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5152
5153 /*
5154 * Bump the command counter to ensure the next subcommand in the sequence
5155 * can see the changes so far
5156 */
5157 CommandCounterIncrement();
5158 }
5159
5160 /*
5161 * ATParseTransformCmd: perform parse transformation for one subcommand
5162 *
5163 * Returns the transformed subcommand tree, if there is one, else NULL.
5164 *
5165 * The parser may hand back additional AlterTableCmd(s) and/or other
5166 * utility statements, either before or after the original subcommand.
5167 * Other AlterTableCmds are scheduled into the appropriate slot of the
5168 * AlteredTableInfo (they had better be for later passes than the current one).
5169 * Utility statements that are supposed to happen before the AlterTableCmd
5170 * are executed immediately. Those that are supposed to happen afterwards
5171 * are added to the tab->afterStmts list to be done at the very end.
5172 */
5173 static AlterTableCmd *
ATParseTransformCmd(List ** wqueue,AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,bool recurse,LOCKMODE lockmode,int cur_pass,AlterTableUtilityContext * context)5174 ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5175 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5176 int cur_pass, AlterTableUtilityContext *context)
5177 {
5178 AlterTableCmd *newcmd = NULL;
5179 AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5180 List *beforeStmts;
5181 List *afterStmts;
5182 ListCell *lc;
5183
5184 /* Gin up an AlterTableStmt with just this subcommand and this table */
5185 atstmt->relation =
5186 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5187 pstrdup(RelationGetRelationName(rel)),
5188 -1);
5189 atstmt->relation->inh = recurse;
5190 atstmt->cmds = list_make1(cmd);
5191 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5192 atstmt->missing_ok = false;
5193
5194 /* Transform the AlterTableStmt */
5195 atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5196 atstmt,
5197 context->queryString,
5198 &beforeStmts,
5199 &afterStmts);
5200
5201 /* Execute any statements that should happen before these subcommand(s) */
5202 foreach(lc, beforeStmts)
5203 {
5204 Node *stmt = (Node *) lfirst(lc);
5205
5206 ProcessUtilityForAlterTable(stmt, context);
5207 CommandCounterIncrement();
5208 }
5209
5210 /* Examine the transformed subcommands and schedule them appropriately */
5211 foreach(lc, atstmt->cmds)
5212 {
5213 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5214 int pass;
5215
5216 /*
5217 * This switch need only cover the subcommand types that can be added
5218 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5219 * executing the subcommand immediately, as a substitute for the
5220 * original subcommand. (Note, however, that this does cause
5221 * AT_AddConstraint subcommands to be rescheduled into later passes,
5222 * which is important for index and foreign key constraints.)
5223 *
5224 * We assume we needn't do any phase-1 checks for added subcommands.
5225 */
5226 switch (cmd2->subtype)
5227 {
5228 case AT_SetNotNull:
5229 /* Need command-specific recursion decision */
5230 ATPrepSetNotNull(wqueue, rel, cmd2,
5231 recurse, false,
5232 lockmode, context);
5233 pass = AT_PASS_COL_ATTRS;
5234 break;
5235 case AT_AddIndex:
5236 /* This command never recurses */
5237 /* No command-specific prep needed */
5238 pass = AT_PASS_ADD_INDEX;
5239 break;
5240 case AT_AddIndexConstraint:
5241 /* This command never recurses */
5242 /* No command-specific prep needed */
5243 pass = AT_PASS_ADD_INDEXCONSTR;
5244 break;
5245 case AT_AddConstraint:
5246 /* Recursion occurs during execution phase */
5247 if (recurse)
5248 cmd2->subtype = AT_AddConstraintRecurse;
5249 switch (castNode(Constraint, cmd2->def)->contype)
5250 {
5251 case CONSTR_PRIMARY:
5252 case CONSTR_UNIQUE:
5253 case CONSTR_EXCLUSION:
5254 pass = AT_PASS_ADD_INDEXCONSTR;
5255 break;
5256 default:
5257 pass = AT_PASS_ADD_OTHERCONSTR;
5258 break;
5259 }
5260 break;
5261 case AT_AlterColumnGenericOptions:
5262 /* This command never recurses */
5263 /* No command-specific prep needed */
5264 pass = AT_PASS_MISC;
5265 break;
5266 default:
5267 pass = cur_pass;
5268 break;
5269 }
5270
5271 if (pass < cur_pass)
5272 {
5273 /* Cannot schedule into a pass we already finished */
5274 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5275 pass);
5276 }
5277 else if (pass > cur_pass)
5278 {
5279 /* OK, queue it up for later */
5280 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5281 }
5282 else
5283 {
5284 /*
5285 * We should see at most one subcommand for the current pass,
5286 * which is the transformed version of the original subcommand.
5287 */
5288 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5289 {
5290 /* Found the transformed version of our subcommand */
5291 newcmd = cmd2;
5292 }
5293 else
5294 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5295 pass);
5296 }
5297 }
5298
5299 /* Queue up any after-statements to happen at the end */
5300 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5301
5302 return newcmd;
5303 }
5304
5305 /*
5306 * ATRewriteTables: ALTER TABLE phase 3
5307 */
5308 static void
ATRewriteTables(AlterTableStmt * parsetree,List ** wqueue,LOCKMODE lockmode,AlterTableUtilityContext * context)5309 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5310 AlterTableUtilityContext *context)
5311 {
5312 ListCell *ltab;
5313
5314 /* Go through each table that needs to be checked or rewritten */
5315 foreach(ltab, *wqueue)
5316 {
5317 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5318
5319 /* Relations without storage may be ignored here */
5320 if (!RELKIND_HAS_STORAGE(tab->relkind))
5321 continue;
5322
5323 /*
5324 * If we change column data types, the operation has to be propagated
5325 * to tables that use this table's rowtype as a column type.
5326 * tab->newvals will also be non-NULL in the case where we're adding a
5327 * column with a default. We choose to forbid that case as well,
5328 * since composite types might eventually support defaults.
5329 *
5330 * (Eventually we'll probably need to check for composite type
5331 * dependencies even when we're just scanning the table without a
5332 * rewrite, but at the moment a composite type does not enforce any
5333 * constraints, so it's not necessary/appropriate to enforce them just
5334 * during ALTER.)
5335 */
5336 if (tab->newvals != NIL || tab->rewrite > 0)
5337 {
5338 Relation rel;
5339
5340 rel = table_open(tab->relid, NoLock);
5341 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5342 table_close(rel, NoLock);
5343 }
5344
5345 /*
5346 * We only need to rewrite the table if at least one column needs to
5347 * be recomputed, or we are changing its persistence.
5348 *
5349 * There are two reasons for requiring a rewrite when changing
5350 * persistence: on one hand, we need to ensure that the buffers
5351 * belonging to each of the two relations are marked with or without
5352 * BM_PERMANENT properly. On the other hand, since rewriting creates
5353 * and assigns a new relfilenode, we automatically create or drop an
5354 * init fork for the relation as appropriate.
5355 */
5356 if (tab->rewrite > 0)
5357 {
5358 /* Build a temporary relation and copy data */
5359 Relation OldHeap;
5360 Oid OIDNewHeap;
5361 Oid NewTableSpace;
5362 char persistence;
5363
5364 OldHeap = table_open(tab->relid, NoLock);
5365
5366 /*
5367 * We don't support rewriting of system catalogs; there are too
5368 * many corner cases and too little benefit. In particular this
5369 * is certainly not going to work for mapped catalogs.
5370 */
5371 if (IsSystemRelation(OldHeap))
5372 ereport(ERROR,
5373 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5374 errmsg("cannot rewrite system relation \"%s\"",
5375 RelationGetRelationName(OldHeap))));
5376
5377 if (RelationIsUsedAsCatalogTable(OldHeap))
5378 ereport(ERROR,
5379 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5380 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5381 RelationGetRelationName(OldHeap))));
5382
5383 /*
5384 * Don't allow rewrite on temp tables of other backends ... their
5385 * local buffer manager is not going to cope.
5386 */
5387 if (RELATION_IS_OTHER_TEMP(OldHeap))
5388 ereport(ERROR,
5389 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5390 errmsg("cannot rewrite temporary tables of other sessions")));
5391
5392 /*
5393 * Select destination tablespace (same as original unless user
5394 * requested a change)
5395 */
5396 if (tab->newTableSpace)
5397 NewTableSpace = tab->newTableSpace;
5398 else
5399 NewTableSpace = OldHeap->rd_rel->reltablespace;
5400
5401 /*
5402 * Select persistence of transient table (same as original unless
5403 * user requested a change)
5404 */
5405 persistence = tab->chgPersistence ?
5406 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5407
5408 table_close(OldHeap, NoLock);
5409
5410 /*
5411 * Fire off an Event Trigger now, before actually rewriting the
5412 * table.
5413 *
5414 * We don't support Event Trigger for nested commands anywhere,
5415 * here included, and parsetree is given NULL when coming from
5416 * AlterTableInternal.
5417 *
5418 * And fire it only once.
5419 */
5420 if (parsetree)
5421 EventTriggerTableRewrite((Node *) parsetree,
5422 tab->relid,
5423 tab->rewrite);
5424
5425 /*
5426 * Create transient table that will receive the modified data.
5427 *
5428 * Ensure it is marked correctly as logged or unlogged. We have
5429 * to do this here so that buffers for the new relfilenode will
5430 * have the right persistence set, and at the same time ensure
5431 * that the original filenode's buffers will get read in with the
5432 * correct setting (i.e. the original one). Otherwise a rollback
5433 * after the rewrite would possibly result with buffers for the
5434 * original filenode having the wrong persistence setting.
5435 *
5436 * NB: This relies on swap_relation_files() also swapping the
5437 * persistence. That wouldn't work for pg_class, but that can't be
5438 * unlogged anyway.
5439 */
5440 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
5441 lockmode);
5442
5443 /*
5444 * Copy the heap data into the new table with the desired
5445 * modifications, and test the current data within the table
5446 * against new constraints generated by ALTER TABLE commands.
5447 */
5448 ATRewriteTable(tab, OIDNewHeap, lockmode);
5449
5450 /*
5451 * Swap the physical files of the old and new heaps, then rebuild
5452 * indexes and discard the old heap. We can use RecentXmin for
5453 * the table's new relfrozenxid because we rewrote all the tuples
5454 * in ATRewriteTable, so no older Xid remains in the table. Also,
5455 * we never try to swap toast tables by content, since we have no
5456 * interest in letting this code work on system catalogs.
5457 */
5458 finish_heap_swap(tab->relid, OIDNewHeap,
5459 false, false, true,
5460 !OidIsValid(tab->newTableSpace),
5461 RecentXmin,
5462 ReadNextMultiXactId(),
5463 persistence);
5464 }
5465 else
5466 {
5467 /*
5468 * If required, test the current data within the table against new
5469 * constraints generated by ALTER TABLE commands, but don't
5470 * rebuild data.
5471 */
5472 if (tab->constraints != NIL || tab->verify_new_notnull ||
5473 tab->partition_constraint != NULL)
5474 ATRewriteTable(tab, InvalidOid, lockmode);
5475
5476 /*
5477 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5478 * just do a block-by-block copy.
5479 */
5480 if (tab->newTableSpace)
5481 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5482 }
5483 }
5484
5485 /*
5486 * Foreign key constraints are checked in a final pass, since (a) it's
5487 * generally best to examine each one separately, and (b) it's at least
5488 * theoretically possible that we have changed both relations of the
5489 * foreign key, and we'd better have finished both rewrites before we try
5490 * to read the tables.
5491 */
5492 foreach(ltab, *wqueue)
5493 {
5494 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5495 Relation rel = NULL;
5496 ListCell *lcon;
5497
5498 /* Relations without storage may be ignored here too */
5499 if (!RELKIND_HAS_STORAGE(tab->relkind))
5500 continue;
5501
5502 foreach(lcon, tab->constraints)
5503 {
5504 NewConstraint *con = lfirst(lcon);
5505
5506 if (con->contype == CONSTR_FOREIGN)
5507 {
5508 Constraint *fkconstraint = (Constraint *) con->qual;
5509 Relation refrel;
5510
5511 if (rel == NULL)
5512 {
5513 /* Long since locked, no need for another */
5514 rel = table_open(tab->relid, NoLock);
5515 }
5516
5517 refrel = table_open(con->refrelid, RowShareLock);
5518
5519 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5520 con->refindid,
5521 con->conid);
5522
5523 /*
5524 * No need to mark the constraint row as validated, we did
5525 * that when we inserted the row earlier.
5526 */
5527
5528 table_close(refrel, NoLock);
5529 }
5530 }
5531
5532 if (rel)
5533 table_close(rel, NoLock);
5534 }
5535
5536 /* Finally, run any afterStmts that were queued up */
5537 foreach(ltab, *wqueue)
5538 {
5539 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5540 ListCell *lc;
5541
5542 foreach(lc, tab->afterStmts)
5543 {
5544 Node *stmt = (Node *) lfirst(lc);
5545
5546 ProcessUtilityForAlterTable(stmt, context);
5547 CommandCounterIncrement();
5548 }
5549 }
5550 }
5551
5552 /*
5553 * ATRewriteTable: scan or rewrite one table
5554 *
5555 * OIDNewHeap is InvalidOid if we don't need to rewrite
5556 */
5557 static void
ATRewriteTable(AlteredTableInfo * tab,Oid OIDNewHeap,LOCKMODE lockmode)5558 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
5559 {
5560 Relation oldrel;
5561 Relation newrel;
5562 TupleDesc oldTupDesc;
5563 TupleDesc newTupDesc;
5564 bool needscan = false;
5565 List *notnull_attrs;
5566 int i;
5567 ListCell *l;
5568 EState *estate;
5569 CommandId mycid;
5570 BulkInsertState bistate;
5571 int ti_options;
5572 ExprState *partqualstate = NULL;
5573
5574 /*
5575 * Open the relation(s). We have surely already locked the existing
5576 * table.
5577 */
5578 oldrel = table_open(tab->relid, NoLock);
5579 oldTupDesc = tab->oldDesc;
5580 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
5581
5582 if (OidIsValid(OIDNewHeap))
5583 newrel = table_open(OIDNewHeap, lockmode);
5584 else
5585 newrel = NULL;
5586
5587 /*
5588 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
5589 * is empty, so don't bother using it.
5590 */
5591 if (newrel)
5592 {
5593 mycid = GetCurrentCommandId(true);
5594 bistate = GetBulkInsertState();
5595 ti_options = TABLE_INSERT_SKIP_FSM;
5596 }
5597 else
5598 {
5599 /* keep compiler quiet about using these uninitialized */
5600 mycid = 0;
5601 bistate = NULL;
5602 ti_options = 0;
5603 }
5604
5605 /*
5606 * Generate the constraint and default execution states
5607 */
5608
5609 estate = CreateExecutorState();
5610
5611 /* Build the needed expression execution states */
5612 foreach(l, tab->constraints)
5613 {
5614 NewConstraint *con = lfirst(l);
5615
5616 switch (con->contype)
5617 {
5618 case CONSTR_CHECK:
5619 needscan = true;
5620 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
5621 break;
5622 case CONSTR_FOREIGN:
5623 /* Nothing to do here */
5624 break;
5625 default:
5626 elog(ERROR, "unrecognized constraint type: %d",
5627 (int) con->contype);
5628 }
5629 }
5630
5631 /* Build expression execution states for partition check quals */
5632 if (tab->partition_constraint)
5633 {
5634 needscan = true;
5635 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
5636 }
5637
5638 foreach(l, tab->newvals)
5639 {
5640 NewColumnValue *ex = lfirst(l);
5641
5642 /* expr already planned */
5643 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
5644 }
5645
5646 notnull_attrs = NIL;
5647 if (newrel || tab->verify_new_notnull)
5648 {
5649 /*
5650 * If we are rebuilding the tuples OR if we added any new but not
5651 * verified NOT NULL constraints, check all not-null constraints. This
5652 * is a bit of overkill but it minimizes risk of bugs, and
5653 * heap_attisnull is a pretty cheap test anyway.
5654 */
5655 for (i = 0; i < newTupDesc->natts; i++)
5656 {
5657 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
5658
5659 if (attr->attnotnull && !attr->attisdropped)
5660 notnull_attrs = lappend_int(notnull_attrs, i);
5661 }
5662 if (notnull_attrs)
5663 needscan = true;
5664 }
5665
5666 if (newrel || needscan)
5667 {
5668 ExprContext *econtext;
5669 TupleTableSlot *oldslot;
5670 TupleTableSlot *newslot;
5671 TableScanDesc scan;
5672 MemoryContext oldCxt;
5673 List *dropped_attrs = NIL;
5674 ListCell *lc;
5675 Snapshot snapshot;
5676
5677 if (newrel)
5678 ereport(DEBUG1,
5679 (errmsg_internal("rewriting table \"%s\"",
5680 RelationGetRelationName(oldrel))));
5681 else
5682 ereport(DEBUG1,
5683 (errmsg_internal("verifying table \"%s\"",
5684 RelationGetRelationName(oldrel))));
5685
5686 if (newrel)
5687 {
5688 /*
5689 * All predicate locks on the tuples or pages are about to be made
5690 * invalid, because we move tuples around. Promote them to
5691 * relation locks.
5692 */
5693 TransferPredicateLocksToHeapRelation(oldrel);
5694 }
5695
5696 econtext = GetPerTupleExprContext(estate);
5697
5698 /*
5699 * Create necessary tuple slots. When rewriting, two slots are needed,
5700 * otherwise one suffices. In the case where one slot suffices, we
5701 * need to use the new tuple descriptor, otherwise some constraints
5702 * can't be evaluated. Note that even when the tuple layout is the
5703 * same and no rewrite is required, the tupDescs might not be
5704 * (consider ADD COLUMN without a default).
5705 */
5706 if (tab->rewrite)
5707 {
5708 Assert(newrel != NULL);
5709 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
5710 table_slot_callbacks(oldrel));
5711 newslot = MakeSingleTupleTableSlot(newTupDesc,
5712 table_slot_callbacks(newrel));
5713
5714 /*
5715 * Set all columns in the new slot to NULL initially, to ensure
5716 * columns added as part of the rewrite are initialized to NULL.
5717 * That is necessary as tab->newvals will not contain an
5718 * expression for columns with a NULL default, e.g. when adding a
5719 * column without a default together with a column with a default
5720 * requiring an actual rewrite.
5721 */
5722 ExecStoreAllNullTuple(newslot);
5723 }
5724 else
5725 {
5726 oldslot = MakeSingleTupleTableSlot(newTupDesc,
5727 table_slot_callbacks(oldrel));
5728 newslot = NULL;
5729 }
5730
5731 /*
5732 * Any attributes that are dropped according to the new tuple
5733 * descriptor can be set to NULL. We precompute the list of dropped
5734 * attributes to avoid needing to do so in the per-tuple loop.
5735 */
5736 for (i = 0; i < newTupDesc->natts; i++)
5737 {
5738 if (TupleDescAttr(newTupDesc, i)->attisdropped)
5739 dropped_attrs = lappend_int(dropped_attrs, i);
5740 }
5741
5742 /*
5743 * Scan through the rows, generating a new row if needed and then
5744 * checking all the constraints.
5745 */
5746 snapshot = RegisterSnapshot(GetLatestSnapshot());
5747 scan = table_beginscan(oldrel, snapshot, 0, NULL);
5748
5749 /*
5750 * Switch to per-tuple memory context and reset it for each tuple
5751 * produced, so we don't leak memory.
5752 */
5753 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
5754
5755 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
5756 {
5757 TupleTableSlot *insertslot;
5758
5759 if (tab->rewrite > 0)
5760 {
5761 /* Extract data from old tuple */
5762 slot_getallattrs(oldslot);
5763 ExecClearTuple(newslot);
5764
5765 /* copy attributes */
5766 memcpy(newslot->tts_values, oldslot->tts_values,
5767 sizeof(Datum) * oldslot->tts_nvalid);
5768 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
5769 sizeof(bool) * oldslot->tts_nvalid);
5770
5771 /* Set dropped attributes to null in new tuple */
5772 foreach(lc, dropped_attrs)
5773 newslot->tts_isnull[lfirst_int(lc)] = true;
5774
5775 /*
5776 * Constraints and GENERATED expressions might reference the
5777 * tableoid column, so fill tts_tableOid with the desired
5778 * value. (We must do this each time, because it gets
5779 * overwritten with newrel's OID during storing.)
5780 */
5781 newslot->tts_tableOid = RelationGetRelid(oldrel);
5782
5783 /*
5784 * Process supplied expressions to replace selected columns.
5785 *
5786 * First, evaluate expressions whose inputs come from the old
5787 * tuple.
5788 */
5789 econtext->ecxt_scantuple = oldslot;
5790
5791 foreach(l, tab->newvals)
5792 {
5793 NewColumnValue *ex = lfirst(l);
5794
5795 if (ex->is_generated)
5796 continue;
5797
5798 newslot->tts_values[ex->attnum - 1]
5799 = ExecEvalExpr(ex->exprstate,
5800 econtext,
5801 &newslot->tts_isnull[ex->attnum - 1]);
5802 }
5803
5804 ExecStoreVirtualTuple(newslot);
5805
5806 /*
5807 * Now, evaluate any expressions whose inputs come from the
5808 * new tuple. We assume these columns won't reference each
5809 * other, so that there's no ordering dependency.
5810 */
5811 econtext->ecxt_scantuple = newslot;
5812
5813 foreach(l, tab->newvals)
5814 {
5815 NewColumnValue *ex = lfirst(l);
5816
5817 if (!ex->is_generated)
5818 continue;
5819
5820 newslot->tts_values[ex->attnum - 1]
5821 = ExecEvalExpr(ex->exprstate,
5822 econtext,
5823 &newslot->tts_isnull[ex->attnum - 1]);
5824 }
5825
5826 insertslot = newslot;
5827 }
5828 else
5829 {
5830 /*
5831 * If there's no rewrite, old and new table are guaranteed to
5832 * have the same AM, so we can just use the old slot to verify
5833 * new constraints etc.
5834 */
5835 insertslot = oldslot;
5836 }
5837
5838 /* Now check any constraints on the possibly-changed tuple */
5839 econtext->ecxt_scantuple = insertslot;
5840
5841 foreach(l, notnull_attrs)
5842 {
5843 int attn = lfirst_int(l);
5844
5845 if (slot_attisnull(insertslot, attn + 1))
5846 {
5847 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
5848
5849 ereport(ERROR,
5850 (errcode(ERRCODE_NOT_NULL_VIOLATION),
5851 errmsg("column \"%s\" of relation \"%s\" contains null values",
5852 NameStr(attr->attname),
5853 RelationGetRelationName(oldrel)),
5854 errtablecol(oldrel, attn + 1)));
5855 }
5856 }
5857
5858 foreach(l, tab->constraints)
5859 {
5860 NewConstraint *con = lfirst(l);
5861
5862 switch (con->contype)
5863 {
5864 case CONSTR_CHECK:
5865 if (!ExecCheck(con->qualstate, econtext))
5866 ereport(ERROR,
5867 (errcode(ERRCODE_CHECK_VIOLATION),
5868 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
5869 con->name,
5870 RelationGetRelationName(oldrel)),
5871 errtableconstraint(oldrel, con->name)));
5872 break;
5873 case CONSTR_FOREIGN:
5874 /* Nothing to do here */
5875 break;
5876 default:
5877 elog(ERROR, "unrecognized constraint type: %d",
5878 (int) con->contype);
5879 }
5880 }
5881
5882 if (partqualstate && !ExecCheck(partqualstate, econtext))
5883 {
5884 if (tab->validate_default)
5885 ereport(ERROR,
5886 (errcode(ERRCODE_CHECK_VIOLATION),
5887 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
5888 RelationGetRelationName(oldrel)),
5889 errtable(oldrel)));
5890 else
5891 ereport(ERROR,
5892 (errcode(ERRCODE_CHECK_VIOLATION),
5893 errmsg("partition constraint of relation \"%s\" is violated by some row",
5894 RelationGetRelationName(oldrel)),
5895 errtable(oldrel)));
5896 }
5897
5898 /* Write the tuple out to the new relation */
5899 if (newrel)
5900 table_tuple_insert(newrel, insertslot, mycid,
5901 ti_options, bistate);
5902
5903 ResetExprContext(econtext);
5904
5905 CHECK_FOR_INTERRUPTS();
5906 }
5907
5908 MemoryContextSwitchTo(oldCxt);
5909 table_endscan(scan);
5910 UnregisterSnapshot(snapshot);
5911
5912 ExecDropSingleTupleTableSlot(oldslot);
5913 if (newslot)
5914 ExecDropSingleTupleTableSlot(newslot);
5915 }
5916
5917 FreeExecutorState(estate);
5918
5919 table_close(oldrel, NoLock);
5920 if (newrel)
5921 {
5922 FreeBulkInsertState(bistate);
5923
5924 table_finish_bulk_insert(newrel, ti_options);
5925
5926 table_close(newrel, NoLock);
5927 }
5928 }
5929
5930 /*
5931 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
5932 */
5933 static AlteredTableInfo *
ATGetQueueEntry(List ** wqueue,Relation rel)5934 ATGetQueueEntry(List **wqueue, Relation rel)
5935 {
5936 Oid relid = RelationGetRelid(rel);
5937 AlteredTableInfo *tab;
5938 ListCell *ltab;
5939
5940 foreach(ltab, *wqueue)
5941 {
5942 tab = (AlteredTableInfo *) lfirst(ltab);
5943 if (tab->relid == relid)
5944 return tab;
5945 }
5946
5947 /*
5948 * Not there, so add it. Note that we make a copy of the relation's
5949 * existing descriptor before anything interesting can happen to it.
5950 */
5951 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
5952 tab->relid = relid;
5953 tab->rel = NULL; /* set later */
5954 tab->relkind = rel->rd_rel->relkind;
5955 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
5956 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5957 tab->chgPersistence = false;
5958
5959 *wqueue = lappend(*wqueue, tab);
5960
5961 return tab;
5962 }
5963
5964 /*
5965 * ATSimplePermissions
5966 *
5967 * - Ensure that it is a relation (or possibly a view)
5968 * - Ensure this user is the owner
5969 * - Ensure that it is not a system table
5970 */
5971 static void
ATSimplePermissions(Relation rel,int allowed_targets)5972 ATSimplePermissions(Relation rel, int allowed_targets)
5973 {
5974 int actual_target;
5975
5976 switch (rel->rd_rel->relkind)
5977 {
5978 case RELKIND_RELATION:
5979 case RELKIND_PARTITIONED_TABLE:
5980 actual_target = ATT_TABLE;
5981 break;
5982 case RELKIND_VIEW:
5983 actual_target = ATT_VIEW;
5984 break;
5985 case RELKIND_MATVIEW:
5986 actual_target = ATT_MATVIEW;
5987 break;
5988 case RELKIND_INDEX:
5989 actual_target = ATT_INDEX;
5990 break;
5991 case RELKIND_PARTITIONED_INDEX:
5992 actual_target = ATT_PARTITIONED_INDEX;
5993 break;
5994 case RELKIND_COMPOSITE_TYPE:
5995 actual_target = ATT_COMPOSITE_TYPE;
5996 break;
5997 case RELKIND_FOREIGN_TABLE:
5998 actual_target = ATT_FOREIGN_TABLE;
5999 break;
6000 default:
6001 actual_target = 0;
6002 break;
6003 }
6004
6005 /* Wrong target type? */
6006 if ((actual_target & allowed_targets) == 0)
6007 ATWrongRelkindError(rel, allowed_targets);
6008
6009 /* Permissions checks */
6010 if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
6011 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6012 RelationGetRelationName(rel));
6013
6014 if (!allowSystemTableMods && IsSystemRelation(rel))
6015 ereport(ERROR,
6016 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6017 errmsg("permission denied: \"%s\" is a system catalog",
6018 RelationGetRelationName(rel))));
6019 }
6020
6021 /*
6022 * ATWrongRelkindError
6023 *
6024 * Throw an error when a relation has been determined to be of the wrong
6025 * type.
6026 */
6027 static void
ATWrongRelkindError(Relation rel,int allowed_targets)6028 ATWrongRelkindError(Relation rel, int allowed_targets)
6029 {
6030 char *msg;
6031
6032 switch (allowed_targets)
6033 {
6034 case ATT_TABLE:
6035 msg = _("\"%s\" is not a table");
6036 break;
6037 case ATT_TABLE | ATT_VIEW:
6038 msg = _("\"%s\" is not a table or view");
6039 break;
6040 case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
6041 msg = _("\"%s\" is not a table, view, or foreign table");
6042 break;
6043 case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
6044 msg = _("\"%s\" is not a table, view, materialized view, or index");
6045 break;
6046 case ATT_TABLE | ATT_MATVIEW:
6047 msg = _("\"%s\" is not a table or materialized view");
6048 break;
6049 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX:
6050 msg = _("\"%s\" is not a table, materialized view, or index");
6051 break;
6052 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX:
6053 msg = _("\"%s\" is not a table, materialized view, index, or partitioned index");
6054 break;
6055 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE:
6056 msg = _("\"%s\" is not a table, materialized view, index, partitioned index, or foreign table");
6057 break;
6058 case ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE:
6059 msg = _("\"%s\" is not a table, materialized view, or foreign table");
6060 break;
6061 case ATT_TABLE | ATT_FOREIGN_TABLE:
6062 msg = _("\"%s\" is not a table or foreign table");
6063 break;
6064 case ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE:
6065 msg = _("\"%s\" is not a table, composite type, or foreign table");
6066 break;
6067 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE:
6068 msg = _("\"%s\" is not a table, materialized view, index, or foreign table");
6069 break;
6070 case ATT_TABLE | ATT_PARTITIONED_INDEX:
6071 msg = _("\"%s\" is not a table or partitioned index");
6072 break;
6073 case ATT_VIEW:
6074 msg = _("\"%s\" is not a view");
6075 break;
6076 case ATT_FOREIGN_TABLE:
6077 msg = _("\"%s\" is not a foreign table");
6078 break;
6079 default:
6080 /* shouldn't get here, add all necessary cases above */
6081 msg = _("\"%s\" is of the wrong type");
6082 break;
6083 }
6084
6085 ereport(ERROR,
6086 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6087 errmsg(msg, RelationGetRelationName(rel))));
6088 }
6089
6090 /*
6091 * ATSimpleRecursion
6092 *
6093 * Simple table recursion sufficient for most ALTER TABLE operations.
6094 * All direct and indirect children are processed in an unspecified order.
6095 * Note that if a child inherits from the original table via multiple
6096 * inheritance paths, it will be visited just once.
6097 */
6098 static void
ATSimpleRecursion(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,LOCKMODE lockmode,AlterTableUtilityContext * context)6099 ATSimpleRecursion(List **wqueue, Relation rel,
6100 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6101 AlterTableUtilityContext *context)
6102 {
6103 /*
6104 * Propagate to children, if desired and if there are (or might be) any
6105 * children.
6106 */
6107 if (recurse && rel->rd_rel->relhassubclass)
6108 {
6109 Oid relid = RelationGetRelid(rel);
6110 ListCell *child;
6111 List *children;
6112
6113 children = find_all_inheritors(relid, lockmode, NULL);
6114
6115 /*
6116 * find_all_inheritors does the recursive search of the inheritance
6117 * hierarchy, so all we have to do is process all of the relids in the
6118 * list that it returns.
6119 */
6120 foreach(child, children)
6121 {
6122 Oid childrelid = lfirst_oid(child);
6123 Relation childrel;
6124
6125 if (childrelid == relid)
6126 continue;
6127 /* find_all_inheritors already got lock */
6128 childrel = relation_open(childrelid, NoLock);
6129 CheckTableNotInUse(childrel, "ALTER TABLE");
6130 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6131 relation_close(childrel, NoLock);
6132 }
6133 }
6134 }
6135
6136 /*
6137 * Obtain list of partitions of the given table, locking them all at the given
6138 * lockmode and ensuring that they all pass CheckTableNotInUse.
6139 *
6140 * This function is a no-op if the given relation is not a partitioned table;
6141 * in particular, nothing is done if it's a legacy inheritance parent.
6142 */
6143 static void
ATCheckPartitionsNotInUse(Relation rel,LOCKMODE lockmode)6144 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6145 {
6146 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6147 {
6148 List *inh;
6149 ListCell *cell;
6150
6151 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6152 /* first element is the parent rel; must ignore it */
6153 for_each_from(cell, inh, 1)
6154 {
6155 Relation childrel;
6156
6157 /* find_all_inheritors already got lock */
6158 childrel = table_open(lfirst_oid(cell), NoLock);
6159 CheckTableNotInUse(childrel, "ALTER TABLE");
6160 table_close(childrel, NoLock);
6161 }
6162 list_free(inh);
6163 }
6164 }
6165
6166 /*
6167 * ATTypedTableRecursion
6168 *
6169 * Propagate ALTER TYPE operations to the typed tables of that type.
6170 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6171 * recursion to inheritance children of the typed tables.
6172 */
6173 static void
ATTypedTableRecursion(List ** wqueue,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode,AlterTableUtilityContext * context)6174 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6175 LOCKMODE lockmode, AlterTableUtilityContext *context)
6176 {
6177 ListCell *child;
6178 List *children;
6179
6180 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6181
6182 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6183 RelationGetRelationName(rel),
6184 cmd->behavior);
6185
6186 foreach(child, children)
6187 {
6188 Oid childrelid = lfirst_oid(child);
6189 Relation childrel;
6190
6191 childrel = relation_open(childrelid, lockmode);
6192 CheckTableNotInUse(childrel, "ALTER TABLE");
6193 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6194 relation_close(childrel, NoLock);
6195 }
6196 }
6197
6198
6199 /*
6200 * find_composite_type_dependencies
6201 *
6202 * Check to see if the type "typeOid" is being used as a column in some table
6203 * (possibly nested several levels deep in composite types, arrays, etc!).
6204 * Eventually, we'd like to propagate the check or rewrite operation
6205 * into such tables, but for now, just error out if we find any.
6206 *
6207 * Caller should provide either the associated relation of a rowtype,
6208 * or a type name (not both) for use in the error message, if any.
6209 *
6210 * Note that "typeOid" is not necessarily a composite type; it could also be
6211 * another container type such as an array or range, or a domain over one of
6212 * these things. The name of this function is therefore somewhat historical,
6213 * but it's not worth changing.
6214 *
6215 * We assume that functions and views depending on the type are not reasons
6216 * to reject the ALTER. (How safe is this really?)
6217 */
6218 void
find_composite_type_dependencies(Oid typeOid,Relation origRelation,const char * origTypeName)6219 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6220 const char *origTypeName)
6221 {
6222 Relation depRel;
6223 ScanKeyData key[2];
6224 SysScanDesc depScan;
6225 HeapTuple depTup;
6226
6227 /* since this function recurses, it could be driven to stack overflow */
6228 check_stack_depth();
6229
6230 /*
6231 * We scan pg_depend to find those things that depend on the given type.
6232 * (We assume we can ignore refobjsubid for a type.)
6233 */
6234 depRel = table_open(DependRelationId, AccessShareLock);
6235
6236 ScanKeyInit(&key[0],
6237 Anum_pg_depend_refclassid,
6238 BTEqualStrategyNumber, F_OIDEQ,
6239 ObjectIdGetDatum(TypeRelationId));
6240 ScanKeyInit(&key[1],
6241 Anum_pg_depend_refobjid,
6242 BTEqualStrategyNumber, F_OIDEQ,
6243 ObjectIdGetDatum(typeOid));
6244
6245 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6246 NULL, 2, key);
6247
6248 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6249 {
6250 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6251 Relation rel;
6252 Form_pg_attribute att;
6253
6254 /* Check for directly dependent types */
6255 if (pg_depend->classid == TypeRelationId)
6256 {
6257 /*
6258 * This must be an array, domain, or range containing the given
6259 * type, so recursively check for uses of this type. Note that
6260 * any error message will mention the original type not the
6261 * container; this is intentional.
6262 */
6263 find_composite_type_dependencies(pg_depend->objid,
6264 origRelation, origTypeName);
6265 continue;
6266 }
6267
6268 /* Else, ignore dependees that aren't user columns of relations */
6269 /* (we assume system columns are never of interesting types) */
6270 if (pg_depend->classid != RelationRelationId ||
6271 pg_depend->objsubid <= 0)
6272 continue;
6273
6274 rel = relation_open(pg_depend->objid, AccessShareLock);
6275 att = TupleDescAttr(rel->rd_att, pg_depend->objsubid - 1);
6276
6277 if (rel->rd_rel->relkind == RELKIND_RELATION ||
6278 rel->rd_rel->relkind == RELKIND_MATVIEW ||
6279 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6280 {
6281 if (origTypeName)
6282 ereport(ERROR,
6283 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6284 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6285 origTypeName,
6286 RelationGetRelationName(rel),
6287 NameStr(att->attname))));
6288 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6289 ereport(ERROR,
6290 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6291 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6292 RelationGetRelationName(origRelation),
6293 RelationGetRelationName(rel),
6294 NameStr(att->attname))));
6295 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6296 ereport(ERROR,
6297 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6298 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6299 RelationGetRelationName(origRelation),
6300 RelationGetRelationName(rel),
6301 NameStr(att->attname))));
6302 else
6303 ereport(ERROR,
6304 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6305 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6306 RelationGetRelationName(origRelation),
6307 RelationGetRelationName(rel),
6308 NameStr(att->attname))));
6309 }
6310 else if (OidIsValid(rel->rd_rel->reltype))
6311 {
6312 /*
6313 * A view or composite type itself isn't a problem, but we must
6314 * recursively check for indirect dependencies via its rowtype.
6315 */
6316 find_composite_type_dependencies(rel->rd_rel->reltype,
6317 origRelation, origTypeName);
6318 }
6319
6320 relation_close(rel, AccessShareLock);
6321 }
6322
6323 systable_endscan(depScan);
6324
6325 relation_close(depRel, AccessShareLock);
6326 }
6327
6328
6329 /*
6330 * find_typed_table_dependencies
6331 *
6332 * Check to see if a composite type is being used as the type of a
6333 * typed table. Abort if any are found and behavior is RESTRICT.
6334 * Else return the list of tables.
6335 */
6336 static List *
find_typed_table_dependencies(Oid typeOid,const char * typeName,DropBehavior behavior)6337 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6338 {
6339 Relation classRel;
6340 ScanKeyData key[1];
6341 TableScanDesc scan;
6342 HeapTuple tuple;
6343 List *result = NIL;
6344
6345 classRel = table_open(RelationRelationId, AccessShareLock);
6346
6347 ScanKeyInit(&key[0],
6348 Anum_pg_class_reloftype,
6349 BTEqualStrategyNumber, F_OIDEQ,
6350 ObjectIdGetDatum(typeOid));
6351
6352 scan = table_beginscan_catalog(classRel, 1, key);
6353
6354 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6355 {
6356 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6357
6358 if (behavior == DROP_RESTRICT)
6359 ereport(ERROR,
6360 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6361 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6362 typeName),
6363 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6364 else
6365 result = lappend_oid(result, classform->oid);
6366 }
6367
6368 table_endscan(scan);
6369 table_close(classRel, AccessShareLock);
6370
6371 return result;
6372 }
6373
6374
6375 /*
6376 * check_of_type
6377 *
6378 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6379 * isn't suitable, throw an error. Currently, we require that the type
6380 * originated with CREATE TYPE AS. We could support any row type, but doing so
6381 * would require handling a number of extra corner cases in the DDL commands.
6382 * (Also, allowing domain-over-composite would open up a can of worms about
6383 * whether and how the domain's constraints should apply to derived tables.)
6384 */
6385 void
check_of_type(HeapTuple typetuple)6386 check_of_type(HeapTuple typetuple)
6387 {
6388 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6389 bool typeOk = false;
6390
6391 if (typ->typtype == TYPTYPE_COMPOSITE)
6392 {
6393 Relation typeRelation;
6394
6395 Assert(OidIsValid(typ->typrelid));
6396 typeRelation = relation_open(typ->typrelid, AccessShareLock);
6397 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6398
6399 /*
6400 * Close the parent rel, but keep our AccessShareLock on it until xact
6401 * commit. That will prevent someone else from deleting or ALTERing
6402 * the type before the typed table creation/conversion commits.
6403 */
6404 relation_close(typeRelation, NoLock);
6405 }
6406 if (!typeOk)
6407 ereport(ERROR,
6408 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6409 errmsg("type %s is not a composite type",
6410 format_type_be(typ->oid))));
6411 }
6412
6413
6414 /*
6415 * ALTER TABLE ADD COLUMN
6416 *
6417 * Adds an additional attribute to a relation making the assumption that
6418 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6419 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6420 * AlterTableCmd's.
6421 *
6422 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6423 * have to decide at runtime whether to recurse or not depending on whether we
6424 * actually add a column or merely merge with an existing column. (We can't
6425 * check this in a static pre-pass because it won't handle multiple inheritance
6426 * situations correctly.)
6427 */
6428 static void
ATPrepAddColumn(List ** wqueue,Relation rel,bool recurse,bool recursing,bool is_view,AlterTableCmd * cmd,LOCKMODE lockmode,AlterTableUtilityContext * context)6429 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6430 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
6431 AlterTableUtilityContext *context)
6432 {
6433 if (rel->rd_rel->reloftype && !recursing)
6434 ereport(ERROR,
6435 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6436 errmsg("cannot add column to typed table")));
6437
6438 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6439 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
6440
6441 if (recurse && !is_view)
6442 cmd->subtype = AT_AddColumnRecurse;
6443 }
6444
6445 /*
6446 * Add a column to a table. The return value is the address of the
6447 * new column in the parent relation.
6448 *
6449 * cmd is pass-by-ref so that we can replace it with the parse-transformed
6450 * copy (but that happens only after we check for IF NOT EXISTS).
6451 */
6452 static ObjectAddress
ATExecAddColumn(List ** wqueue,AlteredTableInfo * tab,Relation rel,AlterTableCmd ** cmd,bool recurse,bool recursing,LOCKMODE lockmode,int cur_pass,AlterTableUtilityContext * context)6453 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
6454 AlterTableCmd **cmd,
6455 bool recurse, bool recursing,
6456 LOCKMODE lockmode, int cur_pass,
6457 AlterTableUtilityContext *context)
6458 {
6459 Oid myrelid = RelationGetRelid(rel);
6460 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
6461 bool if_not_exists = (*cmd)->missing_ok;
6462 Relation pgclass,
6463 attrdesc;
6464 HeapTuple reltup;
6465 FormData_pg_attribute attribute;
6466 int newattnum;
6467 char relkind;
6468 HeapTuple typeTuple;
6469 Oid typeOid;
6470 int32 typmod;
6471 Oid collOid;
6472 Form_pg_type tform;
6473 Expr *defval;
6474 List *children;
6475 ListCell *child;
6476 AlterTableCmd *childcmd;
6477 AclResult aclresult;
6478 ObjectAddress address;
6479 TupleDesc tupdesc;
6480 FormData_pg_attribute *aattr[] = {&attribute};
6481
6482 /* At top level, permission check was done in ATPrepCmd, else do it */
6483 if (recursing)
6484 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6485
6486 if (rel->rd_rel->relispartition && !recursing)
6487 ereport(ERROR,
6488 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6489 errmsg("cannot add column to a partition")));
6490
6491 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
6492
6493 /*
6494 * Are we adding the column to a recursion child? If so, check whether to
6495 * merge with an existing definition for the column. If we do merge, we
6496 * must not recurse. Children will already have the column, and recursing
6497 * into them would mess up attinhcount.
6498 */
6499 if (colDef->inhcount > 0)
6500 {
6501 HeapTuple tuple;
6502
6503 /* Does child already have a column by this name? */
6504 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
6505 if (HeapTupleIsValid(tuple))
6506 {
6507 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
6508 Oid ctypeId;
6509 int32 ctypmod;
6510 Oid ccollid;
6511
6512 /* Child column must match on type, typmod, and collation */
6513 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
6514 if (ctypeId != childatt->atttypid ||
6515 ctypmod != childatt->atttypmod)
6516 ereport(ERROR,
6517 (errcode(ERRCODE_DATATYPE_MISMATCH),
6518 errmsg("child table \"%s\" has different type for column \"%s\"",
6519 RelationGetRelationName(rel), colDef->colname)));
6520 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
6521 if (ccollid != childatt->attcollation)
6522 ereport(ERROR,
6523 (errcode(ERRCODE_COLLATION_MISMATCH),
6524 errmsg("child table \"%s\" has different collation for column \"%s\"",
6525 RelationGetRelationName(rel), colDef->colname),
6526 errdetail("\"%s\" versus \"%s\"",
6527 get_collation_name(ccollid),
6528 get_collation_name(childatt->attcollation))));
6529
6530 /* Bump the existing child att's inhcount */
6531 childatt->attinhcount++;
6532 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
6533
6534 heap_freetuple(tuple);
6535
6536 /* Inform the user about the merge */
6537 ereport(NOTICE,
6538 (errmsg("merging definition of column \"%s\" for child \"%s\"",
6539 colDef->colname, RelationGetRelationName(rel))));
6540
6541 table_close(attrdesc, RowExclusiveLock);
6542 return InvalidObjectAddress;
6543 }
6544 }
6545
6546 /* skip if the name already exists and if_not_exists is true */
6547 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
6548 {
6549 table_close(attrdesc, RowExclusiveLock);
6550 return InvalidObjectAddress;
6551 }
6552
6553 /*
6554 * Okay, we need to add the column, so go ahead and do parse
6555 * transformation. This can result in queueing up, or even immediately
6556 * executing, subsidiary operations (such as creation of unique indexes);
6557 * so we mustn't do it until we have made the if_not_exists check.
6558 *
6559 * When recursing, the command was already transformed and we needn't do
6560 * so again. Also, if context isn't given we can't transform. (That
6561 * currently happens only for AT_AddColumnToView; we expect that view.c
6562 * passed us a ColumnDef that doesn't need work.)
6563 */
6564 if (context != NULL && !recursing)
6565 {
6566 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
6567 cur_pass, context);
6568 Assert(*cmd != NULL);
6569 colDef = castNode(ColumnDef, (*cmd)->def);
6570 }
6571
6572 /*
6573 * Cannot add identity column if table has children, because identity does
6574 * not inherit. (Adding column and identity separately will work.)
6575 */
6576 if (colDef->identity &&
6577 recurse &&
6578 find_inheritance_children(myrelid, NoLock) != NIL)
6579 ereport(ERROR,
6580 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6581 errmsg("cannot recursively add identity column to table that has child tables")));
6582
6583 pgclass = table_open(RelationRelationId, RowExclusiveLock);
6584
6585 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
6586 if (!HeapTupleIsValid(reltup))
6587 elog(ERROR, "cache lookup failed for relation %u", myrelid);
6588 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
6589
6590 /* Determine the new attribute's number */
6591 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
6592 if (newattnum > MaxHeapAttributeNumber)
6593 ereport(ERROR,
6594 (errcode(ERRCODE_TOO_MANY_COLUMNS),
6595 errmsg("tables can have at most %d columns",
6596 MaxHeapAttributeNumber)));
6597
6598 typeTuple = typenameType(NULL, colDef->typeName, &typmod);
6599 tform = (Form_pg_type) GETSTRUCT(typeTuple);
6600 typeOid = tform->oid;
6601
6602 aclresult = pg_type_aclcheck(typeOid, GetUserId(), ACL_USAGE);
6603 if (aclresult != ACLCHECK_OK)
6604 aclcheck_error_type(aclresult, typeOid);
6605
6606 collOid = GetColumnDefCollation(NULL, colDef, typeOid);
6607
6608 /* make sure datatype is legal for a column */
6609 CheckAttributeType(colDef->colname, typeOid, collOid,
6610 list_make1_oid(rel->rd_rel->reltype),
6611 0);
6612
6613 /* construct new attribute's pg_attribute entry */
6614 attribute.attrelid = myrelid;
6615 namestrcpy(&(attribute.attname), colDef->colname);
6616 attribute.atttypid = typeOid;
6617 attribute.attstattarget = (newattnum > 0) ? -1 : 0;
6618 attribute.attlen = tform->typlen;
6619 attribute.attnum = newattnum;
6620 attribute.attndims = list_length(colDef->typeName->arrayBounds);
6621 attribute.atttypmod = typmod;
6622 attribute.attbyval = tform->typbyval;
6623 attribute.attalign = tform->typalign;
6624 attribute.attstorage = tform->typstorage;
6625 attribute.attcompression = GetAttributeCompression(typeOid,
6626 colDef->compression);
6627 attribute.attnotnull = colDef->is_not_null;
6628 attribute.atthasdef = false;
6629 attribute.atthasmissing = false;
6630 attribute.attidentity = colDef->identity;
6631 attribute.attgenerated = colDef->generated;
6632 attribute.attisdropped = false;
6633 attribute.attislocal = colDef->is_local;
6634 attribute.attinhcount = colDef->inhcount;
6635 attribute.attcollation = collOid;
6636
6637 /* attribute.attacl is handled by InsertPgAttributeTuples() */
6638
6639 ReleaseSysCache(typeTuple);
6640
6641 tupdesc = CreateTupleDesc(lengthof(aattr), (FormData_pg_attribute **) &aattr);
6642
6643 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
6644
6645 table_close(attrdesc, RowExclusiveLock);
6646
6647 /*
6648 * Update pg_class tuple as appropriate
6649 */
6650 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
6651
6652 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
6653
6654 heap_freetuple(reltup);
6655
6656 /* Post creation hook for new attribute */
6657 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
6658
6659 table_close(pgclass, RowExclusiveLock);
6660
6661 /* Make the attribute's catalog entry visible */
6662 CommandCounterIncrement();
6663
6664 /*
6665 * Store the DEFAULT, if any, in the catalogs
6666 */
6667 if (colDef->raw_default)
6668 {
6669 RawColumnDefault *rawEnt;
6670
6671 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
6672 rawEnt->attnum = attribute.attnum;
6673 rawEnt->raw_default = copyObject(colDef->raw_default);
6674
6675 /*
6676 * Attempt to skip a complete table rewrite by storing the specified
6677 * DEFAULT value outside of the heap. This may be disabled inside
6678 * AddRelationNewConstraints if the optimization cannot be applied.
6679 */
6680 rawEnt->missingMode = (!colDef->generated);
6681
6682 rawEnt->generated = colDef->generated;
6683
6684 /*
6685 * This function is intended for CREATE TABLE, so it processes a
6686 * _list_ of defaults, but we just do one.
6687 */
6688 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
6689 false, true, false, NULL);
6690
6691 /* Make the additional catalog changes visible */
6692 CommandCounterIncrement();
6693
6694 /*
6695 * Did the request for a missing value work? If not we'll have to do a
6696 * rewrite
6697 */
6698 if (!rawEnt->missingMode)
6699 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
6700 }
6701
6702 /*
6703 * Tell Phase 3 to fill in the default expression, if there is one.
6704 *
6705 * If there is no default, Phase 3 doesn't have to do anything, because
6706 * that effectively means that the default is NULL. The heap tuple access
6707 * routines always check for attnum > # of attributes in tuple, and return
6708 * NULL if so, so without any modification of the tuple data we will get
6709 * the effect of NULL values in the new column.
6710 *
6711 * An exception occurs when the new column is of a domain type: the domain
6712 * might have a NOT NULL constraint, or a check constraint that indirectly
6713 * rejects nulls. If there are any domain constraints then we construct
6714 * an explicit NULL default value that will be passed through
6715 * CoerceToDomain processing. (This is a tad inefficient, since it causes
6716 * rewriting the table which we really don't have to do, but the present
6717 * design of domain processing doesn't offer any simple way of checking
6718 * the constraints more directly.)
6719 *
6720 * Note: we use build_column_default, and not just the cooked default
6721 * returned by AddRelationNewConstraints, so that the right thing happens
6722 * when a datatype's default applies.
6723 *
6724 * Note: it might seem that this should happen at the end of Phase 2, so
6725 * that the effects of subsequent subcommands can be taken into account.
6726 * It's intentional that we do it now, though. The new column should be
6727 * filled according to what is said in the ADD COLUMN subcommand, so that
6728 * the effects are the same as if this subcommand had been run by itself
6729 * and the later subcommands had been issued in new ALTER TABLE commands.
6730 *
6731 * We can skip this entirely for relations without storage, since Phase 3
6732 * is certainly not going to touch them. System attributes don't have
6733 * interesting defaults, either.
6734 */
6735 if (RELKIND_HAS_STORAGE(relkind) && attribute.attnum > 0)
6736 {
6737 /*
6738 * For an identity column, we can't use build_column_default(),
6739 * because the sequence ownership isn't set yet. So do it manually.
6740 */
6741 if (colDef->identity)
6742 {
6743 NextValueExpr *nve = makeNode(NextValueExpr);
6744
6745 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
6746 nve->typeId = typeOid;
6747
6748 defval = (Expr *) nve;
6749
6750 /* must do a rewrite for identity columns */
6751 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
6752 }
6753 else
6754 defval = (Expr *) build_column_default(rel, attribute.attnum);
6755
6756 if (!defval && DomainHasConstraints(typeOid))
6757 {
6758 Oid baseTypeId;
6759 int32 baseTypeMod;
6760 Oid baseTypeColl;
6761
6762 baseTypeMod = typmod;
6763 baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
6764 baseTypeColl = get_typcollation(baseTypeId);
6765 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
6766 defval = (Expr *) coerce_to_target_type(NULL,
6767 (Node *) defval,
6768 baseTypeId,
6769 typeOid,
6770 typmod,
6771 COERCION_ASSIGNMENT,
6772 COERCE_IMPLICIT_CAST,
6773 -1);
6774 if (defval == NULL) /* should not happen */
6775 elog(ERROR, "failed to coerce base type to domain");
6776 }
6777
6778 if (defval)
6779 {
6780 NewColumnValue *newval;
6781
6782 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
6783 newval->attnum = attribute.attnum;
6784 newval->expr = expression_planner(defval);
6785 newval->is_generated = (colDef->generated != '\0');
6786
6787 tab->newvals = lappend(tab->newvals, newval);
6788 }
6789
6790 if (DomainHasConstraints(typeOid))
6791 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
6792
6793 if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing)
6794 {
6795 /*
6796 * If the new column is NOT NULL, and there is no missing value,
6797 * tell Phase 3 it needs to check for NULLs.
6798 */
6799 tab->verify_new_notnull |= colDef->is_not_null;
6800 }
6801 }
6802
6803 /*
6804 * Add needed dependency entries for the new column.
6805 */
6806 add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
6807 add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
6808
6809 /*
6810 * Propagate to children as appropriate. Unlike most other ALTER
6811 * routines, we have to do this one level of recursion at a time; we can't
6812 * use find_all_inheritors to do it in one pass.
6813 */
6814 children =
6815 find_inheritance_children(RelationGetRelid(rel), lockmode);
6816
6817 /*
6818 * If we are told not to recurse, there had better not be any child
6819 * tables; else the addition would put them out of step.
6820 */
6821 if (children && !recurse)
6822 ereport(ERROR,
6823 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6824 errmsg("column must be added to child tables too")));
6825
6826 /* Children should see column as singly inherited */
6827 if (!recursing)
6828 {
6829 childcmd = copyObject(*cmd);
6830 colDef = castNode(ColumnDef, childcmd->def);
6831 colDef->inhcount = 1;
6832 colDef->is_local = false;
6833 }
6834 else
6835 childcmd = *cmd; /* no need to copy again */
6836
6837 foreach(child, children)
6838 {
6839 Oid childrelid = lfirst_oid(child);
6840 Relation childrel;
6841 AlteredTableInfo *childtab;
6842
6843 /* find_inheritance_children already got lock */
6844 childrel = table_open(childrelid, NoLock);
6845 CheckTableNotInUse(childrel, "ALTER TABLE");
6846
6847 /* Find or create work queue entry for this table */
6848 childtab = ATGetQueueEntry(wqueue, childrel);
6849
6850 /* Recurse to child; return value is ignored */
6851 ATExecAddColumn(wqueue, childtab, childrel,
6852 &childcmd, recurse, true,
6853 lockmode, cur_pass, context);
6854
6855 table_close(childrel, NoLock);
6856 }
6857
6858 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
6859 return address;
6860 }
6861
6862 /*
6863 * If a new or renamed column will collide with the name of an existing
6864 * column and if_not_exists is false then error out, else do nothing.
6865 */
6866 static bool
check_for_column_name_collision(Relation rel,const char * colname,bool if_not_exists)6867 check_for_column_name_collision(Relation rel, const char *colname,
6868 bool if_not_exists)
6869 {
6870 HeapTuple attTuple;
6871 int attnum;
6872
6873 /*
6874 * this test is deliberately not attisdropped-aware, since if one tries to
6875 * add a column matching a dropped column name, it's gonna fail anyway.
6876 */
6877 attTuple = SearchSysCache2(ATTNAME,
6878 ObjectIdGetDatum(RelationGetRelid(rel)),
6879 PointerGetDatum(colname));
6880 if (!HeapTupleIsValid(attTuple))
6881 return true;
6882
6883 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
6884 ReleaseSysCache(attTuple);
6885
6886 /*
6887 * We throw a different error message for conflicts with system column
6888 * names, since they are normally not shown and the user might otherwise
6889 * be confused about the reason for the conflict.
6890 */
6891 if (attnum <= 0)
6892 ereport(ERROR,
6893 (errcode(ERRCODE_DUPLICATE_COLUMN),
6894 errmsg("column name \"%s\" conflicts with a system column name",
6895 colname)));
6896 else
6897 {
6898 if (if_not_exists)
6899 {
6900 ereport(NOTICE,
6901 (errcode(ERRCODE_DUPLICATE_COLUMN),
6902 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
6903 colname, RelationGetRelationName(rel))));
6904 return false;
6905 }
6906
6907 ereport(ERROR,
6908 (errcode(ERRCODE_DUPLICATE_COLUMN),
6909 errmsg("column \"%s\" of relation \"%s\" already exists",
6910 colname, RelationGetRelationName(rel))));
6911 }
6912
6913 return true;
6914 }
6915
6916 /*
6917 * Install a column's dependency on its datatype.
6918 */
6919 static void
add_column_datatype_dependency(Oid relid,int32 attnum,Oid typid)6920 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
6921 {
6922 ObjectAddress myself,
6923 referenced;
6924
6925 myself.classId = RelationRelationId;
6926 myself.objectId = relid;
6927 myself.objectSubId = attnum;
6928 referenced.classId = TypeRelationId;
6929 referenced.objectId = typid;
6930 referenced.objectSubId = 0;
6931 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
6932 }
6933
6934 /*
6935 * Install a column's dependency on its collation.
6936 */
6937 static void
add_column_collation_dependency(Oid relid,int32 attnum,Oid collid)6938 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
6939 {
6940 ObjectAddress myself,
6941 referenced;
6942
6943 /* We know the default collation is pinned, so don't bother recording it */
6944 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
6945 {
6946 myself.classId = RelationRelationId;
6947 myself.objectId = relid;
6948 myself.objectSubId = attnum;
6949 referenced.classId = CollationRelationId;
6950 referenced.objectId = collid;
6951 referenced.objectSubId = 0;
6952 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
6953 }
6954 }
6955
6956 /*
6957 * ALTER TABLE ALTER COLUMN DROP NOT NULL
6958 */
6959
6960 static void
ATPrepDropNotNull(Relation rel,bool recurse,bool recursing)6961 ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
6962 {
6963 /*
6964 * If the parent is a partitioned table, like check constraints, we do not
6965 * support removing the NOT NULL while partitions exist.
6966 */
6967 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6968 {
6969 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
6970
6971 Assert(partdesc != NULL);
6972 if (partdesc->nparts > 0 && !recurse && !recursing)
6973 ereport(ERROR,
6974 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6975 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
6976 errhint("Do not specify the ONLY keyword.")));
6977 }
6978 }
6979
6980 /*
6981 * Return the address of the modified column. If the column was already
6982 * nullable, InvalidObjectAddress is returned.
6983 */
6984 static ObjectAddress
ATExecDropNotNull(Relation rel,const char * colName,LOCKMODE lockmode)6985 ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
6986 {
6987 HeapTuple tuple;
6988 Form_pg_attribute attTup;
6989 AttrNumber attnum;
6990 Relation attr_rel;
6991 List *indexoidlist;
6992 ListCell *indexoidscan;
6993 ObjectAddress address;
6994
6995 /*
6996 * lookup the attribute
6997 */
6998 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
6999
7000 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7001 if (!HeapTupleIsValid(tuple))
7002 ereport(ERROR,
7003 (errcode(ERRCODE_UNDEFINED_COLUMN),
7004 errmsg("column \"%s\" of relation \"%s\" does not exist",
7005 colName, RelationGetRelationName(rel))));
7006 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7007 attnum = attTup->attnum;
7008
7009 /* Prevent them from altering a system attribute */
7010 if (attnum <= 0)
7011 ereport(ERROR,
7012 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7013 errmsg("cannot alter system column \"%s\"",
7014 colName)));
7015
7016 if (attTup->attidentity)
7017 ereport(ERROR,
7018 (errcode(ERRCODE_SYNTAX_ERROR),
7019 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7020 colName, RelationGetRelationName(rel))));
7021
7022 /*
7023 * Check that the attribute is not in a primary key
7024 *
7025 * Note: we'll throw error even if the pkey index is not valid.
7026 */
7027
7028 /* Loop over all indexes on the relation */
7029 indexoidlist = RelationGetIndexList(rel);
7030
7031 foreach(indexoidscan, indexoidlist)
7032 {
7033 Oid indexoid = lfirst_oid(indexoidscan);
7034 HeapTuple indexTuple;
7035 Form_pg_index indexStruct;
7036 int i;
7037
7038 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7039 if (!HeapTupleIsValid(indexTuple))
7040 elog(ERROR, "cache lookup failed for index %u", indexoid);
7041 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
7042
7043 /* If the index is not a primary key, skip the check */
7044 if (indexStruct->indisprimary)
7045 {
7046 /*
7047 * Loop over each attribute in the primary key and see if it
7048 * matches the to-be-altered attribute
7049 */
7050 for (i = 0; i < indexStruct->indnkeyatts; i++)
7051 {
7052 if (indexStruct->indkey.values[i] == attnum)
7053 ereport(ERROR,
7054 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7055 errmsg("column \"%s\" is in a primary key",
7056 colName)));
7057 }
7058 }
7059
7060 ReleaseSysCache(indexTuple);
7061 }
7062
7063 list_free(indexoidlist);
7064
7065 /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
7066 if (rel->rd_rel->relispartition)
7067 {
7068 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7069 Relation parent = table_open(parentId, AccessShareLock);
7070 TupleDesc tupDesc = RelationGetDescr(parent);
7071 AttrNumber parent_attnum;
7072
7073 parent_attnum = get_attnum(parentId, colName);
7074 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7075 ereport(ERROR,
7076 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7077 errmsg("column \"%s\" is marked NOT NULL in parent table",
7078 colName)));
7079 table_close(parent, AccessShareLock);
7080 }
7081
7082 /*
7083 * Okay, actually perform the catalog change ... if needed
7084 */
7085 if (attTup->attnotnull)
7086 {
7087 attTup->attnotnull = false;
7088
7089 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7090
7091 ObjectAddressSubSet(address, RelationRelationId,
7092 RelationGetRelid(rel), attnum);
7093 }
7094 else
7095 address = InvalidObjectAddress;
7096
7097 InvokeObjectPostAlterHook(RelationRelationId,
7098 RelationGetRelid(rel), attnum);
7099
7100 table_close(attr_rel, RowExclusiveLock);
7101
7102 return address;
7103 }
7104
7105 /*
7106 * ALTER TABLE ALTER COLUMN SET NOT NULL
7107 */
7108
7109 static void
ATPrepSetNotNull(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode,AlterTableUtilityContext * context)7110 ATPrepSetNotNull(List **wqueue, Relation rel,
7111 AlterTableCmd *cmd, bool recurse, bool recursing,
7112 LOCKMODE lockmode, AlterTableUtilityContext *context)
7113 {
7114 /*
7115 * If we're already recursing, there's nothing to do; the topmost
7116 * invocation of ATSimpleRecursion already visited all children.
7117 */
7118 if (recursing)
7119 return;
7120
7121 /*
7122 * If the target column is already marked NOT NULL, we can skip recursing
7123 * to children, because their columns should already be marked NOT NULL as
7124 * well. But there's no point in checking here unless the relation has
7125 * some children; else we can just wait till execution to check. (If it
7126 * does have children, however, this can save taking per-child locks
7127 * unnecessarily. This greatly improves concurrency in some parallel
7128 * restore scenarios.)
7129 *
7130 * Unfortunately, we can only apply this optimization to partitioned
7131 * tables, because traditional inheritance doesn't enforce that child
7132 * columns be NOT NULL when their parent is. (That's a bug that should
7133 * get fixed someday.)
7134 */
7135 if (rel->rd_rel->relhassubclass &&
7136 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7137 {
7138 HeapTuple tuple;
7139 bool attnotnull;
7140
7141 tuple = SearchSysCacheAttName(RelationGetRelid(rel), cmd->name);
7142
7143 /* Might as well throw the error now, if name is bad */
7144 if (!HeapTupleIsValid(tuple))
7145 ereport(ERROR,
7146 (errcode(ERRCODE_UNDEFINED_COLUMN),
7147 errmsg("column \"%s\" of relation \"%s\" does not exist",
7148 cmd->name, RelationGetRelationName(rel))));
7149
7150 attnotnull = ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull;
7151 ReleaseSysCache(tuple);
7152 if (attnotnull)
7153 return;
7154 }
7155
7156 /*
7157 * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
7158 * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use
7159 * normal recursion logic.
7160 */
7161 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
7162 !recurse)
7163 {
7164 AlterTableCmd *newcmd = makeNode(AlterTableCmd);
7165
7166 newcmd->subtype = AT_CheckNotNull;
7167 newcmd->name = pstrdup(cmd->name);
7168 ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context);
7169 }
7170 else
7171 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
7172 }
7173
7174 /*
7175 * Return the address of the modified column. If the column was already NOT
7176 * NULL, InvalidObjectAddress is returned.
7177 */
7178 static ObjectAddress
ATExecSetNotNull(AlteredTableInfo * tab,Relation rel,const char * colName,LOCKMODE lockmode)7179 ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
7180 const char *colName, LOCKMODE lockmode)
7181 {
7182 HeapTuple tuple;
7183 AttrNumber attnum;
7184 Relation attr_rel;
7185 ObjectAddress address;
7186
7187 /*
7188 * lookup the attribute
7189 */
7190 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7191
7192 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7193
7194 if (!HeapTupleIsValid(tuple))
7195 ereport(ERROR,
7196 (errcode(ERRCODE_UNDEFINED_COLUMN),
7197 errmsg("column \"%s\" of relation \"%s\" does not exist",
7198 colName, RelationGetRelationName(rel))));
7199
7200 attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
7201
7202 /* Prevent them from altering a system attribute */
7203 if (attnum <= 0)
7204 ereport(ERROR,
7205 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7206 errmsg("cannot alter system column \"%s\"",
7207 colName)));
7208
7209 /*
7210 * Okay, actually perform the catalog change ... if needed
7211 */
7212 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7213 {
7214 ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = true;
7215
7216 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7217
7218 /*
7219 * Ordinarily phase 3 must ensure that no NULLs exist in columns that
7220 * are set NOT NULL; however, if we can find a constraint which proves
7221 * this then we can skip that. We needn't bother looking if we've
7222 * already found that we must verify some other NOT NULL constraint.
7223 */
7224 if (!tab->verify_new_notnull &&
7225 !NotNullImpliedByRelConstraints(rel, (Form_pg_attribute) GETSTRUCT(tuple)))
7226 {
7227 /* Tell Phase 3 it needs to test the constraint */
7228 tab->verify_new_notnull = true;
7229 }
7230
7231 ObjectAddressSubSet(address, RelationRelationId,
7232 RelationGetRelid(rel), attnum);
7233 }
7234 else
7235 address = InvalidObjectAddress;
7236
7237 InvokeObjectPostAlterHook(RelationRelationId,
7238 RelationGetRelid(rel), attnum);
7239
7240 table_close(attr_rel, RowExclusiveLock);
7241
7242 return address;
7243 }
7244
7245 /*
7246 * ALTER TABLE ALTER COLUMN CHECK NOT NULL
7247 *
7248 * This doesn't exist in the grammar, but we generate AT_CheckNotNull
7249 * commands against the partitions of a partitioned table if the user
7250 * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
7251 * or tries to create a primary key on it (which internally creates
7252 * AT_SetNotNull on the partitioned table). Such a command doesn't
7253 * allow us to actually modify any partition, but we want to let it
7254 * go through if the partitions are already properly marked.
7255 *
7256 * In future, this might need to adjust the child table's state, likely
7257 * by incrementing an inheritance count for the attnotnull constraint.
7258 * For now we need only check for the presence of the flag.
7259 */
7260 static void
ATExecCheckNotNull(AlteredTableInfo * tab,Relation rel,const char * colName,LOCKMODE lockmode)7261 ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
7262 const char *colName, LOCKMODE lockmode)
7263 {
7264 HeapTuple tuple;
7265
7266 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7267
7268 if (!HeapTupleIsValid(tuple))
7269 ereport(ERROR,
7270 (errcode(ERRCODE_UNDEFINED_COLUMN),
7271 errmsg("column \"%s\" of relation \"%s\" does not exist",
7272 colName, RelationGetRelationName(rel))));
7273
7274 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7275 ereport(ERROR,
7276 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7277 errmsg("constraint must be added to child tables too"),
7278 errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
7279 colName, RelationGetRelationName(rel)),
7280 errhint("Do not specify the ONLY keyword.")));
7281
7282 ReleaseSysCache(tuple);
7283 }
7284
7285 /*
7286 * NotNullImpliedByRelConstraints
7287 * Does rel's existing constraints imply NOT NULL for the given attribute?
7288 */
7289 static bool
NotNullImpliedByRelConstraints(Relation rel,Form_pg_attribute attr)7290 NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7291 {
7292 NullTest *nnulltest = makeNode(NullTest);
7293
7294 nnulltest->arg = (Expr *) makeVar(1,
7295 attr->attnum,
7296 attr->atttypid,
7297 attr->atttypmod,
7298 attr->attcollation,
7299 0);
7300 nnulltest->nulltesttype = IS_NOT_NULL;
7301
7302 /*
7303 * argisrow = false is correct even for a composite column, because
7304 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7305 * case, just IS DISTINCT FROM NULL.
7306 */
7307 nnulltest->argisrow = false;
7308 nnulltest->location = -1;
7309
7310 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7311 {
7312 ereport(DEBUG1,
7313 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7314 RelationGetRelationName(rel), NameStr(attr->attname))));
7315 return true;
7316 }
7317
7318 return false;
7319 }
7320
7321 /*
7322 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7323 *
7324 * Return the address of the affected column.
7325 */
7326 static ObjectAddress
ATExecColumnDefault(Relation rel,const char * colName,Node * newDefault,LOCKMODE lockmode)7327 ATExecColumnDefault(Relation rel, const char *colName,
7328 Node *newDefault, LOCKMODE lockmode)
7329 {
7330 TupleDesc tupdesc = RelationGetDescr(rel);
7331 AttrNumber attnum;
7332 ObjectAddress address;
7333
7334 /*
7335 * get the number of the attribute
7336 */
7337 attnum = get_attnum(RelationGetRelid(rel), colName);
7338 if (attnum == InvalidAttrNumber)
7339 ereport(ERROR,
7340 (errcode(ERRCODE_UNDEFINED_COLUMN),
7341 errmsg("column \"%s\" of relation \"%s\" does not exist",
7342 colName, RelationGetRelationName(rel))));
7343
7344 /* Prevent them from altering a system attribute */
7345 if (attnum <= 0)
7346 ereport(ERROR,
7347 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7348 errmsg("cannot alter system column \"%s\"",
7349 colName)));
7350
7351 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7352 ereport(ERROR,
7353 (errcode(ERRCODE_SYNTAX_ERROR),
7354 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7355 colName, RelationGetRelationName(rel)),
7356 newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
7357
7358 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7359 ereport(ERROR,
7360 (errcode(ERRCODE_SYNTAX_ERROR),
7361 errmsg("column \"%s\" of relation \"%s\" is a generated column",
7362 colName, RelationGetRelationName(rel)),
7363 newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
7364 errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.")));
7365
7366 /*
7367 * Remove any old default for the column. We use RESTRICT here for
7368 * safety, but at present we do not expect anything to depend on the
7369 * default.
7370 *
7371 * We treat removing the existing default as an internal operation when it
7372 * is preparatory to adding a new default, but as a user-initiated
7373 * operation when the user asked for a drop.
7374 */
7375 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7376 newDefault == NULL ? false : true);
7377
7378 if (newDefault)
7379 {
7380 /* SET DEFAULT */
7381 RawColumnDefault *rawEnt;
7382
7383 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7384 rawEnt->attnum = attnum;
7385 rawEnt->raw_default = newDefault;
7386 rawEnt->missingMode = false;
7387 rawEnt->generated = '\0';
7388
7389 /*
7390 * This function is intended for CREATE TABLE, so it processes a
7391 * _list_ of defaults, but we just do one.
7392 */
7393 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7394 false, true, false, NULL);
7395 }
7396
7397 ObjectAddressSubSet(address, RelationRelationId,
7398 RelationGetRelid(rel), attnum);
7399 return address;
7400 }
7401
7402 /*
7403 * Add a pre-cooked default expression.
7404 *
7405 * Return the address of the affected column.
7406 */
7407 static ObjectAddress
ATExecCookedColumnDefault(Relation rel,AttrNumber attnum,Node * newDefault)7408 ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
7409 Node *newDefault)
7410 {
7411 ObjectAddress address;
7412
7413 /* We assume no checking is required */
7414
7415 /*
7416 * Remove any old default for the column. We use RESTRICT here for
7417 * safety, but at present we do not expect anything to depend on the
7418 * default. (In ordinary cases, there could not be a default in place
7419 * anyway, but it's possible when combining LIKE with inheritance.)
7420 */
7421 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7422 true);
7423
7424 (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
7425
7426 ObjectAddressSubSet(address, RelationRelationId,
7427 RelationGetRelid(rel), attnum);
7428 return address;
7429 }
7430
7431 /*
7432 * ALTER TABLE ALTER COLUMN ADD IDENTITY
7433 *
7434 * Return the address of the affected column.
7435 */
7436 static ObjectAddress
ATExecAddIdentity(Relation rel,const char * colName,Node * def,LOCKMODE lockmode)7437 ATExecAddIdentity(Relation rel, const char *colName,
7438 Node *def, LOCKMODE lockmode)
7439 {
7440 Relation attrelation;
7441 HeapTuple tuple;
7442 Form_pg_attribute attTup;
7443 AttrNumber attnum;
7444 ObjectAddress address;
7445 ColumnDef *cdef = castNode(ColumnDef, def);
7446
7447 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7448
7449 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7450 if (!HeapTupleIsValid(tuple))
7451 ereport(ERROR,
7452 (errcode(ERRCODE_UNDEFINED_COLUMN),
7453 errmsg("column \"%s\" of relation \"%s\" does not exist",
7454 colName, RelationGetRelationName(rel))));
7455 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7456 attnum = attTup->attnum;
7457
7458 /* Can't alter a system attribute */
7459 if (attnum <= 0)
7460 ereport(ERROR,
7461 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7462 errmsg("cannot alter system column \"%s\"",
7463 colName)));
7464
7465 /*
7466 * Creating a column as identity implies NOT NULL, so adding the identity
7467 * to an existing column that is not NOT NULL would create a state that
7468 * cannot be reproduced without contortions.
7469 */
7470 if (!attTup->attnotnull)
7471 ereport(ERROR,
7472 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7473 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
7474 colName, RelationGetRelationName(rel))));
7475
7476 if (attTup->attidentity)
7477 ereport(ERROR,
7478 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7479 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
7480 colName, RelationGetRelationName(rel))));
7481
7482 if (attTup->atthasdef)
7483 ereport(ERROR,
7484 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7485 errmsg("column \"%s\" of relation \"%s\" already has a default value",
7486 colName, RelationGetRelationName(rel))));
7487
7488 attTup->attidentity = cdef->identity;
7489 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7490
7491 InvokeObjectPostAlterHook(RelationRelationId,
7492 RelationGetRelid(rel),
7493 attTup->attnum);
7494 ObjectAddressSubSet(address, RelationRelationId,
7495 RelationGetRelid(rel), attnum);
7496 heap_freetuple(tuple);
7497
7498 table_close(attrelation, RowExclusiveLock);
7499
7500 return address;
7501 }
7502
7503 /*
7504 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
7505 *
7506 * Return the address of the affected column.
7507 */
7508 static ObjectAddress
ATExecSetIdentity(Relation rel,const char * colName,Node * def,LOCKMODE lockmode)7509 ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
7510 {
7511 ListCell *option;
7512 DefElem *generatedEl = NULL;
7513 HeapTuple tuple;
7514 Form_pg_attribute attTup;
7515 AttrNumber attnum;
7516 Relation attrelation;
7517 ObjectAddress address;
7518
7519 foreach(option, castNode(List, def))
7520 {
7521 DefElem *defel = lfirst_node(DefElem, option);
7522
7523 if (strcmp(defel->defname, "generated") == 0)
7524 {
7525 if (generatedEl)
7526 ereport(ERROR,
7527 (errcode(ERRCODE_SYNTAX_ERROR),
7528 errmsg("conflicting or redundant options")));
7529 generatedEl = defel;
7530 }
7531 else
7532 elog(ERROR, "option \"%s\" not recognized",
7533 defel->defname);
7534 }
7535
7536 /*
7537 * Even if there is nothing to change here, we run all the checks. There
7538 * will be a subsequent ALTER SEQUENCE that relies on everything being
7539 * there.
7540 */
7541
7542 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7543 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7544 if (!HeapTupleIsValid(tuple))
7545 ereport(ERROR,
7546 (errcode(ERRCODE_UNDEFINED_COLUMN),
7547 errmsg("column \"%s\" of relation \"%s\" does not exist",
7548 colName, RelationGetRelationName(rel))));
7549
7550 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7551 attnum = attTup->attnum;
7552
7553 if (attnum <= 0)
7554 ereport(ERROR,
7555 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7556 errmsg("cannot alter system column \"%s\"",
7557 colName)));
7558
7559 if (!attTup->attidentity)
7560 ereport(ERROR,
7561 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7562 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
7563 colName, RelationGetRelationName(rel))));
7564
7565 if (generatedEl)
7566 {
7567 attTup->attidentity = defGetInt32(generatedEl);
7568 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7569
7570 InvokeObjectPostAlterHook(RelationRelationId,
7571 RelationGetRelid(rel),
7572 attTup->attnum);
7573 ObjectAddressSubSet(address, RelationRelationId,
7574 RelationGetRelid(rel), attnum);
7575 }
7576 else
7577 address = InvalidObjectAddress;
7578
7579 heap_freetuple(tuple);
7580 table_close(attrelation, RowExclusiveLock);
7581
7582 return address;
7583 }
7584
7585 /*
7586 * ALTER TABLE ALTER COLUMN DROP IDENTITY
7587 *
7588 * Return the address of the affected column.
7589 */
7590 static ObjectAddress
ATExecDropIdentity(Relation rel,const char * colName,bool missing_ok,LOCKMODE lockmode)7591 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
7592 {
7593 HeapTuple tuple;
7594 Form_pg_attribute attTup;
7595 AttrNumber attnum;
7596 Relation attrelation;
7597 ObjectAddress address;
7598 Oid seqid;
7599 ObjectAddress seqaddress;
7600
7601 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7602 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7603 if (!HeapTupleIsValid(tuple))
7604 ereport(ERROR,
7605 (errcode(ERRCODE_UNDEFINED_COLUMN),
7606 errmsg("column \"%s\" of relation \"%s\" does not exist",
7607 colName, RelationGetRelationName(rel))));
7608
7609 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7610 attnum = attTup->attnum;
7611
7612 if (attnum <= 0)
7613 ereport(ERROR,
7614 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7615 errmsg("cannot alter system column \"%s\"",
7616 colName)));
7617
7618 if (!attTup->attidentity)
7619 {
7620 if (!missing_ok)
7621 ereport(ERROR,
7622 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7623 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
7624 colName, RelationGetRelationName(rel))));
7625 else
7626 {
7627 ereport(NOTICE,
7628 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
7629 colName, RelationGetRelationName(rel))));
7630 heap_freetuple(tuple);
7631 table_close(attrelation, RowExclusiveLock);
7632 return InvalidObjectAddress;
7633 }
7634 }
7635
7636 attTup->attidentity = '\0';
7637 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7638
7639 InvokeObjectPostAlterHook(RelationRelationId,
7640 RelationGetRelid(rel),
7641 attTup->attnum);
7642 ObjectAddressSubSet(address, RelationRelationId,
7643 RelationGetRelid(rel), attnum);
7644 heap_freetuple(tuple);
7645
7646 table_close(attrelation, RowExclusiveLock);
7647
7648 /* drop the internal sequence */
7649 seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
7650 deleteDependencyRecordsForClass(RelationRelationId, seqid,
7651 RelationRelationId, DEPENDENCY_INTERNAL);
7652 CommandCounterIncrement();
7653 seqaddress.classId = RelationRelationId;
7654 seqaddress.objectId = seqid;
7655 seqaddress.objectSubId = 0;
7656 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
7657
7658 return address;
7659 }
7660
7661 /*
7662 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
7663 */
7664 static void
ATPrepDropExpression(Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode)7665 ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
7666 {
7667 /*
7668 * Reject ONLY if there are child tables. We could implement this, but it
7669 * is a bit complicated. GENERATED clauses must be attached to the column
7670 * definition and cannot be added later like DEFAULT, so if a child table
7671 * has a generation expression that the parent does not have, the child
7672 * column will necessarily be an attlocal column. So to implement ONLY
7673 * here, we'd need extra code to update attislocal of the direct child
7674 * tables, somewhat similar to how DROP COLUMN does it, so that the
7675 * resulting state can be properly dumped and restored.
7676 */
7677 if (!recurse &&
7678 find_inheritance_children(RelationGetRelid(rel), lockmode))
7679 ereport(ERROR,
7680 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7681 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
7682
7683 /*
7684 * Cannot drop generation expression from inherited columns.
7685 */
7686 if (!recursing)
7687 {
7688 HeapTuple tuple;
7689 Form_pg_attribute attTup;
7690
7691 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
7692 if (!HeapTupleIsValid(tuple))
7693 ereport(ERROR,
7694 (errcode(ERRCODE_UNDEFINED_COLUMN),
7695 errmsg("column \"%s\" of relation \"%s\" does not exist",
7696 cmd->name, RelationGetRelationName(rel))));
7697
7698 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7699
7700 if (attTup->attinhcount > 0)
7701 ereport(ERROR,
7702 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7703 errmsg("cannot drop generation expression from inherited column")));
7704 }
7705 }
7706
7707 /*
7708 * Return the address of the affected column.
7709 */
7710 static ObjectAddress
ATExecDropExpression(Relation rel,const char * colName,bool missing_ok,LOCKMODE lockmode)7711 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
7712 {
7713 HeapTuple tuple;
7714 Form_pg_attribute attTup;
7715 AttrNumber attnum;
7716 Relation attrelation;
7717 ObjectAddress address;
7718
7719 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7720 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7721 if (!HeapTupleIsValid(tuple))
7722 ereport(ERROR,
7723 (errcode(ERRCODE_UNDEFINED_COLUMN),
7724 errmsg("column \"%s\" of relation \"%s\" does not exist",
7725 colName, RelationGetRelationName(rel))));
7726
7727 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7728 attnum = attTup->attnum;
7729
7730 if (attnum <= 0)
7731 ereport(ERROR,
7732 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7733 errmsg("cannot alter system column \"%s\"",
7734 colName)));
7735
7736 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
7737 {
7738 if (!missing_ok)
7739 ereport(ERROR,
7740 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7741 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
7742 colName, RelationGetRelationName(rel))));
7743 else
7744 {
7745 ereport(NOTICE,
7746 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
7747 colName, RelationGetRelationName(rel))));
7748 heap_freetuple(tuple);
7749 table_close(attrelation, RowExclusiveLock);
7750 return InvalidObjectAddress;
7751 }
7752 }
7753
7754 attTup->attgenerated = '\0';
7755 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7756
7757 InvokeObjectPostAlterHook(RelationRelationId,
7758 RelationGetRelid(rel),
7759 attTup->attnum);
7760 ObjectAddressSubSet(address, RelationRelationId,
7761 RelationGetRelid(rel), attnum);
7762 heap_freetuple(tuple);
7763
7764 table_close(attrelation, RowExclusiveLock);
7765
7766 CommandCounterIncrement();
7767
7768 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false, false);
7769
7770 /*
7771 * Remove all dependencies of this (formerly generated) column on other
7772 * columns in the same table. (See StoreAttrDefault() for which
7773 * dependencies are created.) We don't expect there to be dependencies
7774 * between columns of the same table for other reasons, so it's okay to
7775 * remove all of them.
7776 */
7777 {
7778 Relation depRel;
7779 ScanKeyData key[3];
7780 SysScanDesc scan;
7781 HeapTuple tup;
7782
7783 depRel = table_open(DependRelationId, RowExclusiveLock);
7784
7785 ScanKeyInit(&key[0],
7786 Anum_pg_depend_classid,
7787 BTEqualStrategyNumber, F_OIDEQ,
7788 ObjectIdGetDatum(RelationRelationId));
7789 ScanKeyInit(&key[1],
7790 Anum_pg_depend_objid,
7791 BTEqualStrategyNumber, F_OIDEQ,
7792 ObjectIdGetDatum(RelationGetRelid(rel)));
7793 ScanKeyInit(&key[2],
7794 Anum_pg_depend_objsubid,
7795 BTEqualStrategyNumber, F_INT4EQ,
7796 Int32GetDatum(attnum));
7797
7798 scan = systable_beginscan(depRel, DependDependerIndexId, true,
7799 NULL, 3, key);
7800
7801 while (HeapTupleIsValid(tup = systable_getnext(scan)))
7802 {
7803 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
7804
7805 if (depform->refclassid == RelationRelationId &&
7806 depform->refobjid == RelationGetRelid(rel) &&
7807 depform->refobjsubid != 0 &&
7808 depform->deptype == DEPENDENCY_AUTO)
7809 {
7810 CatalogTupleDelete(depRel, &tup->t_self);
7811 }
7812 }
7813
7814 systable_endscan(scan);
7815
7816 table_close(depRel, RowExclusiveLock);
7817 }
7818
7819 return address;
7820 }
7821
7822 /*
7823 * ALTER TABLE ALTER COLUMN SET STATISTICS
7824 *
7825 * Return value is the address of the modified column
7826 */
7827 static ObjectAddress
ATExecSetStatistics(Relation rel,const char * colName,int16 colNum,Node * newValue,LOCKMODE lockmode)7828 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
7829 {
7830 int newtarget;
7831 Relation attrelation;
7832 HeapTuple tuple;
7833 Form_pg_attribute attrtuple;
7834 AttrNumber attnum;
7835 ObjectAddress address;
7836
7837 /*
7838 * We allow referencing columns by numbers only for indexes, since table
7839 * column numbers could contain gaps if columns are later dropped.
7840 */
7841 if (rel->rd_rel->relkind != RELKIND_INDEX &&
7842 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
7843 !colName)
7844 ereport(ERROR,
7845 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7846 errmsg("cannot refer to non-index column by number")));
7847
7848 Assert(IsA(newValue, Integer));
7849 newtarget = intVal(newValue);
7850
7851 /*
7852 * Limit target to a sane range
7853 */
7854 if (newtarget < -1)
7855 {
7856 ereport(ERROR,
7857 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
7858 errmsg("statistics target %d is too low",
7859 newtarget)));
7860 }
7861 else if (newtarget > 10000)
7862 {
7863 newtarget = 10000;
7864 ereport(WARNING,
7865 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
7866 errmsg("lowering statistics target to %d",
7867 newtarget)));
7868 }
7869
7870 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7871
7872 if (colName)
7873 {
7874 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7875
7876 if (!HeapTupleIsValid(tuple))
7877 ereport(ERROR,
7878 (errcode(ERRCODE_UNDEFINED_COLUMN),
7879 errmsg("column \"%s\" of relation \"%s\" does not exist",
7880 colName, RelationGetRelationName(rel))));
7881 }
7882 else
7883 {
7884 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
7885
7886 if (!HeapTupleIsValid(tuple))
7887 ereport(ERROR,
7888 (errcode(ERRCODE_UNDEFINED_COLUMN),
7889 errmsg("column number %d of relation \"%s\" does not exist",
7890 colNum, RelationGetRelationName(rel))));
7891 }
7892
7893 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
7894
7895 attnum = attrtuple->attnum;
7896 if (attnum <= 0)
7897 ereport(ERROR,
7898 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7899 errmsg("cannot alter system column \"%s\"",
7900 colName)));
7901
7902 if (rel->rd_rel->relkind == RELKIND_INDEX ||
7903 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
7904 {
7905 if (attnum > rel->rd_index->indnkeyatts)
7906 ereport(ERROR,
7907 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7908 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
7909 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
7910 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
7911 ereport(ERROR,
7912 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7913 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
7914 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
7915 errhint("Alter statistics on table column instead.")));
7916 }
7917
7918 attrtuple->attstattarget = newtarget;
7919
7920 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7921
7922 InvokeObjectPostAlterHook(RelationRelationId,
7923 RelationGetRelid(rel),
7924 attrtuple->attnum);
7925 ObjectAddressSubSet(address, RelationRelationId,
7926 RelationGetRelid(rel), attnum);
7927 heap_freetuple(tuple);
7928
7929 table_close(attrelation, RowExclusiveLock);
7930
7931 return address;
7932 }
7933
7934 /*
7935 * Return value is the address of the modified column
7936 */
7937 static ObjectAddress
ATExecSetOptions(Relation rel,const char * colName,Node * options,bool isReset,LOCKMODE lockmode)7938 ATExecSetOptions(Relation rel, const char *colName, Node *options,
7939 bool isReset, LOCKMODE lockmode)
7940 {
7941 Relation attrelation;
7942 HeapTuple tuple,
7943 newtuple;
7944 Form_pg_attribute attrtuple;
7945 AttrNumber attnum;
7946 Datum datum,
7947 newOptions;
7948 bool isnull;
7949 ObjectAddress address;
7950 Datum repl_val[Natts_pg_attribute];
7951 bool repl_null[Natts_pg_attribute];
7952 bool repl_repl[Natts_pg_attribute];
7953
7954 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7955
7956 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7957
7958 if (!HeapTupleIsValid(tuple))
7959 ereport(ERROR,
7960 (errcode(ERRCODE_UNDEFINED_COLUMN),
7961 errmsg("column \"%s\" of relation \"%s\" does not exist",
7962 colName, RelationGetRelationName(rel))));
7963 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
7964
7965 attnum = attrtuple->attnum;
7966 if (attnum <= 0)
7967 ereport(ERROR,
7968 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7969 errmsg("cannot alter system column \"%s\"",
7970 colName)));
7971
7972 /* Generate new proposed attoptions (text array) */
7973 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
7974 &isnull);
7975 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
7976 castNode(List, options), NULL, NULL,
7977 false, isReset);
7978 /* Validate new options */
7979 (void) attribute_reloptions(newOptions, true);
7980
7981 /* Build new tuple. */
7982 memset(repl_null, false, sizeof(repl_null));
7983 memset(repl_repl, false, sizeof(repl_repl));
7984 if (newOptions != (Datum) 0)
7985 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
7986 else
7987 repl_null[Anum_pg_attribute_attoptions - 1] = true;
7988 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
7989 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
7990 repl_val, repl_null, repl_repl);
7991
7992 /* Update system catalog. */
7993 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
7994
7995 InvokeObjectPostAlterHook(RelationRelationId,
7996 RelationGetRelid(rel),
7997 attrtuple->attnum);
7998 ObjectAddressSubSet(address, RelationRelationId,
7999 RelationGetRelid(rel), attnum);
8000
8001 heap_freetuple(newtuple);
8002
8003 ReleaseSysCache(tuple);
8004
8005 table_close(attrelation, RowExclusiveLock);
8006
8007 return address;
8008 }
8009
8010 /*
8011 * Helper function for ATExecSetStorage and ATExecSetCompression
8012 *
8013 * Set the attstorage and/or attcompression fields for index columns
8014 * associated with the specified table column.
8015 */
8016 static void
SetIndexStorageProperties(Relation rel,Relation attrelation,AttrNumber attnum,bool setstorage,char newstorage,bool setcompression,char newcompression,LOCKMODE lockmode)8017 SetIndexStorageProperties(Relation rel, Relation attrelation,
8018 AttrNumber attnum,
8019 bool setstorage, char newstorage,
8020 bool setcompression, char newcompression,
8021 LOCKMODE lockmode)
8022 {
8023 ListCell *lc;
8024
8025 foreach(lc, RelationGetIndexList(rel))
8026 {
8027 Oid indexoid = lfirst_oid(lc);
8028 Relation indrel;
8029 AttrNumber indattnum = 0;
8030 HeapTuple tuple;
8031
8032 indrel = index_open(indexoid, lockmode);
8033
8034 for (int i = 0; i < indrel->rd_index->indnatts; i++)
8035 {
8036 if (indrel->rd_index->indkey.values[i] == attnum)
8037 {
8038 indattnum = i + 1;
8039 break;
8040 }
8041 }
8042
8043 if (indattnum == 0)
8044 {
8045 index_close(indrel, lockmode);
8046 continue;
8047 }
8048
8049 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8050
8051 if (HeapTupleIsValid(tuple))
8052 {
8053 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8054
8055 if (setstorage)
8056 attrtuple->attstorage = newstorage;
8057
8058 if (setcompression)
8059 attrtuple->attcompression = newcompression;
8060
8061 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8062
8063 InvokeObjectPostAlterHook(RelationRelationId,
8064 RelationGetRelid(rel),
8065 attrtuple->attnum);
8066
8067 heap_freetuple(tuple);
8068 }
8069
8070 index_close(indrel, lockmode);
8071 }
8072 }
8073
8074 /*
8075 * ALTER TABLE ALTER COLUMN SET STORAGE
8076 *
8077 * Return value is the address of the modified column
8078 */
8079 static ObjectAddress
ATExecSetStorage(Relation rel,const char * colName,Node * newValue,LOCKMODE lockmode)8080 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8081 {
8082 char *storagemode;
8083 char newstorage;
8084 Relation attrelation;
8085 HeapTuple tuple;
8086 Form_pg_attribute attrtuple;
8087 AttrNumber attnum;
8088 ObjectAddress address;
8089
8090 Assert(IsA(newValue, String));
8091 storagemode = strVal(newValue);
8092
8093 if (pg_strcasecmp(storagemode, "plain") == 0)
8094 newstorage = TYPSTORAGE_PLAIN;
8095 else if (pg_strcasecmp(storagemode, "external") == 0)
8096 newstorage = TYPSTORAGE_EXTERNAL;
8097 else if (pg_strcasecmp(storagemode, "extended") == 0)
8098 newstorage = TYPSTORAGE_EXTENDED;
8099 else if (pg_strcasecmp(storagemode, "main") == 0)
8100 newstorage = TYPSTORAGE_MAIN;
8101 else
8102 {
8103 ereport(ERROR,
8104 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8105 errmsg("invalid storage type \"%s\"",
8106 storagemode)));
8107 newstorage = 0; /* keep compiler quiet */
8108 }
8109
8110 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8111
8112 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8113
8114 if (!HeapTupleIsValid(tuple))
8115 ereport(ERROR,
8116 (errcode(ERRCODE_UNDEFINED_COLUMN),
8117 errmsg("column \"%s\" of relation \"%s\" does not exist",
8118 colName, RelationGetRelationName(rel))));
8119 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8120
8121 attnum = attrtuple->attnum;
8122 if (attnum <= 0)
8123 ereport(ERROR,
8124 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8125 errmsg("cannot alter system column \"%s\"",
8126 colName)));
8127
8128 /*
8129 * safety check: do not allow toasted storage modes unless column datatype
8130 * is TOAST-aware.
8131 */
8132 if (newstorage == TYPSTORAGE_PLAIN || TypeIsToastable(attrtuple->atttypid))
8133 attrtuple->attstorage = newstorage;
8134 else
8135 ereport(ERROR,
8136 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8137 errmsg("column data type %s can only have storage PLAIN",
8138 format_type_be(attrtuple->atttypid))));
8139
8140 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8141
8142 InvokeObjectPostAlterHook(RelationRelationId,
8143 RelationGetRelid(rel),
8144 attrtuple->attnum);
8145
8146 heap_freetuple(tuple);
8147
8148 /*
8149 * Apply the change to indexes as well (only for simple index columns,
8150 * matching behavior of index.c ConstructTupleDescriptor()).
8151 */
8152 SetIndexStorageProperties(rel, attrelation, attnum,
8153 true, newstorage,
8154 false, 0,
8155 lockmode);
8156
8157 table_close(attrelation, RowExclusiveLock);
8158
8159 ObjectAddressSubSet(address, RelationRelationId,
8160 RelationGetRelid(rel), attnum);
8161 return address;
8162 }
8163
8164
8165 /*
8166 * ALTER TABLE DROP COLUMN
8167 *
8168 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8169 * because we have to decide at runtime whether to recurse or not depending
8170 * on whether attinhcount goes to zero or not. (We can't check this in a
8171 * static pre-pass because it won't handle multiple inheritance situations
8172 * correctly.)
8173 */
8174 static void
ATPrepDropColumn(List ** wqueue,Relation rel,bool recurse,bool recursing,AlterTableCmd * cmd,LOCKMODE lockmode,AlterTableUtilityContext * context)8175 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8176 AlterTableCmd *cmd, LOCKMODE lockmode,
8177 AlterTableUtilityContext *context)
8178 {
8179 if (rel->rd_rel->reloftype && !recursing)
8180 ereport(ERROR,
8181 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8182 errmsg("cannot drop column from typed table")));
8183
8184 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
8185 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8186
8187 if (recurse)
8188 cmd->subtype = AT_DropColumnRecurse;
8189 }
8190
8191 /*
8192 * Drops column 'colName' from relation 'rel' and returns the address of the
8193 * dropped column. The column is also dropped (or marked as no longer
8194 * inherited from relation) from the relation's inheritance children, if any.
8195 *
8196 * In the recursive invocations for inheritance child relations, instead of
8197 * dropping the column directly (if to be dropped at all), its object address
8198 * is added to 'addrs', which must be non-NULL in such invocations. All
8199 * columns are dropped at the same time after all the children have been
8200 * checked recursively.
8201 */
8202 static ObjectAddress
ATExecDropColumn(List ** wqueue,Relation rel,const char * colName,DropBehavior behavior,bool recurse,bool recursing,bool missing_ok,LOCKMODE lockmode,ObjectAddresses * addrs)8203 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
8204 DropBehavior behavior,
8205 bool recurse, bool recursing,
8206 bool missing_ok, LOCKMODE lockmode,
8207 ObjectAddresses *addrs)
8208 {
8209 HeapTuple tuple;
8210 Form_pg_attribute targetatt;
8211 AttrNumber attnum;
8212 List *children;
8213 ObjectAddress object;
8214 bool is_expr;
8215
8216 /* At top level, permission check was done in ATPrepCmd, else do it */
8217 if (recursing)
8218 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8219
8220 /* Initialize addrs on the first invocation */
8221 Assert(!recursing || addrs != NULL);
8222 if (!recursing)
8223 addrs = new_object_addresses();
8224
8225 /*
8226 * get the number of the attribute
8227 */
8228 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8229 if (!HeapTupleIsValid(tuple))
8230 {
8231 if (!missing_ok)
8232 {
8233 ereport(ERROR,
8234 (errcode(ERRCODE_UNDEFINED_COLUMN),
8235 errmsg("column \"%s\" of relation \"%s\" does not exist",
8236 colName, RelationGetRelationName(rel))));
8237 }
8238 else
8239 {
8240 ereport(NOTICE,
8241 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8242 colName, RelationGetRelationName(rel))));
8243 return InvalidObjectAddress;
8244 }
8245 }
8246 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
8247
8248 attnum = targetatt->attnum;
8249
8250 /* Can't drop a system attribute */
8251 if (attnum <= 0)
8252 ereport(ERROR,
8253 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8254 errmsg("cannot drop system column \"%s\"",
8255 colName)));
8256
8257 /*
8258 * Don't drop inherited columns, unless recursing (presumably from a drop
8259 * of the parent column)
8260 */
8261 if (targetatt->attinhcount > 0 && !recursing)
8262 ereport(ERROR,
8263 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8264 errmsg("cannot drop inherited column \"%s\"",
8265 colName)));
8266
8267 /*
8268 * Don't drop columns used in the partition key, either. (If we let this
8269 * go through, the key column's dependencies would cause a cascaded drop
8270 * of the whole table, which is surely not what the user expected.)
8271 */
8272 if (has_partition_attrs(rel,
8273 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
8274 &is_expr))
8275 ereport(ERROR,
8276 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8277 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
8278 colName, RelationGetRelationName(rel))));
8279
8280 ReleaseSysCache(tuple);
8281
8282 /*
8283 * Propagate to children as appropriate. Unlike most other ALTER
8284 * routines, we have to do this one level of recursion at a time; we can't
8285 * use find_all_inheritors to do it in one pass.
8286 */
8287 children =
8288 find_inheritance_children(RelationGetRelid(rel), lockmode);
8289
8290 if (children)
8291 {
8292 Relation attr_rel;
8293 ListCell *child;
8294
8295 /*
8296 * In case of a partitioned table, the column must be dropped from the
8297 * partitions as well.
8298 */
8299 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
8300 ereport(ERROR,
8301 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8302 errmsg("cannot drop column from only the partitioned table when partitions exist"),
8303 errhint("Do not specify the ONLY keyword.")));
8304
8305 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
8306 foreach(child, children)
8307 {
8308 Oid childrelid = lfirst_oid(child);
8309 Relation childrel;
8310 Form_pg_attribute childatt;
8311
8312 /* find_inheritance_children already got lock */
8313 childrel = table_open(childrelid, NoLock);
8314 CheckTableNotInUse(childrel, "ALTER TABLE");
8315
8316 tuple = SearchSysCacheCopyAttName(childrelid, colName);
8317 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
8318 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
8319 colName, childrelid);
8320 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
8321
8322 if (childatt->attinhcount <= 0) /* shouldn't happen */
8323 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
8324 childrelid, colName);
8325
8326 if (recurse)
8327 {
8328 /*
8329 * If the child column has other definition sources, just
8330 * decrement its inheritance count; if not, recurse to delete
8331 * it.
8332 */
8333 if (childatt->attinhcount == 1 && !childatt->attislocal)
8334 {
8335 /* Time to delete this child column, too */
8336 ATExecDropColumn(wqueue, childrel, colName,
8337 behavior, true, true,
8338 false, lockmode, addrs);
8339 }
8340 else
8341 {
8342 /* Child column must survive my deletion */
8343 childatt->attinhcount--;
8344
8345 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8346
8347 /* Make update visible */
8348 CommandCounterIncrement();
8349 }
8350 }
8351 else
8352 {
8353 /*
8354 * If we were told to drop ONLY in this table (no recursion),
8355 * we need to mark the inheritors' attributes as locally
8356 * defined rather than inherited.
8357 */
8358 childatt->attinhcount--;
8359 childatt->attislocal = true;
8360
8361 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8362
8363 /* Make update visible */
8364 CommandCounterIncrement();
8365 }
8366
8367 heap_freetuple(tuple);
8368
8369 table_close(childrel, NoLock);
8370 }
8371 table_close(attr_rel, RowExclusiveLock);
8372 }
8373
8374 /* Add object to delete */
8375 object.classId = RelationRelationId;
8376 object.objectId = RelationGetRelid(rel);
8377 object.objectSubId = attnum;
8378 add_exact_object_address(&object, addrs);
8379
8380 if (!recursing)
8381 {
8382 /* Recursion has ended, drop everything that was collected */
8383 performMultipleDeletions(addrs, behavior, 0);
8384 free_object_addresses(addrs);
8385 }
8386
8387 return object;
8388 }
8389
8390 /*
8391 * ALTER TABLE ADD INDEX
8392 *
8393 * There is no such command in the grammar, but parse_utilcmd.c converts
8394 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
8395 * us schedule creation of the index at the appropriate time during ALTER.
8396 *
8397 * Return value is the address of the new index.
8398 */
8399 static ObjectAddress
ATExecAddIndex(AlteredTableInfo * tab,Relation rel,IndexStmt * stmt,bool is_rebuild,LOCKMODE lockmode)8400 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
8401 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
8402 {
8403 bool check_rights;
8404 bool skip_build;
8405 bool quiet;
8406 ObjectAddress address;
8407
8408 Assert(IsA(stmt, IndexStmt));
8409 Assert(!stmt->concurrent);
8410
8411 /* The IndexStmt has already been through transformIndexStmt */
8412 Assert(stmt->transformed);
8413
8414 /* suppress schema rights check when rebuilding existing index */
8415 check_rights = !is_rebuild;
8416 /* skip index build if phase 3 will do it or we're reusing an old one */
8417 skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode);
8418 /* suppress notices when rebuilding existing index */
8419 quiet = is_rebuild;
8420
8421 address = DefineIndex(RelationGetRelid(rel),
8422 stmt,
8423 InvalidOid, /* no predefined OID */
8424 InvalidOid, /* no parent index */
8425 InvalidOid, /* no parent constraint */
8426 true, /* is_alter_table */
8427 check_rights,
8428 false, /* check_not_in_use - we did it already */
8429 skip_build,
8430 quiet);
8431
8432 /*
8433 * If TryReuseIndex() stashed a relfilenode for us, we used it for the new
8434 * index instead of building from scratch. Restore associated fields.
8435 * This may store InvalidSubTransactionId in both fields, in which case
8436 * relcache.c will assume it can rebuild the relcache entry. Hence, do
8437 * this after the CCI that made catalog rows visible to any rebuild. The
8438 * DROP of the old edition of this index will have scheduled the storage
8439 * for deletion at commit, so cancel that pending deletion.
8440 */
8441 if (OidIsValid(stmt->oldNode))
8442 {
8443 Relation irel = index_open(address.objectId, NoLock);
8444
8445 irel->rd_createSubid = stmt->oldCreateSubid;
8446 irel->rd_firstRelfilenodeSubid = stmt->oldFirstRelfilenodeSubid;
8447 RelationPreserveStorage(irel->rd_node, true);
8448 index_close(irel, NoLock);
8449 }
8450
8451 return address;
8452 }
8453
8454 /*
8455 * ALTER TABLE ADD STATISTICS
8456 *
8457 * This is no such command in the grammar, but we use this internally to add
8458 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
8459 * column type change.
8460 */
8461 static ObjectAddress
ATExecAddStatistics(AlteredTableInfo * tab,Relation rel,CreateStatsStmt * stmt,bool is_rebuild,LOCKMODE lockmode)8462 ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
8463 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
8464 {
8465 ObjectAddress address;
8466
8467 Assert(IsA(stmt, CreateStatsStmt));
8468
8469 /* The CreateStatsStmt has already been through transformStatsStmt */
8470 Assert(stmt->transformed);
8471
8472 address = CreateStatistics(stmt);
8473
8474 return address;
8475 }
8476
8477 /*
8478 * ALTER TABLE ADD CONSTRAINT USING INDEX
8479 *
8480 * Returns the address of the new constraint.
8481 */
8482 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo * tab,Relation rel,IndexStmt * stmt,LOCKMODE lockmode)8483 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
8484 IndexStmt *stmt, LOCKMODE lockmode)
8485 {
8486 Oid index_oid = stmt->indexOid;
8487 Relation indexRel;
8488 char *indexName;
8489 IndexInfo *indexInfo;
8490 char *constraintName;
8491 char constraintType;
8492 ObjectAddress address;
8493 bits16 flags;
8494
8495 Assert(IsA(stmt, IndexStmt));
8496 Assert(OidIsValid(index_oid));
8497 Assert(stmt->isconstraint);
8498
8499 /*
8500 * Doing this on partitioned tables is not a simple feature to implement,
8501 * so let's punt for now.
8502 */
8503 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8504 ereport(ERROR,
8505 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8506 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
8507
8508 indexRel = index_open(index_oid, AccessShareLock);
8509
8510 indexName = pstrdup(RelationGetRelationName(indexRel));
8511
8512 indexInfo = BuildIndexInfo(indexRel);
8513
8514 /* this should have been checked at parse time */
8515 if (!indexInfo->ii_Unique)
8516 elog(ERROR, "index \"%s\" is not unique", indexName);
8517
8518 /*
8519 * Determine name to assign to constraint. We require a constraint to
8520 * have the same name as the underlying index; therefore, use the index's
8521 * existing name as the default constraint name, and if the user
8522 * explicitly gives some other name for the constraint, rename the index
8523 * to match.
8524 */
8525 constraintName = stmt->idxname;
8526 if (constraintName == NULL)
8527 constraintName = indexName;
8528 else if (strcmp(constraintName, indexName) != 0)
8529 {
8530 ereport(NOTICE,
8531 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
8532 indexName, constraintName)));
8533 RenameRelationInternal(index_oid, constraintName, false, true);
8534 }
8535
8536 /* Extra checks needed if making primary key */
8537 if (stmt->primary)
8538 index_check_primary_key(rel, indexInfo, true, stmt);
8539
8540 /* Note we currently don't support EXCLUSION constraints here */
8541 if (stmt->primary)
8542 constraintType = CONSTRAINT_PRIMARY;
8543 else
8544 constraintType = CONSTRAINT_UNIQUE;
8545
8546 /* Create the catalog entries for the constraint */
8547 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
8548 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
8549 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
8550 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
8551 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
8552
8553 address = index_constraint_create(rel,
8554 index_oid,
8555 InvalidOid,
8556 indexInfo,
8557 constraintName,
8558 constraintType,
8559 flags,
8560 allowSystemTableMods,
8561 false); /* is_internal */
8562
8563 index_close(indexRel, NoLock);
8564
8565 return address;
8566 }
8567
8568 /*
8569 * ALTER TABLE ADD CONSTRAINT
8570 *
8571 * Return value is the address of the new constraint; if no constraint was
8572 * added, InvalidObjectAddress is returned.
8573 */
8574 static ObjectAddress
ATExecAddConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * newConstraint,bool recurse,bool is_readd,LOCKMODE lockmode)8575 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
8576 Constraint *newConstraint, bool recurse, bool is_readd,
8577 LOCKMODE lockmode)
8578 {
8579 ObjectAddress address = InvalidObjectAddress;
8580
8581 Assert(IsA(newConstraint, Constraint));
8582
8583 /*
8584 * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
8585 * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
8586 * switch anyway to make it easier to add more code later.
8587 */
8588 switch (newConstraint->contype)
8589 {
8590 case CONSTR_CHECK:
8591 address =
8592 ATAddCheckConstraint(wqueue, tab, rel,
8593 newConstraint, recurse, false, is_readd,
8594 lockmode);
8595 break;
8596
8597 case CONSTR_FOREIGN:
8598
8599 /*
8600 * Assign or validate constraint name
8601 */
8602 if (newConstraint->conname)
8603 {
8604 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
8605 RelationGetRelid(rel),
8606 newConstraint->conname))
8607 ereport(ERROR,
8608 (errcode(ERRCODE_DUPLICATE_OBJECT),
8609 errmsg("constraint \"%s\" for relation \"%s\" already exists",
8610 newConstraint->conname,
8611 RelationGetRelationName(rel))));
8612 }
8613 else
8614 newConstraint->conname =
8615 ChooseConstraintName(RelationGetRelationName(rel),
8616 ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
8617 "fkey",
8618 RelationGetNamespace(rel),
8619 NIL);
8620
8621 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
8622 newConstraint,
8623 recurse, false,
8624 lockmode);
8625 break;
8626
8627 default:
8628 elog(ERROR, "unrecognized constraint type: %d",
8629 (int) newConstraint->contype);
8630 }
8631
8632 return address;
8633 }
8634
8635 /*
8636 * Generate the column-name portion of the constraint name for a new foreign
8637 * key given the list of column names that reference the referenced
8638 * table. This will be passed to ChooseConstraintName along with the parent
8639 * table name and the "fkey" suffix.
8640 *
8641 * We know that less than NAMEDATALEN characters will actually be used, so we
8642 * can truncate the result once we've generated that many.
8643 *
8644 * XXX see also ChooseExtendedStatisticNameAddition and
8645 * ChooseIndexNameAddition.
8646 */
8647 static char *
ChooseForeignKeyConstraintNameAddition(List * colnames)8648 ChooseForeignKeyConstraintNameAddition(List *colnames)
8649 {
8650 char buf[NAMEDATALEN * 2];
8651 int buflen = 0;
8652 ListCell *lc;
8653
8654 buf[0] = '\0';
8655 foreach(lc, colnames)
8656 {
8657 const char *name = strVal(lfirst(lc));
8658
8659 if (buflen > 0)
8660 buf[buflen++] = '_'; /* insert _ between names */
8661
8662 /*
8663 * At this point we have buflen <= NAMEDATALEN. name should be less
8664 * than NAMEDATALEN already, but use strlcpy for paranoia.
8665 */
8666 strlcpy(buf + buflen, name, NAMEDATALEN);
8667 buflen += strlen(buf + buflen);
8668 if (buflen >= NAMEDATALEN)
8669 break;
8670 }
8671 return pstrdup(buf);
8672 }
8673
8674 /*
8675 * Add a check constraint to a single table and its children. Returns the
8676 * address of the constraint added to the parent relation, if one gets added,
8677 * or InvalidObjectAddress otherwise.
8678 *
8679 * Subroutine for ATExecAddConstraint.
8680 *
8681 * We must recurse to child tables during execution, rather than using
8682 * ALTER TABLE's normal prep-time recursion. The reason is that all the
8683 * constraints *must* be given the same name, else they won't be seen as
8684 * related later. If the user didn't explicitly specify a name, then
8685 * AddRelationNewConstraints would normally assign different names to the
8686 * child constraints. To fix that, we must capture the name assigned at
8687 * the parent table and pass that down.
8688 */
8689 static ObjectAddress
ATAddCheckConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * constr,bool recurse,bool recursing,bool is_readd,LOCKMODE lockmode)8690 ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
8691 Constraint *constr, bool recurse, bool recursing,
8692 bool is_readd, LOCKMODE lockmode)
8693 {
8694 List *newcons;
8695 ListCell *lcon;
8696 List *children;
8697 ListCell *child;
8698 ObjectAddress address = InvalidObjectAddress;
8699
8700 /* At top level, permission check was done in ATPrepCmd, else do it */
8701 if (recursing)
8702 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8703
8704 /*
8705 * Call AddRelationNewConstraints to do the work, making sure it works on
8706 * a copy of the Constraint so transformExpr can't modify the original. It
8707 * returns a list of cooked constraints.
8708 *
8709 * If the constraint ends up getting merged with a pre-existing one, it's
8710 * omitted from the returned list, which is what we want: we do not need
8711 * to do any validation work. That can only happen at child tables,
8712 * though, since we disallow merging at the top level.
8713 */
8714 newcons = AddRelationNewConstraints(rel, NIL,
8715 list_make1(copyObject(constr)),
8716 recursing | is_readd, /* allow_merge */
8717 !recursing, /* is_local */
8718 is_readd, /* is_internal */
8719 NULL); /* queryString not available
8720 * here */
8721
8722 /* we don't expect more than one constraint here */
8723 Assert(list_length(newcons) <= 1);
8724
8725 /* Add each to-be-validated constraint to Phase 3's queue */
8726 foreach(lcon, newcons)
8727 {
8728 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
8729
8730 if (!ccon->skip_validation)
8731 {
8732 NewConstraint *newcon;
8733
8734 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
8735 newcon->name = ccon->name;
8736 newcon->contype = ccon->contype;
8737 newcon->qual = ccon->expr;
8738
8739 tab->constraints = lappend(tab->constraints, newcon);
8740 }
8741
8742 /* Save the actually assigned name if it was defaulted */
8743 if (constr->conname == NULL)
8744 constr->conname = ccon->name;
8745
8746 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8747 }
8748
8749 /* At this point we must have a locked-down name to use */
8750 Assert(constr->conname != NULL);
8751
8752 /* Advance command counter in case same table is visited multiple times */
8753 CommandCounterIncrement();
8754
8755 /*
8756 * If the constraint got merged with an existing constraint, we're done.
8757 * We mustn't recurse to child tables in this case, because they've
8758 * already got the constraint, and visiting them again would lead to an
8759 * incorrect value for coninhcount.
8760 */
8761 if (newcons == NIL)
8762 return address;
8763
8764 /*
8765 * If adding a NO INHERIT constraint, no need to find our children.
8766 */
8767 if (constr->is_no_inherit)
8768 return address;
8769
8770 /*
8771 * Propagate to children as appropriate. Unlike most other ALTER
8772 * routines, we have to do this one level of recursion at a time; we can't
8773 * use find_all_inheritors to do it in one pass.
8774 */
8775 children =
8776 find_inheritance_children(RelationGetRelid(rel), lockmode);
8777
8778 /*
8779 * Check if ONLY was specified with ALTER TABLE. If so, allow the
8780 * constraint creation only if there are no children currently. Error out
8781 * otherwise.
8782 */
8783 if (!recurse && children != NIL)
8784 ereport(ERROR,
8785 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8786 errmsg("constraint must be added to child tables too")));
8787
8788 foreach(child, children)
8789 {
8790 Oid childrelid = lfirst_oid(child);
8791 Relation childrel;
8792 AlteredTableInfo *childtab;
8793
8794 /* find_inheritance_children already got lock */
8795 childrel = table_open(childrelid, NoLock);
8796 CheckTableNotInUse(childrel, "ALTER TABLE");
8797
8798 /* Find or create work queue entry for this table */
8799 childtab = ATGetQueueEntry(wqueue, childrel);
8800
8801 /* Recurse to child */
8802 ATAddCheckConstraint(wqueue, childtab, childrel,
8803 constr, recurse, true, is_readd, lockmode);
8804
8805 table_close(childrel, NoLock);
8806 }
8807
8808 return address;
8809 }
8810
8811 /*
8812 * Add a foreign-key constraint to a single table; return the new constraint's
8813 * address.
8814 *
8815 * Subroutine for ATExecAddConstraint. Must already hold exclusive
8816 * lock on the rel, and have done appropriate validity checks for it.
8817 * We do permissions checks here, however.
8818 *
8819 * When the referenced or referencing tables (or both) are partitioned,
8820 * multiple pg_constraint rows are required -- one for each partitioned table
8821 * and each partition on each side (fortunately, not one for every combination
8822 * thereof). We also need action triggers on each leaf partition on the
8823 * referenced side, and check triggers on each leaf partition on the
8824 * referencing side.
8825 */
8826 static ObjectAddress
ATAddForeignKeyConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * fkconstraint,bool recurse,bool recursing,LOCKMODE lockmode)8827 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
8828 Constraint *fkconstraint,
8829 bool recurse, bool recursing, LOCKMODE lockmode)
8830 {
8831 Relation pkrel;
8832 int16 pkattnum[INDEX_MAX_KEYS];
8833 int16 fkattnum[INDEX_MAX_KEYS];
8834 Oid pktypoid[INDEX_MAX_KEYS];
8835 Oid fktypoid[INDEX_MAX_KEYS];
8836 Oid opclasses[INDEX_MAX_KEYS];
8837 Oid pfeqoperators[INDEX_MAX_KEYS];
8838 Oid ppeqoperators[INDEX_MAX_KEYS];
8839 Oid ffeqoperators[INDEX_MAX_KEYS];
8840 int i;
8841 int numfks,
8842 numpks;
8843 Oid indexOid;
8844 bool old_check_ok;
8845 ObjectAddress address;
8846 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
8847
8848 /*
8849 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
8850 * delete rows out from under us.
8851 */
8852 if (OidIsValid(fkconstraint->old_pktable_oid))
8853 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
8854 else
8855 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
8856
8857 /*
8858 * Validity checks (permission checks wait till we have the column
8859 * numbers)
8860 */
8861 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8862 {
8863 if (!recurse)
8864 ereport(ERROR,
8865 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8866 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
8867 RelationGetRelationName(rel),
8868 RelationGetRelationName(pkrel))));
8869 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
8870 ereport(ERROR,
8871 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8872 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
8873 RelationGetRelationName(rel),
8874 RelationGetRelationName(pkrel)),
8875 errdetail("This feature is not yet supported on partitioned tables.")));
8876 }
8877
8878 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
8879 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
8880 ereport(ERROR,
8881 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8882 errmsg("referenced relation \"%s\" is not a table",
8883 RelationGetRelationName(pkrel))));
8884
8885 if (!allowSystemTableMods && IsSystemRelation(pkrel))
8886 ereport(ERROR,
8887 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
8888 errmsg("permission denied: \"%s\" is a system catalog",
8889 RelationGetRelationName(pkrel))));
8890
8891 /*
8892 * References from permanent or unlogged tables to temp tables, and from
8893 * permanent tables to unlogged tables, are disallowed because the
8894 * referenced data can vanish out from under us. References from temp
8895 * tables to any other table type are also disallowed, because other
8896 * backends might need to run the RI triggers on the perm table, but they
8897 * can't reliably see tuples in the local buffers of other backends.
8898 */
8899 switch (rel->rd_rel->relpersistence)
8900 {
8901 case RELPERSISTENCE_PERMANENT:
8902 if (!RelationIsPermanent(pkrel))
8903 ereport(ERROR,
8904 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8905 errmsg("constraints on permanent tables may reference only permanent tables")));
8906 break;
8907 case RELPERSISTENCE_UNLOGGED:
8908 if (!RelationIsPermanent(pkrel)
8909 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
8910 ereport(ERROR,
8911 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8912 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
8913 break;
8914 case RELPERSISTENCE_TEMP:
8915 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
8916 ereport(ERROR,
8917 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8918 errmsg("constraints on temporary tables may reference only temporary tables")));
8919 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
8920 ereport(ERROR,
8921 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8922 errmsg("constraints on temporary tables must involve temporary tables of this session")));
8923 break;
8924 }
8925
8926 /*
8927 * Look up the referencing attributes to make sure they exist, and record
8928 * their attnums and type OIDs.
8929 */
8930 MemSet(pkattnum, 0, sizeof(pkattnum));
8931 MemSet(fkattnum, 0, sizeof(fkattnum));
8932 MemSet(pktypoid, 0, sizeof(pktypoid));
8933 MemSet(fktypoid, 0, sizeof(fktypoid));
8934 MemSet(opclasses, 0, sizeof(opclasses));
8935 MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
8936 MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
8937 MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
8938
8939 numfks = transformColumnNameList(RelationGetRelid(rel),
8940 fkconstraint->fk_attrs,
8941 fkattnum, fktypoid);
8942
8943 /*
8944 * If the attribute list for the referenced table was omitted, lookup the
8945 * definition of the primary key and use it. Otherwise, validate the
8946 * supplied attribute list. In either case, discover the index OID and
8947 * index opclasses, and the attnums and type OIDs of the attributes.
8948 */
8949 if (fkconstraint->pk_attrs == NIL)
8950 {
8951 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
8952 &fkconstraint->pk_attrs,
8953 pkattnum, pktypoid,
8954 opclasses);
8955 }
8956 else
8957 {
8958 numpks = transformColumnNameList(RelationGetRelid(pkrel),
8959 fkconstraint->pk_attrs,
8960 pkattnum, pktypoid);
8961 /* Look for an index matching the column list */
8962 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
8963 opclasses);
8964 }
8965
8966 /*
8967 * Now we can check permissions.
8968 */
8969 checkFkeyPermissions(pkrel, pkattnum, numpks);
8970
8971 /*
8972 * Check some things for generated columns.
8973 */
8974 for (i = 0; i < numfks; i++)
8975 {
8976 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
8977
8978 if (attgenerated)
8979 {
8980 /*
8981 * Check restrictions on UPDATE/DELETE actions, per SQL standard
8982 */
8983 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
8984 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
8985 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
8986 ereport(ERROR,
8987 (errcode(ERRCODE_SYNTAX_ERROR),
8988 errmsg("invalid %s action for foreign key constraint containing generated column",
8989 "ON UPDATE")));
8990 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
8991 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
8992 ereport(ERROR,
8993 (errcode(ERRCODE_SYNTAX_ERROR),
8994 errmsg("invalid %s action for foreign key constraint containing generated column",
8995 "ON DELETE")));
8996 }
8997 }
8998
8999 /*
9000 * Look up the equality operators to use in the constraint.
9001 *
9002 * Note that we have to be careful about the difference between the actual
9003 * PK column type and the opclass' declared input type, which might be
9004 * only binary-compatible with it. The declared opcintype is the right
9005 * thing to probe pg_amop with.
9006 */
9007 if (numfks != numpks)
9008 ereport(ERROR,
9009 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9010 errmsg("number of referencing and referenced columns for foreign key disagree")));
9011
9012 /*
9013 * On the strength of a previous constraint, we might avoid scanning
9014 * tables to validate this one. See below.
9015 */
9016 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9017 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9018
9019 for (i = 0; i < numpks; i++)
9020 {
9021 Oid pktype = pktypoid[i];
9022 Oid fktype = fktypoid[i];
9023 Oid fktyped;
9024 HeapTuple cla_ht;
9025 Form_pg_opclass cla_tup;
9026 Oid amid;
9027 Oid opfamily;
9028 Oid opcintype;
9029 Oid pfeqop;
9030 Oid ppeqop;
9031 Oid ffeqop;
9032 int16 eqstrategy;
9033 Oid pfeqop_right;
9034
9035 /* We need several fields out of the pg_opclass entry */
9036 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9037 if (!HeapTupleIsValid(cla_ht))
9038 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
9039 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9040 amid = cla_tup->opcmethod;
9041 opfamily = cla_tup->opcfamily;
9042 opcintype = cla_tup->opcintype;
9043 ReleaseSysCache(cla_ht);
9044
9045 /*
9046 * Check it's a btree; currently this can never fail since no other
9047 * index AMs support unique indexes. If we ever did have other types
9048 * of unique indexes, we'd need a way to determine which operator
9049 * strategy number is equality. (Is it reasonable to insist that
9050 * every such index AM use btree's number for equality?)
9051 */
9052 if (amid != BTREE_AM_OID)
9053 elog(ERROR, "only b-tree indexes are supported for foreign keys");
9054 eqstrategy = BTEqualStrategyNumber;
9055
9056 /*
9057 * There had better be a primary equality operator for the index.
9058 * We'll use it for PK = PK comparisons.
9059 */
9060 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
9061 eqstrategy);
9062
9063 if (!OidIsValid(ppeqop))
9064 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
9065 eqstrategy, opcintype, opcintype, opfamily);
9066
9067 /*
9068 * Are there equality operators that take exactly the FK type? Assume
9069 * we should look through any domain here.
9070 */
9071 fktyped = getBaseType(fktype);
9072
9073 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
9074 eqstrategy);
9075 if (OidIsValid(pfeqop))
9076 {
9077 pfeqop_right = fktyped;
9078 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
9079 eqstrategy);
9080 }
9081 else
9082 {
9083 /* keep compiler quiet */
9084 pfeqop_right = InvalidOid;
9085 ffeqop = InvalidOid;
9086 }
9087
9088 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9089 {
9090 /*
9091 * Otherwise, look for an implicit cast from the FK type to the
9092 * opcintype, and if found, use the primary equality operator.
9093 * This is a bit tricky because opcintype might be a polymorphic
9094 * type such as ANYARRAY or ANYENUM; so what we have to test is
9095 * whether the two actual column types can be concurrently cast to
9096 * that type. (Otherwise, we'd fail to reject combinations such
9097 * as int[] and point[].)
9098 */
9099 Oid input_typeids[2];
9100 Oid target_typeids[2];
9101
9102 input_typeids[0] = pktype;
9103 input_typeids[1] = fktype;
9104 target_typeids[0] = opcintype;
9105 target_typeids[1] = opcintype;
9106 if (can_coerce_type(2, input_typeids, target_typeids,
9107 COERCION_IMPLICIT))
9108 {
9109 pfeqop = ffeqop = ppeqop;
9110 pfeqop_right = opcintype;
9111 }
9112 }
9113
9114 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9115 ereport(ERROR,
9116 (errcode(ERRCODE_DATATYPE_MISMATCH),
9117 errmsg("foreign key constraint \"%s\" cannot be implemented",
9118 fkconstraint->conname),
9119 errdetail("Key columns \"%s\" and \"%s\" "
9120 "are of incompatible types: %s and %s.",
9121 strVal(list_nth(fkconstraint->fk_attrs, i)),
9122 strVal(list_nth(fkconstraint->pk_attrs, i)),
9123 format_type_be(fktype),
9124 format_type_be(pktype))));
9125
9126 if (old_check_ok)
9127 {
9128 /*
9129 * When a pfeqop changes, revalidate the constraint. We could
9130 * permit intra-opfamily changes, but that adds subtle complexity
9131 * without any concrete benefit for core types. We need not
9132 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9133 */
9134 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
9135 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
9136 old_pfeqop_item);
9137 }
9138 if (old_check_ok)
9139 {
9140 Oid old_fktype;
9141 Oid new_fktype;
9142 CoercionPathType old_pathtype;
9143 CoercionPathType new_pathtype;
9144 Oid old_castfunc;
9145 Oid new_castfunc;
9146 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
9147 fkattnum[i] - 1);
9148
9149 /*
9150 * Identify coercion pathways from each of the old and new FK-side
9151 * column types to the right (foreign) operand type of the pfeqop.
9152 * We may assume that pg_constraint.conkey is not changing.
9153 */
9154 old_fktype = attr->atttypid;
9155 new_fktype = fktype;
9156 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
9157 &old_castfunc);
9158 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
9159 &new_castfunc);
9160
9161 /*
9162 * Upon a change to the cast from the FK column to its pfeqop
9163 * operand, revalidate the constraint. For this evaluation, a
9164 * binary coercion cast is equivalent to no cast at all. While
9165 * type implementors should design implicit casts with an eye
9166 * toward consistency of operations like equality, we cannot
9167 * assume here that they have done so.
9168 *
9169 * A function with a polymorphic argument could change behavior
9170 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9171 * when the cast destination is polymorphic, we only avoid
9172 * revalidation if the input type has not changed at all. Given
9173 * just the core data types and operator classes, this requirement
9174 * prevents no would-be optimizations.
9175 *
9176 * If the cast converts from a base type to a domain thereon, then
9177 * that domain type must be the opcintype of the unique index.
9178 * Necessarily, the primary key column must then be of the domain
9179 * type. Since the constraint was previously valid, all values on
9180 * the foreign side necessarily exist on the primary side and in
9181 * turn conform to the domain. Consequently, we need not treat
9182 * domains specially here.
9183 *
9184 * Since we require that all collations share the same notion of
9185 * equality (which they do, because texteq reduces to bitwise
9186 * equality), we don't compare collation here.
9187 *
9188 * We need not directly consider the PK type. It's necessarily
9189 * binary coercible to the opcintype of the unique index column,
9190 * and ri_triggers.c will only deal with PK datums in terms of
9191 * that opcintype. Changing the opcintype also changes pfeqop.
9192 */
9193 old_check_ok = (new_pathtype == old_pathtype &&
9194 new_castfunc == old_castfunc &&
9195 (!IsPolymorphicType(pfeqop_right) ||
9196 new_fktype == old_fktype));
9197 }
9198
9199 pfeqoperators[i] = pfeqop;
9200 ppeqoperators[i] = ppeqop;
9201 ffeqoperators[i] = ffeqop;
9202 }
9203
9204 /*
9205 * Create all the constraint and trigger objects, recursing to partitions
9206 * as necessary. First handle the referenced side.
9207 */
9208 address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
9209 indexOid,
9210 InvalidOid, /* no parent constraint */
9211 numfks,
9212 pkattnum,
9213 fkattnum,
9214 pfeqoperators,
9215 ppeqoperators,
9216 ffeqoperators,
9217 old_check_ok);
9218
9219 /* Now handle the referencing side. */
9220 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
9221 indexOid,
9222 address.objectId,
9223 numfks,
9224 pkattnum,
9225 fkattnum,
9226 pfeqoperators,
9227 ppeqoperators,
9228 ffeqoperators,
9229 old_check_ok,
9230 lockmode);
9231
9232 /*
9233 * Done. Close pk table, but keep lock until we've committed.
9234 */
9235 table_close(pkrel, NoLock);
9236
9237 return address;
9238 }
9239
9240 /*
9241 * addFkRecurseReferenced
9242 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
9243 * side of the constraint
9244 *
9245 * Create pg_constraint rows for the referenced side of the constraint,
9246 * referencing the parent of the referencing side; also create action triggers
9247 * on leaf partitions. If the table is partitioned, recurse to handle each
9248 * partition.
9249 *
9250 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9251 * of an ALTER TABLE sequence.
9252 * fkconstraint is the constraint being added.
9253 * rel is the root referencing relation.
9254 * pkrel is the referenced relation; might be a partition, if recursing.
9255 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9256 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
9257 * top-level constraint.
9258 * numfks is the number of columns in the foreign key
9259 * pkattnum is the attnum array of referenced attributes.
9260 * fkattnum is the attnum array of referencing attributes.
9261 * pf/pp/ffeqoperators are OID array of operators between columns.
9262 * old_check_ok signals that this constraint replaces an existing one that
9263 * was already validated (thus this one doesn't need validation).
9264 */
9265 static ObjectAddress
addFkRecurseReferenced(List ** wqueue,Constraint * fkconstraint,Relation rel,Relation pkrel,Oid indexOid,Oid parentConstr,int numfks,int16 * pkattnum,int16 * fkattnum,Oid * pfeqoperators,Oid * ppeqoperators,Oid * ffeqoperators,bool old_check_ok)9266 addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
9267 Relation pkrel, Oid indexOid, Oid parentConstr,
9268 int numfks,
9269 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
9270 Oid *ppeqoperators, Oid *ffeqoperators, bool old_check_ok)
9271 {
9272 ObjectAddress address;
9273 Oid constrOid;
9274 char *conname;
9275 bool conislocal;
9276 int coninhcount;
9277 bool connoinherit;
9278
9279 /*
9280 * Verify relkind for each referenced partition. At the top level, this
9281 * is redundant with a previous check, but we need it when recursing.
9282 */
9283 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9284 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9285 ereport(ERROR,
9286 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9287 errmsg("referenced relation \"%s\" is not a table",
9288 RelationGetRelationName(pkrel))));
9289
9290 /*
9291 * Caller supplies us with a constraint name; however, it may be used in
9292 * this partition, so come up with a different one in that case.
9293 */
9294 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9295 RelationGetRelid(rel),
9296 fkconstraint->conname))
9297 conname = ChooseConstraintName(RelationGetRelationName(rel),
9298 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
9299 "fkey",
9300 RelationGetNamespace(rel), NIL);
9301 else
9302 conname = fkconstraint->conname;
9303
9304 if (OidIsValid(parentConstr))
9305 {
9306 conislocal = false;
9307 coninhcount = 1;
9308 connoinherit = false;
9309 }
9310 else
9311 {
9312 conislocal = true;
9313 coninhcount = 0;
9314
9315 /*
9316 * always inherit for partitioned tables, never for legacy inheritance
9317 */
9318 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
9319 }
9320
9321 /*
9322 * Record the FK constraint in pg_constraint.
9323 */
9324 constrOid = CreateConstraintEntry(conname,
9325 RelationGetNamespace(rel),
9326 CONSTRAINT_FOREIGN,
9327 fkconstraint->deferrable,
9328 fkconstraint->initdeferred,
9329 fkconstraint->initially_valid,
9330 parentConstr,
9331 RelationGetRelid(rel),
9332 fkattnum,
9333 numfks,
9334 numfks,
9335 InvalidOid, /* not a domain constraint */
9336 indexOid,
9337 RelationGetRelid(pkrel),
9338 pkattnum,
9339 pfeqoperators,
9340 ppeqoperators,
9341 ffeqoperators,
9342 numfks,
9343 fkconstraint->fk_upd_action,
9344 fkconstraint->fk_del_action,
9345 fkconstraint->fk_matchtype,
9346 NULL, /* no exclusion constraint */
9347 NULL, /* no check constraint */
9348 NULL,
9349 conislocal, /* islocal */
9350 coninhcount, /* inhcount */
9351 connoinherit, /* conNoInherit */
9352 false); /* is_internal */
9353
9354 ObjectAddressSet(address, ConstraintRelationId, constrOid);
9355
9356 /*
9357 * Mark the child constraint as part of the parent constraint; it must not
9358 * be dropped on its own. (This constraint is deleted when the partition
9359 * is detached, but a special check needs to occur that the partition
9360 * contains no referenced values.)
9361 */
9362 if (OidIsValid(parentConstr))
9363 {
9364 ObjectAddress referenced;
9365
9366 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
9367 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
9368 }
9369
9370 /* make new constraint visible, in case we add more */
9371 CommandCounterIncrement();
9372
9373 /*
9374 * If the referenced table is a plain relation, create the action triggers
9375 * that enforce the constraint.
9376 */
9377 if (pkrel->rd_rel->relkind == RELKIND_RELATION)
9378 {
9379 createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
9380 fkconstraint,
9381 constrOid, indexOid);
9382 }
9383
9384 /*
9385 * If the referenced table is partitioned, recurse on ourselves to handle
9386 * each partition. We need one pg_constraint row created for each
9387 * partition in addition to the pg_constraint row for the parent table.
9388 */
9389 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9390 {
9391 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
9392
9393 for (int i = 0; i < pd->nparts; i++)
9394 {
9395 Relation partRel;
9396 AttrMap *map;
9397 AttrNumber *mapped_pkattnum;
9398 Oid partIndexId;
9399
9400 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
9401
9402 /*
9403 * Map the attribute numbers in the referenced side of the FK
9404 * definition to match the partition's column layout.
9405 */
9406 map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
9407 RelationGetDescr(pkrel));
9408 if (map)
9409 {
9410 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
9411 for (int j = 0; j < numfks; j++)
9412 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
9413 }
9414 else
9415 mapped_pkattnum = pkattnum;
9416
9417 /* do the deed */
9418 partIndexId = index_get_partition(partRel, indexOid);
9419 if (!OidIsValid(partIndexId))
9420 elog(ERROR, "index for %u not found in partition %s",
9421 indexOid, RelationGetRelationName(partRel));
9422 addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
9423 partIndexId, constrOid, numfks,
9424 mapped_pkattnum, fkattnum,
9425 pfeqoperators, ppeqoperators, ffeqoperators,
9426 old_check_ok);
9427
9428 /* Done -- clean up (but keep the lock) */
9429 table_close(partRel, NoLock);
9430 if (map)
9431 {
9432 pfree(mapped_pkattnum);
9433 free_attrmap(map);
9434 }
9435 }
9436 }
9437
9438 return address;
9439 }
9440
9441 /*
9442 * addFkRecurseReferencing
9443 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
9444 *
9445 * If the referencing relation is a plain relation, create the necessary check
9446 * triggers that implement the constraint, and set up for Phase 3 constraint
9447 * verification. If the referencing relation is a partitioned table, then
9448 * we create a pg_constraint row for it and recurse on this routine for each
9449 * partition.
9450 *
9451 * We assume that the referenced relation is locked against concurrent
9452 * deletions. If it's a partitioned relation, every partition must be so
9453 * locked.
9454 *
9455 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9456 * of an ALTER TABLE sequence.
9457 * fkconstraint is the constraint being added.
9458 * rel is the referencing relation; might be a partition, if recursing.
9459 * pkrel is the root referenced relation.
9460 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9461 * parentConstr is the OID of the parent constraint (there is always one).
9462 * numfks is the number of columns in the foreign key
9463 * pkattnum is the attnum array of referenced attributes.
9464 * fkattnum is the attnum array of referencing attributes.
9465 * pf/pp/ffeqoperators are OID array of operators between columns.
9466 * old_check_ok signals that this constraint replaces an existing one that
9467 * was already validated (thus this one doesn't need validation).
9468 * lockmode is the lockmode to acquire on partitions when recursing.
9469 */
9470 static void
addFkRecurseReferencing(List ** wqueue,Constraint * fkconstraint,Relation rel,Relation pkrel,Oid indexOid,Oid parentConstr,int numfks,int16 * pkattnum,int16 * fkattnum,Oid * pfeqoperators,Oid * ppeqoperators,Oid * ffeqoperators,bool old_check_ok,LOCKMODE lockmode)9471 addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
9472 Relation pkrel, Oid indexOid, Oid parentConstr,
9473 int numfks, int16 *pkattnum, int16 *fkattnum,
9474 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
9475 bool old_check_ok, LOCKMODE lockmode)
9476 {
9477 AssertArg(OidIsValid(parentConstr));
9478
9479 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
9480 ereport(ERROR,
9481 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9482 errmsg("foreign key constraints are not supported on foreign tables")));
9483
9484 /*
9485 * If the referencing relation is a plain table, add the check triggers to
9486 * it and, if necessary, schedule it to be checked in Phase 3.
9487 *
9488 * If the relation is partitioned, drill down to do it to its partitions.
9489 */
9490 if (rel->rd_rel->relkind == RELKIND_RELATION)
9491 {
9492 createForeignKeyCheckTriggers(RelationGetRelid(rel),
9493 RelationGetRelid(pkrel),
9494 fkconstraint,
9495 parentConstr,
9496 indexOid);
9497
9498 /*
9499 * Tell Phase 3 to check that the constraint is satisfied by existing
9500 * rows. We can skip this during table creation, when requested
9501 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
9502 * and when we're recreating a constraint following a SET DATA TYPE
9503 * operation that did not impugn its validity.
9504 */
9505 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
9506 {
9507 NewConstraint *newcon;
9508 AlteredTableInfo *tab;
9509
9510 tab = ATGetQueueEntry(wqueue, rel);
9511
9512 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9513 newcon->name = get_constraint_name(parentConstr);
9514 newcon->contype = CONSTR_FOREIGN;
9515 newcon->refrelid = RelationGetRelid(pkrel);
9516 newcon->refindid = indexOid;
9517 newcon->conid = parentConstr;
9518 newcon->qual = (Node *) fkconstraint;
9519
9520 tab->constraints = lappend(tab->constraints, newcon);
9521 }
9522 }
9523 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9524 {
9525 PartitionDesc pd = RelationGetPartitionDesc(rel, true);
9526
9527 /*
9528 * Recurse to take appropriate action on each partition; either we
9529 * find an existing constraint to reparent to ours, or we create a new
9530 * one.
9531 */
9532 for (int i = 0; i < pd->nparts; i++)
9533 {
9534 Oid partitionId = pd->oids[i];
9535 Relation partition = table_open(partitionId, lockmode);
9536 List *partFKs;
9537 AttrMap *attmap;
9538 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
9539 bool attached;
9540 char *conname;
9541 Oid constrOid;
9542 ObjectAddress address,
9543 referenced;
9544 ListCell *cell;
9545
9546 CheckTableNotInUse(partition, "ALTER TABLE");
9547
9548 attmap = build_attrmap_by_name(RelationGetDescr(partition),
9549 RelationGetDescr(rel));
9550 for (int j = 0; j < numfks; j++)
9551 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
9552
9553 /* Check whether an existing constraint can be repurposed */
9554 partFKs = copyObject(RelationGetFKeyList(partition));
9555 attached = false;
9556 foreach(cell, partFKs)
9557 {
9558 ForeignKeyCacheInfo *fk;
9559
9560 fk = lfirst_node(ForeignKeyCacheInfo, cell);
9561 if (tryAttachPartitionForeignKey(fk,
9562 partitionId,
9563 parentConstr,
9564 numfks,
9565 mapped_fkattnum,
9566 pkattnum,
9567 pfeqoperators))
9568 {
9569 attached = true;
9570 break;
9571 }
9572 }
9573 if (attached)
9574 {
9575 table_close(partition, NoLock);
9576 continue;
9577 }
9578
9579 /*
9580 * No luck finding a good constraint to reuse; create our own.
9581 */
9582 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9583 RelationGetRelid(partition),
9584 fkconstraint->conname))
9585 conname = ChooseConstraintName(RelationGetRelationName(partition),
9586 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
9587 "fkey",
9588 RelationGetNamespace(partition), NIL);
9589 else
9590 conname = fkconstraint->conname;
9591 constrOid =
9592 CreateConstraintEntry(conname,
9593 RelationGetNamespace(partition),
9594 CONSTRAINT_FOREIGN,
9595 fkconstraint->deferrable,
9596 fkconstraint->initdeferred,
9597 fkconstraint->initially_valid,
9598 parentConstr,
9599 partitionId,
9600 mapped_fkattnum,
9601 numfks,
9602 numfks,
9603 InvalidOid,
9604 indexOid,
9605 RelationGetRelid(pkrel),
9606 pkattnum,
9607 pfeqoperators,
9608 ppeqoperators,
9609 ffeqoperators,
9610 numfks,
9611 fkconstraint->fk_upd_action,
9612 fkconstraint->fk_del_action,
9613 fkconstraint->fk_matchtype,
9614 NULL,
9615 NULL,
9616 NULL,
9617 false,
9618 1,
9619 false,
9620 false);
9621
9622 /*
9623 * Give this constraint partition-type dependencies on the parent
9624 * constraint as well as the table.
9625 */
9626 ObjectAddressSet(address, ConstraintRelationId, constrOid);
9627 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
9628 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
9629 ObjectAddressSet(referenced, RelationRelationId, partitionId);
9630 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
9631
9632 /* Make all this visible before recursing */
9633 CommandCounterIncrement();
9634
9635 /* call ourselves to finalize the creation and we're done */
9636 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
9637 indexOid,
9638 constrOid,
9639 numfks,
9640 pkattnum,
9641 mapped_fkattnum,
9642 pfeqoperators,
9643 ppeqoperators,
9644 ffeqoperators,
9645 old_check_ok,
9646 lockmode);
9647
9648 table_close(partition, NoLock);
9649 }
9650 }
9651 }
9652
9653 /*
9654 * CloneForeignKeyConstraints
9655 * Clone foreign keys from a partitioned table to a newly acquired
9656 * partition.
9657 *
9658 * partitionRel is a partition of parentRel, so we can be certain that it has
9659 * the same columns with the same datatypes. The columns may be in different
9660 * order, though.
9661 *
9662 * wqueue must be passed to set up phase 3 constraint checking, unless the
9663 * referencing-side partition is known to be empty (such as in CREATE TABLE /
9664 * PARTITION OF).
9665 */
9666 static void
CloneForeignKeyConstraints(List ** wqueue,Relation parentRel,Relation partitionRel)9667 CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
9668 Relation partitionRel)
9669 {
9670 /* This only works for declarative partitioning */
9671 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
9672
9673 /*
9674 * Clone constraints for which the parent is on the referenced side.
9675 */
9676 CloneFkReferenced(parentRel, partitionRel);
9677
9678 /*
9679 * Now clone constraints where the parent is on the referencing side.
9680 */
9681 CloneFkReferencing(wqueue, parentRel, partitionRel);
9682 }
9683
9684 /*
9685 * CloneFkReferenced
9686 * Subroutine for CloneForeignKeyConstraints
9687 *
9688 * Find all the FKs that have the parent relation on the referenced side;
9689 * clone those constraints to the given partition. This is to be called
9690 * when the partition is being created or attached.
9691 *
9692 * This recurses to partitions, if the relation being attached is partitioned.
9693 * Recursion is done by calling addFkRecurseReferenced.
9694 */
9695 static void
CloneFkReferenced(Relation parentRel,Relation partitionRel)9696 CloneFkReferenced(Relation parentRel, Relation partitionRel)
9697 {
9698 Relation pg_constraint;
9699 AttrMap *attmap;
9700 ListCell *cell;
9701 SysScanDesc scan;
9702 ScanKeyData key[2];
9703 HeapTuple tuple;
9704 List *clone = NIL;
9705
9706 /*
9707 * Search for any constraints where this partition's parent is in the
9708 * referenced side. However, we must not clone any constraint whose
9709 * parent constraint is also going to be cloned, to avoid duplicates. So
9710 * do it in two steps: first construct the list of constraints to clone,
9711 * then go over that list cloning those whose parents are not in the list.
9712 * (We must not rely on the parent being seen first, since the catalog
9713 * scan could return children first.)
9714 */
9715 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
9716 ScanKeyInit(&key[0],
9717 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
9718 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
9719 ScanKeyInit(&key[1],
9720 Anum_pg_constraint_contype, BTEqualStrategyNumber,
9721 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
9722 /* This is a seqscan, as we don't have a usable index ... */
9723 scan = systable_beginscan(pg_constraint, InvalidOid, true,
9724 NULL, 2, key);
9725 while ((tuple = systable_getnext(scan)) != NULL)
9726 {
9727 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
9728
9729 clone = lappend_oid(clone, constrForm->oid);
9730 }
9731 systable_endscan(scan);
9732 table_close(pg_constraint, RowShareLock);
9733
9734 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
9735 RelationGetDescr(parentRel));
9736 foreach(cell, clone)
9737 {
9738 Oid constrOid = lfirst_oid(cell);
9739 Form_pg_constraint constrForm;
9740 Relation fkRel;
9741 Oid indexOid;
9742 Oid partIndexId;
9743 int numfks;
9744 AttrNumber conkey[INDEX_MAX_KEYS];
9745 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
9746 AttrNumber confkey[INDEX_MAX_KEYS];
9747 Oid conpfeqop[INDEX_MAX_KEYS];
9748 Oid conppeqop[INDEX_MAX_KEYS];
9749 Oid conffeqop[INDEX_MAX_KEYS];
9750 Constraint *fkconstraint;
9751
9752 tuple = SearchSysCache1(CONSTROID, constrOid);
9753 if (!HeapTupleIsValid(tuple))
9754 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
9755 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
9756
9757 /*
9758 * As explained above: don't try to clone a constraint for which we're
9759 * going to clone the parent.
9760 */
9761 if (list_member_oid(clone, constrForm->conparentid))
9762 {
9763 ReleaseSysCache(tuple);
9764 continue;
9765 }
9766
9767 /*
9768 * Because we're only expanding the key space at the referenced side,
9769 * we don't need to prevent any operation in the referencing table, so
9770 * AccessShareLock suffices (assumes that dropping the constraint
9771 * acquires AEL).
9772 */
9773 fkRel = table_open(constrForm->conrelid, AccessShareLock);
9774
9775 indexOid = constrForm->conindid;
9776 DeconstructFkConstraintRow(tuple,
9777 &numfks,
9778 conkey,
9779 confkey,
9780 conpfeqop,
9781 conppeqop,
9782 conffeqop);
9783
9784 for (int i = 0; i < numfks; i++)
9785 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
9786
9787 fkconstraint = makeNode(Constraint);
9788 /* for now this is all we need */
9789 fkconstraint->conname = NameStr(constrForm->conname);
9790 fkconstraint->fk_upd_action = constrForm->confupdtype;
9791 fkconstraint->fk_del_action = constrForm->confdeltype;
9792 fkconstraint->deferrable = constrForm->condeferrable;
9793 fkconstraint->initdeferred = constrForm->condeferred;
9794 fkconstraint->initially_valid = true;
9795 fkconstraint->fk_matchtype = constrForm->confmatchtype;
9796
9797 /* set up colnames that are used to generate the constraint name */
9798 for (int i = 0; i < numfks; i++)
9799 {
9800 Form_pg_attribute att;
9801
9802 att = TupleDescAttr(RelationGetDescr(fkRel),
9803 conkey[i] - 1);
9804 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
9805 makeString(NameStr(att->attname)));
9806 }
9807
9808 /*
9809 * Add the new foreign key constraint pointing to the new partition.
9810 * Because this new partition appears in the referenced side of the
9811 * constraint, we don't need to set up for Phase 3 check.
9812 */
9813 partIndexId = index_get_partition(partitionRel, indexOid);
9814 if (!OidIsValid(partIndexId))
9815 elog(ERROR, "index for %u not found in partition %s",
9816 indexOid, RelationGetRelationName(partitionRel));
9817 addFkRecurseReferenced(NULL,
9818 fkconstraint,
9819 fkRel,
9820 partitionRel,
9821 partIndexId,
9822 constrOid,
9823 numfks,
9824 mapped_confkey,
9825 conkey,
9826 conpfeqop,
9827 conppeqop,
9828 conffeqop,
9829 true);
9830
9831 table_close(fkRel, NoLock);
9832 ReleaseSysCache(tuple);
9833 }
9834 }
9835
9836 /*
9837 * CloneFkReferencing
9838 * Subroutine for CloneForeignKeyConstraints
9839 *
9840 * For each FK constraint of the parent relation in the given list, find an
9841 * equivalent constraint in its partition relation that can be reparented;
9842 * if one cannot be found, create a new constraint in the partition as its
9843 * child.
9844 *
9845 * If wqueue is given, it is used to set up phase-3 verification for each
9846 * cloned constraint; if omitted, we assume that such verification is not
9847 * needed (example: the partition is being created anew).
9848 */
9849 static void
CloneFkReferencing(List ** wqueue,Relation parentRel,Relation partRel)9850 CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
9851 {
9852 AttrMap *attmap;
9853 List *partFKs;
9854 List *clone = NIL;
9855 ListCell *cell;
9856
9857 /* obtain a list of constraints that we need to clone */
9858 foreach(cell, RelationGetFKeyList(parentRel))
9859 {
9860 ForeignKeyCacheInfo *fk = lfirst(cell);
9861
9862 clone = lappend_oid(clone, fk->conoid);
9863 }
9864
9865 /*
9866 * Silently do nothing if there's nothing to do. In particular, this
9867 * avoids throwing a spurious error for foreign tables.
9868 */
9869 if (clone == NIL)
9870 return;
9871
9872 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
9873 ereport(ERROR,
9874 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9875 errmsg("foreign key constraints are not supported on foreign tables")));
9876
9877 /*
9878 * The constraint key may differ, if the columns in the partition are
9879 * different. This map is used to convert them.
9880 */
9881 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
9882 RelationGetDescr(parentRel));
9883
9884 partFKs = copyObject(RelationGetFKeyList(partRel));
9885
9886 foreach(cell, clone)
9887 {
9888 Oid parentConstrOid = lfirst_oid(cell);
9889 Form_pg_constraint constrForm;
9890 Relation pkrel;
9891 HeapTuple tuple;
9892 int numfks;
9893 AttrNumber conkey[INDEX_MAX_KEYS];
9894 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
9895 AttrNumber confkey[INDEX_MAX_KEYS];
9896 Oid conpfeqop[INDEX_MAX_KEYS];
9897 Oid conppeqop[INDEX_MAX_KEYS];
9898 Oid conffeqop[INDEX_MAX_KEYS];
9899 Constraint *fkconstraint;
9900 bool attached;
9901 Oid indexOid;
9902 Oid constrOid;
9903 ObjectAddress address,
9904 referenced;
9905 ListCell *cell;
9906
9907 tuple = SearchSysCache1(CONSTROID, parentConstrOid);
9908 if (!HeapTupleIsValid(tuple))
9909 elog(ERROR, "cache lookup failed for constraint %u",
9910 parentConstrOid);
9911 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
9912
9913 /* Don't clone constraints whose parents are being cloned */
9914 if (list_member_oid(clone, constrForm->conparentid))
9915 {
9916 ReleaseSysCache(tuple);
9917 continue;
9918 }
9919
9920 /*
9921 * Need to prevent concurrent deletions. If pkrel is a partitioned
9922 * relation, that means to lock all partitions.
9923 */
9924 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
9925 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9926 (void) find_all_inheritors(RelationGetRelid(pkrel),
9927 ShareRowExclusiveLock, NULL);
9928
9929 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
9930 conpfeqop, conppeqop, conffeqop);
9931 for (int i = 0; i < numfks; i++)
9932 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
9933
9934 /*
9935 * Before creating a new constraint, see whether any existing FKs are
9936 * fit for the purpose. If one is, attach the parent constraint to
9937 * it, and don't clone anything. This way we avoid the expensive
9938 * verification step and don't end up with a duplicate FK, and we
9939 * don't need to recurse to partitions for this constraint.
9940 */
9941 attached = false;
9942 foreach(cell, partFKs)
9943 {
9944 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, cell);
9945
9946 if (tryAttachPartitionForeignKey(fk,
9947 RelationGetRelid(partRel),
9948 parentConstrOid,
9949 numfks,
9950 mapped_conkey,
9951 confkey,
9952 conpfeqop))
9953 {
9954 attached = true;
9955 table_close(pkrel, NoLock);
9956 break;
9957 }
9958 }
9959 if (attached)
9960 {
9961 ReleaseSysCache(tuple);
9962 continue;
9963 }
9964
9965 /* No dice. Set up to create our own constraint */
9966 fkconstraint = makeNode(Constraint);
9967 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9968 RelationGetRelid(partRel),
9969 NameStr(constrForm->conname)))
9970 fkconstraint->conname =
9971 ChooseConstraintName(RelationGetRelationName(partRel),
9972 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
9973 "fkey",
9974 RelationGetNamespace(partRel), NIL);
9975 else
9976 fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
9977 fkconstraint->fk_upd_action = constrForm->confupdtype;
9978 fkconstraint->fk_del_action = constrForm->confdeltype;
9979 fkconstraint->deferrable = constrForm->condeferrable;
9980 fkconstraint->initdeferred = constrForm->condeferred;
9981 fkconstraint->fk_matchtype = constrForm->confmatchtype;
9982 for (int i = 0; i < numfks; i++)
9983 {
9984 Form_pg_attribute att;
9985
9986 att = TupleDescAttr(RelationGetDescr(partRel),
9987 mapped_conkey[i] - 1);
9988 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
9989 makeString(NameStr(att->attname)));
9990 }
9991
9992 indexOid = constrForm->conindid;
9993 constrOid =
9994 CreateConstraintEntry(fkconstraint->conname,
9995 constrForm->connamespace,
9996 CONSTRAINT_FOREIGN,
9997 fkconstraint->deferrable,
9998 fkconstraint->initdeferred,
9999 constrForm->convalidated,
10000 parentConstrOid,
10001 RelationGetRelid(partRel),
10002 mapped_conkey,
10003 numfks,
10004 numfks,
10005 InvalidOid, /* not a domain constraint */
10006 indexOid,
10007 constrForm->confrelid, /* same foreign rel */
10008 confkey,
10009 conpfeqop,
10010 conppeqop,
10011 conffeqop,
10012 numfks,
10013 fkconstraint->fk_upd_action,
10014 fkconstraint->fk_del_action,
10015 fkconstraint->fk_matchtype,
10016 NULL,
10017 NULL,
10018 NULL,
10019 false, /* islocal */
10020 1, /* inhcount */
10021 false, /* conNoInherit */
10022 true);
10023
10024 /* Set up partition dependencies for the new constraint */
10025 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10026 ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
10027 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10028 ObjectAddressSet(referenced, RelationRelationId,
10029 RelationGetRelid(partRel));
10030 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10031
10032 /* Done with the cloned constraint's tuple */
10033 ReleaseSysCache(tuple);
10034
10035 /* Make all this visible before recursing */
10036 CommandCounterIncrement();
10037
10038 addFkRecurseReferencing(wqueue,
10039 fkconstraint,
10040 partRel,
10041 pkrel,
10042 indexOid,
10043 constrOid,
10044 numfks,
10045 confkey,
10046 mapped_conkey,
10047 conpfeqop,
10048 conppeqop,
10049 conffeqop,
10050 false, /* no old check exists */
10051 AccessExclusiveLock);
10052 table_close(pkrel, NoLock);
10053 }
10054 }
10055
10056 /*
10057 * When the parent of a partition receives [the referencing side of] a foreign
10058 * key, we must propagate that foreign key to the partition. However, the
10059 * partition might already have an equivalent foreign key; this routine
10060 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
10061 * by the other parameters. If they are equivalent, create the link between
10062 * the two constraints and return true.
10063 *
10064 * If the given FK does not match the one defined by rest of the params,
10065 * return false.
10066 */
10067 static bool
tryAttachPartitionForeignKey(ForeignKeyCacheInfo * fk,Oid partRelid,Oid parentConstrOid,int numfks,AttrNumber * mapped_conkey,AttrNumber * confkey,Oid * conpfeqop)10068 tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
10069 Oid partRelid,
10070 Oid parentConstrOid,
10071 int numfks,
10072 AttrNumber *mapped_conkey,
10073 AttrNumber *confkey,
10074 Oid *conpfeqop)
10075 {
10076 HeapTuple parentConstrTup;
10077 Form_pg_constraint parentConstr;
10078 HeapTuple partcontup;
10079 Form_pg_constraint partConstr;
10080 Relation trigrel;
10081 ScanKeyData key;
10082 SysScanDesc scan;
10083 HeapTuple trigtup;
10084
10085 parentConstrTup = SearchSysCache1(CONSTROID,
10086 ObjectIdGetDatum(parentConstrOid));
10087 if (!HeapTupleIsValid(parentConstrTup))
10088 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
10089 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
10090
10091 /*
10092 * Do some quick & easy initial checks. If any of these fail, we cannot
10093 * use this constraint.
10094 */
10095 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
10096 {
10097 ReleaseSysCache(parentConstrTup);
10098 return false;
10099 }
10100 for (int i = 0; i < numfks; i++)
10101 {
10102 if (fk->conkey[i] != mapped_conkey[i] ||
10103 fk->confkey[i] != confkey[i] ||
10104 fk->conpfeqop[i] != conpfeqop[i])
10105 {
10106 ReleaseSysCache(parentConstrTup);
10107 return false;
10108 }
10109 }
10110
10111 /*
10112 * Looks good so far; do some more extensive checks. Presumably the check
10113 * for 'convalidated' could be dropped, since we don't really care about
10114 * that, but let's be careful for now.
10115 */
10116 partcontup = SearchSysCache1(CONSTROID,
10117 ObjectIdGetDatum(fk->conoid));
10118 if (!HeapTupleIsValid(partcontup))
10119 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
10120 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
10121 if (OidIsValid(partConstr->conparentid) ||
10122 !partConstr->convalidated ||
10123 partConstr->condeferrable != parentConstr->condeferrable ||
10124 partConstr->condeferred != parentConstr->condeferred ||
10125 partConstr->confupdtype != parentConstr->confupdtype ||
10126 partConstr->confdeltype != parentConstr->confdeltype ||
10127 partConstr->confmatchtype != parentConstr->confmatchtype)
10128 {
10129 ReleaseSysCache(parentConstrTup);
10130 ReleaseSysCache(partcontup);
10131 return false;
10132 }
10133
10134 ReleaseSysCache(partcontup);
10135 ReleaseSysCache(parentConstrTup);
10136
10137 /*
10138 * Looks good! Attach this constraint. The action triggers in the new
10139 * partition become redundant -- the parent table already has equivalent
10140 * ones, and those will be able to reach the partition. Remove the ones
10141 * in the partition. We identify them because they have our constraint
10142 * OID, as well as being on the referenced rel.
10143 */
10144 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10145 ScanKeyInit(&key,
10146 Anum_pg_trigger_tgconstraint,
10147 BTEqualStrategyNumber, F_OIDEQ,
10148 ObjectIdGetDatum(fk->conoid));
10149
10150 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
10151 NULL, 1, &key);
10152 while ((trigtup = systable_getnext(scan)) != NULL)
10153 {
10154 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
10155 ObjectAddress trigger;
10156
10157 if (trgform->tgconstrrelid != fk->conrelid)
10158 continue;
10159 if (trgform->tgrelid != fk->confrelid)
10160 continue;
10161
10162 /*
10163 * The constraint is originally set up to contain this trigger as an
10164 * implementation object, so there's a dependency record that links
10165 * the two; however, since the trigger is no longer needed, we remove
10166 * the dependency link in order to be able to drop the trigger while
10167 * keeping the constraint intact.
10168 */
10169 deleteDependencyRecordsFor(TriggerRelationId,
10170 trgform->oid,
10171 false);
10172 /* make dependency deletion visible to performDeletion */
10173 CommandCounterIncrement();
10174 ObjectAddressSet(trigger, TriggerRelationId,
10175 trgform->oid);
10176 performDeletion(&trigger, DROP_RESTRICT, 0);
10177 /* make trigger drop visible, in case the loop iterates */
10178 CommandCounterIncrement();
10179 }
10180
10181 systable_endscan(scan);
10182 table_close(trigrel, RowExclusiveLock);
10183
10184 ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
10185 CommandCounterIncrement();
10186 return true;
10187 }
10188
10189
10190 /*
10191 * ALTER TABLE ALTER CONSTRAINT
10192 *
10193 * Update the attributes of a constraint.
10194 *
10195 * Currently only works for Foreign Key constraints.
10196 *
10197 * If the constraint is modified, returns its address; otherwise, return
10198 * InvalidObjectAddress.
10199 */
10200 static ObjectAddress
ATExecAlterConstraint(Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode)10201 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
10202 bool recursing, LOCKMODE lockmode)
10203 {
10204 Constraint *cmdcon;
10205 Relation conrel;
10206 Relation tgrel;
10207 SysScanDesc scan;
10208 ScanKeyData skey[3];
10209 HeapTuple contuple;
10210 Form_pg_constraint currcon;
10211 ObjectAddress address;
10212 List *otherrelids = NIL;
10213 ListCell *lc;
10214
10215 cmdcon = castNode(Constraint, cmd->def);
10216
10217 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
10218 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
10219
10220 /*
10221 * Find and check the target constraint
10222 */
10223 ScanKeyInit(&skey[0],
10224 Anum_pg_constraint_conrelid,
10225 BTEqualStrategyNumber, F_OIDEQ,
10226 ObjectIdGetDatum(RelationGetRelid(rel)));
10227 ScanKeyInit(&skey[1],
10228 Anum_pg_constraint_contypid,
10229 BTEqualStrategyNumber, F_OIDEQ,
10230 ObjectIdGetDatum(InvalidOid));
10231 ScanKeyInit(&skey[2],
10232 Anum_pg_constraint_conname,
10233 BTEqualStrategyNumber, F_NAMEEQ,
10234 CStringGetDatum(cmdcon->conname));
10235 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
10236 true, NULL, 3, skey);
10237
10238 /* There can be at most one matching row */
10239 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
10240 ereport(ERROR,
10241 (errcode(ERRCODE_UNDEFINED_OBJECT),
10242 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
10243 cmdcon->conname, RelationGetRelationName(rel))));
10244
10245 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
10246 if (currcon->contype != CONSTRAINT_FOREIGN)
10247 ereport(ERROR,
10248 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10249 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
10250 cmdcon->conname, RelationGetRelationName(rel))));
10251
10252 /*
10253 * If it's not the topmost constraint, raise an error.
10254 *
10255 * Altering a non-topmost constraint leaves some triggers untouched, since
10256 * they are not directly connected to this constraint; also, pg_dump would
10257 * ignore the deferrability status of the individual constraint, since it
10258 * only dumps topmost constraints. Avoid these problems by refusing this
10259 * operation and telling the user to alter the parent constraint instead.
10260 */
10261 if (OidIsValid(currcon->conparentid))
10262 {
10263 HeapTuple tp;
10264 Oid parent = currcon->conparentid;
10265 char *ancestorname = NULL;
10266 char *ancestortable = NULL;
10267
10268 /* Loop to find the topmost constraint */
10269 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
10270 {
10271 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
10272
10273 /* If no parent, this is the constraint we want */
10274 if (!OidIsValid(contup->conparentid))
10275 {
10276 ancestorname = pstrdup(NameStr(contup->conname));
10277 ancestortable = get_rel_name(contup->conrelid);
10278 ReleaseSysCache(tp);
10279 break;
10280 }
10281
10282 parent = contup->conparentid;
10283 ReleaseSysCache(tp);
10284 }
10285
10286 ereport(ERROR,
10287 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
10288 cmdcon->conname, RelationGetRelationName(rel)),
10289 ancestorname && ancestortable ?
10290 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
10291 cmdcon->conname, ancestorname, ancestortable) : 0,
10292 errhint("You may alter the constraint it derives from, instead.")));
10293 }
10294
10295 /*
10296 * Do the actual catalog work. We can skip changing if already in the
10297 * desired state, but not if a partitioned table: partitions need to be
10298 * processed regardless, in case they had the constraint locally changed.
10299 */
10300 address = InvalidObjectAddress;
10301 if (currcon->condeferrable != cmdcon->deferrable ||
10302 currcon->condeferred != cmdcon->initdeferred ||
10303 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10304 {
10305 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
10306 &otherrelids, lockmode))
10307 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
10308 }
10309
10310 /*
10311 * ATExecConstrRecurse already invalidated relcache for the relations
10312 * having the constraint itself; here we also invalidate for relations
10313 * that have any triggers that are part of the constraint.
10314 */
10315 foreach(lc, otherrelids)
10316 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
10317
10318 systable_endscan(scan);
10319
10320 table_close(tgrel, RowExclusiveLock);
10321 table_close(conrel, RowExclusiveLock);
10322
10323 return address;
10324 }
10325
10326 /*
10327 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
10328 * constraint is altered.
10329 *
10330 * *otherrelids is appended OIDs of relations containing affected triggers.
10331 *
10332 * Note that we must recurse even when the values are correct, in case
10333 * indirect descendants have had their constraints altered locally.
10334 * (This could be avoided if we forbade altering constraints in partitions
10335 * but existing releases don't do that.)
10336 */
10337 static bool
ATExecAlterConstrRecurse(Constraint * cmdcon,Relation conrel,Relation tgrel,Relation rel,HeapTuple contuple,List ** otherrelids,LOCKMODE lockmode)10338 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
10339 Relation rel, HeapTuple contuple, List **otherrelids,
10340 LOCKMODE lockmode)
10341 {
10342 Form_pg_constraint currcon;
10343 Oid conoid;
10344 Oid refrelid;
10345 bool changed = false;
10346
10347 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
10348 conoid = currcon->oid;
10349 refrelid = currcon->confrelid;
10350
10351 /*
10352 * Update pg_constraint with the flags from cmdcon.
10353 *
10354 * If called to modify a constraint that's already in the desired state,
10355 * silently do nothing.
10356 */
10357 if (currcon->condeferrable != cmdcon->deferrable ||
10358 currcon->condeferred != cmdcon->initdeferred)
10359 {
10360 HeapTuple copyTuple;
10361 Form_pg_constraint copy_con;
10362 HeapTuple tgtuple;
10363 ScanKeyData tgkey;
10364 SysScanDesc tgscan;
10365
10366 copyTuple = heap_copytuple(contuple);
10367 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
10368 copy_con->condeferrable = cmdcon->deferrable;
10369 copy_con->condeferred = cmdcon->initdeferred;
10370 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
10371
10372 InvokeObjectPostAlterHook(ConstraintRelationId,
10373 conoid, 0);
10374
10375 heap_freetuple(copyTuple);
10376 changed = true;
10377
10378 /* Make new constraint flags visible to others */
10379 CacheInvalidateRelcache(rel);
10380
10381 /*
10382 * Now we need to update the multiple entries in pg_trigger that
10383 * implement the constraint.
10384 */
10385 ScanKeyInit(&tgkey,
10386 Anum_pg_trigger_tgconstraint,
10387 BTEqualStrategyNumber, F_OIDEQ,
10388 ObjectIdGetDatum(conoid));
10389 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
10390 NULL, 1, &tgkey);
10391 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
10392 {
10393 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
10394 Form_pg_trigger copy_tg;
10395 HeapTuple copyTuple;
10396
10397 /*
10398 * Remember OIDs of other relation(s) involved in FK constraint.
10399 * (Note: it's likely that we could skip forcing a relcache inval
10400 * for other rels that don't have a trigger whose properties
10401 * change, but let's be conservative.)
10402 */
10403 if (tgform->tgrelid != RelationGetRelid(rel))
10404 *otherrelids = list_append_unique_oid(*otherrelids,
10405 tgform->tgrelid);
10406
10407 /*
10408 * Update deferrability of RI_FKey_noaction_del,
10409 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
10410 * triggers, but not others; see createForeignKeyActionTriggers
10411 * and CreateFKCheckTrigger.
10412 */
10413 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
10414 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
10415 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
10416 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
10417 continue;
10418
10419 copyTuple = heap_copytuple(tgtuple);
10420 copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
10421
10422 copy_tg->tgdeferrable = cmdcon->deferrable;
10423 copy_tg->tginitdeferred = cmdcon->initdeferred;
10424 CatalogTupleUpdate(tgrel, ©Tuple->t_self, copyTuple);
10425
10426 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
10427
10428 heap_freetuple(copyTuple);
10429 }
10430
10431 systable_endscan(tgscan);
10432 }
10433
10434 /*
10435 * If the table at either end of the constraint is partitioned, we need to
10436 * recurse and handle every constraint that is a child of this one.
10437 *
10438 * (This assumes that the recurse flag is forcibly set for partitioned
10439 * tables, and not set for legacy inheritance, though we don't check for
10440 * that here.)
10441 */
10442 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
10443 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
10444 {
10445 ScanKeyData pkey;
10446 SysScanDesc pscan;
10447 HeapTuple childtup;
10448
10449 ScanKeyInit(&pkey,
10450 Anum_pg_constraint_conparentid,
10451 BTEqualStrategyNumber, F_OIDEQ,
10452 ObjectIdGetDatum(conoid));
10453
10454 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
10455 true, NULL, 1, &pkey);
10456
10457 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
10458 {
10459 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
10460 Relation childrel;
10461
10462 childrel = table_open(childcon->conrelid, lockmode);
10463 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
10464 otherrelids, lockmode);
10465 table_close(childrel, NoLock);
10466 }
10467
10468 systable_endscan(pscan);
10469 }
10470
10471 return changed;
10472 }
10473
10474 /*
10475 * ALTER TABLE VALIDATE CONSTRAINT
10476 *
10477 * XXX The reason we handle recursion here rather than at Phase 1 is because
10478 * there's no good way to skip recursing when handling foreign keys: there is
10479 * no need to lock children in that case, yet we wouldn't be able to avoid
10480 * doing so at that level.
10481 *
10482 * Return value is the address of the validated constraint. If the constraint
10483 * was already validated, InvalidObjectAddress is returned.
10484 */
10485 static ObjectAddress
ATExecValidateConstraint(List ** wqueue,Relation rel,char * constrName,bool recurse,bool recursing,LOCKMODE lockmode)10486 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
10487 bool recurse, bool recursing, LOCKMODE lockmode)
10488 {
10489 Relation conrel;
10490 SysScanDesc scan;
10491 ScanKeyData skey[3];
10492 HeapTuple tuple;
10493 Form_pg_constraint con;
10494 ObjectAddress address;
10495
10496 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
10497
10498 /*
10499 * Find and check the target constraint
10500 */
10501 ScanKeyInit(&skey[0],
10502 Anum_pg_constraint_conrelid,
10503 BTEqualStrategyNumber, F_OIDEQ,
10504 ObjectIdGetDatum(RelationGetRelid(rel)));
10505 ScanKeyInit(&skey[1],
10506 Anum_pg_constraint_contypid,
10507 BTEqualStrategyNumber, F_OIDEQ,
10508 ObjectIdGetDatum(InvalidOid));
10509 ScanKeyInit(&skey[2],
10510 Anum_pg_constraint_conname,
10511 BTEqualStrategyNumber, F_NAMEEQ,
10512 CStringGetDatum(constrName));
10513 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
10514 true, NULL, 3, skey);
10515
10516 /* There can be at most one matching row */
10517 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
10518 ereport(ERROR,
10519 (errcode(ERRCODE_UNDEFINED_OBJECT),
10520 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
10521 constrName, RelationGetRelationName(rel))));
10522
10523 con = (Form_pg_constraint) GETSTRUCT(tuple);
10524 if (con->contype != CONSTRAINT_FOREIGN &&
10525 con->contype != CONSTRAINT_CHECK)
10526 ereport(ERROR,
10527 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10528 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
10529 constrName, RelationGetRelationName(rel))));
10530
10531 if (!con->convalidated)
10532 {
10533 AlteredTableInfo *tab;
10534 HeapTuple copyTuple;
10535 Form_pg_constraint copy_con;
10536
10537 if (con->contype == CONSTRAINT_FOREIGN)
10538 {
10539 NewConstraint *newcon;
10540 Constraint *fkconstraint;
10541
10542 /* Queue validation for phase 3 */
10543 fkconstraint = makeNode(Constraint);
10544 /* for now this is all we need */
10545 fkconstraint->conname = constrName;
10546
10547 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10548 newcon->name = constrName;
10549 newcon->contype = CONSTR_FOREIGN;
10550 newcon->refrelid = con->confrelid;
10551 newcon->refindid = con->conindid;
10552 newcon->conid = con->oid;
10553 newcon->qual = (Node *) fkconstraint;
10554
10555 /* Find or create work queue entry for this table */
10556 tab = ATGetQueueEntry(wqueue, rel);
10557 tab->constraints = lappend(tab->constraints, newcon);
10558
10559 /*
10560 * We disallow creating invalid foreign keys to or from
10561 * partitioned tables, so ignoring the recursion bit is okay.
10562 */
10563 }
10564 else if (con->contype == CONSTRAINT_CHECK)
10565 {
10566 List *children = NIL;
10567 ListCell *child;
10568 NewConstraint *newcon;
10569 bool isnull;
10570 Datum val;
10571 char *conbin;
10572
10573 /*
10574 * If we're recursing, the parent has already done this, so skip
10575 * it. Also, if the constraint is a NO INHERIT constraint, we
10576 * shouldn't try to look for it in the children.
10577 */
10578 if (!recursing && !con->connoinherit)
10579 children = find_all_inheritors(RelationGetRelid(rel),
10580 lockmode, NULL);
10581
10582 /*
10583 * For CHECK constraints, we must ensure that we only mark the
10584 * constraint as validated on the parent if it's already validated
10585 * on the children.
10586 *
10587 * We recurse before validating on the parent, to reduce risk of
10588 * deadlocks.
10589 */
10590 foreach(child, children)
10591 {
10592 Oid childoid = lfirst_oid(child);
10593 Relation childrel;
10594
10595 if (childoid == RelationGetRelid(rel))
10596 continue;
10597
10598 /*
10599 * If we are told not to recurse, there had better not be any
10600 * child tables, because we can't mark the constraint on the
10601 * parent valid unless it is valid for all child tables.
10602 */
10603 if (!recurse)
10604 ereport(ERROR,
10605 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10606 errmsg("constraint must be validated on child tables too")));
10607
10608 /* find_all_inheritors already got lock */
10609 childrel = table_open(childoid, NoLock);
10610
10611 ATExecValidateConstraint(wqueue, childrel, constrName, false,
10612 true, lockmode);
10613 table_close(childrel, NoLock);
10614 }
10615
10616 /* Queue validation for phase 3 */
10617 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10618 newcon->name = constrName;
10619 newcon->contype = CONSTR_CHECK;
10620 newcon->refrelid = InvalidOid;
10621 newcon->refindid = InvalidOid;
10622 newcon->conid = con->oid;
10623
10624 val = SysCacheGetAttr(CONSTROID, tuple,
10625 Anum_pg_constraint_conbin, &isnull);
10626 if (isnull)
10627 elog(ERROR, "null conbin for constraint %u", con->oid);
10628
10629 conbin = TextDatumGetCString(val);
10630 newcon->qual = (Node *) stringToNode(conbin);
10631
10632 /* Find or create work queue entry for this table */
10633 tab = ATGetQueueEntry(wqueue, rel);
10634 tab->constraints = lappend(tab->constraints, newcon);
10635
10636 /*
10637 * Invalidate relcache so that others see the new validated
10638 * constraint.
10639 */
10640 CacheInvalidateRelcache(rel);
10641 }
10642
10643 /*
10644 * Now update the catalog, while we have the door open.
10645 */
10646 copyTuple = heap_copytuple(tuple);
10647 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
10648 copy_con->convalidated = true;
10649 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
10650
10651 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
10652
10653 heap_freetuple(copyTuple);
10654
10655 ObjectAddressSet(address, ConstraintRelationId, con->oid);
10656 }
10657 else
10658 address = InvalidObjectAddress; /* already validated */
10659
10660 systable_endscan(scan);
10661
10662 table_close(conrel, RowExclusiveLock);
10663
10664 return address;
10665 }
10666
10667
10668 /*
10669 * transformColumnNameList - transform list of column names
10670 *
10671 * Lookup each name and return its attnum and type OID
10672 */
10673 static int
transformColumnNameList(Oid relId,List * colList,int16 * attnums,Oid * atttypids)10674 transformColumnNameList(Oid relId, List *colList,
10675 int16 *attnums, Oid *atttypids)
10676 {
10677 ListCell *l;
10678 int attnum;
10679
10680 attnum = 0;
10681 foreach(l, colList)
10682 {
10683 char *attname = strVal(lfirst(l));
10684 HeapTuple atttuple;
10685
10686 atttuple = SearchSysCacheAttName(relId, attname);
10687 if (!HeapTupleIsValid(atttuple))
10688 ereport(ERROR,
10689 (errcode(ERRCODE_UNDEFINED_COLUMN),
10690 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
10691 attname)));
10692 if (attnum >= INDEX_MAX_KEYS)
10693 ereport(ERROR,
10694 (errcode(ERRCODE_TOO_MANY_COLUMNS),
10695 errmsg("cannot have more than %d keys in a foreign key",
10696 INDEX_MAX_KEYS)));
10697 attnums[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->attnum;
10698 atttypids[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
10699 ReleaseSysCache(atttuple);
10700 attnum++;
10701 }
10702
10703 return attnum;
10704 }
10705
10706 /*
10707 * transformFkeyGetPrimaryKey -
10708 *
10709 * Look up the names, attnums, and types of the primary key attributes
10710 * for the pkrel. Also return the index OID and index opclasses of the
10711 * index supporting the primary key.
10712 *
10713 * All parameters except pkrel are output parameters. Also, the function
10714 * return value is the number of attributes in the primary key.
10715 *
10716 * Used when the column list in the REFERENCES specification is omitted.
10717 */
10718 static int
transformFkeyGetPrimaryKey(Relation pkrel,Oid * indexOid,List ** attnamelist,int16 * attnums,Oid * atttypids,Oid * opclasses)10719 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
10720 List **attnamelist,
10721 int16 *attnums, Oid *atttypids,
10722 Oid *opclasses)
10723 {
10724 List *indexoidlist;
10725 ListCell *indexoidscan;
10726 HeapTuple indexTuple = NULL;
10727 Form_pg_index indexStruct = NULL;
10728 Datum indclassDatum;
10729 bool isnull;
10730 oidvector *indclass;
10731 int i;
10732
10733 /*
10734 * Get the list of index OIDs for the table from the relcache, and look up
10735 * each one in the pg_index syscache until we find one marked primary key
10736 * (hopefully there isn't more than one such). Insist it's valid, too.
10737 */
10738 *indexOid = InvalidOid;
10739
10740 indexoidlist = RelationGetIndexList(pkrel);
10741
10742 foreach(indexoidscan, indexoidlist)
10743 {
10744 Oid indexoid = lfirst_oid(indexoidscan);
10745
10746 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
10747 if (!HeapTupleIsValid(indexTuple))
10748 elog(ERROR, "cache lookup failed for index %u", indexoid);
10749 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
10750 if (indexStruct->indisprimary && indexStruct->indisvalid)
10751 {
10752 /*
10753 * Refuse to use a deferrable primary key. This is per SQL spec,
10754 * and there would be a lot of interesting semantic problems if we
10755 * tried to allow it.
10756 */
10757 if (!indexStruct->indimmediate)
10758 ereport(ERROR,
10759 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
10760 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
10761 RelationGetRelationName(pkrel))));
10762
10763 *indexOid = indexoid;
10764 break;
10765 }
10766 ReleaseSysCache(indexTuple);
10767 }
10768
10769 list_free(indexoidlist);
10770
10771 /*
10772 * Check that we found it
10773 */
10774 if (!OidIsValid(*indexOid))
10775 ereport(ERROR,
10776 (errcode(ERRCODE_UNDEFINED_OBJECT),
10777 errmsg("there is no primary key for referenced table \"%s\"",
10778 RelationGetRelationName(pkrel))));
10779
10780 /* Must get indclass the hard way */
10781 indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
10782 Anum_pg_index_indclass, &isnull);
10783 Assert(!isnull);
10784 indclass = (oidvector *) DatumGetPointer(indclassDatum);
10785
10786 /*
10787 * Now build the list of PK attributes from the indkey definition (we
10788 * assume a primary key cannot have expressional elements)
10789 */
10790 *attnamelist = NIL;
10791 for (i = 0; i < indexStruct->indnkeyatts; i++)
10792 {
10793 int pkattno = indexStruct->indkey.values[i];
10794
10795 attnums[i] = pkattno;
10796 atttypids[i] = attnumTypeId(pkrel, pkattno);
10797 opclasses[i] = indclass->values[i];
10798 *attnamelist = lappend(*attnamelist,
10799 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
10800 }
10801
10802 ReleaseSysCache(indexTuple);
10803
10804 return i;
10805 }
10806
10807 /*
10808 * transformFkeyCheckAttrs -
10809 *
10810 * Make sure that the attributes of a referenced table belong to a unique
10811 * (or primary key) constraint. Return the OID of the index supporting
10812 * the constraint, as well as the opclasses associated with the index
10813 * columns.
10814 */
10815 static Oid
transformFkeyCheckAttrs(Relation pkrel,int numattrs,int16 * attnums,Oid * opclasses)10816 transformFkeyCheckAttrs(Relation pkrel,
10817 int numattrs, int16 *attnums,
10818 Oid *opclasses) /* output parameter */
10819 {
10820 Oid indexoid = InvalidOid;
10821 bool found = false;
10822 bool found_deferrable = false;
10823 List *indexoidlist;
10824 ListCell *indexoidscan;
10825 int i,
10826 j;
10827
10828 /*
10829 * Reject duplicate appearances of columns in the referenced-columns list.
10830 * Such a case is forbidden by the SQL standard, and even if we thought it
10831 * useful to allow it, there would be ambiguity about how to match the
10832 * list to unique indexes (in particular, it'd be unclear which index
10833 * opclass goes with which FK column).
10834 */
10835 for (i = 0; i < numattrs; i++)
10836 {
10837 for (j = i + 1; j < numattrs; j++)
10838 {
10839 if (attnums[i] == attnums[j])
10840 ereport(ERROR,
10841 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10842 errmsg("foreign key referenced-columns list must not contain duplicates")));
10843 }
10844 }
10845
10846 /*
10847 * Get the list of index OIDs for the table from the relcache, and look up
10848 * each one in the pg_index syscache, and match unique indexes to the list
10849 * of attnums we are given.
10850 */
10851 indexoidlist = RelationGetIndexList(pkrel);
10852
10853 foreach(indexoidscan, indexoidlist)
10854 {
10855 HeapTuple indexTuple;
10856 Form_pg_index indexStruct;
10857
10858 indexoid = lfirst_oid(indexoidscan);
10859 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
10860 if (!HeapTupleIsValid(indexTuple))
10861 elog(ERROR, "cache lookup failed for index %u", indexoid);
10862 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
10863
10864 /*
10865 * Must have the right number of columns; must be unique and not a
10866 * partial index; forget it if there are any expressions, too. Invalid
10867 * indexes are out as well.
10868 */
10869 if (indexStruct->indnkeyatts == numattrs &&
10870 indexStruct->indisunique &&
10871 indexStruct->indisvalid &&
10872 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
10873 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
10874 {
10875 Datum indclassDatum;
10876 bool isnull;
10877 oidvector *indclass;
10878
10879 /* Must get indclass the hard way */
10880 indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
10881 Anum_pg_index_indclass, &isnull);
10882 Assert(!isnull);
10883 indclass = (oidvector *) DatumGetPointer(indclassDatum);
10884
10885 /*
10886 * The given attnum list may match the index columns in any order.
10887 * Check for a match, and extract the appropriate opclasses while
10888 * we're at it.
10889 *
10890 * We know that attnums[] is duplicate-free per the test at the
10891 * start of this function, and we checked above that the number of
10892 * index columns agrees, so if we find a match for each attnums[]
10893 * entry then we must have a one-to-one match in some order.
10894 */
10895 for (i = 0; i < numattrs; i++)
10896 {
10897 found = false;
10898 for (j = 0; j < numattrs; j++)
10899 {
10900 if (attnums[i] == indexStruct->indkey.values[j])
10901 {
10902 opclasses[i] = indclass->values[j];
10903 found = true;
10904 break;
10905 }
10906 }
10907 if (!found)
10908 break;
10909 }
10910
10911 /*
10912 * Refuse to use a deferrable unique/primary key. This is per SQL
10913 * spec, and there would be a lot of interesting semantic problems
10914 * if we tried to allow it.
10915 */
10916 if (found && !indexStruct->indimmediate)
10917 {
10918 /*
10919 * Remember that we found an otherwise matching index, so that
10920 * we can generate a more appropriate error message.
10921 */
10922 found_deferrable = true;
10923 found = false;
10924 }
10925 }
10926 ReleaseSysCache(indexTuple);
10927 if (found)
10928 break;
10929 }
10930
10931 if (!found)
10932 {
10933 if (found_deferrable)
10934 ereport(ERROR,
10935 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
10936 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
10937 RelationGetRelationName(pkrel))));
10938 else
10939 ereport(ERROR,
10940 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10941 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
10942 RelationGetRelationName(pkrel))));
10943 }
10944
10945 list_free(indexoidlist);
10946
10947 return indexoid;
10948 }
10949
10950 /*
10951 * findFkeyCast -
10952 *
10953 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
10954 * Caller has equal regard for binary coercibility and for an exact match.
10955 */
10956 static CoercionPathType
findFkeyCast(Oid targetTypeId,Oid sourceTypeId,Oid * funcid)10957 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
10958 {
10959 CoercionPathType ret;
10960
10961 if (targetTypeId == sourceTypeId)
10962 {
10963 ret = COERCION_PATH_RELABELTYPE;
10964 *funcid = InvalidOid;
10965 }
10966 else
10967 {
10968 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
10969 COERCION_IMPLICIT, funcid);
10970 if (ret == COERCION_PATH_NONE)
10971 /* A previously-relied-upon cast is now gone. */
10972 elog(ERROR, "could not find cast from %u to %u",
10973 sourceTypeId, targetTypeId);
10974 }
10975
10976 return ret;
10977 }
10978
10979 /*
10980 * Permissions checks on the referenced table for ADD FOREIGN KEY
10981 *
10982 * Note: we have already checked that the user owns the referencing table,
10983 * else we'd have failed much earlier; no additional checks are needed for it.
10984 */
10985 static void
checkFkeyPermissions(Relation rel,int16 * attnums,int natts)10986 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
10987 {
10988 Oid roleid = GetUserId();
10989 AclResult aclresult;
10990 int i;
10991
10992 /* Okay if we have relation-level REFERENCES permission */
10993 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
10994 ACL_REFERENCES);
10995 if (aclresult == ACLCHECK_OK)
10996 return;
10997 /* Else we must have REFERENCES on each column */
10998 for (i = 0; i < natts; i++)
10999 {
11000 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
11001 roleid, ACL_REFERENCES);
11002 if (aclresult != ACLCHECK_OK)
11003 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
11004 RelationGetRelationName(rel));
11005 }
11006 }
11007
11008 /*
11009 * Scan the existing rows in a table to verify they meet a proposed FK
11010 * constraint.
11011 *
11012 * Caller must have opened and locked both relations appropriately.
11013 */
11014 static void
validateForeignKeyConstraint(char * conname,Relation rel,Relation pkrel,Oid pkindOid,Oid constraintOid)11015 validateForeignKeyConstraint(char *conname,
11016 Relation rel,
11017 Relation pkrel,
11018 Oid pkindOid,
11019 Oid constraintOid)
11020 {
11021 TupleTableSlot *slot;
11022 TableScanDesc scan;
11023 Trigger trig;
11024 Snapshot snapshot;
11025 MemoryContext oldcxt;
11026 MemoryContext perTupCxt;
11027
11028 ereport(DEBUG1,
11029 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
11030
11031 /*
11032 * Build a trigger call structure; we'll need it either way.
11033 */
11034 MemSet(&trig, 0, sizeof(trig));
11035 trig.tgoid = InvalidOid;
11036 trig.tgname = conname;
11037 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
11038 trig.tgisinternal = true;
11039 trig.tgconstrrelid = RelationGetRelid(pkrel);
11040 trig.tgconstrindid = pkindOid;
11041 trig.tgconstraint = constraintOid;
11042 trig.tgdeferrable = false;
11043 trig.tginitdeferred = false;
11044 /* we needn't fill in remaining fields */
11045
11046 /*
11047 * See if we can do it with a single LEFT JOIN query. A false result
11048 * indicates we must proceed with the fire-the-trigger method.
11049 */
11050 if (RI_Initial_Check(&trig, rel, pkrel))
11051 return;
11052
11053 /*
11054 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
11055 * if that tuple had just been inserted. If any of those fail, it should
11056 * ereport(ERROR) and that's that.
11057 */
11058 snapshot = RegisterSnapshot(GetLatestSnapshot());
11059 slot = table_slot_create(rel, NULL);
11060 scan = table_beginscan(rel, snapshot, 0, NULL);
11061
11062 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
11063 "validateForeignKeyConstraint",
11064 ALLOCSET_SMALL_SIZES);
11065 oldcxt = MemoryContextSwitchTo(perTupCxt);
11066
11067 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
11068 {
11069 LOCAL_FCINFO(fcinfo, 0);
11070 TriggerData trigdata = {0};
11071
11072 CHECK_FOR_INTERRUPTS();
11073
11074 /*
11075 * Make a call to the trigger function
11076 *
11077 * No parameters are passed, but we do set a context
11078 */
11079 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
11080
11081 /*
11082 * We assume RI_FKey_check_ins won't look at flinfo...
11083 */
11084 trigdata.type = T_TriggerData;
11085 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
11086 trigdata.tg_relation = rel;
11087 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
11088 trigdata.tg_trigslot = slot;
11089 trigdata.tg_trigger = &trig;
11090
11091 fcinfo->context = (Node *) &trigdata;
11092
11093 RI_FKey_check_ins(fcinfo);
11094
11095 MemoryContextReset(perTupCxt);
11096 }
11097
11098 MemoryContextSwitchTo(oldcxt);
11099 MemoryContextDelete(perTupCxt);
11100 table_endscan(scan);
11101 UnregisterSnapshot(snapshot);
11102 ExecDropSingleTupleTableSlot(slot);
11103 }
11104
11105 static void
CreateFKCheckTrigger(Oid myRelOid,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid,bool on_insert)11106 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
11107 Oid constraintOid, Oid indexOid, bool on_insert)
11108 {
11109 CreateTrigStmt *fk_trigger;
11110
11111 /*
11112 * Note: for a self-referential FK (referencing and referenced tables are
11113 * the same), it is important that the ON UPDATE action fires before the
11114 * CHECK action, since both triggers will fire on the same row during an
11115 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
11116 * state of the row. Triggers fire in name order, so we ensure this by
11117 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
11118 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
11119 */
11120 fk_trigger = makeNode(CreateTrigStmt);
11121 fk_trigger->replace = false;
11122 fk_trigger->isconstraint = true;
11123 fk_trigger->trigname = "RI_ConstraintTrigger_c";
11124 fk_trigger->relation = NULL;
11125
11126 /* Either ON INSERT or ON UPDATE */
11127 if (on_insert)
11128 {
11129 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
11130 fk_trigger->events = TRIGGER_TYPE_INSERT;
11131 }
11132 else
11133 {
11134 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
11135 fk_trigger->events = TRIGGER_TYPE_UPDATE;
11136 }
11137
11138 fk_trigger->args = NIL;
11139 fk_trigger->row = true;
11140 fk_trigger->timing = TRIGGER_TYPE_AFTER;
11141 fk_trigger->columns = NIL;
11142 fk_trigger->whenClause = NULL;
11143 fk_trigger->transitionRels = NIL;
11144 fk_trigger->deferrable = fkconstraint->deferrable;
11145 fk_trigger->initdeferred = fkconstraint->initdeferred;
11146 fk_trigger->constrrel = NULL;
11147
11148 (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid,
11149 indexOid, InvalidOid, InvalidOid, NULL, true, false);
11150
11151 /* Make changes-so-far visible */
11152 CommandCounterIncrement();
11153 }
11154
11155 /*
11156 * createForeignKeyActionTriggers
11157 * Create the referenced-side "action" triggers that implement a foreign
11158 * key.
11159 */
11160 static void
createForeignKeyActionTriggers(Relation rel,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid)11161 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
11162 Oid constraintOid, Oid indexOid)
11163 {
11164 CreateTrigStmt *fk_trigger;
11165
11166 /*
11167 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
11168 * DELETE action on the referenced table.
11169 */
11170 fk_trigger = makeNode(CreateTrigStmt);
11171 fk_trigger->replace = false;
11172 fk_trigger->isconstraint = true;
11173 fk_trigger->trigname = "RI_ConstraintTrigger_a";
11174 fk_trigger->relation = NULL;
11175 fk_trigger->args = NIL;
11176 fk_trigger->row = true;
11177 fk_trigger->timing = TRIGGER_TYPE_AFTER;
11178 fk_trigger->events = TRIGGER_TYPE_DELETE;
11179 fk_trigger->columns = NIL;
11180 fk_trigger->whenClause = NULL;
11181 fk_trigger->transitionRels = NIL;
11182 fk_trigger->constrrel = NULL;
11183 switch (fkconstraint->fk_del_action)
11184 {
11185 case FKCONSTR_ACTION_NOACTION:
11186 fk_trigger->deferrable = fkconstraint->deferrable;
11187 fk_trigger->initdeferred = fkconstraint->initdeferred;
11188 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
11189 break;
11190 case FKCONSTR_ACTION_RESTRICT:
11191 fk_trigger->deferrable = false;
11192 fk_trigger->initdeferred = false;
11193 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
11194 break;
11195 case FKCONSTR_ACTION_CASCADE:
11196 fk_trigger->deferrable = false;
11197 fk_trigger->initdeferred = false;
11198 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
11199 break;
11200 case FKCONSTR_ACTION_SETNULL:
11201 fk_trigger->deferrable = false;
11202 fk_trigger->initdeferred = false;
11203 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
11204 break;
11205 case FKCONSTR_ACTION_SETDEFAULT:
11206 fk_trigger->deferrable = false;
11207 fk_trigger->initdeferred = false;
11208 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
11209 break;
11210 default:
11211 elog(ERROR, "unrecognized FK action type: %d",
11212 (int) fkconstraint->fk_del_action);
11213 break;
11214 }
11215
11216 (void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel),
11217 constraintOid,
11218 indexOid, InvalidOid, InvalidOid, NULL, true, false);
11219
11220 /* Make changes-so-far visible */
11221 CommandCounterIncrement();
11222
11223 /*
11224 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
11225 * UPDATE action on the referenced table.
11226 */
11227 fk_trigger = makeNode(CreateTrigStmt);
11228 fk_trigger->replace = false;
11229 fk_trigger->isconstraint = true;
11230 fk_trigger->trigname = "RI_ConstraintTrigger_a";
11231 fk_trigger->relation = NULL;
11232 fk_trigger->args = NIL;
11233 fk_trigger->row = true;
11234 fk_trigger->timing = TRIGGER_TYPE_AFTER;
11235 fk_trigger->events = TRIGGER_TYPE_UPDATE;
11236 fk_trigger->columns = NIL;
11237 fk_trigger->whenClause = NULL;
11238 fk_trigger->transitionRels = NIL;
11239 fk_trigger->constrrel = NULL;
11240 switch (fkconstraint->fk_upd_action)
11241 {
11242 case FKCONSTR_ACTION_NOACTION:
11243 fk_trigger->deferrable = fkconstraint->deferrable;
11244 fk_trigger->initdeferred = fkconstraint->initdeferred;
11245 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
11246 break;
11247 case FKCONSTR_ACTION_RESTRICT:
11248 fk_trigger->deferrable = false;
11249 fk_trigger->initdeferred = false;
11250 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
11251 break;
11252 case FKCONSTR_ACTION_CASCADE:
11253 fk_trigger->deferrable = false;
11254 fk_trigger->initdeferred = false;
11255 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
11256 break;
11257 case FKCONSTR_ACTION_SETNULL:
11258 fk_trigger->deferrable = false;
11259 fk_trigger->initdeferred = false;
11260 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
11261 break;
11262 case FKCONSTR_ACTION_SETDEFAULT:
11263 fk_trigger->deferrable = false;
11264 fk_trigger->initdeferred = false;
11265 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
11266 break;
11267 default:
11268 elog(ERROR, "unrecognized FK action type: %d",
11269 (int) fkconstraint->fk_upd_action);
11270 break;
11271 }
11272
11273 (void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel),
11274 constraintOid,
11275 indexOid, InvalidOid, InvalidOid, NULL, true, false);
11276 }
11277
11278 /*
11279 * createForeignKeyCheckTriggers
11280 * Create the referencing-side "check" triggers that implement a foreign
11281 * key.
11282 */
11283 static void
createForeignKeyCheckTriggers(Oid myRelOid,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid)11284 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
11285 Constraint *fkconstraint, Oid constraintOid,
11286 Oid indexOid)
11287 {
11288 CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
11289 indexOid, true);
11290 CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
11291 indexOid, false);
11292 }
11293
11294 /*
11295 * ALTER TABLE DROP CONSTRAINT
11296 *
11297 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
11298 */
11299 static void
ATExecDropConstraint(Relation rel,const char * constrName,DropBehavior behavior,bool recurse,bool recursing,bool missing_ok,LOCKMODE lockmode)11300 ATExecDropConstraint(Relation rel, const char *constrName,
11301 DropBehavior behavior,
11302 bool recurse, bool recursing,
11303 bool missing_ok, LOCKMODE lockmode)
11304 {
11305 List *children;
11306 ListCell *child;
11307 Relation conrel;
11308 Form_pg_constraint con;
11309 SysScanDesc scan;
11310 ScanKeyData skey[3];
11311 HeapTuple tuple;
11312 bool found = false;
11313 bool is_no_inherit_constraint = false;
11314 char contype;
11315
11316 /* At top level, permission check was done in ATPrepCmd, else do it */
11317 if (recursing)
11318 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
11319
11320 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11321
11322 /*
11323 * Find and drop the target constraint
11324 */
11325 ScanKeyInit(&skey[0],
11326 Anum_pg_constraint_conrelid,
11327 BTEqualStrategyNumber, F_OIDEQ,
11328 ObjectIdGetDatum(RelationGetRelid(rel)));
11329 ScanKeyInit(&skey[1],
11330 Anum_pg_constraint_contypid,
11331 BTEqualStrategyNumber, F_OIDEQ,
11332 ObjectIdGetDatum(InvalidOid));
11333 ScanKeyInit(&skey[2],
11334 Anum_pg_constraint_conname,
11335 BTEqualStrategyNumber, F_NAMEEQ,
11336 CStringGetDatum(constrName));
11337 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11338 true, NULL, 3, skey);
11339
11340 /* There can be at most one matching row */
11341 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
11342 {
11343 ObjectAddress conobj;
11344
11345 con = (Form_pg_constraint) GETSTRUCT(tuple);
11346
11347 /* Don't drop inherited constraints */
11348 if (con->coninhcount > 0 && !recursing)
11349 ereport(ERROR,
11350 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11351 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
11352 constrName, RelationGetRelationName(rel))));
11353
11354 is_no_inherit_constraint = con->connoinherit;
11355 contype = con->contype;
11356
11357 /*
11358 * If it's a foreign-key constraint, we'd better lock the referenced
11359 * table and check that that's not in use, just as we've already done
11360 * for the constrained table (else we might, eg, be dropping a trigger
11361 * that has unfired events). But we can/must skip that in the
11362 * self-referential case.
11363 */
11364 if (contype == CONSTRAINT_FOREIGN &&
11365 con->confrelid != RelationGetRelid(rel))
11366 {
11367 Relation frel;
11368
11369 /* Must match lock taken by RemoveTriggerById: */
11370 frel = table_open(con->confrelid, AccessExclusiveLock);
11371 CheckTableNotInUse(frel, "ALTER TABLE");
11372 table_close(frel, NoLock);
11373 }
11374
11375 /*
11376 * Perform the actual constraint deletion
11377 */
11378 conobj.classId = ConstraintRelationId;
11379 conobj.objectId = con->oid;
11380 conobj.objectSubId = 0;
11381
11382 performDeletion(&conobj, behavior, 0);
11383
11384 found = true;
11385 }
11386
11387 systable_endscan(scan);
11388
11389 if (!found)
11390 {
11391 if (!missing_ok)
11392 {
11393 ereport(ERROR,
11394 (errcode(ERRCODE_UNDEFINED_OBJECT),
11395 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11396 constrName, RelationGetRelationName(rel))));
11397 }
11398 else
11399 {
11400 ereport(NOTICE,
11401 (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
11402 constrName, RelationGetRelationName(rel))));
11403 table_close(conrel, RowExclusiveLock);
11404 return;
11405 }
11406 }
11407
11408 /*
11409 * For partitioned tables, non-CHECK inherited constraints are dropped via
11410 * the dependency mechanism, so we're done here.
11411 */
11412 if (contype != CONSTRAINT_CHECK &&
11413 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11414 {
11415 table_close(conrel, RowExclusiveLock);
11416 return;
11417 }
11418
11419 /*
11420 * Propagate to children as appropriate. Unlike most other ALTER
11421 * routines, we have to do this one level of recursion at a time; we can't
11422 * use find_all_inheritors to do it in one pass.
11423 */
11424 if (!is_no_inherit_constraint)
11425 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
11426 else
11427 children = NIL;
11428
11429 /*
11430 * For a partitioned table, if partitions exist and we are told not to
11431 * recurse, it's a user error. It doesn't make sense to have a constraint
11432 * be defined only on the parent, especially if it's a partitioned table.
11433 */
11434 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
11435 children != NIL && !recurse)
11436 ereport(ERROR,
11437 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11438 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
11439 errhint("Do not specify the ONLY keyword.")));
11440
11441 foreach(child, children)
11442 {
11443 Oid childrelid = lfirst_oid(child);
11444 Relation childrel;
11445 HeapTuple copy_tuple;
11446
11447 /* find_inheritance_children already got lock */
11448 childrel = table_open(childrelid, NoLock);
11449 CheckTableNotInUse(childrel, "ALTER TABLE");
11450
11451 ScanKeyInit(&skey[0],
11452 Anum_pg_constraint_conrelid,
11453 BTEqualStrategyNumber, F_OIDEQ,
11454 ObjectIdGetDatum(childrelid));
11455 ScanKeyInit(&skey[1],
11456 Anum_pg_constraint_contypid,
11457 BTEqualStrategyNumber, F_OIDEQ,
11458 ObjectIdGetDatum(InvalidOid));
11459 ScanKeyInit(&skey[2],
11460 Anum_pg_constraint_conname,
11461 BTEqualStrategyNumber, F_NAMEEQ,
11462 CStringGetDatum(constrName));
11463 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11464 true, NULL, 3, skey);
11465
11466 /* There can be at most one matching row */
11467 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11468 ereport(ERROR,
11469 (errcode(ERRCODE_UNDEFINED_OBJECT),
11470 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11471 constrName,
11472 RelationGetRelationName(childrel))));
11473
11474 copy_tuple = heap_copytuple(tuple);
11475
11476 systable_endscan(scan);
11477
11478 con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
11479
11480 /* Right now only CHECK constraints can be inherited */
11481 if (con->contype != CONSTRAINT_CHECK)
11482 elog(ERROR, "inherited constraint is not a CHECK constraint");
11483
11484 if (con->coninhcount <= 0) /* shouldn't happen */
11485 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
11486 childrelid, constrName);
11487
11488 if (recurse)
11489 {
11490 /*
11491 * If the child constraint has other definition sources, just
11492 * decrement its inheritance count; if not, recurse to delete it.
11493 */
11494 if (con->coninhcount == 1 && !con->conislocal)
11495 {
11496 /* Time to delete this child constraint, too */
11497 ATExecDropConstraint(childrel, constrName, behavior,
11498 true, true,
11499 false, lockmode);
11500 }
11501 else
11502 {
11503 /* Child constraint must survive my deletion */
11504 con->coninhcount--;
11505 CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
11506
11507 /* Make update visible */
11508 CommandCounterIncrement();
11509 }
11510 }
11511 else
11512 {
11513 /*
11514 * If we were told to drop ONLY in this table (no recursion), we
11515 * need to mark the inheritors' constraints as locally defined
11516 * rather than inherited.
11517 */
11518 con->coninhcount--;
11519 con->conislocal = true;
11520
11521 CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
11522
11523 /* Make update visible */
11524 CommandCounterIncrement();
11525 }
11526
11527 heap_freetuple(copy_tuple);
11528
11529 table_close(childrel, NoLock);
11530 }
11531
11532 table_close(conrel, RowExclusiveLock);
11533 }
11534
11535 /*
11536 * ALTER COLUMN TYPE
11537 *
11538 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
11539 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
11540 * transformed (and must be, because we rely on some transformed fields).
11541 *
11542 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
11543 * table will be done "in parallel" during phase 3, so all the USING
11544 * expressions should be parsed assuming the original column types. Also,
11545 * this allows a USING expression to refer to a field that will be dropped.
11546 *
11547 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
11548 * the first two execution steps in phase 2; they must not see the effects
11549 * of any other subcommand types, since the USING expressions are parsed
11550 * against the unmodified table's state.
11551 */
11552 static void
ATPrepAlterColumnType(List ** wqueue,AlteredTableInfo * tab,Relation rel,bool recurse,bool recursing,AlterTableCmd * cmd,LOCKMODE lockmode,AlterTableUtilityContext * context)11553 ATPrepAlterColumnType(List **wqueue,
11554 AlteredTableInfo *tab, Relation rel,
11555 bool recurse, bool recursing,
11556 AlterTableCmd *cmd, LOCKMODE lockmode,
11557 AlterTableUtilityContext *context)
11558 {
11559 char *colName = cmd->name;
11560 ColumnDef *def = (ColumnDef *) cmd->def;
11561 TypeName *typeName = def->typeName;
11562 Node *transform = def->cooked_default;
11563 HeapTuple tuple;
11564 Form_pg_attribute attTup;
11565 AttrNumber attnum;
11566 Oid targettype;
11567 int32 targettypmod;
11568 Oid targetcollid;
11569 NewColumnValue *newval;
11570 ParseState *pstate = make_parsestate(NULL);
11571 AclResult aclresult;
11572 bool is_expr;
11573
11574 if (rel->rd_rel->reloftype && !recursing)
11575 ereport(ERROR,
11576 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11577 errmsg("cannot alter column type of typed table")));
11578
11579 /* lookup the attribute so we can check inheritance status */
11580 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
11581 if (!HeapTupleIsValid(tuple))
11582 ereport(ERROR,
11583 (errcode(ERRCODE_UNDEFINED_COLUMN),
11584 errmsg("column \"%s\" of relation \"%s\" does not exist",
11585 colName, RelationGetRelationName(rel))));
11586 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
11587 attnum = attTup->attnum;
11588
11589 /* Can't alter a system attribute */
11590 if (attnum <= 0)
11591 ereport(ERROR,
11592 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11593 errmsg("cannot alter system column \"%s\"",
11594 colName)));
11595
11596 /*
11597 * Don't alter inherited columns. At outer level, there had better not be
11598 * any inherited definition; when recursing, we assume this was checked at
11599 * the parent level (see below).
11600 */
11601 if (attTup->attinhcount > 0 && !recursing)
11602 ereport(ERROR,
11603 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11604 errmsg("cannot alter inherited column \"%s\"",
11605 colName)));
11606
11607 /* Don't alter columns used in the partition key */
11608 if (has_partition_attrs(rel,
11609 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
11610 &is_expr))
11611 ereport(ERROR,
11612 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11613 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
11614 colName, RelationGetRelationName(rel))));
11615
11616 /* Look up the target type */
11617 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
11618
11619 aclresult = pg_type_aclcheck(targettype, GetUserId(), ACL_USAGE);
11620 if (aclresult != ACLCHECK_OK)
11621 aclcheck_error_type(aclresult, targettype);
11622
11623 /* And the collation */
11624 targetcollid = GetColumnDefCollation(NULL, def, targettype);
11625
11626 /* make sure datatype is legal for a column */
11627 CheckAttributeType(colName, targettype, targetcollid,
11628 list_make1_oid(rel->rd_rel->reltype),
11629 0);
11630
11631 if (tab->relkind == RELKIND_RELATION ||
11632 tab->relkind == RELKIND_PARTITIONED_TABLE)
11633 {
11634 /*
11635 * Set up an expression to transform the old data value to the new
11636 * type. If a USING option was given, use the expression as
11637 * transformed by transformAlterTableStmt, else just take the old
11638 * value and try to coerce it. We do this first so that type
11639 * incompatibility can be detected before we waste effort, and because
11640 * we need the expression to be parsed against the original table row
11641 * type.
11642 */
11643 if (!transform)
11644 {
11645 transform = (Node *) makeVar(1, attnum,
11646 attTup->atttypid, attTup->atttypmod,
11647 attTup->attcollation,
11648 0);
11649 }
11650
11651 transform = coerce_to_target_type(pstate,
11652 transform, exprType(transform),
11653 targettype, targettypmod,
11654 COERCION_ASSIGNMENT,
11655 COERCE_IMPLICIT_CAST,
11656 -1);
11657 if (transform == NULL)
11658 {
11659 /* error text depends on whether USING was specified or not */
11660 if (def->cooked_default != NULL)
11661 ereport(ERROR,
11662 (errcode(ERRCODE_DATATYPE_MISMATCH),
11663 errmsg("result of USING clause for column \"%s\""
11664 " cannot be cast automatically to type %s",
11665 colName, format_type_be(targettype)),
11666 errhint("You might need to add an explicit cast.")));
11667 else
11668 ereport(ERROR,
11669 (errcode(ERRCODE_DATATYPE_MISMATCH),
11670 errmsg("column \"%s\" cannot be cast automatically to type %s",
11671 colName, format_type_be(targettype)),
11672 /* translator: USING is SQL, don't translate it */
11673 errhint("You might need to specify \"USING %s::%s\".",
11674 quote_identifier(colName),
11675 format_type_with_typemod(targettype,
11676 targettypmod))));
11677 }
11678
11679 /* Fix collations after all else */
11680 assign_expr_collations(pstate, transform);
11681
11682 /* Plan the expr now so we can accurately assess the need to rewrite. */
11683 transform = (Node *) expression_planner((Expr *) transform);
11684
11685 /*
11686 * Add a work queue item to make ATRewriteTable update the column
11687 * contents.
11688 */
11689 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
11690 newval->attnum = attnum;
11691 newval->expr = (Expr *) transform;
11692 newval->is_generated = false;
11693
11694 tab->newvals = lappend(tab->newvals, newval);
11695 if (ATColumnChangeRequiresRewrite(transform, attnum))
11696 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
11697 }
11698 else if (transform)
11699 ereport(ERROR,
11700 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11701 errmsg("\"%s\" is not a table",
11702 RelationGetRelationName(rel))));
11703
11704 if (tab->relkind == RELKIND_COMPOSITE_TYPE ||
11705 tab->relkind == RELKIND_FOREIGN_TABLE)
11706 {
11707 /*
11708 * For composite types and foreign tables, do this check now. Regular
11709 * tables will check it later when the table is being rewritten.
11710 */
11711 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
11712 }
11713
11714 ReleaseSysCache(tuple);
11715
11716 /*
11717 * Recurse manually by queueing a new command for each child, if
11718 * necessary. We cannot apply ATSimpleRecursion here because we need to
11719 * remap attribute numbers in the USING expression, if any.
11720 *
11721 * If we are told not to recurse, there had better not be any child
11722 * tables; else the alter would put them out of step.
11723 */
11724 if (recurse)
11725 {
11726 Oid relid = RelationGetRelid(rel);
11727 List *child_oids,
11728 *child_numparents;
11729 ListCell *lo,
11730 *li;
11731
11732 child_oids = find_all_inheritors(relid, lockmode,
11733 &child_numparents);
11734
11735 /*
11736 * find_all_inheritors does the recursive search of the inheritance
11737 * hierarchy, so all we have to do is process all of the relids in the
11738 * list that it returns.
11739 */
11740 forboth(lo, child_oids, li, child_numparents)
11741 {
11742 Oid childrelid = lfirst_oid(lo);
11743 int numparents = lfirst_int(li);
11744 Relation childrel;
11745 HeapTuple childtuple;
11746 Form_pg_attribute childattTup;
11747
11748 if (childrelid == relid)
11749 continue;
11750
11751 /* find_all_inheritors already got lock */
11752 childrel = relation_open(childrelid, NoLock);
11753 CheckTableNotInUse(childrel, "ALTER TABLE");
11754
11755 /*
11756 * Verify that the child doesn't have any inherited definitions of
11757 * this column that came from outside this inheritance hierarchy.
11758 * (renameatt makes a similar test, though in a different way
11759 * because of its different recursion mechanism.)
11760 */
11761 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
11762 colName);
11763 if (!HeapTupleIsValid(childtuple))
11764 ereport(ERROR,
11765 (errcode(ERRCODE_UNDEFINED_COLUMN),
11766 errmsg("column \"%s\" of relation \"%s\" does not exist",
11767 colName, RelationGetRelationName(childrel))));
11768 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
11769
11770 if (childattTup->attinhcount > numparents)
11771 ereport(ERROR,
11772 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11773 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
11774 colName, RelationGetRelationName(childrel))));
11775
11776 ReleaseSysCache(childtuple);
11777
11778 /*
11779 * Remap the attribute numbers. If no USING expression was
11780 * specified, there is no need for this step.
11781 */
11782 if (def->cooked_default)
11783 {
11784 AttrMap *attmap;
11785 bool found_whole_row;
11786
11787 /* create a copy to scribble on */
11788 cmd = copyObject(cmd);
11789
11790 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
11791 RelationGetDescr(rel));
11792 ((ColumnDef *) cmd->def)->cooked_default =
11793 map_variable_attnos(def->cooked_default,
11794 1, 0,
11795 attmap,
11796 InvalidOid, &found_whole_row);
11797 if (found_whole_row)
11798 ereport(ERROR,
11799 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11800 errmsg("cannot convert whole-row table reference"),
11801 errdetail("USING expression contains a whole-row table reference.")));
11802 pfree(attmap);
11803 }
11804 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
11805 relation_close(childrel, NoLock);
11806 }
11807 }
11808 else if (!recursing &&
11809 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
11810 ereport(ERROR,
11811 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11812 errmsg("type of inherited column \"%s\" must be changed in child tables too",
11813 colName)));
11814
11815 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
11816 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
11817 }
11818
11819 /*
11820 * When the data type of a column is changed, a rewrite might not be required
11821 * if the new type is sufficiently identical to the old one, and the USING
11822 * clause isn't trying to insert some other value. It's safe to skip the
11823 * rewrite in these cases:
11824 *
11825 * - the old type is binary coercible to the new type
11826 * - the new type is an unconstrained domain over the old type
11827 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
11828 *
11829 * In the case of a constrained domain, we could get by with scanning the
11830 * table and checking the constraint rather than actually rewriting it, but we
11831 * don't currently try to do that.
11832 */
11833 static bool
ATColumnChangeRequiresRewrite(Node * expr,AttrNumber varattno)11834 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
11835 {
11836 Assert(expr != NULL);
11837
11838 for (;;)
11839 {
11840 /* only one varno, so no need to check that */
11841 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
11842 return false;
11843 else if (IsA(expr, RelabelType))
11844 expr = (Node *) ((RelabelType *) expr)->arg;
11845 else if (IsA(expr, CoerceToDomain))
11846 {
11847 CoerceToDomain *d = (CoerceToDomain *) expr;
11848
11849 if (DomainHasConstraints(d->resulttype))
11850 return true;
11851 expr = (Node *) d->arg;
11852 }
11853 else if (IsA(expr, FuncExpr))
11854 {
11855 FuncExpr *f = (FuncExpr *) expr;
11856
11857 switch (f->funcid)
11858 {
11859 case F_TIMESTAMPTZ_TIMESTAMP:
11860 case F_TIMESTAMP_TIMESTAMPTZ:
11861 if (TimestampTimestampTzRequiresRewrite())
11862 return true;
11863 else
11864 expr = linitial(f->args);
11865 break;
11866 default:
11867 return true;
11868 }
11869 }
11870 else
11871 return true;
11872 }
11873 }
11874
11875 /*
11876 * ALTER COLUMN .. SET DATA TYPE
11877 *
11878 * Return the address of the modified column.
11879 */
11880 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode)11881 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
11882 AlterTableCmd *cmd, LOCKMODE lockmode)
11883 {
11884 char *colName = cmd->name;
11885 ColumnDef *def = (ColumnDef *) cmd->def;
11886 TypeName *typeName = def->typeName;
11887 HeapTuple heapTup;
11888 Form_pg_attribute attTup,
11889 attOldTup;
11890 AttrNumber attnum;
11891 HeapTuple typeTuple;
11892 Form_pg_type tform;
11893 Oid targettype;
11894 int32 targettypmod;
11895 Oid targetcollid;
11896 Node *defaultexpr;
11897 Relation attrelation;
11898 Relation depRel;
11899 ScanKeyData key[3];
11900 SysScanDesc scan;
11901 HeapTuple depTup;
11902 ObjectAddress address;
11903
11904 /*
11905 * Clear all the missing values if we're rewriting the table, since this
11906 * renders them pointless.
11907 */
11908 if (tab->rewrite)
11909 {
11910 Relation newrel;
11911
11912 newrel = table_open(RelationGetRelid(rel), NoLock);
11913 RelationClearMissing(newrel);
11914 relation_close(newrel, NoLock);
11915 /* make sure we don't conflict with later attribute modifications */
11916 CommandCounterIncrement();
11917 }
11918
11919 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
11920
11921 /* Look up the target column */
11922 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
11923 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
11924 ereport(ERROR,
11925 (errcode(ERRCODE_UNDEFINED_COLUMN),
11926 errmsg("column \"%s\" of relation \"%s\" does not exist",
11927 colName, RelationGetRelationName(rel))));
11928 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
11929 attnum = attTup->attnum;
11930 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
11931
11932 /* Check for multiple ALTER TYPE on same column --- can't cope */
11933 if (attTup->atttypid != attOldTup->atttypid ||
11934 attTup->atttypmod != attOldTup->atttypmod)
11935 ereport(ERROR,
11936 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11937 errmsg("cannot alter type of column \"%s\" twice",
11938 colName)));
11939
11940 /* Look up the target type (should not fail, since prep found it) */
11941 typeTuple = typenameType(NULL, typeName, &targettypmod);
11942 tform = (Form_pg_type) GETSTRUCT(typeTuple);
11943 targettype = tform->oid;
11944 /* And the collation */
11945 targetcollid = GetColumnDefCollation(NULL, def, targettype);
11946
11947 /*
11948 * If there is a default expression for the column, get it and ensure we
11949 * can coerce it to the new datatype. (We must do this before changing
11950 * the column type, because build_column_default itself will try to
11951 * coerce, and will not issue the error message we want if it fails.)
11952 *
11953 * We remove any implicit coercion steps at the top level of the old
11954 * default expression; this has been agreed to satisfy the principle of
11955 * least surprise. (The conversion to the new column type should act like
11956 * it started from what the user sees as the stored expression, and the
11957 * implicit coercions aren't going to be shown.)
11958 */
11959 if (attTup->atthasdef)
11960 {
11961 defaultexpr = build_column_default(rel, attnum);
11962 Assert(defaultexpr);
11963 defaultexpr = strip_implicit_coercions(defaultexpr);
11964 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
11965 defaultexpr, exprType(defaultexpr),
11966 targettype, targettypmod,
11967 COERCION_ASSIGNMENT,
11968 COERCE_IMPLICIT_CAST,
11969 -1);
11970 if (defaultexpr == NULL)
11971 {
11972 if (attTup->attgenerated)
11973 ereport(ERROR,
11974 (errcode(ERRCODE_DATATYPE_MISMATCH),
11975 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
11976 colName, format_type_be(targettype))));
11977 else
11978 ereport(ERROR,
11979 (errcode(ERRCODE_DATATYPE_MISMATCH),
11980 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
11981 colName, format_type_be(targettype))));
11982 }
11983 }
11984 else
11985 defaultexpr = NULL;
11986
11987 /*
11988 * Find everything that depends on the column (constraints, indexes, etc),
11989 * and record enough information to let us recreate the objects.
11990 *
11991 * The actual recreation does not happen here, but only after we have
11992 * performed all the individual ALTER TYPE operations. We have to save
11993 * the info before executing ALTER TYPE, though, else the deparser will
11994 * get confused.
11995 */
11996 depRel = table_open(DependRelationId, RowExclusiveLock);
11997
11998 ScanKeyInit(&key[0],
11999 Anum_pg_depend_refclassid,
12000 BTEqualStrategyNumber, F_OIDEQ,
12001 ObjectIdGetDatum(RelationRelationId));
12002 ScanKeyInit(&key[1],
12003 Anum_pg_depend_refobjid,
12004 BTEqualStrategyNumber, F_OIDEQ,
12005 ObjectIdGetDatum(RelationGetRelid(rel)));
12006 ScanKeyInit(&key[2],
12007 Anum_pg_depend_refobjsubid,
12008 BTEqualStrategyNumber, F_INT4EQ,
12009 Int32GetDatum((int32) attnum));
12010
12011 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
12012 NULL, 3, key);
12013
12014 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
12015 {
12016 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
12017 ObjectAddress foundObject;
12018
12019 /* We don't expect any PIN dependencies on columns */
12020 if (foundDep->deptype == DEPENDENCY_PIN)
12021 elog(ERROR, "cannot alter type of a pinned column");
12022
12023 foundObject.classId = foundDep->classid;
12024 foundObject.objectId = foundDep->objid;
12025 foundObject.objectSubId = foundDep->objsubid;
12026
12027 switch (getObjectClass(&foundObject))
12028 {
12029 case OCLASS_CLASS:
12030 {
12031 char relKind = get_rel_relkind(foundObject.objectId);
12032
12033 if (relKind == RELKIND_INDEX ||
12034 relKind == RELKIND_PARTITIONED_INDEX)
12035 {
12036 Assert(foundObject.objectSubId == 0);
12037 RememberIndexForRebuilding(foundObject.objectId, tab);
12038 }
12039 else if (relKind == RELKIND_SEQUENCE)
12040 {
12041 /*
12042 * This must be a SERIAL column's sequence. We need
12043 * not do anything to it.
12044 */
12045 Assert(foundObject.objectSubId == 0);
12046 }
12047 else if (relKind == RELKIND_RELATION &&
12048 foundObject.objectSubId != 0 &&
12049 get_attgenerated(foundObject.objectId, foundObject.objectSubId))
12050 {
12051 /*
12052 * Changing the type of a column that is used by a
12053 * generated column is not allowed by SQL standard. It
12054 * might be doable with some thinking and effort.
12055 */
12056 ereport(ERROR,
12057 (errcode(ERRCODE_SYNTAX_ERROR),
12058 errmsg("cannot alter type of a column used by a generated column"),
12059 errdetail("Column \"%s\" is used by generated column \"%s\".",
12060 colName, get_attname(foundObject.objectId, foundObject.objectSubId, false))));
12061 }
12062 else
12063 {
12064 /* Not expecting any other direct dependencies... */
12065 elog(ERROR, "unexpected object depending on column: %s",
12066 getObjectDescription(&foundObject, false));
12067 }
12068 break;
12069 }
12070
12071 case OCLASS_CONSTRAINT:
12072 Assert(foundObject.objectSubId == 0);
12073 RememberConstraintForRebuilding(foundObject.objectId, tab);
12074 break;
12075
12076 case OCLASS_REWRITE:
12077 /* XXX someday see if we can cope with revising views */
12078 ereport(ERROR,
12079 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12080 errmsg("cannot alter type of a column used by a view or rule"),
12081 errdetail("%s depends on column \"%s\"",
12082 getObjectDescription(&foundObject, false),
12083 colName)));
12084 break;
12085
12086 case OCLASS_TRIGGER:
12087
12088 /*
12089 * A trigger can depend on a column because the column is
12090 * specified as an update target, or because the column is
12091 * used in the trigger's WHEN condition. The first case would
12092 * not require any extra work, but the second case would
12093 * require updating the WHEN expression, which will take a
12094 * significant amount of new code. Since we can't easily tell
12095 * which case applies, we punt for both. FIXME someday.
12096 */
12097 ereport(ERROR,
12098 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12099 errmsg("cannot alter type of a column used in a trigger definition"),
12100 errdetail("%s depends on column \"%s\"",
12101 getObjectDescription(&foundObject, false),
12102 colName)));
12103 break;
12104
12105 case OCLASS_POLICY:
12106
12107 /*
12108 * A policy can depend on a column because the column is
12109 * specified in the policy's USING or WITH CHECK qual
12110 * expressions. It might be possible to rewrite and recheck
12111 * the policy expression, but punt for now. It's certainly
12112 * easy enough to remove and recreate the policy; still, FIXME
12113 * someday.
12114 */
12115 ereport(ERROR,
12116 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12117 errmsg("cannot alter type of a column used in a policy definition"),
12118 errdetail("%s depends on column \"%s\"",
12119 getObjectDescription(&foundObject, false),
12120 colName)));
12121 break;
12122
12123 case OCLASS_DEFAULT:
12124
12125 /*
12126 * Ignore the column's default expression, since we will fix
12127 * it below.
12128 */
12129 Assert(defaultexpr);
12130 break;
12131
12132 case OCLASS_STATISTIC_EXT:
12133
12134 /*
12135 * Give the extended-stats machinery a chance to fix anything
12136 * that this column type change would break.
12137 */
12138 RememberStatisticsForRebuilding(foundObject.objectId, tab);
12139 break;
12140
12141 case OCLASS_PROC:
12142 case OCLASS_TYPE:
12143 case OCLASS_CAST:
12144 case OCLASS_COLLATION:
12145 case OCLASS_CONVERSION:
12146 case OCLASS_LANGUAGE:
12147 case OCLASS_LARGEOBJECT:
12148 case OCLASS_OPERATOR:
12149 case OCLASS_OPCLASS:
12150 case OCLASS_OPFAMILY:
12151 case OCLASS_AM:
12152 case OCLASS_AMOP:
12153 case OCLASS_AMPROC:
12154 case OCLASS_SCHEMA:
12155 case OCLASS_TSPARSER:
12156 case OCLASS_TSDICT:
12157 case OCLASS_TSTEMPLATE:
12158 case OCLASS_TSCONFIG:
12159 case OCLASS_ROLE:
12160 case OCLASS_DATABASE:
12161 case OCLASS_TBLSPACE:
12162 case OCLASS_FDW:
12163 case OCLASS_FOREIGN_SERVER:
12164 case OCLASS_USER_MAPPING:
12165 case OCLASS_DEFACL:
12166 case OCLASS_EXTENSION:
12167 case OCLASS_EVENT_TRIGGER:
12168 case OCLASS_PUBLICATION:
12169 case OCLASS_PUBLICATION_REL:
12170 case OCLASS_SUBSCRIPTION:
12171 case OCLASS_TRANSFORM:
12172
12173 /*
12174 * We don't expect any of these sorts of objects to depend on
12175 * a column.
12176 */
12177 elog(ERROR, "unexpected object depending on column: %s",
12178 getObjectDescription(&foundObject, false));
12179 break;
12180
12181 /*
12182 * There's intentionally no default: case here; we want the
12183 * compiler to warn if a new OCLASS hasn't been handled above.
12184 */
12185 }
12186 }
12187
12188 systable_endscan(scan);
12189
12190 /*
12191 * Now scan for dependencies of this column on other things. The only
12192 * thing we should find is the dependency on the column datatype, which we
12193 * want to remove, possibly a collation dependency, and dependencies on
12194 * other columns if it is a generated column.
12195 */
12196 ScanKeyInit(&key[0],
12197 Anum_pg_depend_classid,
12198 BTEqualStrategyNumber, F_OIDEQ,
12199 ObjectIdGetDatum(RelationRelationId));
12200 ScanKeyInit(&key[1],
12201 Anum_pg_depend_objid,
12202 BTEqualStrategyNumber, F_OIDEQ,
12203 ObjectIdGetDatum(RelationGetRelid(rel)));
12204 ScanKeyInit(&key[2],
12205 Anum_pg_depend_objsubid,
12206 BTEqualStrategyNumber, F_INT4EQ,
12207 Int32GetDatum((int32) attnum));
12208
12209 scan = systable_beginscan(depRel, DependDependerIndexId, true,
12210 NULL, 3, key);
12211
12212 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
12213 {
12214 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
12215 ObjectAddress foundObject;
12216
12217 foundObject.classId = foundDep->refclassid;
12218 foundObject.objectId = foundDep->refobjid;
12219 foundObject.objectSubId = foundDep->refobjsubid;
12220
12221 if (foundDep->deptype != DEPENDENCY_NORMAL &&
12222 foundDep->deptype != DEPENDENCY_AUTO)
12223 elog(ERROR, "found unexpected dependency type '%c'",
12224 foundDep->deptype);
12225 if (!(foundDep->refclassid == TypeRelationId &&
12226 foundDep->refobjid == attTup->atttypid) &&
12227 !(foundDep->refclassid == CollationRelationId &&
12228 foundDep->refobjid == attTup->attcollation) &&
12229 !(foundDep->refclassid == RelationRelationId &&
12230 foundDep->refobjid == RelationGetRelid(rel) &&
12231 foundDep->refobjsubid != 0)
12232 )
12233 elog(ERROR, "found unexpected dependency for column: %s",
12234 getObjectDescription(&foundObject, false));
12235
12236 CatalogTupleDelete(depRel, &depTup->t_self);
12237 }
12238
12239 systable_endscan(scan);
12240
12241 table_close(depRel, RowExclusiveLock);
12242
12243 /*
12244 * Here we go --- change the recorded column type and collation. (Note
12245 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
12246 * fix up the missing value if any.
12247 */
12248 if (attTup->atthasmissing)
12249 {
12250 Datum missingval;
12251 bool missingNull;
12252
12253 /* if rewrite is true the missing value should already be cleared */
12254 Assert(tab->rewrite == 0);
12255
12256 /* Get the missing value datum */
12257 missingval = heap_getattr(heapTup,
12258 Anum_pg_attribute_attmissingval,
12259 attrelation->rd_att,
12260 &missingNull);
12261
12262 /* if it's a null array there is nothing to do */
12263
12264 if (!missingNull)
12265 {
12266 /*
12267 * Get the datum out of the array and repack it in a new array
12268 * built with the new type data. We assume that since the table
12269 * doesn't need rewriting, the actual Datum doesn't need to be
12270 * changed, only the array metadata.
12271 */
12272
12273 int one = 1;
12274 bool isNull;
12275 Datum valuesAtt[Natts_pg_attribute];
12276 bool nullsAtt[Natts_pg_attribute];
12277 bool replacesAtt[Natts_pg_attribute];
12278 HeapTuple newTup;
12279
12280 MemSet(valuesAtt, 0, sizeof(valuesAtt));
12281 MemSet(nullsAtt, false, sizeof(nullsAtt));
12282 MemSet(replacesAtt, false, sizeof(replacesAtt));
12283
12284 missingval = array_get_element(missingval,
12285 1,
12286 &one,
12287 0,
12288 attTup->attlen,
12289 attTup->attbyval,
12290 attTup->attalign,
12291 &isNull);
12292 missingval = PointerGetDatum(construct_array(&missingval,
12293 1,
12294 targettype,
12295 tform->typlen,
12296 tform->typbyval,
12297 tform->typalign));
12298
12299 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
12300 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
12301 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
12302
12303 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
12304 valuesAtt, nullsAtt, replacesAtt);
12305 heap_freetuple(heapTup);
12306 heapTup = newTup;
12307 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
12308 }
12309 }
12310
12311 attTup->atttypid = targettype;
12312 attTup->atttypmod = targettypmod;
12313 attTup->attcollation = targetcollid;
12314 attTup->attndims = list_length(typeName->arrayBounds);
12315 attTup->attlen = tform->typlen;
12316 attTup->attbyval = tform->typbyval;
12317 attTup->attalign = tform->typalign;
12318 attTup->attstorage = tform->typstorage;
12319 attTup->attcompression = InvalidCompressionMethod;
12320
12321 ReleaseSysCache(typeTuple);
12322
12323 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
12324
12325 table_close(attrelation, RowExclusiveLock);
12326
12327 /* Install dependencies on new datatype and collation */
12328 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
12329 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
12330
12331 /*
12332 * Drop any pg_statistic entry for the column, since it's now wrong type
12333 */
12334 RemoveStatistics(RelationGetRelid(rel), attnum);
12335
12336 InvokeObjectPostAlterHook(RelationRelationId,
12337 RelationGetRelid(rel), attnum);
12338
12339 /*
12340 * Update the default, if present, by brute force --- remove and re-add
12341 * the default. Probably unsafe to take shortcuts, since the new version
12342 * may well have additional dependencies. (It's okay to do this now,
12343 * rather than after other ALTER TYPE commands, since the default won't
12344 * depend on other column types.)
12345 */
12346 if (defaultexpr)
12347 {
12348 /* Must make new row visible since it will be updated again */
12349 CommandCounterIncrement();
12350
12351 /*
12352 * We use RESTRICT here for safety, but at present we do not expect
12353 * anything to depend on the default.
12354 */
12355 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
12356 true);
12357
12358 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
12359 }
12360
12361 ObjectAddressSubSet(address, RelationRelationId,
12362 RelationGetRelid(rel), attnum);
12363
12364 /* Cleanup */
12365 heap_freetuple(heapTup);
12366
12367 return address;
12368 }
12369
12370 /*
12371 * Subroutine for ATExecAlterColumnType: remember that a replica identity
12372 * needs to be reset.
12373 */
12374 static void
RememberReplicaIdentityForRebuilding(Oid indoid,AlteredTableInfo * tab)12375 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
12376 {
12377 if (!get_index_isreplident(indoid))
12378 return;
12379
12380 if (tab->replicaIdentityIndex)
12381 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
12382
12383 tab->replicaIdentityIndex = get_rel_name(indoid);
12384 }
12385
12386 /*
12387 * Subroutine for ATExecAlterColumnType: remember any clustered index.
12388 */
12389 static void
RememberClusterOnForRebuilding(Oid indoid,AlteredTableInfo * tab)12390 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
12391 {
12392 if (!get_index_isclustered(indoid))
12393 return;
12394
12395 if (tab->clusterOnIndex)
12396 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
12397
12398 tab->clusterOnIndex = get_rel_name(indoid);
12399 }
12400
12401 /*
12402 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
12403 * to be rebuilt (which we might already know).
12404 */
12405 static void
RememberConstraintForRebuilding(Oid conoid,AlteredTableInfo * tab)12406 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
12407 {
12408 /*
12409 * This de-duplication check is critical for two independent reasons: we
12410 * mustn't try to recreate the same constraint twice, and if a constraint
12411 * depends on more than one column whose type is to be altered, we must
12412 * capture its definition string before applying any of the column type
12413 * changes. ruleutils.c will get confused if we ask again later.
12414 */
12415 if (!list_member_oid(tab->changedConstraintOids, conoid))
12416 {
12417 /* OK, capture the constraint's existing definition string */
12418 char *defstring = pg_get_constraintdef_command(conoid);
12419 Oid indoid;
12420
12421 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
12422 conoid);
12423 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
12424 defstring);
12425
12426 /*
12427 * For the index of a constraint, if any, remember if it is used for
12428 * the table's replica identity or if it is a clustered index, so that
12429 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
12430 * those properties.
12431 */
12432 indoid = get_constraint_index(conoid);
12433 if (OidIsValid(indoid))
12434 {
12435 RememberReplicaIdentityForRebuilding(indoid, tab);
12436 RememberClusterOnForRebuilding(indoid, tab);
12437 }
12438 }
12439 }
12440
12441 /*
12442 * Subroutine for ATExecAlterColumnType: remember that an index needs
12443 * to be rebuilt (which we might already know).
12444 */
12445 static void
RememberIndexForRebuilding(Oid indoid,AlteredTableInfo * tab)12446 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
12447 {
12448 /*
12449 * This de-duplication check is critical for two independent reasons: we
12450 * mustn't try to recreate the same index twice, and if an index depends
12451 * on more than one column whose type is to be altered, we must capture
12452 * its definition string before applying any of the column type changes.
12453 * ruleutils.c will get confused if we ask again later.
12454 */
12455 if (!list_member_oid(tab->changedIndexOids, indoid))
12456 {
12457 /*
12458 * Before adding it as an index-to-rebuild, we'd better see if it
12459 * belongs to a constraint, and if so rebuild the constraint instead.
12460 * Typically this check fails, because constraint indexes normally
12461 * have only dependencies on their constraint. But it's possible for
12462 * such an index to also have direct dependencies on table columns,
12463 * for example with a partial exclusion constraint.
12464 */
12465 Oid conoid = get_index_constraint(indoid);
12466
12467 if (OidIsValid(conoid))
12468 {
12469 RememberConstraintForRebuilding(conoid, tab);
12470 }
12471 else
12472 {
12473 /* OK, capture the index's existing definition string */
12474 char *defstring = pg_get_indexdef_string(indoid);
12475
12476 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
12477 indoid);
12478 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
12479 defstring);
12480
12481 /*
12482 * Remember if this index is used for the table's replica identity
12483 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
12484 * can queue up commands necessary to restore those properties.
12485 */
12486 RememberReplicaIdentityForRebuilding(indoid, tab);
12487 RememberClusterOnForRebuilding(indoid, tab);
12488 }
12489 }
12490 }
12491
12492 /*
12493 * Subroutine for ATExecAlterColumnType: remember that a statistics object
12494 * needs to be rebuilt (which we might already know).
12495 */
12496 static void
RememberStatisticsForRebuilding(Oid stxoid,AlteredTableInfo * tab)12497 RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
12498 {
12499 /*
12500 * This de-duplication check is critical for two independent reasons: we
12501 * mustn't try to recreate the same statistics object twice, and if the
12502 * statistics object depends on more than one column whose type is to be
12503 * altered, we must capture its definition string before applying any of
12504 * the type changes. ruleutils.c will get confused if we ask again later.
12505 */
12506 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
12507 {
12508 /* OK, capture the statistics object's existing definition string */
12509 char *defstring = pg_get_statisticsobjdef_string(stxoid);
12510
12511 tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
12512 stxoid);
12513 tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
12514 defstring);
12515 }
12516 }
12517
12518 /*
12519 * Cleanup after we've finished all the ALTER TYPE operations for a
12520 * particular relation. We have to drop and recreate all the indexes
12521 * and constraints that depend on the altered columns. We do the
12522 * actual dropping here, but re-creation is managed by adding work
12523 * queue entries to do those steps later.
12524 */
12525 static void
ATPostAlterTypeCleanup(List ** wqueue,AlteredTableInfo * tab,LOCKMODE lockmode)12526 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
12527 {
12528 ObjectAddress obj;
12529 ObjectAddresses *objects;
12530 ListCell *def_item;
12531 ListCell *oid_item;
12532
12533 /*
12534 * Collect all the constraints and indexes to drop so we can process them
12535 * in a single call. That way we don't have to worry about dependencies
12536 * among them.
12537 */
12538 objects = new_object_addresses();
12539
12540 /*
12541 * Re-parse the index and constraint definitions, and attach them to the
12542 * appropriate work queue entries. We do this before dropping because in
12543 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
12544 * lock on the table the constraint is attached to, and we need to get
12545 * that before reparsing/dropping.
12546 *
12547 * We can't rely on the output of deparsing to tell us which relation to
12548 * operate on, because concurrent activity might have made the name
12549 * resolve differently. Instead, we've got to use the OID of the
12550 * constraint or index we're processing to figure out which relation to
12551 * operate on.
12552 */
12553 forboth(oid_item, tab->changedConstraintOids,
12554 def_item, tab->changedConstraintDefs)
12555 {
12556 Oid oldId = lfirst_oid(oid_item);
12557 HeapTuple tup;
12558 Form_pg_constraint con;
12559 Oid relid;
12560 Oid confrelid;
12561 char contype;
12562 bool conislocal;
12563
12564 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
12565 if (!HeapTupleIsValid(tup)) /* should not happen */
12566 elog(ERROR, "cache lookup failed for constraint %u", oldId);
12567 con = (Form_pg_constraint) GETSTRUCT(tup);
12568 if (OidIsValid(con->conrelid))
12569 relid = con->conrelid;
12570 else
12571 {
12572 /* must be a domain constraint */
12573 relid = get_typ_typrelid(getBaseType(con->contypid));
12574 if (!OidIsValid(relid))
12575 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
12576 }
12577 confrelid = con->confrelid;
12578 contype = con->contype;
12579 conislocal = con->conislocal;
12580 ReleaseSysCache(tup);
12581
12582 ObjectAddressSet(obj, ConstraintRelationId, oldId);
12583 add_exact_object_address(&obj, objects);
12584
12585 /*
12586 * If the constraint is inherited (only), we don't want to inject a
12587 * new definition here; it'll get recreated when ATAddCheckConstraint
12588 * recurses from adding the parent table's constraint. But we had to
12589 * carry the info this far so that we can drop the constraint below.
12590 */
12591 if (!conislocal)
12592 continue;
12593
12594 /*
12595 * When rebuilding an FK constraint that references the table we're
12596 * modifying, we might not yet have any lock on the FK's table, so get
12597 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
12598 * step, so there's no value in asking for anything weaker.
12599 */
12600 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
12601 LockRelationOid(relid, AccessExclusiveLock);
12602
12603 ATPostAlterTypeParse(oldId, relid, confrelid,
12604 (char *) lfirst(def_item),
12605 wqueue, lockmode, tab->rewrite);
12606 }
12607 forboth(oid_item, tab->changedIndexOids,
12608 def_item, tab->changedIndexDefs)
12609 {
12610 Oid oldId = lfirst_oid(oid_item);
12611 Oid relid;
12612
12613 relid = IndexGetRelation(oldId, false);
12614 ATPostAlterTypeParse(oldId, relid, InvalidOid,
12615 (char *) lfirst(def_item),
12616 wqueue, lockmode, tab->rewrite);
12617
12618 ObjectAddressSet(obj, RelationRelationId, oldId);
12619 add_exact_object_address(&obj, objects);
12620 }
12621
12622 /* add dependencies for new statistics */
12623 forboth(oid_item, tab->changedStatisticsOids,
12624 def_item, tab->changedStatisticsDefs)
12625 {
12626 Oid oldId = lfirst_oid(oid_item);
12627 Oid relid;
12628
12629 relid = StatisticsGetRelation(oldId, false);
12630 ATPostAlterTypeParse(oldId, relid, InvalidOid,
12631 (char *) lfirst(def_item),
12632 wqueue, lockmode, tab->rewrite);
12633
12634 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
12635 add_exact_object_address(&obj, objects);
12636 }
12637
12638 /*
12639 * Queue up command to restore replica identity index marking
12640 */
12641 if (tab->replicaIdentityIndex)
12642 {
12643 AlterTableCmd *cmd = makeNode(AlterTableCmd);
12644 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
12645
12646 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
12647 subcmd->name = tab->replicaIdentityIndex;
12648 cmd->subtype = AT_ReplicaIdentity;
12649 cmd->def = (Node *) subcmd;
12650
12651 /* do it after indexes and constraints */
12652 tab->subcmds[AT_PASS_OLD_CONSTR] =
12653 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
12654 }
12655
12656 /*
12657 * Queue up command to restore marking of index used for cluster.
12658 */
12659 if (tab->clusterOnIndex)
12660 {
12661 AlterTableCmd *cmd = makeNode(AlterTableCmd);
12662
12663 cmd->subtype = AT_ClusterOn;
12664 cmd->name = tab->clusterOnIndex;
12665
12666 /* do it after indexes and constraints */
12667 tab->subcmds[AT_PASS_OLD_CONSTR] =
12668 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
12669 }
12670
12671 /*
12672 * It should be okay to use DROP_RESTRICT here, since nothing else should
12673 * be depending on these objects.
12674 */
12675 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
12676
12677 free_object_addresses(objects);
12678
12679 /*
12680 * The objects will get recreated during subsequent passes over the work
12681 * queue.
12682 */
12683 }
12684
12685 /*
12686 * Parse the previously-saved definition string for a constraint, index or
12687 * statistics object against the newly-established column data type(s), and
12688 * queue up the resulting command parsetrees for execution.
12689 *
12690 * This might fail if, for example, you have a WHERE clause that uses an
12691 * operator that's not available for the new column type.
12692 */
12693 static void
ATPostAlterTypeParse(Oid oldId,Oid oldRelId,Oid refRelId,char * cmd,List ** wqueue,LOCKMODE lockmode,bool rewrite)12694 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
12695 List **wqueue, LOCKMODE lockmode, bool rewrite)
12696 {
12697 List *raw_parsetree_list;
12698 List *querytree_list;
12699 ListCell *list_item;
12700 Relation rel;
12701
12702 /*
12703 * We expect that we will get only ALTER TABLE and CREATE INDEX
12704 * statements. Hence, there is no need to pass them through
12705 * parse_analyze() or the rewriter, but instead we need to pass them
12706 * through parse_utilcmd.c to make them ready for execution.
12707 */
12708 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
12709 querytree_list = NIL;
12710 foreach(list_item, raw_parsetree_list)
12711 {
12712 RawStmt *rs = lfirst_node(RawStmt, list_item);
12713 Node *stmt = rs->stmt;
12714
12715 if (IsA(stmt, IndexStmt))
12716 querytree_list = lappend(querytree_list,
12717 transformIndexStmt(oldRelId,
12718 (IndexStmt *) stmt,
12719 cmd));
12720 else if (IsA(stmt, AlterTableStmt))
12721 {
12722 List *beforeStmts;
12723 List *afterStmts;
12724
12725 stmt = (Node *) transformAlterTableStmt(oldRelId,
12726 (AlterTableStmt *) stmt,
12727 cmd,
12728 &beforeStmts,
12729 &afterStmts);
12730 querytree_list = list_concat(querytree_list, beforeStmts);
12731 querytree_list = lappend(querytree_list, stmt);
12732 querytree_list = list_concat(querytree_list, afterStmts);
12733 }
12734 else if (IsA(stmt, CreateStatsStmt))
12735 querytree_list = lappend(querytree_list,
12736 transformStatsStmt(oldRelId,
12737 (CreateStatsStmt *) stmt,
12738 cmd));
12739 else
12740 querytree_list = lappend(querytree_list, stmt);
12741 }
12742
12743 /* Caller should already have acquired whatever lock we need. */
12744 rel = relation_open(oldRelId, NoLock);
12745
12746 /*
12747 * Attach each generated command to the proper place in the work queue.
12748 * Note this could result in creation of entirely new work-queue entries.
12749 *
12750 * Also note that we have to tweak the command subtypes, because it turns
12751 * out that re-creation of indexes and constraints has to act a bit
12752 * differently from initial creation.
12753 */
12754 foreach(list_item, querytree_list)
12755 {
12756 Node *stm = (Node *) lfirst(list_item);
12757 AlteredTableInfo *tab;
12758
12759 tab = ATGetQueueEntry(wqueue, rel);
12760
12761 if (IsA(stm, IndexStmt))
12762 {
12763 IndexStmt *stmt = (IndexStmt *) stm;
12764 AlterTableCmd *newcmd;
12765
12766 if (!rewrite)
12767 TryReuseIndex(oldId, stmt);
12768 stmt->reset_default_tblspc = true;
12769 /* keep the index's comment */
12770 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
12771
12772 newcmd = makeNode(AlterTableCmd);
12773 newcmd->subtype = AT_ReAddIndex;
12774 newcmd->def = (Node *) stmt;
12775 tab->subcmds[AT_PASS_OLD_INDEX] =
12776 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
12777 }
12778 else if (IsA(stm, AlterTableStmt))
12779 {
12780 AlterTableStmt *stmt = (AlterTableStmt *) stm;
12781 ListCell *lcmd;
12782
12783 foreach(lcmd, stmt->cmds)
12784 {
12785 AlterTableCmd *cmd = castNode(AlterTableCmd, lfirst(lcmd));
12786
12787 if (cmd->subtype == AT_AddIndex)
12788 {
12789 IndexStmt *indstmt;
12790 Oid indoid;
12791
12792 indstmt = castNode(IndexStmt, cmd->def);
12793 indoid = get_constraint_index(oldId);
12794
12795 if (!rewrite)
12796 TryReuseIndex(indoid, indstmt);
12797 /* keep any comment on the index */
12798 indstmt->idxcomment = GetComment(indoid,
12799 RelationRelationId, 0);
12800 indstmt->reset_default_tblspc = true;
12801
12802 cmd->subtype = AT_ReAddIndex;
12803 tab->subcmds[AT_PASS_OLD_INDEX] =
12804 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
12805
12806 /* recreate any comment on the constraint */
12807 RebuildConstraintComment(tab,
12808 AT_PASS_OLD_INDEX,
12809 oldId,
12810 rel,
12811 NIL,
12812 indstmt->idxname);
12813 }
12814 else if (cmd->subtype == AT_AddConstraint)
12815 {
12816 Constraint *con = castNode(Constraint, cmd->def);
12817
12818 con->old_pktable_oid = refRelId;
12819 /* rewriting neither side of a FK */
12820 if (con->contype == CONSTR_FOREIGN &&
12821 !rewrite && tab->rewrite == 0)
12822 TryReuseForeignKey(oldId, con);
12823 con->reset_default_tblspc = true;
12824 cmd->subtype = AT_ReAddConstraint;
12825 tab->subcmds[AT_PASS_OLD_CONSTR] =
12826 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
12827
12828 /* recreate any comment on the constraint */
12829 RebuildConstraintComment(tab,
12830 AT_PASS_OLD_CONSTR,
12831 oldId,
12832 rel,
12833 NIL,
12834 con->conname);
12835 }
12836 else if (cmd->subtype == AT_SetNotNull)
12837 {
12838 /*
12839 * The parser will create AT_SetNotNull subcommands for
12840 * columns of PRIMARY KEY indexes/constraints, but we need
12841 * not do anything with them here, because the columns'
12842 * NOT NULL marks will already have been propagated into
12843 * the new table definition.
12844 */
12845 }
12846 else
12847 elog(ERROR, "unexpected statement subtype: %d",
12848 (int) cmd->subtype);
12849 }
12850 }
12851 else if (IsA(stm, AlterDomainStmt))
12852 {
12853 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
12854
12855 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
12856 {
12857 Constraint *con = castNode(Constraint, stmt->def);
12858 AlterTableCmd *cmd = makeNode(AlterTableCmd);
12859
12860 cmd->subtype = AT_ReAddDomainConstraint;
12861 cmd->def = (Node *) stmt;
12862 tab->subcmds[AT_PASS_OLD_CONSTR] =
12863 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
12864
12865 /* recreate any comment on the constraint */
12866 RebuildConstraintComment(tab,
12867 AT_PASS_OLD_CONSTR,
12868 oldId,
12869 NULL,
12870 stmt->typeName,
12871 con->conname);
12872 }
12873 else
12874 elog(ERROR, "unexpected statement subtype: %d",
12875 (int) stmt->subtype);
12876 }
12877 else if (IsA(stm, CreateStatsStmt))
12878 {
12879 CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
12880 AlterTableCmd *newcmd;
12881
12882 /* keep the statistics object's comment */
12883 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
12884
12885 newcmd = makeNode(AlterTableCmd);
12886 newcmd->subtype = AT_ReAddStatistics;
12887 newcmd->def = (Node *) stmt;
12888 tab->subcmds[AT_PASS_MISC] =
12889 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
12890 }
12891 else
12892 elog(ERROR, "unexpected statement type: %d",
12893 (int) nodeTag(stm));
12894 }
12895
12896 relation_close(rel, NoLock);
12897 }
12898
12899 /*
12900 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
12901 * for a table or domain constraint that is being rebuilt.
12902 *
12903 * objid is the OID of the constraint.
12904 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
12905 * as a string list) for a domain constraint.
12906 * (We could dig that info, as well as the conname, out of the pg_constraint
12907 * entry; but callers already have them so might as well pass them.)
12908 */
12909 static void
RebuildConstraintComment(AlteredTableInfo * tab,int pass,Oid objid,Relation rel,List * domname,const char * conname)12910 RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
12911 Relation rel, List *domname,
12912 const char *conname)
12913 {
12914 CommentStmt *cmd;
12915 char *comment_str;
12916 AlterTableCmd *newcmd;
12917
12918 /* Look for comment for object wanted, and leave if none */
12919 comment_str = GetComment(objid, ConstraintRelationId, 0);
12920 if (comment_str == NULL)
12921 return;
12922
12923 /* Build CommentStmt node, copying all input data for safety */
12924 cmd = makeNode(CommentStmt);
12925 if (rel)
12926 {
12927 cmd->objtype = OBJECT_TABCONSTRAINT;
12928 cmd->object = (Node *)
12929 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
12930 makeString(pstrdup(RelationGetRelationName(rel))),
12931 makeString(pstrdup(conname)));
12932 }
12933 else
12934 {
12935 cmd->objtype = OBJECT_DOMCONSTRAINT;
12936 cmd->object = (Node *)
12937 list_make2(makeTypeNameFromNameList(copyObject(domname)),
12938 makeString(pstrdup(conname)));
12939 }
12940 cmd->comment = comment_str;
12941
12942 /* Append it to list of commands */
12943 newcmd = makeNode(AlterTableCmd);
12944 newcmd->subtype = AT_ReAddComment;
12945 newcmd->def = (Node *) cmd;
12946 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
12947 }
12948
12949 /*
12950 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
12951 * for the real analysis, then mutates the IndexStmt based on that verdict.
12952 */
12953 static void
TryReuseIndex(Oid oldId,IndexStmt * stmt)12954 TryReuseIndex(Oid oldId, IndexStmt *stmt)
12955 {
12956 if (CheckIndexCompatible(oldId,
12957 stmt->accessMethod,
12958 stmt->indexParams,
12959 stmt->excludeOpNames))
12960 {
12961 Relation irel = index_open(oldId, NoLock);
12962
12963 /* If it's a partitioned index, there is no storage to share. */
12964 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
12965 {
12966 stmt->oldNode = irel->rd_node.relNode;
12967 stmt->oldCreateSubid = irel->rd_createSubid;
12968 stmt->oldFirstRelfilenodeSubid = irel->rd_firstRelfilenodeSubid;
12969 }
12970 index_close(irel, NoLock);
12971 }
12972 }
12973
12974 /*
12975 * Subroutine for ATPostAlterTypeParse().
12976 *
12977 * Stash the old P-F equality operator into the Constraint node, for possible
12978 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
12979 * this constraint can be skipped.
12980 */
12981 static void
TryReuseForeignKey(Oid oldId,Constraint * con)12982 TryReuseForeignKey(Oid oldId, Constraint *con)
12983 {
12984 HeapTuple tup;
12985 Datum adatum;
12986 bool isNull;
12987 ArrayType *arr;
12988 Oid *rawarr;
12989 int numkeys;
12990 int i;
12991
12992 Assert(con->contype == CONSTR_FOREIGN);
12993 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
12994
12995 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
12996 if (!HeapTupleIsValid(tup)) /* should not happen */
12997 elog(ERROR, "cache lookup failed for constraint %u", oldId);
12998
12999 adatum = SysCacheGetAttr(CONSTROID, tup,
13000 Anum_pg_constraint_conpfeqop, &isNull);
13001 if (isNull)
13002 elog(ERROR, "null conpfeqop for constraint %u", oldId);
13003 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
13004 numkeys = ARR_DIMS(arr)[0];
13005 /* test follows the one in ri_FetchConstraintInfo() */
13006 if (ARR_NDIM(arr) != 1 ||
13007 ARR_HASNULL(arr) ||
13008 ARR_ELEMTYPE(arr) != OIDOID)
13009 elog(ERROR, "conpfeqop is not a 1-D Oid array");
13010 rawarr = (Oid *) ARR_DATA_PTR(arr);
13011
13012 /* stash a List of the operator Oids in our Constraint node */
13013 for (i = 0; i < numkeys; i++)
13014 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
13015
13016 ReleaseSysCache(tup);
13017 }
13018
13019 /*
13020 * ALTER COLUMN .. OPTIONS ( ... )
13021 *
13022 * Returns the address of the modified column
13023 */
13024 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel,const char * colName,List * options,LOCKMODE lockmode)13025 ATExecAlterColumnGenericOptions(Relation rel,
13026 const char *colName,
13027 List *options,
13028 LOCKMODE lockmode)
13029 {
13030 Relation ftrel;
13031 Relation attrel;
13032 ForeignServer *server;
13033 ForeignDataWrapper *fdw;
13034 HeapTuple tuple;
13035 HeapTuple newtuple;
13036 bool isnull;
13037 Datum repl_val[Natts_pg_attribute];
13038 bool repl_null[Natts_pg_attribute];
13039 bool repl_repl[Natts_pg_attribute];
13040 Datum datum;
13041 Form_pg_foreign_table fttableform;
13042 Form_pg_attribute atttableform;
13043 AttrNumber attnum;
13044 ObjectAddress address;
13045
13046 if (options == NIL)
13047 return InvalidObjectAddress;
13048
13049 /* First, determine FDW validator associated to the foreign table. */
13050 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
13051 tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
13052 if (!HeapTupleIsValid(tuple))
13053 ereport(ERROR,
13054 (errcode(ERRCODE_UNDEFINED_OBJECT),
13055 errmsg("foreign table \"%s\" does not exist",
13056 RelationGetRelationName(rel))));
13057 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
13058 server = GetForeignServer(fttableform->ftserver);
13059 fdw = GetForeignDataWrapper(server->fdwid);
13060
13061 table_close(ftrel, AccessShareLock);
13062 ReleaseSysCache(tuple);
13063
13064 attrel = table_open(AttributeRelationId, RowExclusiveLock);
13065 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13066 if (!HeapTupleIsValid(tuple))
13067 ereport(ERROR,
13068 (errcode(ERRCODE_UNDEFINED_COLUMN),
13069 errmsg("column \"%s\" of relation \"%s\" does not exist",
13070 colName, RelationGetRelationName(rel))));
13071
13072 /* Prevent them from altering a system attribute */
13073 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
13074 attnum = atttableform->attnum;
13075 if (attnum <= 0)
13076 ereport(ERROR,
13077 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13078 errmsg("cannot alter system column \"%s\"", colName)));
13079
13080
13081 /* Initialize buffers for new tuple values */
13082 memset(repl_val, 0, sizeof(repl_val));
13083 memset(repl_null, false, sizeof(repl_null));
13084 memset(repl_repl, false, sizeof(repl_repl));
13085
13086 /* Extract the current options */
13087 datum = SysCacheGetAttr(ATTNAME,
13088 tuple,
13089 Anum_pg_attribute_attfdwoptions,
13090 &isnull);
13091 if (isnull)
13092 datum = PointerGetDatum(NULL);
13093
13094 /* Transform the options */
13095 datum = transformGenericOptions(AttributeRelationId,
13096 datum,
13097 options,
13098 fdw->fdwvalidator);
13099
13100 if (PointerIsValid(DatumGetPointer(datum)))
13101 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
13102 else
13103 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
13104
13105 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
13106
13107 /* Everything looks good - update the tuple */
13108
13109 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
13110 repl_val, repl_null, repl_repl);
13111
13112 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
13113
13114 InvokeObjectPostAlterHook(RelationRelationId,
13115 RelationGetRelid(rel),
13116 atttableform->attnum);
13117 ObjectAddressSubSet(address, RelationRelationId,
13118 RelationGetRelid(rel), attnum);
13119
13120 ReleaseSysCache(tuple);
13121
13122 table_close(attrel, RowExclusiveLock);
13123
13124 heap_freetuple(newtuple);
13125
13126 return address;
13127 }
13128
13129 /*
13130 * ALTER TABLE OWNER
13131 *
13132 * recursing is true if we are recursing from a table to its indexes,
13133 * sequences, or toast table. We don't allow the ownership of those things to
13134 * be changed separately from the parent table. Also, we can skip permission
13135 * checks (this is necessary not just an optimization, else we'd fail to
13136 * handle toast tables properly).
13137 *
13138 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
13139 * free-standing composite type.
13140 */
13141 void
ATExecChangeOwner(Oid relationOid,Oid newOwnerId,bool recursing,LOCKMODE lockmode)13142 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
13143 {
13144 Relation target_rel;
13145 Relation class_rel;
13146 HeapTuple tuple;
13147 Form_pg_class tuple_class;
13148
13149 /*
13150 * Get exclusive lock till end of transaction on the target table. Use
13151 * relation_open so that we can work on indexes and sequences.
13152 */
13153 target_rel = relation_open(relationOid, lockmode);
13154
13155 /* Get its pg_class tuple, too */
13156 class_rel = table_open(RelationRelationId, RowExclusiveLock);
13157
13158 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
13159 if (!HeapTupleIsValid(tuple))
13160 elog(ERROR, "cache lookup failed for relation %u", relationOid);
13161 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
13162
13163 /* Can we change the ownership of this tuple? */
13164 switch (tuple_class->relkind)
13165 {
13166 case RELKIND_RELATION:
13167 case RELKIND_VIEW:
13168 case RELKIND_MATVIEW:
13169 case RELKIND_FOREIGN_TABLE:
13170 case RELKIND_PARTITIONED_TABLE:
13171 /* ok to change owner */
13172 break;
13173 case RELKIND_INDEX:
13174 if (!recursing)
13175 {
13176 /*
13177 * Because ALTER INDEX OWNER used to be allowed, and in fact
13178 * is generated by old versions of pg_dump, we give a warning
13179 * and do nothing rather than erroring out. Also, to avoid
13180 * unnecessary chatter while restoring those old dumps, say
13181 * nothing at all if the command would be a no-op anyway.
13182 */
13183 if (tuple_class->relowner != newOwnerId)
13184 ereport(WARNING,
13185 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13186 errmsg("cannot change owner of index \"%s\"",
13187 NameStr(tuple_class->relname)),
13188 errhint("Change the ownership of the index's table, instead.")));
13189 /* quick hack to exit via the no-op path */
13190 newOwnerId = tuple_class->relowner;
13191 }
13192 break;
13193 case RELKIND_PARTITIONED_INDEX:
13194 if (recursing)
13195 break;
13196 ereport(ERROR,
13197 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13198 errmsg("cannot change owner of index \"%s\"",
13199 NameStr(tuple_class->relname)),
13200 errhint("Change the ownership of the index's table, instead.")));
13201 break;
13202 case RELKIND_SEQUENCE:
13203 if (!recursing &&
13204 tuple_class->relowner != newOwnerId)
13205 {
13206 /* if it's an owned sequence, disallow changing it by itself */
13207 Oid tableId;
13208 int32 colId;
13209
13210 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
13211 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
13212 ereport(ERROR,
13213 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13214 errmsg("cannot change owner of sequence \"%s\"",
13215 NameStr(tuple_class->relname)),
13216 errdetail("Sequence \"%s\" is linked to table \"%s\".",
13217 NameStr(tuple_class->relname),
13218 get_rel_name(tableId))));
13219 }
13220 break;
13221 case RELKIND_COMPOSITE_TYPE:
13222 if (recursing)
13223 break;
13224 ereport(ERROR,
13225 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13226 errmsg("\"%s\" is a composite type",
13227 NameStr(tuple_class->relname)),
13228 errhint("Use ALTER TYPE instead.")));
13229 break;
13230 case RELKIND_TOASTVALUE:
13231 if (recursing)
13232 break;
13233 /* FALL THRU */
13234 default:
13235 ereport(ERROR,
13236 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13237 errmsg("\"%s\" is not a table, view, sequence, or foreign table",
13238 NameStr(tuple_class->relname))));
13239 }
13240
13241 /*
13242 * If the new owner is the same as the existing owner, consider the
13243 * command to have succeeded. This is for dump restoration purposes.
13244 */
13245 if (tuple_class->relowner != newOwnerId)
13246 {
13247 Datum repl_val[Natts_pg_class];
13248 bool repl_null[Natts_pg_class];
13249 bool repl_repl[Natts_pg_class];
13250 Acl *newAcl;
13251 Datum aclDatum;
13252 bool isNull;
13253 HeapTuple newtuple;
13254
13255 /* skip permission checks when recursing to index or toast table */
13256 if (!recursing)
13257 {
13258 /* Superusers can always do it */
13259 if (!superuser())
13260 {
13261 Oid namespaceOid = tuple_class->relnamespace;
13262 AclResult aclresult;
13263
13264 /* Otherwise, must be owner of the existing object */
13265 if (!pg_class_ownercheck(relationOid, GetUserId()))
13266 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
13267 RelationGetRelationName(target_rel));
13268
13269 /* Must be able to become new owner */
13270 check_is_member_of_role(GetUserId(), newOwnerId);
13271
13272 /* New owner must have CREATE privilege on namespace */
13273 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
13274 ACL_CREATE);
13275 if (aclresult != ACLCHECK_OK)
13276 aclcheck_error(aclresult, OBJECT_SCHEMA,
13277 get_namespace_name(namespaceOid));
13278 }
13279 }
13280
13281 memset(repl_null, false, sizeof(repl_null));
13282 memset(repl_repl, false, sizeof(repl_repl));
13283
13284 repl_repl[Anum_pg_class_relowner - 1] = true;
13285 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
13286
13287 /*
13288 * Determine the modified ACL for the new owner. This is only
13289 * necessary when the ACL is non-null.
13290 */
13291 aclDatum = SysCacheGetAttr(RELOID, tuple,
13292 Anum_pg_class_relacl,
13293 &isNull);
13294 if (!isNull)
13295 {
13296 newAcl = aclnewowner(DatumGetAclP(aclDatum),
13297 tuple_class->relowner, newOwnerId);
13298 repl_repl[Anum_pg_class_relacl - 1] = true;
13299 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
13300 }
13301
13302 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
13303
13304 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
13305
13306 heap_freetuple(newtuple);
13307
13308 /*
13309 * We must similarly update any per-column ACLs to reflect the new
13310 * owner; for neatness reasons that's split out as a subroutine.
13311 */
13312 change_owner_fix_column_acls(relationOid,
13313 tuple_class->relowner,
13314 newOwnerId);
13315
13316 /*
13317 * Update owner dependency reference, if any. A composite type has
13318 * none, because it's tracked for the pg_type entry instead of here;
13319 * indexes and TOAST tables don't have their own entries either.
13320 */
13321 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
13322 tuple_class->relkind != RELKIND_INDEX &&
13323 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
13324 tuple_class->relkind != RELKIND_TOASTVALUE)
13325 changeDependencyOnOwner(RelationRelationId, relationOid,
13326 newOwnerId);
13327
13328 /*
13329 * Also change the ownership of the table's row type, if it has one
13330 */
13331 if (OidIsValid(tuple_class->reltype))
13332 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
13333
13334 /*
13335 * If we are operating on a table or materialized view, also change
13336 * the ownership of any indexes and sequences that belong to the
13337 * relation, as well as its toast table (if it has one).
13338 */
13339 if (tuple_class->relkind == RELKIND_RELATION ||
13340 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
13341 tuple_class->relkind == RELKIND_MATVIEW ||
13342 tuple_class->relkind == RELKIND_TOASTVALUE)
13343 {
13344 List *index_oid_list;
13345 ListCell *i;
13346
13347 /* Find all the indexes belonging to this relation */
13348 index_oid_list = RelationGetIndexList(target_rel);
13349
13350 /* For each index, recursively change its ownership */
13351 foreach(i, index_oid_list)
13352 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
13353
13354 list_free(index_oid_list);
13355 }
13356
13357 /* If it has a toast table, recurse to change its ownership */
13358 if (tuple_class->reltoastrelid != InvalidOid)
13359 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
13360 true, lockmode);
13361
13362 /* If it has dependent sequences, recurse to change them too */
13363 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
13364 }
13365
13366 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
13367
13368 ReleaseSysCache(tuple);
13369 table_close(class_rel, RowExclusiveLock);
13370 relation_close(target_rel, NoLock);
13371 }
13372
13373 /*
13374 * change_owner_fix_column_acls
13375 *
13376 * Helper function for ATExecChangeOwner. Scan the columns of the table
13377 * and fix any non-null column ACLs to reflect the new owner.
13378 */
13379 static void
change_owner_fix_column_acls(Oid relationOid,Oid oldOwnerId,Oid newOwnerId)13380 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
13381 {
13382 Relation attRelation;
13383 SysScanDesc scan;
13384 ScanKeyData key[1];
13385 HeapTuple attributeTuple;
13386
13387 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
13388 ScanKeyInit(&key[0],
13389 Anum_pg_attribute_attrelid,
13390 BTEqualStrategyNumber, F_OIDEQ,
13391 ObjectIdGetDatum(relationOid));
13392 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
13393 true, NULL, 1, key);
13394 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
13395 {
13396 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
13397 Datum repl_val[Natts_pg_attribute];
13398 bool repl_null[Natts_pg_attribute];
13399 bool repl_repl[Natts_pg_attribute];
13400 Acl *newAcl;
13401 Datum aclDatum;
13402 bool isNull;
13403 HeapTuple newtuple;
13404
13405 /* Ignore dropped columns */
13406 if (att->attisdropped)
13407 continue;
13408
13409 aclDatum = heap_getattr(attributeTuple,
13410 Anum_pg_attribute_attacl,
13411 RelationGetDescr(attRelation),
13412 &isNull);
13413 /* Null ACLs do not require changes */
13414 if (isNull)
13415 continue;
13416
13417 memset(repl_null, false, sizeof(repl_null));
13418 memset(repl_repl, false, sizeof(repl_repl));
13419
13420 newAcl = aclnewowner(DatumGetAclP(aclDatum),
13421 oldOwnerId, newOwnerId);
13422 repl_repl[Anum_pg_attribute_attacl - 1] = true;
13423 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
13424
13425 newtuple = heap_modify_tuple(attributeTuple,
13426 RelationGetDescr(attRelation),
13427 repl_val, repl_null, repl_repl);
13428
13429 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
13430
13431 heap_freetuple(newtuple);
13432 }
13433 systable_endscan(scan);
13434 table_close(attRelation, RowExclusiveLock);
13435 }
13436
13437 /*
13438 * change_owner_recurse_to_sequences
13439 *
13440 * Helper function for ATExecChangeOwner. Examines pg_depend searching
13441 * for sequences that are dependent on serial columns, and changes their
13442 * ownership.
13443 */
13444 static void
change_owner_recurse_to_sequences(Oid relationOid,Oid newOwnerId,LOCKMODE lockmode)13445 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
13446 {
13447 Relation depRel;
13448 SysScanDesc scan;
13449 ScanKeyData key[2];
13450 HeapTuple tup;
13451
13452 /*
13453 * SERIAL sequences are those having an auto dependency on one of the
13454 * table's columns (we don't care *which* column, exactly).
13455 */
13456 depRel = table_open(DependRelationId, AccessShareLock);
13457
13458 ScanKeyInit(&key[0],
13459 Anum_pg_depend_refclassid,
13460 BTEqualStrategyNumber, F_OIDEQ,
13461 ObjectIdGetDatum(RelationRelationId));
13462 ScanKeyInit(&key[1],
13463 Anum_pg_depend_refobjid,
13464 BTEqualStrategyNumber, F_OIDEQ,
13465 ObjectIdGetDatum(relationOid));
13466 /* we leave refobjsubid unspecified */
13467
13468 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13469 NULL, 2, key);
13470
13471 while (HeapTupleIsValid(tup = systable_getnext(scan)))
13472 {
13473 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
13474 Relation seqRel;
13475
13476 /* skip dependencies other than auto dependencies on columns */
13477 if (depForm->refobjsubid == 0 ||
13478 depForm->classid != RelationRelationId ||
13479 depForm->objsubid != 0 ||
13480 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
13481 continue;
13482
13483 /* Use relation_open just in case it's an index */
13484 seqRel = relation_open(depForm->objid, lockmode);
13485
13486 /* skip non-sequence relations */
13487 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
13488 {
13489 /* No need to keep the lock */
13490 relation_close(seqRel, lockmode);
13491 continue;
13492 }
13493
13494 /* We don't need to close the sequence while we alter it. */
13495 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
13496
13497 /* Now we can close it. Keep the lock till end of transaction. */
13498 relation_close(seqRel, NoLock);
13499 }
13500
13501 systable_endscan(scan);
13502
13503 relation_close(depRel, AccessShareLock);
13504 }
13505
13506 /*
13507 * ALTER TABLE CLUSTER ON
13508 *
13509 * The only thing we have to do is to change the indisclustered bits.
13510 *
13511 * Return the address of the new clustering index.
13512 */
13513 static ObjectAddress
ATExecClusterOn(Relation rel,const char * indexName,LOCKMODE lockmode)13514 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
13515 {
13516 Oid indexOid;
13517 ObjectAddress address;
13518
13519 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
13520
13521 if (!OidIsValid(indexOid))
13522 ereport(ERROR,
13523 (errcode(ERRCODE_UNDEFINED_OBJECT),
13524 errmsg("index \"%s\" for table \"%s\" does not exist",
13525 indexName, RelationGetRelationName(rel))));
13526
13527 /* Check index is valid to cluster on */
13528 check_index_is_clusterable(rel, indexOid, false, lockmode);
13529
13530 /* And do the work */
13531 mark_index_clustered(rel, indexOid, false);
13532
13533 ObjectAddressSet(address,
13534 RelationRelationId, indexOid);
13535
13536 return address;
13537 }
13538
13539 /*
13540 * ALTER TABLE SET WITHOUT CLUSTER
13541 *
13542 * We have to find any indexes on the table that have indisclustered bit
13543 * set and turn it off.
13544 */
13545 static void
ATExecDropCluster(Relation rel,LOCKMODE lockmode)13546 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
13547 {
13548 mark_index_clustered(rel, InvalidOid, false);
13549 }
13550
13551 /*
13552 * ALTER TABLE SET TABLESPACE
13553 */
13554 static void
ATPrepSetTableSpace(AlteredTableInfo * tab,Relation rel,const char * tablespacename,LOCKMODE lockmode)13555 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
13556 {
13557 Oid tablespaceId;
13558
13559 /* Check that the tablespace exists */
13560 tablespaceId = get_tablespace_oid(tablespacename, false);
13561
13562 /* Check permissions except when moving to database's default */
13563 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
13564 {
13565 AclResult aclresult;
13566
13567 aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
13568 if (aclresult != ACLCHECK_OK)
13569 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
13570 }
13571
13572 /* Save info for Phase 3 to do the real work */
13573 if (OidIsValid(tab->newTableSpace))
13574 ereport(ERROR,
13575 (errcode(ERRCODE_SYNTAX_ERROR),
13576 errmsg("cannot have multiple SET TABLESPACE subcommands")));
13577
13578 tab->newTableSpace = tablespaceId;
13579 }
13580
13581 /*
13582 * Set, reset, or replace reloptions.
13583 */
13584 static void
ATExecSetRelOptions(Relation rel,List * defList,AlterTableType operation,LOCKMODE lockmode)13585 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
13586 LOCKMODE lockmode)
13587 {
13588 Oid relid;
13589 Relation pgclass;
13590 HeapTuple tuple;
13591 HeapTuple newtuple;
13592 Datum datum;
13593 bool isnull;
13594 Datum newOptions;
13595 Datum repl_val[Natts_pg_class];
13596 bool repl_null[Natts_pg_class];
13597 bool repl_repl[Natts_pg_class];
13598 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
13599
13600 if (defList == NIL && operation != AT_ReplaceRelOptions)
13601 return; /* nothing to do */
13602
13603 pgclass = table_open(RelationRelationId, RowExclusiveLock);
13604
13605 /* Fetch heap tuple */
13606 relid = RelationGetRelid(rel);
13607 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13608 if (!HeapTupleIsValid(tuple))
13609 elog(ERROR, "cache lookup failed for relation %u", relid);
13610
13611 if (operation == AT_ReplaceRelOptions)
13612 {
13613 /*
13614 * If we're supposed to replace the reloptions list, we just pretend
13615 * there were none before.
13616 */
13617 datum = (Datum) 0;
13618 isnull = true;
13619 }
13620 else
13621 {
13622 /* Get the old reloptions */
13623 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
13624 &isnull);
13625 }
13626
13627 /* Generate new proposed reloptions (text array) */
13628 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
13629 defList, NULL, validnsps, false,
13630 operation == AT_ResetRelOptions);
13631
13632 /* Validate */
13633 switch (rel->rd_rel->relkind)
13634 {
13635 case RELKIND_RELATION:
13636 case RELKIND_TOASTVALUE:
13637 case RELKIND_MATVIEW:
13638 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
13639 break;
13640 case RELKIND_PARTITIONED_TABLE:
13641 (void) partitioned_table_reloptions(newOptions, true);
13642 break;
13643 case RELKIND_VIEW:
13644 (void) view_reloptions(newOptions, true);
13645 break;
13646 case RELKIND_INDEX:
13647 case RELKIND_PARTITIONED_INDEX:
13648 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
13649 break;
13650 default:
13651 ereport(ERROR,
13652 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13653 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
13654 RelationGetRelationName(rel))));
13655 break;
13656 }
13657
13658 /* Special-case validation of view options */
13659 if (rel->rd_rel->relkind == RELKIND_VIEW)
13660 {
13661 Query *view_query = get_view_query(rel);
13662 List *view_options = untransformRelOptions(newOptions);
13663 ListCell *cell;
13664 bool check_option = false;
13665
13666 foreach(cell, view_options)
13667 {
13668 DefElem *defel = (DefElem *) lfirst(cell);
13669
13670 if (strcmp(defel->defname, "check_option") == 0)
13671 check_option = true;
13672 }
13673
13674 /*
13675 * If the check option is specified, look to see if the view is
13676 * actually auto-updatable or not.
13677 */
13678 if (check_option)
13679 {
13680 const char *view_updatable_error =
13681 view_query_is_auto_updatable(view_query, true);
13682
13683 if (view_updatable_error)
13684 ereport(ERROR,
13685 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13686 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
13687 errhint("%s", _(view_updatable_error))));
13688 }
13689 }
13690
13691 /*
13692 * All we need do here is update the pg_class row; the new options will be
13693 * propagated into relcaches during post-commit cache inval.
13694 */
13695 memset(repl_val, 0, sizeof(repl_val));
13696 memset(repl_null, false, sizeof(repl_null));
13697 memset(repl_repl, false, sizeof(repl_repl));
13698
13699 if (newOptions != (Datum) 0)
13700 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
13701 else
13702 repl_null[Anum_pg_class_reloptions - 1] = true;
13703
13704 repl_repl[Anum_pg_class_reloptions - 1] = true;
13705
13706 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
13707 repl_val, repl_null, repl_repl);
13708
13709 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
13710
13711 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13712
13713 heap_freetuple(newtuple);
13714
13715 ReleaseSysCache(tuple);
13716
13717 /* repeat the whole exercise for the toast table, if there's one */
13718 if (OidIsValid(rel->rd_rel->reltoastrelid))
13719 {
13720 Relation toastrel;
13721 Oid toastid = rel->rd_rel->reltoastrelid;
13722
13723 toastrel = table_open(toastid, lockmode);
13724
13725 /* Fetch heap tuple */
13726 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
13727 if (!HeapTupleIsValid(tuple))
13728 elog(ERROR, "cache lookup failed for relation %u", toastid);
13729
13730 if (operation == AT_ReplaceRelOptions)
13731 {
13732 /*
13733 * If we're supposed to replace the reloptions list, we just
13734 * pretend there were none before.
13735 */
13736 datum = (Datum) 0;
13737 isnull = true;
13738 }
13739 else
13740 {
13741 /* Get the old reloptions */
13742 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
13743 &isnull);
13744 }
13745
13746 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
13747 defList, "toast", validnsps, false,
13748 operation == AT_ResetRelOptions);
13749
13750 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
13751
13752 memset(repl_val, 0, sizeof(repl_val));
13753 memset(repl_null, false, sizeof(repl_null));
13754 memset(repl_repl, false, sizeof(repl_repl));
13755
13756 if (newOptions != (Datum) 0)
13757 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
13758 else
13759 repl_null[Anum_pg_class_reloptions - 1] = true;
13760
13761 repl_repl[Anum_pg_class_reloptions - 1] = true;
13762
13763 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
13764 repl_val, repl_null, repl_repl);
13765
13766 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
13767
13768 InvokeObjectPostAlterHookArg(RelationRelationId,
13769 RelationGetRelid(toastrel), 0,
13770 InvalidOid, true);
13771
13772 heap_freetuple(newtuple);
13773
13774 ReleaseSysCache(tuple);
13775
13776 table_close(toastrel, NoLock);
13777 }
13778
13779 table_close(pgclass, RowExclusiveLock);
13780 }
13781
13782 /*
13783 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
13784 * rewriting to be done, so we just want to copy the data as fast as possible.
13785 */
13786 static void
ATExecSetTableSpace(Oid tableOid,Oid newTableSpace,LOCKMODE lockmode)13787 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13788 {
13789 Relation rel;
13790 Oid reltoastrelid;
13791 Oid newrelfilenode;
13792 RelFileNode newrnode;
13793 List *reltoastidxids = NIL;
13794 ListCell *lc;
13795
13796 /*
13797 * Need lock here in case we are recursing to toast table or index
13798 */
13799 rel = relation_open(tableOid, lockmode);
13800
13801 /* Check first if relation can be moved to new tablespace */
13802 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
13803 {
13804 InvokeObjectPostAlterHook(RelationRelationId,
13805 RelationGetRelid(rel), 0);
13806 relation_close(rel, NoLock);
13807 return;
13808 }
13809
13810 reltoastrelid = rel->rd_rel->reltoastrelid;
13811 /* Fetch the list of indexes on toast relation if necessary */
13812 if (OidIsValid(reltoastrelid))
13813 {
13814 Relation toastRel = relation_open(reltoastrelid, lockmode);
13815
13816 reltoastidxids = RelationGetIndexList(toastRel);
13817 relation_close(toastRel, lockmode);
13818 }
13819
13820 /*
13821 * Relfilenodes are not unique in databases across tablespaces, so we need
13822 * to allocate a new one in the new tablespace.
13823 */
13824 newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
13825 rel->rd_rel->relpersistence);
13826
13827 /* Open old and new relation */
13828 newrnode = rel->rd_node;
13829 newrnode.relNode = newrelfilenode;
13830 newrnode.spcNode = newTableSpace;
13831
13832 /* hand off to AM to actually create the new filenode and copy the data */
13833 if (rel->rd_rel->relkind == RELKIND_INDEX)
13834 {
13835 index_copy_data(rel, newrnode);
13836 }
13837 else
13838 {
13839 Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
13840 rel->rd_rel->relkind == RELKIND_MATVIEW ||
13841 rel->rd_rel->relkind == RELKIND_TOASTVALUE);
13842 table_relation_copy_data(rel, &newrnode);
13843 }
13844
13845 /*
13846 * Update the pg_class row.
13847 *
13848 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
13849 * executed on pg_class or its indexes (the above copy wouldn't contain
13850 * the updated pg_class entry), but that's forbidden with
13851 * CheckRelationTableSpaceMove().
13852 */
13853 SetRelationTableSpace(rel, newTableSpace, newrelfilenode);
13854
13855 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13856
13857 RelationAssumeNewRelfilenode(rel);
13858
13859 relation_close(rel, NoLock);
13860
13861 /* Make sure the reltablespace change is visible */
13862 CommandCounterIncrement();
13863
13864 /* Move associated toast relation and/or indexes, too */
13865 if (OidIsValid(reltoastrelid))
13866 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
13867 foreach(lc, reltoastidxids)
13868 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
13869
13870 /* Clean up */
13871 list_free(reltoastidxids);
13872 }
13873
13874 /*
13875 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
13876 * storage that have an interest in preserving tablespace.
13877 *
13878 * Since these have no storage the tablespace can be updated with a simple
13879 * metadata only operation to update the tablespace.
13880 */
13881 static void
ATExecSetTableSpaceNoStorage(Relation rel,Oid newTableSpace)13882 ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
13883 {
13884 /*
13885 * Shouldn't be called on relations having storage; these are processed in
13886 * phase 3.
13887 */
13888 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
13889
13890 /* check if relation can be moved to its new tablespace */
13891 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
13892 {
13893 InvokeObjectPostAlterHook(RelationRelationId,
13894 RelationGetRelid(rel),
13895 0);
13896 return;
13897 }
13898
13899 /* Update can be done, so change reltablespace */
13900 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
13901
13902 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13903
13904 /* Make sure the reltablespace change is visible */
13905 CommandCounterIncrement();
13906 }
13907
13908 /*
13909 * Alter Table ALL ... SET TABLESPACE
13910 *
13911 * Allows a user to move all objects of some type in a given tablespace in the
13912 * current database to another tablespace. Objects can be chosen based on the
13913 * owner of the object also, to allow users to move only their objects.
13914 * The user must have CREATE rights on the new tablespace, as usual. The main
13915 * permissions handling is done by the lower-level table move function.
13916 *
13917 * All to-be-moved objects are locked first. If NOWAIT is specified and the
13918 * lock can't be acquired then we ereport(ERROR).
13919 */
13920 Oid
AlterTableMoveAll(AlterTableMoveAllStmt * stmt)13921 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
13922 {
13923 List *relations = NIL;
13924 ListCell *l;
13925 ScanKeyData key[1];
13926 Relation rel;
13927 TableScanDesc scan;
13928 HeapTuple tuple;
13929 Oid orig_tablespaceoid;
13930 Oid new_tablespaceoid;
13931 List *role_oids = roleSpecsToIds(stmt->roles);
13932
13933 /* Ensure we were not asked to move something we can't */
13934 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
13935 stmt->objtype != OBJECT_MATVIEW)
13936 ereport(ERROR,
13937 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13938 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
13939
13940 /* Get the orig and new tablespace OIDs */
13941 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
13942 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
13943
13944 /* Can't move shared relations in to or out of pg_global */
13945 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
13946 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
13947 new_tablespaceoid == GLOBALTABLESPACE_OID)
13948 ereport(ERROR,
13949 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13950 errmsg("cannot move relations in to or out of pg_global tablespace")));
13951
13952 /*
13953 * Must have CREATE rights on the new tablespace, unless it is the
13954 * database default tablespace (which all users implicitly have CREATE
13955 * rights on).
13956 */
13957 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
13958 {
13959 AclResult aclresult;
13960
13961 aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(),
13962 ACL_CREATE);
13963 if (aclresult != ACLCHECK_OK)
13964 aclcheck_error(aclresult, OBJECT_TABLESPACE,
13965 get_tablespace_name(new_tablespaceoid));
13966 }
13967
13968 /*
13969 * Now that the checks are done, check if we should set either to
13970 * InvalidOid because it is our database's default tablespace.
13971 */
13972 if (orig_tablespaceoid == MyDatabaseTableSpace)
13973 orig_tablespaceoid = InvalidOid;
13974
13975 if (new_tablespaceoid == MyDatabaseTableSpace)
13976 new_tablespaceoid = InvalidOid;
13977
13978 /* no-op */
13979 if (orig_tablespaceoid == new_tablespaceoid)
13980 return new_tablespaceoid;
13981
13982 /*
13983 * Walk the list of objects in the tablespace and move them. This will
13984 * only find objects in our database, of course.
13985 */
13986 ScanKeyInit(&key[0],
13987 Anum_pg_class_reltablespace,
13988 BTEqualStrategyNumber, F_OIDEQ,
13989 ObjectIdGetDatum(orig_tablespaceoid));
13990
13991 rel = table_open(RelationRelationId, AccessShareLock);
13992 scan = table_beginscan_catalog(rel, 1, key);
13993 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
13994 {
13995 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
13996 Oid relOid = relForm->oid;
13997
13998 /*
13999 * Do not move objects in pg_catalog as part of this, if an admin
14000 * really wishes to do so, they can issue the individual ALTER
14001 * commands directly.
14002 *
14003 * Also, explicitly avoid any shared tables, temp tables, or TOAST
14004 * (TOAST will be moved with the main table).
14005 */
14006 if (IsCatalogNamespace(relForm->relnamespace) ||
14007 relForm->relisshared ||
14008 isAnyTempNamespace(relForm->relnamespace) ||
14009 IsToastNamespace(relForm->relnamespace))
14010 continue;
14011
14012 /* Only move the object type requested */
14013 if ((stmt->objtype == OBJECT_TABLE &&
14014 relForm->relkind != RELKIND_RELATION &&
14015 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
14016 (stmt->objtype == OBJECT_INDEX &&
14017 relForm->relkind != RELKIND_INDEX &&
14018 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
14019 (stmt->objtype == OBJECT_MATVIEW &&
14020 relForm->relkind != RELKIND_MATVIEW))
14021 continue;
14022
14023 /* Check if we are only moving objects owned by certain roles */
14024 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
14025 continue;
14026
14027 /*
14028 * Handle permissions-checking here since we are locking the tables
14029 * and also to avoid doing a bunch of work only to fail part-way. Note
14030 * that permissions will also be checked by AlterTableInternal().
14031 *
14032 * Caller must be considered an owner on the table to move it.
14033 */
14034 if (!pg_class_ownercheck(relOid, GetUserId()))
14035 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
14036 NameStr(relForm->relname));
14037
14038 if (stmt->nowait &&
14039 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
14040 ereport(ERROR,
14041 (errcode(ERRCODE_OBJECT_IN_USE),
14042 errmsg("aborting because lock on relation \"%s.%s\" is not available",
14043 get_namespace_name(relForm->relnamespace),
14044 NameStr(relForm->relname))));
14045 else
14046 LockRelationOid(relOid, AccessExclusiveLock);
14047
14048 /* Add to our list of objects to move */
14049 relations = lappend_oid(relations, relOid);
14050 }
14051
14052 table_endscan(scan);
14053 table_close(rel, AccessShareLock);
14054
14055 if (relations == NIL)
14056 ereport(NOTICE,
14057 (errcode(ERRCODE_NO_DATA_FOUND),
14058 errmsg("no matching relations in tablespace \"%s\" found",
14059 orig_tablespaceoid == InvalidOid ? "(database default)" :
14060 get_tablespace_name(orig_tablespaceoid))));
14061
14062 /* Everything is locked, loop through and move all of the relations. */
14063 foreach(l, relations)
14064 {
14065 List *cmds = NIL;
14066 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14067
14068 cmd->subtype = AT_SetTableSpace;
14069 cmd->name = stmt->new_tablespacename;
14070
14071 cmds = lappend(cmds, cmd);
14072
14073 EventTriggerAlterTableStart((Node *) stmt);
14074 /* OID is set by AlterTableInternal */
14075 AlterTableInternal(lfirst_oid(l), cmds, false);
14076 EventTriggerAlterTableEnd();
14077 }
14078
14079 return new_tablespaceoid;
14080 }
14081
14082 static void
index_copy_data(Relation rel,RelFileNode newrnode)14083 index_copy_data(Relation rel, RelFileNode newrnode)
14084 {
14085 SMgrRelation dstrel;
14086
14087 dstrel = smgropen(newrnode, rel->rd_backend);
14088 RelationOpenSmgr(rel);
14089
14090 /*
14091 * Since we copy the file directly without looking at the shared buffers,
14092 * we'd better first flush out any pages of the source relation that are
14093 * in shared buffers. We assume no new changes will be made while we are
14094 * holding exclusive lock on the rel.
14095 */
14096 FlushRelationBuffers(rel);
14097
14098 /*
14099 * Create and copy all forks of the relation, and schedule unlinking of
14100 * old physical files.
14101 *
14102 * NOTE: any conflict in relfilenode value will be caught in
14103 * RelationCreateStorage().
14104 */
14105 RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
14106
14107 /* copy main fork */
14108 RelationCopyStorage(rel->rd_smgr, dstrel, MAIN_FORKNUM,
14109 rel->rd_rel->relpersistence);
14110
14111 /* copy those extra forks that exist */
14112 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
14113 forkNum <= MAX_FORKNUM; forkNum++)
14114 {
14115 if (smgrexists(rel->rd_smgr, forkNum))
14116 {
14117 smgrcreate(dstrel, forkNum, false);
14118
14119 /*
14120 * WAL log creation if the relation is persistent, or this is the
14121 * init fork of an unlogged relation.
14122 */
14123 if (RelationIsPermanent(rel) ||
14124 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
14125 forkNum == INIT_FORKNUM))
14126 log_smgrcreate(&newrnode, forkNum);
14127 RelationCopyStorage(rel->rd_smgr, dstrel, forkNum,
14128 rel->rd_rel->relpersistence);
14129 }
14130 }
14131
14132 /* drop old relation, and close new one */
14133 RelationDropStorage(rel);
14134 smgrclose(dstrel);
14135 }
14136
14137 /*
14138 * ALTER TABLE ENABLE/DISABLE TRIGGER
14139 *
14140 * We just pass this off to trigger.c.
14141 */
14142 static void
ATExecEnableDisableTrigger(Relation rel,const char * trigname,char fires_when,bool skip_system,LOCKMODE lockmode)14143 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
14144 char fires_when, bool skip_system, LOCKMODE lockmode)
14145 {
14146 EnableDisableTrigger(rel, trigname, fires_when, skip_system, lockmode);
14147 }
14148
14149 /*
14150 * ALTER TABLE ENABLE/DISABLE RULE
14151 *
14152 * We just pass this off to rewriteDefine.c.
14153 */
14154 static void
ATExecEnableDisableRule(Relation rel,const char * rulename,char fires_when,LOCKMODE lockmode)14155 ATExecEnableDisableRule(Relation rel, const char *rulename,
14156 char fires_when, LOCKMODE lockmode)
14157 {
14158 EnableDisableRule(rel, rulename, fires_when);
14159 }
14160
14161 /*
14162 * ALTER TABLE INHERIT
14163 *
14164 * Add a parent to the child's parents. This verifies that all the columns and
14165 * check constraints of the parent appear in the child and that they have the
14166 * same data types and expressions.
14167 */
14168 static void
ATPrepAddInherit(Relation child_rel)14169 ATPrepAddInherit(Relation child_rel)
14170 {
14171 if (child_rel->rd_rel->reloftype)
14172 ereport(ERROR,
14173 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14174 errmsg("cannot change inheritance of typed table")));
14175
14176 if (child_rel->rd_rel->relispartition)
14177 ereport(ERROR,
14178 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14179 errmsg("cannot change inheritance of a partition")));
14180
14181 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14182 ereport(ERROR,
14183 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14184 errmsg("cannot change inheritance of partitioned table")));
14185 }
14186
14187 /*
14188 * Return the address of the new parent relation.
14189 */
14190 static ObjectAddress
ATExecAddInherit(Relation child_rel,RangeVar * parent,LOCKMODE lockmode)14191 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
14192 {
14193 Relation parent_rel;
14194 List *children;
14195 ObjectAddress address;
14196 const char *trigger_name;
14197
14198 /*
14199 * A self-exclusive lock is needed here. See the similar case in
14200 * MergeAttributes() for a full explanation.
14201 */
14202 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
14203
14204 /*
14205 * Must be owner of both parent and child -- child was checked by
14206 * ATSimplePermissions call in ATPrepCmd
14207 */
14208 ATSimplePermissions(parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
14209
14210 /* Permanent rels cannot inherit from temporary ones */
14211 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
14212 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
14213 ereport(ERROR,
14214 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14215 errmsg("cannot inherit from temporary relation \"%s\"",
14216 RelationGetRelationName(parent_rel))));
14217
14218 /* If parent rel is temp, it must belong to this session */
14219 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
14220 !parent_rel->rd_islocaltemp)
14221 ereport(ERROR,
14222 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14223 errmsg("cannot inherit from temporary relation of another session")));
14224
14225 /* Ditto for the child */
14226 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
14227 !child_rel->rd_islocaltemp)
14228 ereport(ERROR,
14229 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14230 errmsg("cannot inherit to temporary relation of another session")));
14231
14232 /* Prevent partitioned tables from becoming inheritance parents */
14233 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14234 ereport(ERROR,
14235 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14236 errmsg("cannot inherit from partitioned table \"%s\"",
14237 parent->relname)));
14238
14239 /* Likewise for partitions */
14240 if (parent_rel->rd_rel->relispartition)
14241 ereport(ERROR,
14242 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14243 errmsg("cannot inherit from a partition")));
14244
14245 /*
14246 * Prevent circularity by seeing if proposed parent inherits from child.
14247 * (In particular, this disallows making a rel inherit from itself.)
14248 *
14249 * This is not completely bulletproof because of race conditions: in
14250 * multi-level inheritance trees, someone else could concurrently be
14251 * making another inheritance link that closes the loop but does not join
14252 * either of the rels we have locked. Preventing that seems to require
14253 * exclusive locks on the entire inheritance tree, which is a cure worse
14254 * than the disease. find_all_inheritors() will cope with circularity
14255 * anyway, so don't sweat it too much.
14256 *
14257 * We use weakest lock we can on child's children, namely AccessShareLock.
14258 */
14259 children = find_all_inheritors(RelationGetRelid(child_rel),
14260 AccessShareLock, NULL);
14261
14262 if (list_member_oid(children, RelationGetRelid(parent_rel)))
14263 ereport(ERROR,
14264 (errcode(ERRCODE_DUPLICATE_TABLE),
14265 errmsg("circular inheritance not allowed"),
14266 errdetail("\"%s\" is already a child of \"%s\".",
14267 parent->relname,
14268 RelationGetRelationName(child_rel))));
14269
14270 /*
14271 * If child_rel has row-level triggers with transition tables, we
14272 * currently don't allow it to become an inheritance child. See also
14273 * prohibitions in ATExecAttachPartition() and CreateTrigger().
14274 */
14275 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
14276 if (trigger_name != NULL)
14277 ereport(ERROR,
14278 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14279 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
14280 trigger_name, RelationGetRelationName(child_rel)),
14281 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
14282
14283 /* OK to create inheritance */
14284 CreateInheritance(child_rel, parent_rel);
14285
14286 ObjectAddressSet(address, RelationRelationId,
14287 RelationGetRelid(parent_rel));
14288
14289 /* keep our lock on the parent relation until commit */
14290 table_close(parent_rel, NoLock);
14291
14292 return address;
14293 }
14294
14295 /*
14296 * CreateInheritance
14297 * Catalog manipulation portion of creating inheritance between a child
14298 * table and a parent table.
14299 *
14300 * Common to ATExecAddInherit() and ATExecAttachPartition().
14301 */
14302 static void
CreateInheritance(Relation child_rel,Relation parent_rel)14303 CreateInheritance(Relation child_rel, Relation parent_rel)
14304 {
14305 Relation catalogRelation;
14306 SysScanDesc scan;
14307 ScanKeyData key;
14308 HeapTuple inheritsTuple;
14309 int32 inhseqno;
14310
14311 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
14312 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
14313
14314 /*
14315 * Check for duplicates in the list of parents, and determine the highest
14316 * inhseqno already present; we'll use the next one for the new parent.
14317 * Also, if proposed child is a partition, it cannot already be
14318 * inheriting.
14319 *
14320 * Note: we do not reject the case where the child already inherits from
14321 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
14322 */
14323 ScanKeyInit(&key,
14324 Anum_pg_inherits_inhrelid,
14325 BTEqualStrategyNumber, F_OIDEQ,
14326 ObjectIdGetDatum(RelationGetRelid(child_rel)));
14327 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
14328 true, NULL, 1, &key);
14329
14330 /* inhseqno sequences start at 1 */
14331 inhseqno = 0;
14332 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
14333 {
14334 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
14335
14336 if (inh->inhparent == RelationGetRelid(parent_rel))
14337 ereport(ERROR,
14338 (errcode(ERRCODE_DUPLICATE_TABLE),
14339 errmsg("relation \"%s\" would be inherited from more than once",
14340 RelationGetRelationName(parent_rel))));
14341
14342 if (inh->inhseqno > inhseqno)
14343 inhseqno = inh->inhseqno;
14344 }
14345 systable_endscan(scan);
14346
14347 /* Match up the columns and bump attinhcount as needed */
14348 MergeAttributesIntoExisting(child_rel, parent_rel);
14349
14350 /* Match up the constraints and bump coninhcount as needed */
14351 MergeConstraintsIntoExisting(child_rel, parent_rel);
14352
14353 /*
14354 * OK, it looks valid. Make the catalog entries that show inheritance.
14355 */
14356 StoreCatalogInheritance1(RelationGetRelid(child_rel),
14357 RelationGetRelid(parent_rel),
14358 inhseqno + 1,
14359 catalogRelation,
14360 parent_rel->rd_rel->relkind ==
14361 RELKIND_PARTITIONED_TABLE);
14362
14363 /* Now we're done with pg_inherits */
14364 table_close(catalogRelation, RowExclusiveLock);
14365 }
14366
14367 /*
14368 * Obtain the source-text form of the constraint expression for a check
14369 * constraint, given its pg_constraint tuple
14370 */
14371 static char *
decompile_conbin(HeapTuple contup,TupleDesc tupdesc)14372 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
14373 {
14374 Form_pg_constraint con;
14375 bool isnull;
14376 Datum attr;
14377 Datum expr;
14378
14379 con = (Form_pg_constraint) GETSTRUCT(contup);
14380 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
14381 if (isnull)
14382 elog(ERROR, "null conbin for constraint %u", con->oid);
14383
14384 expr = DirectFunctionCall2(pg_get_expr, attr,
14385 ObjectIdGetDatum(con->conrelid));
14386 return TextDatumGetCString(expr);
14387 }
14388
14389 /*
14390 * Determine whether two check constraints are functionally equivalent
14391 *
14392 * The test we apply is to see whether they reverse-compile to the same
14393 * source string. This insulates us from issues like whether attributes
14394 * have the same physical column numbers in parent and child relations.
14395 */
14396 static bool
constraints_equivalent(HeapTuple a,HeapTuple b,TupleDesc tupleDesc)14397 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
14398 {
14399 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
14400 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
14401
14402 if (acon->condeferrable != bcon->condeferrable ||
14403 acon->condeferred != bcon->condeferred ||
14404 strcmp(decompile_conbin(a, tupleDesc),
14405 decompile_conbin(b, tupleDesc)) != 0)
14406 return false;
14407 else
14408 return true;
14409 }
14410
14411 /*
14412 * Check columns in child table match up with columns in parent, and increment
14413 * their attinhcount.
14414 *
14415 * Called by CreateInheritance
14416 *
14417 * Currently all parent columns must be found in child. Missing columns are an
14418 * error. One day we might consider creating new columns like CREATE TABLE
14419 * does. However, that is widely unpopular --- in the common use case of
14420 * partitioned tables it's a foot-gun.
14421 *
14422 * The data type must match exactly. If the parent column is NOT NULL then
14423 * the child must be as well. Defaults are not compared, however.
14424 */
14425 static void
MergeAttributesIntoExisting(Relation child_rel,Relation parent_rel)14426 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
14427 {
14428 Relation attrrel;
14429 AttrNumber parent_attno;
14430 int parent_natts;
14431 TupleDesc tupleDesc;
14432 HeapTuple tuple;
14433 bool child_is_partition = false;
14434
14435 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
14436
14437 tupleDesc = RelationGetDescr(parent_rel);
14438 parent_natts = tupleDesc->natts;
14439
14440 /* If parent_rel is a partitioned table, child_rel must be a partition */
14441 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14442 child_is_partition = true;
14443
14444 for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
14445 {
14446 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
14447 parent_attno - 1);
14448 char *attributeName = NameStr(attribute->attname);
14449
14450 /* Ignore dropped columns in the parent. */
14451 if (attribute->attisdropped)
14452 continue;
14453
14454 /* Find same column in child (matching on column name). */
14455 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
14456 attributeName);
14457 if (HeapTupleIsValid(tuple))
14458 {
14459 /* Check they are same type, typmod, and collation */
14460 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
14461
14462 if (attribute->atttypid != childatt->atttypid ||
14463 attribute->atttypmod != childatt->atttypmod)
14464 ereport(ERROR,
14465 (errcode(ERRCODE_DATATYPE_MISMATCH),
14466 errmsg("child table \"%s\" has different type for column \"%s\"",
14467 RelationGetRelationName(child_rel),
14468 attributeName)));
14469
14470 if (attribute->attcollation != childatt->attcollation)
14471 ereport(ERROR,
14472 (errcode(ERRCODE_COLLATION_MISMATCH),
14473 errmsg("child table \"%s\" has different collation for column \"%s\"",
14474 RelationGetRelationName(child_rel),
14475 attributeName)));
14476
14477 /*
14478 * Check child doesn't discard NOT NULL property. (Other
14479 * constraints are checked elsewhere.)
14480 */
14481 if (attribute->attnotnull && !childatt->attnotnull)
14482 ereport(ERROR,
14483 (errcode(ERRCODE_DATATYPE_MISMATCH),
14484 errmsg("column \"%s\" in child table must be marked NOT NULL",
14485 attributeName)));
14486
14487 /*
14488 * If parent column is generated, child column must be, too.
14489 */
14490 if (attribute->attgenerated && !childatt->attgenerated)
14491 ereport(ERROR,
14492 (errcode(ERRCODE_DATATYPE_MISMATCH),
14493 errmsg("column \"%s\" in child table must be a generated column",
14494 attributeName)));
14495
14496 /*
14497 * Check that both generation expressions match.
14498 *
14499 * The test we apply is to see whether they reverse-compile to the
14500 * same source string. This insulates us from issues like whether
14501 * attributes have the same physical column numbers in parent and
14502 * child relations. (See also constraints_equivalent().)
14503 */
14504 if (attribute->attgenerated && childatt->attgenerated)
14505 {
14506 TupleConstr *child_constr = child_rel->rd_att->constr;
14507 TupleConstr *parent_constr = parent_rel->rd_att->constr;
14508 char *child_expr = NULL;
14509 char *parent_expr = NULL;
14510
14511 Assert(child_constr != NULL);
14512 Assert(parent_constr != NULL);
14513
14514 for (int i = 0; i < child_constr->num_defval; i++)
14515 {
14516 if (child_constr->defval[i].adnum == childatt->attnum)
14517 {
14518 child_expr =
14519 TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
14520 CStringGetTextDatum(child_constr->defval[i].adbin),
14521 ObjectIdGetDatum(child_rel->rd_id)));
14522 break;
14523 }
14524 }
14525 Assert(child_expr != NULL);
14526
14527 for (int i = 0; i < parent_constr->num_defval; i++)
14528 {
14529 if (parent_constr->defval[i].adnum == attribute->attnum)
14530 {
14531 parent_expr =
14532 TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
14533 CStringGetTextDatum(parent_constr->defval[i].adbin),
14534 ObjectIdGetDatum(parent_rel->rd_id)));
14535 break;
14536 }
14537 }
14538 Assert(parent_expr != NULL);
14539
14540 if (strcmp(child_expr, parent_expr) != 0)
14541 ereport(ERROR,
14542 (errcode(ERRCODE_DATATYPE_MISMATCH),
14543 errmsg("column \"%s\" in child table has a conflicting generation expression",
14544 attributeName)));
14545 }
14546
14547 /*
14548 * OK, bump the child column's inheritance count. (If we fail
14549 * later on, this change will just roll back.)
14550 */
14551 childatt->attinhcount++;
14552
14553 /*
14554 * In case of partitions, we must enforce that value of attislocal
14555 * is same in all partitions. (Note: there are only inherited
14556 * attributes in partitions)
14557 */
14558 if (child_is_partition)
14559 {
14560 Assert(childatt->attinhcount == 1);
14561 childatt->attislocal = false;
14562 }
14563
14564 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
14565 heap_freetuple(tuple);
14566 }
14567 else
14568 {
14569 ereport(ERROR,
14570 (errcode(ERRCODE_DATATYPE_MISMATCH),
14571 errmsg("child table is missing column \"%s\"",
14572 attributeName)));
14573 }
14574 }
14575
14576 table_close(attrrel, RowExclusiveLock);
14577 }
14578
14579 /*
14580 * Check constraints in child table match up with constraints in parent,
14581 * and increment their coninhcount.
14582 *
14583 * Constraints that are marked ONLY in the parent are ignored.
14584 *
14585 * Called by CreateInheritance
14586 *
14587 * Currently all constraints in parent must be present in the child. One day we
14588 * may consider adding new constraints like CREATE TABLE does.
14589 *
14590 * XXX This is O(N^2) which may be an issue with tables with hundreds of
14591 * constraints. As long as tables have more like 10 constraints it shouldn't be
14592 * a problem though. Even 100 constraints ought not be the end of the world.
14593 *
14594 * XXX See MergeWithExistingConstraint too if you change this code.
14595 */
14596 static void
MergeConstraintsIntoExisting(Relation child_rel,Relation parent_rel)14597 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
14598 {
14599 Relation catalog_relation;
14600 TupleDesc tuple_desc;
14601 SysScanDesc parent_scan;
14602 ScanKeyData parent_key;
14603 HeapTuple parent_tuple;
14604 bool child_is_partition = false;
14605
14606 catalog_relation = table_open(ConstraintRelationId, RowExclusiveLock);
14607 tuple_desc = RelationGetDescr(catalog_relation);
14608
14609 /* If parent_rel is a partitioned table, child_rel must be a partition */
14610 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14611 child_is_partition = true;
14612
14613 /* Outer loop scans through the parent's constraint definitions */
14614 ScanKeyInit(&parent_key,
14615 Anum_pg_constraint_conrelid,
14616 BTEqualStrategyNumber, F_OIDEQ,
14617 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
14618 parent_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
14619 true, NULL, 1, &parent_key);
14620
14621 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
14622 {
14623 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
14624 SysScanDesc child_scan;
14625 ScanKeyData child_key;
14626 HeapTuple child_tuple;
14627 bool found = false;
14628
14629 if (parent_con->contype != CONSTRAINT_CHECK)
14630 continue;
14631
14632 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
14633 if (parent_con->connoinherit)
14634 continue;
14635
14636 /* Search for a child constraint matching this one */
14637 ScanKeyInit(&child_key,
14638 Anum_pg_constraint_conrelid,
14639 BTEqualStrategyNumber, F_OIDEQ,
14640 ObjectIdGetDatum(RelationGetRelid(child_rel)));
14641 child_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
14642 true, NULL, 1, &child_key);
14643
14644 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
14645 {
14646 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
14647 HeapTuple child_copy;
14648
14649 if (child_con->contype != CONSTRAINT_CHECK)
14650 continue;
14651
14652 if (strcmp(NameStr(parent_con->conname),
14653 NameStr(child_con->conname)) != 0)
14654 continue;
14655
14656 if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
14657 ereport(ERROR,
14658 (errcode(ERRCODE_DATATYPE_MISMATCH),
14659 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
14660 RelationGetRelationName(child_rel),
14661 NameStr(parent_con->conname))));
14662
14663 /* If the child constraint is "no inherit" then cannot merge */
14664 if (child_con->connoinherit)
14665 ereport(ERROR,
14666 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14667 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
14668 NameStr(child_con->conname),
14669 RelationGetRelationName(child_rel))));
14670
14671 /*
14672 * If the child constraint is "not valid" then cannot merge with a
14673 * valid parent constraint
14674 */
14675 if (parent_con->convalidated && !child_con->convalidated)
14676 ereport(ERROR,
14677 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14678 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
14679 NameStr(child_con->conname),
14680 RelationGetRelationName(child_rel))));
14681
14682 /*
14683 * OK, bump the child constraint's inheritance count. (If we fail
14684 * later on, this change will just roll back.)
14685 */
14686 child_copy = heap_copytuple(child_tuple);
14687 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
14688 child_con->coninhcount++;
14689
14690 /*
14691 * In case of partitions, an inherited constraint must be
14692 * inherited only once since it cannot have multiple parents and
14693 * it is never considered local.
14694 */
14695 if (child_is_partition)
14696 {
14697 Assert(child_con->coninhcount == 1);
14698 child_con->conislocal = false;
14699 }
14700
14701 CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
14702 heap_freetuple(child_copy);
14703
14704 found = true;
14705 break;
14706 }
14707
14708 systable_endscan(child_scan);
14709
14710 if (!found)
14711 ereport(ERROR,
14712 (errcode(ERRCODE_DATATYPE_MISMATCH),
14713 errmsg("child table is missing constraint \"%s\"",
14714 NameStr(parent_con->conname))));
14715 }
14716
14717 systable_endscan(parent_scan);
14718 table_close(catalog_relation, RowExclusiveLock);
14719 }
14720
14721 /*
14722 * ALTER TABLE NO INHERIT
14723 *
14724 * Return value is the address of the relation that is no longer parent.
14725 */
14726 static ObjectAddress
ATExecDropInherit(Relation rel,RangeVar * parent,LOCKMODE lockmode)14727 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
14728 {
14729 ObjectAddress address;
14730 Relation parent_rel;
14731
14732 if (rel->rd_rel->relispartition)
14733 ereport(ERROR,
14734 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14735 errmsg("cannot change inheritance of a partition")));
14736
14737 /*
14738 * AccessShareLock on the parent is probably enough, seeing that DROP
14739 * TABLE doesn't lock parent tables at all. We need some lock since we'll
14740 * be inspecting the parent's schema.
14741 */
14742 parent_rel = table_openrv(parent, AccessShareLock);
14743
14744 /*
14745 * We don't bother to check ownership of the parent table --- ownership of
14746 * the child is presumed enough rights.
14747 */
14748
14749 /* Off to RemoveInheritance() where most of the work happens */
14750 RemoveInheritance(rel, parent_rel, false);
14751
14752 ObjectAddressSet(address, RelationRelationId,
14753 RelationGetRelid(parent_rel));
14754
14755 /* keep our lock on the parent relation until commit */
14756 table_close(parent_rel, NoLock);
14757
14758 return address;
14759 }
14760
14761 /*
14762 * MarkInheritDetached
14763 *
14764 * Set inhdetachpending for a partition, for ATExecDetachPartition
14765 * in concurrent mode. While at it, verify that no other partition is
14766 * already pending detach.
14767 */
14768 static void
MarkInheritDetached(Relation child_rel,Relation parent_rel)14769 MarkInheritDetached(Relation child_rel, Relation parent_rel)
14770 {
14771 Relation catalogRelation;
14772 SysScanDesc scan;
14773 ScanKeyData key;
14774 HeapTuple inheritsTuple;
14775 bool found = false;
14776
14777 Assert(child_rel->rd_rel->relkind == RELKIND_RELATION ||
14778 child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
14779 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
14780
14781 /*
14782 * Find pg_inherits entries by inhparent. (We need to scan them all in
14783 * order to verify that no other partition is pending detach.)
14784 */
14785 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
14786 ScanKeyInit(&key,
14787 Anum_pg_inherits_inhparent,
14788 BTEqualStrategyNumber, F_OIDEQ,
14789 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
14790 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
14791 true, NULL, 1, &key);
14792
14793 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
14794 {
14795 Form_pg_inherits inhForm;
14796
14797 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
14798 if (inhForm->inhdetachpending)
14799 ereport(ERROR,
14800 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14801 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
14802 get_rel_name(inhForm->inhrelid),
14803 get_namespace_name(parent_rel->rd_rel->relnamespace),
14804 RelationGetRelationName(parent_rel)),
14805 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
14806
14807 if (inhForm->inhrelid == RelationGetRelid(child_rel))
14808 {
14809 HeapTuple newtup;
14810
14811 newtup = heap_copytuple(inheritsTuple);
14812 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
14813
14814 CatalogTupleUpdate(catalogRelation,
14815 &inheritsTuple->t_self,
14816 newtup);
14817 found = true;
14818 heap_freetuple(newtup);
14819 /* keep looking, to ensure we catch others pending detach */
14820 }
14821 }
14822
14823 /* Done */
14824 systable_endscan(scan);
14825 table_close(catalogRelation, RowExclusiveLock);
14826
14827 if (!found)
14828 ereport(ERROR,
14829 (errcode(ERRCODE_UNDEFINED_TABLE),
14830 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
14831 RelationGetRelationName(child_rel),
14832 RelationGetRelationName(parent_rel))));
14833 }
14834
14835 /*
14836 * RemoveInheritance
14837 *
14838 * Drop a parent from the child's parents. This just adjusts the attinhcount
14839 * and attislocal of the columns and removes the pg_inherit and pg_depend
14840 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
14841 *
14842 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
14843 * up attislocal stays true, which means if a child is ever removed from a
14844 * parent then its columns will never be automatically dropped which may
14845 * surprise. But at least we'll never surprise by dropping columns someone
14846 * isn't expecting to be dropped which would actually mean data loss.
14847 *
14848 * coninhcount and conislocal for inherited constraints are adjusted in
14849 * exactly the same way.
14850 *
14851 * Common to ATExecDropInherit() and ATExecDetachPartition().
14852 */
14853 static void
RemoveInheritance(Relation child_rel,Relation parent_rel,bool expect_detached)14854 RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
14855 {
14856 Relation catalogRelation;
14857 SysScanDesc scan;
14858 ScanKeyData key[3];
14859 HeapTuple attributeTuple,
14860 constraintTuple;
14861 List *connames;
14862 bool found;
14863 bool child_is_partition = false;
14864
14865 /* If parent_rel is a partitioned table, child_rel must be a partition */
14866 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14867 child_is_partition = true;
14868
14869 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
14870 RelationGetRelid(parent_rel),
14871 expect_detached,
14872 RelationGetRelationName(child_rel));
14873 if (!found)
14874 {
14875 if (child_is_partition)
14876 ereport(ERROR,
14877 (errcode(ERRCODE_UNDEFINED_TABLE),
14878 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
14879 RelationGetRelationName(child_rel),
14880 RelationGetRelationName(parent_rel))));
14881 else
14882 ereport(ERROR,
14883 (errcode(ERRCODE_UNDEFINED_TABLE),
14884 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
14885 RelationGetRelationName(parent_rel),
14886 RelationGetRelationName(child_rel))));
14887 }
14888
14889 /*
14890 * Search through child columns looking for ones matching parent rel
14891 */
14892 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
14893 ScanKeyInit(&key[0],
14894 Anum_pg_attribute_attrelid,
14895 BTEqualStrategyNumber, F_OIDEQ,
14896 ObjectIdGetDatum(RelationGetRelid(child_rel)));
14897 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
14898 true, NULL, 1, key);
14899 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
14900 {
14901 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
14902
14903 /* Ignore if dropped or not inherited */
14904 if (att->attisdropped)
14905 continue;
14906 if (att->attinhcount <= 0)
14907 continue;
14908
14909 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
14910 NameStr(att->attname)))
14911 {
14912 /* Decrement inhcount and possibly set islocal to true */
14913 HeapTuple copyTuple = heap_copytuple(attributeTuple);
14914 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
14915
14916 copy_att->attinhcount--;
14917 if (copy_att->attinhcount == 0)
14918 copy_att->attislocal = true;
14919
14920 CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
14921 heap_freetuple(copyTuple);
14922 }
14923 }
14924 systable_endscan(scan);
14925 table_close(catalogRelation, RowExclusiveLock);
14926
14927 /*
14928 * Likewise, find inherited check constraints and disinherit them. To do
14929 * this, we first need a list of the names of the parent's check
14930 * constraints. (We cheat a bit by only checking for name matches,
14931 * assuming that the expressions will match.)
14932 */
14933 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
14934 ScanKeyInit(&key[0],
14935 Anum_pg_constraint_conrelid,
14936 BTEqualStrategyNumber, F_OIDEQ,
14937 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
14938 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
14939 true, NULL, 1, key);
14940
14941 connames = NIL;
14942
14943 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
14944 {
14945 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
14946
14947 if (con->contype == CONSTRAINT_CHECK)
14948 connames = lappend(connames, pstrdup(NameStr(con->conname)));
14949 }
14950
14951 systable_endscan(scan);
14952
14953 /* Now scan the child's constraints */
14954 ScanKeyInit(&key[0],
14955 Anum_pg_constraint_conrelid,
14956 BTEqualStrategyNumber, F_OIDEQ,
14957 ObjectIdGetDatum(RelationGetRelid(child_rel)));
14958 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
14959 true, NULL, 1, key);
14960
14961 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
14962 {
14963 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
14964 bool match;
14965 ListCell *lc;
14966
14967 if (con->contype != CONSTRAINT_CHECK)
14968 continue;
14969
14970 match = false;
14971 foreach(lc, connames)
14972 {
14973 if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
14974 {
14975 match = true;
14976 break;
14977 }
14978 }
14979
14980 if (match)
14981 {
14982 /* Decrement inhcount and possibly set islocal to true */
14983 HeapTuple copyTuple = heap_copytuple(constraintTuple);
14984 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
14985
14986 if (copy_con->coninhcount <= 0) /* shouldn't happen */
14987 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14988 RelationGetRelid(child_rel), NameStr(copy_con->conname));
14989
14990 copy_con->coninhcount--;
14991 if (copy_con->coninhcount == 0)
14992 copy_con->conislocal = true;
14993
14994 CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
14995 heap_freetuple(copyTuple);
14996 }
14997 }
14998
14999 systable_endscan(scan);
15000 table_close(catalogRelation, RowExclusiveLock);
15001
15002 drop_parent_dependency(RelationGetRelid(child_rel),
15003 RelationRelationId,
15004 RelationGetRelid(parent_rel),
15005 child_dependency_type(child_is_partition));
15006
15007 /*
15008 * Post alter hook of this inherits. Since object_access_hook doesn't take
15009 * multiple object identifiers, we relay oid of parent relation using
15010 * auxiliary_id argument.
15011 */
15012 InvokeObjectPostAlterHookArg(InheritsRelationId,
15013 RelationGetRelid(child_rel), 0,
15014 RelationGetRelid(parent_rel), false);
15015 }
15016
15017 /*
15018 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
15019 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
15020 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
15021 * be TypeRelationId). There's no convenient way to do this, so go trawling
15022 * through pg_depend.
15023 */
15024 static void
drop_parent_dependency(Oid relid,Oid refclassid,Oid refobjid,DependencyType deptype)15025 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
15026 DependencyType deptype)
15027 {
15028 Relation catalogRelation;
15029 SysScanDesc scan;
15030 ScanKeyData key[3];
15031 HeapTuple depTuple;
15032
15033 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
15034
15035 ScanKeyInit(&key[0],
15036 Anum_pg_depend_classid,
15037 BTEqualStrategyNumber, F_OIDEQ,
15038 ObjectIdGetDatum(RelationRelationId));
15039 ScanKeyInit(&key[1],
15040 Anum_pg_depend_objid,
15041 BTEqualStrategyNumber, F_OIDEQ,
15042 ObjectIdGetDatum(relid));
15043 ScanKeyInit(&key[2],
15044 Anum_pg_depend_objsubid,
15045 BTEqualStrategyNumber, F_INT4EQ,
15046 Int32GetDatum(0));
15047
15048 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
15049 NULL, 3, key);
15050
15051 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
15052 {
15053 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
15054
15055 if (dep->refclassid == refclassid &&
15056 dep->refobjid == refobjid &&
15057 dep->refobjsubid == 0 &&
15058 dep->deptype == deptype)
15059 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
15060 }
15061
15062 systable_endscan(scan);
15063 table_close(catalogRelation, RowExclusiveLock);
15064 }
15065
15066 /*
15067 * ALTER TABLE OF
15068 *
15069 * Attach a table to a composite type, as though it had been created with CREATE
15070 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
15071 * subject table must not have inheritance parents. These restrictions ensure
15072 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
15073 *
15074 * The address of the type is returned.
15075 */
15076 static ObjectAddress
ATExecAddOf(Relation rel,const TypeName * ofTypename,LOCKMODE lockmode)15077 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
15078 {
15079 Oid relid = RelationGetRelid(rel);
15080 Type typetuple;
15081 Form_pg_type typeform;
15082 Oid typeid;
15083 Relation inheritsRelation,
15084 relationRelation;
15085 SysScanDesc scan;
15086 ScanKeyData key;
15087 AttrNumber table_attno,
15088 type_attno;
15089 TupleDesc typeTupleDesc,
15090 tableTupleDesc;
15091 ObjectAddress tableobj,
15092 typeobj;
15093 HeapTuple classtuple;
15094
15095 /* Validate the type. */
15096 typetuple = typenameType(NULL, ofTypename, NULL);
15097 check_of_type(typetuple);
15098 typeform = (Form_pg_type) GETSTRUCT(typetuple);
15099 typeid = typeform->oid;
15100
15101 /* Fail if the table has any inheritance parents. */
15102 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
15103 ScanKeyInit(&key,
15104 Anum_pg_inherits_inhrelid,
15105 BTEqualStrategyNumber, F_OIDEQ,
15106 ObjectIdGetDatum(relid));
15107 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
15108 true, NULL, 1, &key);
15109 if (HeapTupleIsValid(systable_getnext(scan)))
15110 ereport(ERROR,
15111 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15112 errmsg("typed tables cannot inherit")));
15113 systable_endscan(scan);
15114 table_close(inheritsRelation, AccessShareLock);
15115
15116 /*
15117 * Check the tuple descriptors for compatibility. Unlike inheritance, we
15118 * require that the order also match. However, attnotnull need not match.
15119 */
15120 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
15121 tableTupleDesc = RelationGetDescr(rel);
15122 table_attno = 1;
15123 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
15124 {
15125 Form_pg_attribute type_attr,
15126 table_attr;
15127 const char *type_attname,
15128 *table_attname;
15129
15130 /* Get the next non-dropped type attribute. */
15131 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
15132 if (type_attr->attisdropped)
15133 continue;
15134 type_attname = NameStr(type_attr->attname);
15135
15136 /* Get the next non-dropped table attribute. */
15137 do
15138 {
15139 if (table_attno > tableTupleDesc->natts)
15140 ereport(ERROR,
15141 (errcode(ERRCODE_DATATYPE_MISMATCH),
15142 errmsg("table is missing column \"%s\"",
15143 type_attname)));
15144 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
15145 table_attno++;
15146 } while (table_attr->attisdropped);
15147 table_attname = NameStr(table_attr->attname);
15148
15149 /* Compare name. */
15150 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
15151 ereport(ERROR,
15152 (errcode(ERRCODE_DATATYPE_MISMATCH),
15153 errmsg("table has column \"%s\" where type requires \"%s\"",
15154 table_attname, type_attname)));
15155
15156 /* Compare type. */
15157 if (table_attr->atttypid != type_attr->atttypid ||
15158 table_attr->atttypmod != type_attr->atttypmod ||
15159 table_attr->attcollation != type_attr->attcollation)
15160 ereport(ERROR,
15161 (errcode(ERRCODE_DATATYPE_MISMATCH),
15162 errmsg("table \"%s\" has different type for column \"%s\"",
15163 RelationGetRelationName(rel), type_attname)));
15164 }
15165 DecrTupleDescRefCount(typeTupleDesc);
15166
15167 /* Any remaining columns at the end of the table had better be dropped. */
15168 for (; table_attno <= tableTupleDesc->natts; table_attno++)
15169 {
15170 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
15171 table_attno - 1);
15172
15173 if (!table_attr->attisdropped)
15174 ereport(ERROR,
15175 (errcode(ERRCODE_DATATYPE_MISMATCH),
15176 errmsg("table has extra column \"%s\"",
15177 NameStr(table_attr->attname))));
15178 }
15179
15180 /* If the table was already typed, drop the existing dependency. */
15181 if (rel->rd_rel->reloftype)
15182 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
15183 DEPENDENCY_NORMAL);
15184
15185 /* Record a dependency on the new type. */
15186 tableobj.classId = RelationRelationId;
15187 tableobj.objectId = relid;
15188 tableobj.objectSubId = 0;
15189 typeobj.classId = TypeRelationId;
15190 typeobj.objectId = typeid;
15191 typeobj.objectSubId = 0;
15192 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
15193
15194 /* Update pg_class.reloftype */
15195 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
15196 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15197 if (!HeapTupleIsValid(classtuple))
15198 elog(ERROR, "cache lookup failed for relation %u", relid);
15199 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
15200 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
15201
15202 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
15203
15204 heap_freetuple(classtuple);
15205 table_close(relationRelation, RowExclusiveLock);
15206
15207 ReleaseSysCache(typetuple);
15208
15209 return typeobj;
15210 }
15211
15212 /*
15213 * ALTER TABLE NOT OF
15214 *
15215 * Detach a typed table from its originating type. Just clear reloftype and
15216 * remove the dependency.
15217 */
15218 static void
ATExecDropOf(Relation rel,LOCKMODE lockmode)15219 ATExecDropOf(Relation rel, LOCKMODE lockmode)
15220 {
15221 Oid relid = RelationGetRelid(rel);
15222 Relation relationRelation;
15223 HeapTuple tuple;
15224
15225 if (!OidIsValid(rel->rd_rel->reloftype))
15226 ereport(ERROR,
15227 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15228 errmsg("\"%s\" is not a typed table",
15229 RelationGetRelationName(rel))));
15230
15231 /*
15232 * We don't bother to check ownership of the type --- ownership of the
15233 * table is presumed enough rights. No lock required on the type, either.
15234 */
15235
15236 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
15237 DEPENDENCY_NORMAL);
15238
15239 /* Clear pg_class.reloftype */
15240 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
15241 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15242 if (!HeapTupleIsValid(tuple))
15243 elog(ERROR, "cache lookup failed for relation %u", relid);
15244 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
15245 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
15246
15247 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
15248
15249 heap_freetuple(tuple);
15250 table_close(relationRelation, RowExclusiveLock);
15251 }
15252
15253 /*
15254 * relation_mark_replica_identity: Update a table's replica identity
15255 *
15256 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
15257 * index. Otherwise, it should be InvalidOid.
15258 */
15259 static void
relation_mark_replica_identity(Relation rel,char ri_type,Oid indexOid,bool is_internal)15260 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
15261 bool is_internal)
15262 {
15263 Relation pg_index;
15264 Relation pg_class;
15265 HeapTuple pg_class_tuple;
15266 HeapTuple pg_index_tuple;
15267 Form_pg_class pg_class_form;
15268 Form_pg_index pg_index_form;
15269
15270 ListCell *index;
15271
15272 /*
15273 * Check whether relreplident has changed, and update it if so.
15274 */
15275 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15276 pg_class_tuple = SearchSysCacheCopy1(RELOID,
15277 ObjectIdGetDatum(RelationGetRelid(rel)));
15278 if (!HeapTupleIsValid(pg_class_tuple))
15279 elog(ERROR, "cache lookup failed for relation \"%s\"",
15280 RelationGetRelationName(rel));
15281 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
15282 if (pg_class_form->relreplident != ri_type)
15283 {
15284 pg_class_form->relreplident = ri_type;
15285 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
15286 }
15287 table_close(pg_class, RowExclusiveLock);
15288 heap_freetuple(pg_class_tuple);
15289
15290 /*
15291 * Check whether the correct index is marked indisreplident; if so, we're
15292 * done.
15293 */
15294 if (OidIsValid(indexOid))
15295 {
15296 Assert(ri_type == REPLICA_IDENTITY_INDEX);
15297
15298 pg_index_tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
15299 if (!HeapTupleIsValid(pg_index_tuple))
15300 elog(ERROR, "cache lookup failed for index %u", indexOid);
15301 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
15302
15303 if (pg_index_form->indisreplident)
15304 {
15305 ReleaseSysCache(pg_index_tuple);
15306 return;
15307 }
15308 ReleaseSysCache(pg_index_tuple);
15309 }
15310
15311 /*
15312 * Clear the indisreplident flag from any index that had it previously,
15313 * and set it for any index that should have it now.
15314 */
15315 pg_index = table_open(IndexRelationId, RowExclusiveLock);
15316 foreach(index, RelationGetIndexList(rel))
15317 {
15318 Oid thisIndexOid = lfirst_oid(index);
15319 bool dirty = false;
15320
15321 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
15322 ObjectIdGetDatum(thisIndexOid));
15323 if (!HeapTupleIsValid(pg_index_tuple))
15324 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
15325 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
15326
15327 /*
15328 * Unset the bit if set. We know it's wrong because we checked this
15329 * earlier.
15330 */
15331 if (pg_index_form->indisreplident)
15332 {
15333 dirty = true;
15334 pg_index_form->indisreplident = false;
15335 }
15336 else if (thisIndexOid == indexOid)
15337 {
15338 dirty = true;
15339 pg_index_form->indisreplident = true;
15340 }
15341
15342 if (dirty)
15343 {
15344 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
15345 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
15346 InvalidOid, is_internal);
15347 }
15348 heap_freetuple(pg_index_tuple);
15349 }
15350
15351 table_close(pg_index, RowExclusiveLock);
15352 }
15353
15354 /*
15355 * ALTER TABLE <name> REPLICA IDENTITY ...
15356 */
15357 static void
ATExecReplicaIdentity(Relation rel,ReplicaIdentityStmt * stmt,LOCKMODE lockmode)15358 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
15359 {
15360 Oid indexOid;
15361 Relation indexRel;
15362 int key;
15363
15364 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
15365 {
15366 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
15367 return;
15368 }
15369 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
15370 {
15371 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
15372 return;
15373 }
15374 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
15375 {
15376 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
15377 return;
15378 }
15379 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
15380 {
15381 /* fallthrough */ ;
15382 }
15383 else
15384 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
15385
15386
15387 /* Check that the index exists */
15388 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
15389 if (!OidIsValid(indexOid))
15390 ereport(ERROR,
15391 (errcode(ERRCODE_UNDEFINED_OBJECT),
15392 errmsg("index \"%s\" for table \"%s\" does not exist",
15393 stmt->name, RelationGetRelationName(rel))));
15394
15395 indexRel = index_open(indexOid, ShareLock);
15396
15397 /* Check that the index is on the relation we're altering. */
15398 if (indexRel->rd_index == NULL ||
15399 indexRel->rd_index->indrelid != RelationGetRelid(rel))
15400 ereport(ERROR,
15401 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15402 errmsg("\"%s\" is not an index for table \"%s\"",
15403 RelationGetRelationName(indexRel),
15404 RelationGetRelationName(rel))));
15405 /* The AM must support uniqueness, and the index must in fact be unique. */
15406 if (!indexRel->rd_indam->amcanunique ||
15407 !indexRel->rd_index->indisunique)
15408 ereport(ERROR,
15409 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15410 errmsg("cannot use non-unique index \"%s\" as replica identity",
15411 RelationGetRelationName(indexRel))));
15412 /* Deferred indexes are not guaranteed to be always unique. */
15413 if (!indexRel->rd_index->indimmediate)
15414 ereport(ERROR,
15415 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15416 errmsg("cannot use non-immediate index \"%s\" as replica identity",
15417 RelationGetRelationName(indexRel))));
15418 /* Expression indexes aren't supported. */
15419 if (RelationGetIndexExpressions(indexRel) != NIL)
15420 ereport(ERROR,
15421 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15422 errmsg("cannot use expression index \"%s\" as replica identity",
15423 RelationGetRelationName(indexRel))));
15424 /* Predicate indexes aren't supported. */
15425 if (RelationGetIndexPredicate(indexRel) != NIL)
15426 ereport(ERROR,
15427 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15428 errmsg("cannot use partial index \"%s\" as replica identity",
15429 RelationGetRelationName(indexRel))));
15430 /* And neither are invalid indexes. */
15431 if (!indexRel->rd_index->indisvalid)
15432 ereport(ERROR,
15433 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15434 errmsg("cannot use invalid index \"%s\" as replica identity",
15435 RelationGetRelationName(indexRel))));
15436
15437 /* Check index for nullable columns. */
15438 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
15439 {
15440 int16 attno = indexRel->rd_index->indkey.values[key];
15441 Form_pg_attribute attr;
15442
15443 /*
15444 * Reject any other system columns. (Going forward, we'll disallow
15445 * indexes containing such columns in the first place, but they might
15446 * exist in older branches.)
15447 */
15448 if (attno <= 0)
15449 ereport(ERROR,
15450 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
15451 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
15452 RelationGetRelationName(indexRel), attno)));
15453
15454 attr = TupleDescAttr(rel->rd_att, attno - 1);
15455 if (!attr->attnotnull)
15456 ereport(ERROR,
15457 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15458 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
15459 RelationGetRelationName(indexRel),
15460 NameStr(attr->attname))));
15461 }
15462
15463 /* This index is suitable for use as a replica identity. Mark it. */
15464 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
15465
15466 index_close(indexRel, NoLock);
15467 }
15468
15469 /*
15470 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
15471 */
15472 static void
ATExecSetRowSecurity(Relation rel,bool rls)15473 ATExecSetRowSecurity(Relation rel, bool rls)
15474 {
15475 Relation pg_class;
15476 Oid relid;
15477 HeapTuple tuple;
15478
15479 relid = RelationGetRelid(rel);
15480
15481 /* Pull the record for this relation and update it */
15482 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15483
15484 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15485
15486 if (!HeapTupleIsValid(tuple))
15487 elog(ERROR, "cache lookup failed for relation %u", relid);
15488
15489 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
15490 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15491
15492 table_close(pg_class, RowExclusiveLock);
15493 heap_freetuple(tuple);
15494 }
15495
15496 /*
15497 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
15498 */
15499 static void
ATExecForceNoForceRowSecurity(Relation rel,bool force_rls)15500 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
15501 {
15502 Relation pg_class;
15503 Oid relid;
15504 HeapTuple tuple;
15505
15506 relid = RelationGetRelid(rel);
15507
15508 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15509
15510 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15511
15512 if (!HeapTupleIsValid(tuple))
15513 elog(ERROR, "cache lookup failed for relation %u", relid);
15514
15515 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
15516 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15517
15518 table_close(pg_class, RowExclusiveLock);
15519 heap_freetuple(tuple);
15520 }
15521
15522 /*
15523 * ALTER FOREIGN TABLE <name> OPTIONS (...)
15524 */
15525 static void
ATExecGenericOptions(Relation rel,List * options)15526 ATExecGenericOptions(Relation rel, List *options)
15527 {
15528 Relation ftrel;
15529 ForeignServer *server;
15530 ForeignDataWrapper *fdw;
15531 HeapTuple tuple;
15532 bool isnull;
15533 Datum repl_val[Natts_pg_foreign_table];
15534 bool repl_null[Natts_pg_foreign_table];
15535 bool repl_repl[Natts_pg_foreign_table];
15536 Datum datum;
15537 Form_pg_foreign_table tableform;
15538
15539 if (options == NIL)
15540 return;
15541
15542 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
15543
15544 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
15545 if (!HeapTupleIsValid(tuple))
15546 ereport(ERROR,
15547 (errcode(ERRCODE_UNDEFINED_OBJECT),
15548 errmsg("foreign table \"%s\" does not exist",
15549 RelationGetRelationName(rel))));
15550 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15551 server = GetForeignServer(tableform->ftserver);
15552 fdw = GetForeignDataWrapper(server->fdwid);
15553
15554 memset(repl_val, 0, sizeof(repl_val));
15555 memset(repl_null, false, sizeof(repl_null));
15556 memset(repl_repl, false, sizeof(repl_repl));
15557
15558 /* Extract the current options */
15559 datum = SysCacheGetAttr(FOREIGNTABLEREL,
15560 tuple,
15561 Anum_pg_foreign_table_ftoptions,
15562 &isnull);
15563 if (isnull)
15564 datum = PointerGetDatum(NULL);
15565
15566 /* Transform the options */
15567 datum = transformGenericOptions(ForeignTableRelationId,
15568 datum,
15569 options,
15570 fdw->fdwvalidator);
15571
15572 if (PointerIsValid(DatumGetPointer(datum)))
15573 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
15574 else
15575 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
15576
15577 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
15578
15579 /* Everything looks good - update the tuple */
15580
15581 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
15582 repl_val, repl_null, repl_repl);
15583
15584 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
15585
15586 /*
15587 * Invalidate relcache so that all sessions will refresh any cached plans
15588 * that might depend on the old options.
15589 */
15590 CacheInvalidateRelcache(rel);
15591
15592 InvokeObjectPostAlterHook(ForeignTableRelationId,
15593 RelationGetRelid(rel), 0);
15594
15595 table_close(ftrel, RowExclusiveLock);
15596
15597 heap_freetuple(tuple);
15598 }
15599
15600 /*
15601 * ALTER TABLE ALTER COLUMN SET COMPRESSION
15602 *
15603 * Return value is the address of the modified column
15604 */
15605 static ObjectAddress
ATExecSetCompression(AlteredTableInfo * tab,Relation rel,const char * column,Node * newValue,LOCKMODE lockmode)15606 ATExecSetCompression(AlteredTableInfo *tab,
15607 Relation rel,
15608 const char *column,
15609 Node *newValue,
15610 LOCKMODE lockmode)
15611 {
15612 Relation attrel;
15613 HeapTuple tuple;
15614 Form_pg_attribute atttableform;
15615 AttrNumber attnum;
15616 char *compression;
15617 char cmethod;
15618 ObjectAddress address;
15619
15620 Assert(IsA(newValue, String));
15621 compression = strVal(newValue);
15622
15623 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15624
15625 /* copy the cache entry so we can scribble on it below */
15626 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
15627 if (!HeapTupleIsValid(tuple))
15628 ereport(ERROR,
15629 (errcode(ERRCODE_UNDEFINED_COLUMN),
15630 errmsg("column \"%s\" of relation \"%s\" does not exist",
15631 column, RelationGetRelationName(rel))));
15632
15633 /* prevent them from altering a system attribute */
15634 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15635 attnum = atttableform->attnum;
15636 if (attnum <= 0)
15637 ereport(ERROR,
15638 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15639 errmsg("cannot alter system column \"%s\"", column)));
15640
15641 /*
15642 * Check that column type is compressible, then get the attribute
15643 * compression method code
15644 */
15645 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
15646
15647 /* update pg_attribute entry */
15648 atttableform->attcompression = cmethod;
15649 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
15650
15651 InvokeObjectPostAlterHook(RelationRelationId,
15652 RelationGetRelid(rel),
15653 attnum);
15654
15655 /*
15656 * Apply the change to indexes as well (only for simple index columns,
15657 * matching behavior of index.c ConstructTupleDescriptor()).
15658 */
15659 SetIndexStorageProperties(rel, attrel, attnum,
15660 false, 0,
15661 true, cmethod,
15662 lockmode);
15663
15664 heap_freetuple(tuple);
15665
15666 table_close(attrel, RowExclusiveLock);
15667
15668 /* make changes visible */
15669 CommandCounterIncrement();
15670
15671 ObjectAddressSubSet(address, RelationRelationId,
15672 RelationGetRelid(rel), attnum);
15673 return address;
15674 }
15675
15676
15677 /*
15678 * Preparation phase for SET LOGGED/UNLOGGED
15679 *
15680 * This verifies that we're not trying to change a temp table. Also,
15681 * existing foreign key constraints are checked to avoid ending up with
15682 * permanent tables referencing unlogged tables.
15683 *
15684 * Return value is false if the operation is a no-op (in which case the
15685 * checks are skipped), otherwise true.
15686 */
15687 static bool
ATPrepChangePersistence(Relation rel,bool toLogged)15688 ATPrepChangePersistence(Relation rel, bool toLogged)
15689 {
15690 Relation pg_constraint;
15691 HeapTuple tuple;
15692 SysScanDesc scan;
15693 ScanKeyData skey[1];
15694
15695 /*
15696 * Disallow changing status for a temp table. Also verify whether we can
15697 * get away with doing nothing; in such cases we don't need to run the
15698 * checks below, either.
15699 */
15700 switch (rel->rd_rel->relpersistence)
15701 {
15702 case RELPERSISTENCE_TEMP:
15703 ereport(ERROR,
15704 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
15705 errmsg("cannot change logged status of table \"%s\" because it is temporary",
15706 RelationGetRelationName(rel)),
15707 errtable(rel)));
15708 break;
15709 case RELPERSISTENCE_PERMANENT:
15710 if (toLogged)
15711 /* nothing to do */
15712 return false;
15713 break;
15714 case RELPERSISTENCE_UNLOGGED:
15715 if (!toLogged)
15716 /* nothing to do */
15717 return false;
15718 break;
15719 }
15720
15721 /*
15722 * Check that the table is not part any publication when changing to
15723 * UNLOGGED as UNLOGGED tables can't be published.
15724 */
15725 if (!toLogged &&
15726 list_length(GetRelationPublications(RelationGetRelid(rel))) > 0)
15727 ereport(ERROR,
15728 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
15729 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
15730 RelationGetRelationName(rel)),
15731 errdetail("Unlogged relations cannot be replicated.")));
15732
15733 /*
15734 * Check existing foreign key constraints to preserve the invariant that
15735 * permanent tables cannot reference unlogged ones. Self-referencing
15736 * foreign keys can safely be ignored.
15737 */
15738 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
15739
15740 /*
15741 * Scan conrelid if changing to permanent, else confrelid. This also
15742 * determines whether a useful index exists.
15743 */
15744 ScanKeyInit(&skey[0],
15745 toLogged ? Anum_pg_constraint_conrelid :
15746 Anum_pg_constraint_confrelid,
15747 BTEqualStrategyNumber, F_OIDEQ,
15748 ObjectIdGetDatum(RelationGetRelid(rel)));
15749 scan = systable_beginscan(pg_constraint,
15750 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
15751 true, NULL, 1, skey);
15752
15753 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
15754 {
15755 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
15756
15757 if (con->contype == CONSTRAINT_FOREIGN)
15758 {
15759 Oid foreignrelid;
15760 Relation foreignrel;
15761
15762 /* the opposite end of what we used as scankey */
15763 foreignrelid = toLogged ? con->confrelid : con->conrelid;
15764
15765 /* ignore if self-referencing */
15766 if (RelationGetRelid(rel) == foreignrelid)
15767 continue;
15768
15769 foreignrel = relation_open(foreignrelid, AccessShareLock);
15770
15771 if (toLogged)
15772 {
15773 if (!RelationIsPermanent(foreignrel))
15774 ereport(ERROR,
15775 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
15776 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
15777 RelationGetRelationName(rel),
15778 RelationGetRelationName(foreignrel)),
15779 errtableconstraint(rel, NameStr(con->conname))));
15780 }
15781 else
15782 {
15783 if (RelationIsPermanent(foreignrel))
15784 ereport(ERROR,
15785 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
15786 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
15787 RelationGetRelationName(rel),
15788 RelationGetRelationName(foreignrel)),
15789 errtableconstraint(rel, NameStr(con->conname))));
15790 }
15791
15792 relation_close(foreignrel, AccessShareLock);
15793 }
15794 }
15795
15796 systable_endscan(scan);
15797
15798 table_close(pg_constraint, AccessShareLock);
15799
15800 return true;
15801 }
15802
15803 /*
15804 * Execute ALTER TABLE SET SCHEMA
15805 */
15806 ObjectAddress
AlterTableNamespace(AlterObjectSchemaStmt * stmt,Oid * oldschema)15807 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
15808 {
15809 Relation rel;
15810 Oid relid;
15811 Oid oldNspOid;
15812 Oid nspOid;
15813 RangeVar *newrv;
15814 ObjectAddresses *objsMoved;
15815 ObjectAddress myself;
15816
15817 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
15818 stmt->missing_ok ? RVR_MISSING_OK : 0,
15819 RangeVarCallbackForAlterRelation,
15820 (void *) stmt);
15821
15822 if (!OidIsValid(relid))
15823 {
15824 ereport(NOTICE,
15825 (errmsg("relation \"%s\" does not exist, skipping",
15826 stmt->relation->relname)));
15827 return InvalidObjectAddress;
15828 }
15829
15830 rel = relation_open(relid, NoLock);
15831
15832 oldNspOid = RelationGetNamespace(rel);
15833
15834 /* If it's an owned sequence, disallow moving it by itself. */
15835 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
15836 {
15837 Oid tableId;
15838 int32 colId;
15839
15840 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
15841 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
15842 ereport(ERROR,
15843 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15844 errmsg("cannot move an owned sequence into another schema"),
15845 errdetail("Sequence \"%s\" is linked to table \"%s\".",
15846 RelationGetRelationName(rel),
15847 get_rel_name(tableId))));
15848 }
15849
15850 /* Get and lock schema OID and check its permissions. */
15851 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
15852 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
15853
15854 /* common checks on switching namespaces */
15855 CheckSetNamespace(oldNspOid, nspOid);
15856
15857 objsMoved = new_object_addresses();
15858 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
15859 free_object_addresses(objsMoved);
15860
15861 ObjectAddressSet(myself, RelationRelationId, relid);
15862
15863 if (oldschema)
15864 *oldschema = oldNspOid;
15865
15866 /* close rel, but keep lock until commit */
15867 relation_close(rel, NoLock);
15868
15869 return myself;
15870 }
15871
15872 /*
15873 * The guts of relocating a table or materialized view to another namespace:
15874 * besides moving the relation itself, its dependent objects are relocated to
15875 * the new schema.
15876 */
15877 void
AlterTableNamespaceInternal(Relation rel,Oid oldNspOid,Oid nspOid,ObjectAddresses * objsMoved)15878 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
15879 ObjectAddresses *objsMoved)
15880 {
15881 Relation classRel;
15882
15883 Assert(objsMoved != NULL);
15884
15885 /* OK, modify the pg_class row and pg_depend entry */
15886 classRel = table_open(RelationRelationId, RowExclusiveLock);
15887
15888 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
15889 nspOid, true, objsMoved);
15890
15891 /* Fix the table's row type too, if it has one */
15892 if (OidIsValid(rel->rd_rel->reltype))
15893 AlterTypeNamespaceInternal(rel->rd_rel->reltype,
15894 nspOid, false, false, objsMoved);
15895
15896 /* Fix other dependent stuff */
15897 if (rel->rd_rel->relkind == RELKIND_RELATION ||
15898 rel->rd_rel->relkind == RELKIND_MATVIEW ||
15899 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15900 {
15901 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
15902 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
15903 objsMoved, AccessExclusiveLock);
15904 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
15905 false, objsMoved);
15906 }
15907
15908 table_close(classRel, RowExclusiveLock);
15909 }
15910
15911 /*
15912 * The guts of relocating a relation to another namespace: fix the pg_class
15913 * entry, and the pg_depend entry if any. Caller must already have
15914 * opened and write-locked pg_class.
15915 */
15916 void
AlterRelationNamespaceInternal(Relation classRel,Oid relOid,Oid oldNspOid,Oid newNspOid,bool hasDependEntry,ObjectAddresses * objsMoved)15917 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
15918 Oid oldNspOid, Oid newNspOid,
15919 bool hasDependEntry,
15920 ObjectAddresses *objsMoved)
15921 {
15922 HeapTuple classTup;
15923 Form_pg_class classForm;
15924 ObjectAddress thisobj;
15925 bool already_done = false;
15926
15927 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
15928 if (!HeapTupleIsValid(classTup))
15929 elog(ERROR, "cache lookup failed for relation %u", relOid);
15930 classForm = (Form_pg_class) GETSTRUCT(classTup);
15931
15932 Assert(classForm->relnamespace == oldNspOid);
15933
15934 thisobj.classId = RelationRelationId;
15935 thisobj.objectId = relOid;
15936 thisobj.objectSubId = 0;
15937
15938 /*
15939 * If the object has already been moved, don't move it again. If it's
15940 * already in the right place, don't move it, but still fire the object
15941 * access hook.
15942 */
15943 already_done = object_address_present(&thisobj, objsMoved);
15944 if (!already_done && oldNspOid != newNspOid)
15945 {
15946 /* check for duplicate name (more friendly than unique-index failure) */
15947 if (get_relname_relid(NameStr(classForm->relname),
15948 newNspOid) != InvalidOid)
15949 ereport(ERROR,
15950 (errcode(ERRCODE_DUPLICATE_TABLE),
15951 errmsg("relation \"%s\" already exists in schema \"%s\"",
15952 NameStr(classForm->relname),
15953 get_namespace_name(newNspOid))));
15954
15955 /* classTup is a copy, so OK to scribble on */
15956 classForm->relnamespace = newNspOid;
15957
15958 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
15959
15960 /* Update dependency on schema if caller said so */
15961 if (hasDependEntry &&
15962 changeDependencyFor(RelationRelationId,
15963 relOid,
15964 NamespaceRelationId,
15965 oldNspOid,
15966 newNspOid) != 1)
15967 elog(ERROR, "failed to change schema dependency for relation \"%s\"",
15968 NameStr(classForm->relname));
15969 }
15970 if (!already_done)
15971 {
15972 add_exact_object_address(&thisobj, objsMoved);
15973
15974 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
15975 }
15976
15977 heap_freetuple(classTup);
15978 }
15979
15980 /*
15981 * Move all indexes for the specified relation to another namespace.
15982 *
15983 * Note: we assume adequate permission checking was done by the caller,
15984 * and that the caller has a suitable lock on the owning relation.
15985 */
15986 static void
AlterIndexNamespaces(Relation classRel,Relation rel,Oid oldNspOid,Oid newNspOid,ObjectAddresses * objsMoved)15987 AlterIndexNamespaces(Relation classRel, Relation rel,
15988 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
15989 {
15990 List *indexList;
15991 ListCell *l;
15992
15993 indexList = RelationGetIndexList(rel);
15994
15995 foreach(l, indexList)
15996 {
15997 Oid indexOid = lfirst_oid(l);
15998 ObjectAddress thisobj;
15999
16000 thisobj.classId = RelationRelationId;
16001 thisobj.objectId = indexOid;
16002 thisobj.objectSubId = 0;
16003
16004 /*
16005 * Note: currently, the index will not have its own dependency on the
16006 * namespace, so we don't need to do changeDependencyFor(). There's no
16007 * row type in pg_type, either.
16008 *
16009 * XXX this objsMoved test may be pointless -- surely we have a single
16010 * dependency link from a relation to each index?
16011 */
16012 if (!object_address_present(&thisobj, objsMoved))
16013 {
16014 AlterRelationNamespaceInternal(classRel, indexOid,
16015 oldNspOid, newNspOid,
16016 false, objsMoved);
16017 add_exact_object_address(&thisobj, objsMoved);
16018 }
16019 }
16020
16021 list_free(indexList);
16022 }
16023
16024 /*
16025 * Move all identity and SERIAL-column sequences of the specified relation to another
16026 * namespace.
16027 *
16028 * Note: we assume adequate permission checking was done by the caller,
16029 * and that the caller has a suitable lock on the owning relation.
16030 */
16031 static void
AlterSeqNamespaces(Relation classRel,Relation rel,Oid oldNspOid,Oid newNspOid,ObjectAddresses * objsMoved,LOCKMODE lockmode)16032 AlterSeqNamespaces(Relation classRel, Relation rel,
16033 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
16034 LOCKMODE lockmode)
16035 {
16036 Relation depRel;
16037 SysScanDesc scan;
16038 ScanKeyData key[2];
16039 HeapTuple tup;
16040
16041 /*
16042 * SERIAL sequences are those having an auto dependency on one of the
16043 * table's columns (we don't care *which* column, exactly).
16044 */
16045 depRel = table_open(DependRelationId, AccessShareLock);
16046
16047 ScanKeyInit(&key[0],
16048 Anum_pg_depend_refclassid,
16049 BTEqualStrategyNumber, F_OIDEQ,
16050 ObjectIdGetDatum(RelationRelationId));
16051 ScanKeyInit(&key[1],
16052 Anum_pg_depend_refobjid,
16053 BTEqualStrategyNumber, F_OIDEQ,
16054 ObjectIdGetDatum(RelationGetRelid(rel)));
16055 /* we leave refobjsubid unspecified */
16056
16057 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16058 NULL, 2, key);
16059
16060 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16061 {
16062 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16063 Relation seqRel;
16064
16065 /* skip dependencies other than auto dependencies on columns */
16066 if (depForm->refobjsubid == 0 ||
16067 depForm->classid != RelationRelationId ||
16068 depForm->objsubid != 0 ||
16069 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16070 continue;
16071
16072 /* Use relation_open just in case it's an index */
16073 seqRel = relation_open(depForm->objid, lockmode);
16074
16075 /* skip non-sequence relations */
16076 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16077 {
16078 /* No need to keep the lock */
16079 relation_close(seqRel, lockmode);
16080 continue;
16081 }
16082
16083 /* Fix the pg_class and pg_depend entries */
16084 AlterRelationNamespaceInternal(classRel, depForm->objid,
16085 oldNspOid, newNspOid,
16086 true, objsMoved);
16087
16088 /*
16089 * Sequences used to have entries in pg_type, but no longer do. If we
16090 * ever re-instate that, we'll need to move the pg_type entry to the
16091 * new namespace, too (using AlterTypeNamespaceInternal).
16092 */
16093 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
16094
16095 /* Now we can close it. Keep the lock till end of transaction. */
16096 relation_close(seqRel, NoLock);
16097 }
16098
16099 systable_endscan(scan);
16100
16101 relation_close(depRel, AccessShareLock);
16102 }
16103
16104
16105 /*
16106 * This code supports
16107 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
16108 *
16109 * Because we only support this for TEMP tables, it's sufficient to remember
16110 * the state in a backend-local data structure.
16111 */
16112
16113 /*
16114 * Register a newly-created relation's ON COMMIT action.
16115 */
16116 void
register_on_commit_action(Oid relid,OnCommitAction action)16117 register_on_commit_action(Oid relid, OnCommitAction action)
16118 {
16119 OnCommitItem *oc;
16120 MemoryContext oldcxt;
16121
16122 /*
16123 * We needn't bother registering the relation unless there is an ON COMMIT
16124 * action we need to take.
16125 */
16126 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
16127 return;
16128
16129 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
16130
16131 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
16132 oc->relid = relid;
16133 oc->oncommit = action;
16134 oc->creating_subid = GetCurrentSubTransactionId();
16135 oc->deleting_subid = InvalidSubTransactionId;
16136
16137 /*
16138 * We use lcons() here so that ON COMMIT actions are processed in reverse
16139 * order of registration. That might not be essential but it seems
16140 * reasonable.
16141 */
16142 on_commits = lcons(oc, on_commits);
16143
16144 MemoryContextSwitchTo(oldcxt);
16145 }
16146
16147 /*
16148 * Unregister any ON COMMIT action when a relation is deleted.
16149 *
16150 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
16151 */
16152 void
remove_on_commit_action(Oid relid)16153 remove_on_commit_action(Oid relid)
16154 {
16155 ListCell *l;
16156
16157 foreach(l, on_commits)
16158 {
16159 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
16160
16161 if (oc->relid == relid)
16162 {
16163 oc->deleting_subid = GetCurrentSubTransactionId();
16164 break;
16165 }
16166 }
16167 }
16168
16169 /*
16170 * Perform ON COMMIT actions.
16171 *
16172 * This is invoked just before actually committing, since it's possible
16173 * to encounter errors.
16174 */
16175 void
PreCommit_on_commit_actions(void)16176 PreCommit_on_commit_actions(void)
16177 {
16178 ListCell *l;
16179 List *oids_to_truncate = NIL;
16180 List *oids_to_drop = NIL;
16181
16182 foreach(l, on_commits)
16183 {
16184 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
16185
16186 /* Ignore entry if already dropped in this xact */
16187 if (oc->deleting_subid != InvalidSubTransactionId)
16188 continue;
16189
16190 switch (oc->oncommit)
16191 {
16192 case ONCOMMIT_NOOP:
16193 case ONCOMMIT_PRESERVE_ROWS:
16194 /* Do nothing (there shouldn't be such entries, actually) */
16195 break;
16196 case ONCOMMIT_DELETE_ROWS:
16197
16198 /*
16199 * If this transaction hasn't accessed any temporary
16200 * relations, we can skip truncating ON COMMIT DELETE ROWS
16201 * tables, as they must still be empty.
16202 */
16203 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
16204 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
16205 break;
16206 case ONCOMMIT_DROP:
16207 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
16208 break;
16209 }
16210 }
16211
16212 /*
16213 * Truncate relations before dropping so that all dependencies between
16214 * relations are removed after they are worked on. Doing it like this
16215 * might be a waste as it is possible that a relation being truncated will
16216 * be dropped anyway due to its parent being dropped, but this makes the
16217 * code more robust because of not having to re-check that the relation
16218 * exists at truncation time.
16219 */
16220 if (oids_to_truncate != NIL)
16221 heap_truncate(oids_to_truncate);
16222
16223 if (oids_to_drop != NIL)
16224 {
16225 ObjectAddresses *targetObjects = new_object_addresses();
16226 ListCell *l;
16227
16228 foreach(l, oids_to_drop)
16229 {
16230 ObjectAddress object;
16231
16232 object.classId = RelationRelationId;
16233 object.objectId = lfirst_oid(l);
16234 object.objectSubId = 0;
16235
16236 Assert(!object_address_present(&object, targetObjects));
16237
16238 add_exact_object_address(&object, targetObjects);
16239 }
16240
16241 /*
16242 * Since this is an automatic drop, rather than one directly initiated
16243 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
16244 */
16245 performMultipleDeletions(targetObjects, DROP_CASCADE,
16246 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
16247
16248 #ifdef USE_ASSERT_CHECKING
16249
16250 /*
16251 * Note that table deletion will call remove_on_commit_action, so the
16252 * entry should get marked as deleted.
16253 */
16254 foreach(l, on_commits)
16255 {
16256 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
16257
16258 if (oc->oncommit != ONCOMMIT_DROP)
16259 continue;
16260
16261 Assert(oc->deleting_subid != InvalidSubTransactionId);
16262 }
16263 #endif
16264 }
16265 }
16266
16267 /*
16268 * Post-commit or post-abort cleanup for ON COMMIT management.
16269 *
16270 * All we do here is remove no-longer-needed OnCommitItem entries.
16271 *
16272 * During commit, remove entries that were deleted during this transaction;
16273 * during abort, remove those created during this transaction.
16274 */
16275 void
AtEOXact_on_commit_actions(bool isCommit)16276 AtEOXact_on_commit_actions(bool isCommit)
16277 {
16278 ListCell *cur_item;
16279
16280 foreach(cur_item, on_commits)
16281 {
16282 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
16283
16284 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
16285 oc->creating_subid != InvalidSubTransactionId)
16286 {
16287 /* cur_item must be removed */
16288 on_commits = foreach_delete_current(on_commits, cur_item);
16289 pfree(oc);
16290 }
16291 else
16292 {
16293 /* cur_item must be preserved */
16294 oc->creating_subid = InvalidSubTransactionId;
16295 oc->deleting_subid = InvalidSubTransactionId;
16296 }
16297 }
16298 }
16299
16300 /*
16301 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
16302 *
16303 * During subabort, we can immediately remove entries created during this
16304 * subtransaction. During subcommit, just relabel entries marked during
16305 * this subtransaction as being the parent's responsibility.
16306 */
16307 void
AtEOSubXact_on_commit_actions(bool isCommit,SubTransactionId mySubid,SubTransactionId parentSubid)16308 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
16309 SubTransactionId parentSubid)
16310 {
16311 ListCell *cur_item;
16312
16313 foreach(cur_item, on_commits)
16314 {
16315 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
16316
16317 if (!isCommit && oc->creating_subid == mySubid)
16318 {
16319 /* cur_item must be removed */
16320 on_commits = foreach_delete_current(on_commits, cur_item);
16321 pfree(oc);
16322 }
16323 else
16324 {
16325 /* cur_item must be preserved */
16326 if (oc->creating_subid == mySubid)
16327 oc->creating_subid = parentSubid;
16328 if (oc->deleting_subid == mySubid)
16329 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
16330 }
16331 }
16332 }
16333
16334 /*
16335 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
16336 * the relation to be locked only if (1) it's a plain table, materialized
16337 * view, or TOAST table and (2) the current user is the owner (or the
16338 * superuser). This meets the permission-checking needs of CLUSTER, REINDEX
16339 * TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it can be
16340 * used by all.
16341 */
16342 void
RangeVarCallbackOwnsTable(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)16343 RangeVarCallbackOwnsTable(const RangeVar *relation,
16344 Oid relId, Oid oldRelId, void *arg)
16345 {
16346 char relkind;
16347
16348 /* Nothing to do if the relation was not found. */
16349 if (!OidIsValid(relId))
16350 return;
16351
16352 /*
16353 * If the relation does exist, check whether it's an index. But note that
16354 * the relation might have been dropped between the time we did the name
16355 * lookup and now. In that case, there's nothing to do.
16356 */
16357 relkind = get_rel_relkind(relId);
16358 if (!relkind)
16359 return;
16360 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
16361 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
16362 ereport(ERROR,
16363 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16364 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
16365
16366 /* Check permissions */
16367 if (!pg_class_ownercheck(relId, GetUserId()))
16368 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)), relation->relname);
16369 }
16370
16371 /*
16372 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
16373 */
16374 static void
RangeVarCallbackForTruncate(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)16375 RangeVarCallbackForTruncate(const RangeVar *relation,
16376 Oid relId, Oid oldRelId, void *arg)
16377 {
16378 HeapTuple tuple;
16379
16380 /* Nothing to do if the relation was not found. */
16381 if (!OidIsValid(relId))
16382 return;
16383
16384 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
16385 if (!HeapTupleIsValid(tuple)) /* should not happen */
16386 elog(ERROR, "cache lookup failed for relation %u", relId);
16387
16388 truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
16389 truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
16390
16391 ReleaseSysCache(tuple);
16392 }
16393
16394 /*
16395 * Callback to RangeVarGetRelidExtended(), similar to
16396 * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
16397 */
16398 void
RangeVarCallbackOwnsRelation(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)16399 RangeVarCallbackOwnsRelation(const RangeVar *relation,
16400 Oid relId, Oid oldRelId, void *arg)
16401 {
16402 HeapTuple tuple;
16403
16404 /* Nothing to do if the relation was not found. */
16405 if (!OidIsValid(relId))
16406 return;
16407
16408 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
16409 if (!HeapTupleIsValid(tuple)) /* should not happen */
16410 elog(ERROR, "cache lookup failed for relation %u", relId);
16411
16412 if (!pg_class_ownercheck(relId, GetUserId()))
16413 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
16414 relation->relname);
16415
16416 if (!allowSystemTableMods &&
16417 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
16418 ereport(ERROR,
16419 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
16420 errmsg("permission denied: \"%s\" is a system catalog",
16421 relation->relname)));
16422
16423 ReleaseSysCache(tuple);
16424 }
16425
16426 /*
16427 * Common RangeVarGetRelid callback for rename, set schema, and alter table
16428 * processing.
16429 */
16430 static void
RangeVarCallbackForAlterRelation(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)16431 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
16432 void *arg)
16433 {
16434 Node *stmt = (Node *) arg;
16435 ObjectType reltype;
16436 HeapTuple tuple;
16437 Form_pg_class classform;
16438 AclResult aclresult;
16439 char relkind;
16440
16441 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
16442 if (!HeapTupleIsValid(tuple))
16443 return; /* concurrently dropped */
16444 classform = (Form_pg_class) GETSTRUCT(tuple);
16445 relkind = classform->relkind;
16446
16447 /* Must own relation. */
16448 if (!pg_class_ownercheck(relid, GetUserId()))
16449 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
16450
16451 /* No system table modifications unless explicitly allowed. */
16452 if (!allowSystemTableMods && IsSystemClass(relid, classform))
16453 ereport(ERROR,
16454 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
16455 errmsg("permission denied: \"%s\" is a system catalog",
16456 rv->relname)));
16457
16458 /*
16459 * Extract the specified relation type from the statement parse tree.
16460 *
16461 * Also, for ALTER .. RENAME, check permissions: the user must (still)
16462 * have CREATE rights on the containing namespace.
16463 */
16464 if (IsA(stmt, RenameStmt))
16465 {
16466 aclresult = pg_namespace_aclcheck(classform->relnamespace,
16467 GetUserId(), ACL_CREATE);
16468 if (aclresult != ACLCHECK_OK)
16469 aclcheck_error(aclresult, OBJECT_SCHEMA,
16470 get_namespace_name(classform->relnamespace));
16471 reltype = ((RenameStmt *) stmt)->renameType;
16472 }
16473 else if (IsA(stmt, AlterObjectSchemaStmt))
16474 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
16475
16476 else if (IsA(stmt, AlterTableStmt))
16477 reltype = ((AlterTableStmt *) stmt)->objtype;
16478 else
16479 {
16480 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
16481 reltype = OBJECT_TABLE; /* placate compiler */
16482 }
16483
16484 /*
16485 * For compatibility with prior releases, we allow ALTER TABLE to be used
16486 * with most other types of relations (but not composite types). We allow
16487 * similar flexibility for ALTER INDEX in the case of RENAME, but not
16488 * otherwise. Otherwise, the user must select the correct form of the
16489 * command for the relation at issue.
16490 */
16491 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
16492 ereport(ERROR,
16493 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16494 errmsg("\"%s\" is not a sequence", rv->relname)));
16495
16496 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
16497 ereport(ERROR,
16498 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16499 errmsg("\"%s\" is not a view", rv->relname)));
16500
16501 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
16502 ereport(ERROR,
16503 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16504 errmsg("\"%s\" is not a materialized view", rv->relname)));
16505
16506 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
16507 ereport(ERROR,
16508 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16509 errmsg("\"%s\" is not a foreign table", rv->relname)));
16510
16511 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
16512 ereport(ERROR,
16513 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16514 errmsg("\"%s\" is not a composite type", rv->relname)));
16515
16516 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
16517 relkind != RELKIND_PARTITIONED_INDEX
16518 && !IsA(stmt, RenameStmt))
16519 ereport(ERROR,
16520 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16521 errmsg("\"%s\" is not an index", rv->relname)));
16522
16523 /*
16524 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
16525 * TYPE for that.
16526 */
16527 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
16528 ereport(ERROR,
16529 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16530 errmsg("\"%s\" is a composite type", rv->relname),
16531 errhint("Use ALTER TYPE instead.")));
16532
16533 /*
16534 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
16535 * to a different schema, such as indexes and TOAST tables.
16536 */
16537 if (IsA(stmt, AlterObjectSchemaStmt) &&
16538 relkind != RELKIND_RELATION &&
16539 relkind != RELKIND_VIEW &&
16540 relkind != RELKIND_MATVIEW &&
16541 relkind != RELKIND_SEQUENCE &&
16542 relkind != RELKIND_FOREIGN_TABLE &&
16543 relkind != RELKIND_PARTITIONED_TABLE)
16544 ereport(ERROR,
16545 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16546 errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table",
16547 rv->relname)));
16548
16549 ReleaseSysCache(tuple);
16550 }
16551
16552 /*
16553 * Transform any expressions present in the partition key
16554 *
16555 * Returns a transformed PartitionSpec, as well as the strategy code
16556 */
16557 static PartitionSpec *
transformPartitionSpec(Relation rel,PartitionSpec * partspec,char * strategy)16558 transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
16559 {
16560 PartitionSpec *newspec;
16561 ParseState *pstate;
16562 ParseNamespaceItem *nsitem;
16563 ListCell *l;
16564
16565 newspec = makeNode(PartitionSpec);
16566
16567 newspec->strategy = partspec->strategy;
16568 newspec->partParams = NIL;
16569 newspec->location = partspec->location;
16570
16571 /* Parse partitioning strategy name */
16572 if (pg_strcasecmp(partspec->strategy, "hash") == 0)
16573 *strategy = PARTITION_STRATEGY_HASH;
16574 else if (pg_strcasecmp(partspec->strategy, "list") == 0)
16575 *strategy = PARTITION_STRATEGY_LIST;
16576 else if (pg_strcasecmp(partspec->strategy, "range") == 0)
16577 *strategy = PARTITION_STRATEGY_RANGE;
16578 else
16579 ereport(ERROR,
16580 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16581 errmsg("unrecognized partitioning strategy \"%s\"",
16582 partspec->strategy)));
16583
16584 /* Check valid number of columns for strategy */
16585 if (*strategy == PARTITION_STRATEGY_LIST &&
16586 list_length(partspec->partParams) != 1)
16587 ereport(ERROR,
16588 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16589 errmsg("cannot use \"list\" partition strategy with more than one column")));
16590
16591 /*
16592 * Create a dummy ParseState and insert the target relation as its sole
16593 * rangetable entry. We need a ParseState for transformExpr.
16594 */
16595 pstate = make_parsestate(NULL);
16596 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
16597 NULL, false, true);
16598 addNSItemToQuery(pstate, nsitem, true, true, true);
16599
16600 /* take care of any partition expressions */
16601 foreach(l, partspec->partParams)
16602 {
16603 PartitionElem *pelem = castNode(PartitionElem, lfirst(l));
16604
16605 if (pelem->expr)
16606 {
16607 /* Copy, to avoid scribbling on the input */
16608 pelem = copyObject(pelem);
16609
16610 /* Now do parse transformation of the expression */
16611 pelem->expr = transformExpr(pstate, pelem->expr,
16612 EXPR_KIND_PARTITION_EXPRESSION);
16613
16614 /* we have to fix its collations too */
16615 assign_expr_collations(pstate, pelem->expr);
16616 }
16617
16618 newspec->partParams = lappend(newspec->partParams, pelem);
16619 }
16620
16621 return newspec;
16622 }
16623
16624 /*
16625 * Compute per-partition-column information from a list of PartitionElems.
16626 * Expressions in the PartitionElems must be parse-analyzed already.
16627 */
16628 static void
ComputePartitionAttrs(ParseState * pstate,Relation rel,List * partParams,AttrNumber * partattrs,List ** partexprs,Oid * partopclass,Oid * partcollation,char strategy)16629 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
16630 List **partexprs, Oid *partopclass, Oid *partcollation,
16631 char strategy)
16632 {
16633 int attn;
16634 ListCell *lc;
16635 Oid am_oid;
16636
16637 attn = 0;
16638 foreach(lc, partParams)
16639 {
16640 PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
16641 Oid atttype;
16642 Oid attcollation;
16643
16644 if (pelem->name != NULL)
16645 {
16646 /* Simple attribute reference */
16647 HeapTuple atttuple;
16648 Form_pg_attribute attform;
16649
16650 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
16651 pelem->name);
16652 if (!HeapTupleIsValid(atttuple))
16653 ereport(ERROR,
16654 (errcode(ERRCODE_UNDEFINED_COLUMN),
16655 errmsg("column \"%s\" named in partition key does not exist",
16656 pelem->name),
16657 parser_errposition(pstate, pelem->location)));
16658 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
16659
16660 if (attform->attnum <= 0)
16661 ereport(ERROR,
16662 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16663 errmsg("cannot use system column \"%s\" in partition key",
16664 pelem->name),
16665 parser_errposition(pstate, pelem->location)));
16666
16667 /*
16668 * Generated columns cannot work: They are computed after BEFORE
16669 * triggers, but partition routing is done before all triggers.
16670 */
16671 if (attform->attgenerated)
16672 ereport(ERROR,
16673 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16674 errmsg("cannot use generated column in partition key"),
16675 errdetail("Column \"%s\" is a generated column.",
16676 pelem->name),
16677 parser_errposition(pstate, pelem->location)));
16678
16679 partattrs[attn] = attform->attnum;
16680 atttype = attform->atttypid;
16681 attcollation = attform->attcollation;
16682 ReleaseSysCache(atttuple);
16683 }
16684 else
16685 {
16686 /* Expression */
16687 Node *expr = pelem->expr;
16688 char partattname[16];
16689
16690 Assert(expr != NULL);
16691 atttype = exprType(expr);
16692 attcollation = exprCollation(expr);
16693
16694 /*
16695 * The expression must be of a storable type (e.g., not RECORD).
16696 * The test is the same as for whether a table column is of a safe
16697 * type (which is why we needn't check for the non-expression
16698 * case).
16699 */
16700 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
16701 CheckAttributeType(partattname,
16702 atttype, attcollation,
16703 NIL, CHKATYPE_IS_PARTKEY);
16704
16705 /*
16706 * Strip any top-level COLLATE clause. This ensures that we treat
16707 * "x COLLATE y" and "(x COLLATE y)" alike.
16708 */
16709 while (IsA(expr, CollateExpr))
16710 expr = (Node *) ((CollateExpr *) expr)->arg;
16711
16712 if (IsA(expr, Var) &&
16713 ((Var *) expr)->varattno > 0)
16714 {
16715 /*
16716 * User wrote "(column)" or "(column COLLATE something)".
16717 * Treat it like simple attribute anyway.
16718 */
16719 partattrs[attn] = ((Var *) expr)->varattno;
16720 }
16721 else
16722 {
16723 Bitmapset *expr_attrs = NULL;
16724 int i;
16725
16726 partattrs[attn] = 0; /* marks the column as expression */
16727 *partexprs = lappend(*partexprs, expr);
16728
16729 /*
16730 * Try to simplify the expression before checking for
16731 * mutability. The main practical value of doing it in this
16732 * order is that an inline-able SQL-language function will be
16733 * accepted if its expansion is immutable, whether or not the
16734 * function itself is marked immutable.
16735 *
16736 * Note that expression_planner does not change the passed in
16737 * expression destructively and we have already saved the
16738 * expression to be stored into the catalog above.
16739 */
16740 expr = (Node *) expression_planner((Expr *) expr);
16741
16742 /*
16743 * Partition expression cannot contain mutable functions,
16744 * because a given row must always map to the same partition
16745 * as long as there is no change in the partition boundary
16746 * structure.
16747 */
16748 if (contain_mutable_functions(expr))
16749 ereport(ERROR,
16750 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16751 errmsg("functions in partition key expression must be marked IMMUTABLE")));
16752
16753 /*
16754 * transformPartitionSpec() should have already rejected
16755 * subqueries, aggregates, window functions, and SRFs, based
16756 * on the EXPR_KIND_ for partition expressions.
16757 */
16758
16759 /*
16760 * Cannot allow system column references, since that would
16761 * make partition routing impossible: their values won't be
16762 * known yet when we need to do that.
16763 */
16764 pull_varattnos(expr, 1, &expr_attrs);
16765 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
16766 {
16767 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
16768 expr_attrs))
16769 ereport(ERROR,
16770 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16771 errmsg("partition key expressions cannot contain system column references")));
16772 }
16773
16774 /*
16775 * Generated columns cannot work: They are computed after
16776 * BEFORE triggers, but partition routing is done before all
16777 * triggers.
16778 */
16779 i = -1;
16780 while ((i = bms_next_member(expr_attrs, i)) >= 0)
16781 {
16782 AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
16783
16784 if (attno > 0 &&
16785 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
16786 ereport(ERROR,
16787 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16788 errmsg("cannot use generated column in partition key"),
16789 errdetail("Column \"%s\" is a generated column.",
16790 get_attname(RelationGetRelid(rel), attno, false)),
16791 parser_errposition(pstate, pelem->location)));
16792 }
16793
16794 /*
16795 * While it is not exactly *wrong* for a partition expression
16796 * to be a constant, it seems better to reject such keys.
16797 */
16798 if (IsA(expr, Const))
16799 ereport(ERROR,
16800 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16801 errmsg("cannot use constant expression as partition key")));
16802 }
16803 }
16804
16805 /*
16806 * Apply collation override if any
16807 */
16808 if (pelem->collation)
16809 attcollation = get_collation_oid(pelem->collation, false);
16810
16811 /*
16812 * Check we have a collation iff it's a collatable type. The only
16813 * expected failures here are (1) COLLATE applied to a noncollatable
16814 * type, or (2) partition expression had an unresolved collation. But
16815 * we might as well code this to be a complete consistency check.
16816 */
16817 if (type_is_collatable(atttype))
16818 {
16819 if (!OidIsValid(attcollation))
16820 ereport(ERROR,
16821 (errcode(ERRCODE_INDETERMINATE_COLLATION),
16822 errmsg("could not determine which collation to use for partition expression"),
16823 errhint("Use the COLLATE clause to set the collation explicitly.")));
16824 }
16825 else
16826 {
16827 if (OidIsValid(attcollation))
16828 ereport(ERROR,
16829 (errcode(ERRCODE_DATATYPE_MISMATCH),
16830 errmsg("collations are not supported by type %s",
16831 format_type_be(atttype))));
16832 }
16833
16834 partcollation[attn] = attcollation;
16835
16836 /*
16837 * Identify the appropriate operator class. For list and range
16838 * partitioning, we use a btree operator class; hash partitioning uses
16839 * a hash operator class.
16840 */
16841 if (strategy == PARTITION_STRATEGY_HASH)
16842 am_oid = HASH_AM_OID;
16843 else
16844 am_oid = BTREE_AM_OID;
16845
16846 if (!pelem->opclass)
16847 {
16848 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
16849
16850 if (!OidIsValid(partopclass[attn]))
16851 {
16852 if (strategy == PARTITION_STRATEGY_HASH)
16853 ereport(ERROR,
16854 (errcode(ERRCODE_UNDEFINED_OBJECT),
16855 errmsg("data type %s has no default operator class for access method \"%s\"",
16856 format_type_be(atttype), "hash"),
16857 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
16858 else
16859 ereport(ERROR,
16860 (errcode(ERRCODE_UNDEFINED_OBJECT),
16861 errmsg("data type %s has no default operator class for access method \"%s\"",
16862 format_type_be(atttype), "btree"),
16863 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
16864
16865 }
16866 }
16867 else
16868 partopclass[attn] = ResolveOpClass(pelem->opclass,
16869 atttype,
16870 am_oid == HASH_AM_OID ? "hash" : "btree",
16871 am_oid);
16872
16873 attn++;
16874 }
16875 }
16876
16877 /*
16878 * PartConstraintImpliedByRelConstraint
16879 * Do scanrel's existing constraints imply the partition constraint?
16880 *
16881 * "Existing constraints" include its check constraints and column-level
16882 * NOT NULL constraints. partConstraint describes the partition constraint,
16883 * in implicit-AND form.
16884 */
16885 bool
PartConstraintImpliedByRelConstraint(Relation scanrel,List * partConstraint)16886 PartConstraintImpliedByRelConstraint(Relation scanrel,
16887 List *partConstraint)
16888 {
16889 List *existConstraint = NIL;
16890 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
16891 int i;
16892
16893 if (constr && constr->has_not_null)
16894 {
16895 int natts = scanrel->rd_att->natts;
16896
16897 for (i = 1; i <= natts; i++)
16898 {
16899 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
16900
16901 if (att->attnotnull && !att->attisdropped)
16902 {
16903 NullTest *ntest = makeNode(NullTest);
16904
16905 ntest->arg = (Expr *) makeVar(1,
16906 i,
16907 att->atttypid,
16908 att->atttypmod,
16909 att->attcollation,
16910 0);
16911 ntest->nulltesttype = IS_NOT_NULL;
16912
16913 /*
16914 * argisrow=false is correct even for a composite column,
16915 * because attnotnull does not represent a SQL-spec IS NOT
16916 * NULL test in such a case, just IS DISTINCT FROM NULL.
16917 */
16918 ntest->argisrow = false;
16919 ntest->location = -1;
16920 existConstraint = lappend(existConstraint, ntest);
16921 }
16922 }
16923 }
16924
16925 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
16926 }
16927
16928 /*
16929 * ConstraintImpliedByRelConstraint
16930 * Do scanrel's existing constraints imply the given constraint?
16931 *
16932 * testConstraint is the constraint to validate. provenConstraint is a
16933 * caller-provided list of conditions which this function may assume
16934 * to be true. Both provenConstraint and testConstraint must be in
16935 * implicit-AND form, must only contain immutable clauses, and must
16936 * contain only Vars with varno = 1.
16937 */
16938 bool
ConstraintImpliedByRelConstraint(Relation scanrel,List * testConstraint,List * provenConstraint)16939 ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
16940 {
16941 List *existConstraint = list_copy(provenConstraint);
16942 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
16943 int num_check,
16944 i;
16945
16946 num_check = (constr != NULL) ? constr->num_check : 0;
16947 for (i = 0; i < num_check; i++)
16948 {
16949 Node *cexpr;
16950
16951 /*
16952 * If this constraint hasn't been fully validated yet, we must ignore
16953 * it here.
16954 */
16955 if (!constr->check[i].ccvalid)
16956 continue;
16957
16958 cexpr = stringToNode(constr->check[i].ccbin);
16959
16960 /*
16961 * Run each expression through const-simplification and
16962 * canonicalization. It is necessary, because we will be comparing it
16963 * to similarly-processed partition constraint expressions, and may
16964 * fail to detect valid matches without this.
16965 */
16966 cexpr = eval_const_expressions(NULL, cexpr);
16967 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
16968
16969 existConstraint = list_concat(existConstraint,
16970 make_ands_implicit((Expr *) cexpr));
16971 }
16972
16973 /*
16974 * Try to make the proof. Since we are comparing CHECK constraints, we
16975 * need to use weak implication, i.e., we assume existConstraint is
16976 * not-false and try to prove the same for testConstraint.
16977 *
16978 * Note that predicate_implied_by assumes its first argument is known
16979 * immutable. That should always be true for both NOT NULL and partition
16980 * constraints, so we don't test it here.
16981 */
16982 return predicate_implied_by(testConstraint, existConstraint, true);
16983 }
16984
16985 /*
16986 * QueuePartitionConstraintValidation
16987 *
16988 * Add an entry to wqueue to have the given partition constraint validated by
16989 * Phase 3, for the given relation, and all its children.
16990 *
16991 * We first verify whether the given constraint is implied by pre-existing
16992 * relation constraints; if it is, there's no need to scan the table to
16993 * validate, so don't queue in that case.
16994 */
16995 static void
QueuePartitionConstraintValidation(List ** wqueue,Relation scanrel,List * partConstraint,bool validate_default)16996 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
16997 List *partConstraint,
16998 bool validate_default)
16999 {
17000 /*
17001 * Based on the table's existing constraints, determine whether or not we
17002 * may skip scanning the table.
17003 */
17004 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
17005 {
17006 if (!validate_default)
17007 ereport(DEBUG1,
17008 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
17009 RelationGetRelationName(scanrel))));
17010 else
17011 ereport(DEBUG1,
17012 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
17013 RelationGetRelationName(scanrel))));
17014 return;
17015 }
17016
17017 /*
17018 * Constraints proved insufficient. For plain relations, queue a
17019 * validation item now; for partitioned tables, recurse to process each
17020 * partition.
17021 */
17022 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
17023 {
17024 AlteredTableInfo *tab;
17025
17026 /* Grab a work queue entry. */
17027 tab = ATGetQueueEntry(wqueue, scanrel);
17028 Assert(tab->partition_constraint == NULL);
17029 tab->partition_constraint = (Expr *) linitial(partConstraint);
17030 tab->validate_default = validate_default;
17031 }
17032 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17033 {
17034 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
17035 int i;
17036
17037 for (i = 0; i < partdesc->nparts; i++)
17038 {
17039 Relation part_rel;
17040 List *thisPartConstraint;
17041
17042 /*
17043 * This is the minimum lock we need to prevent deadlocks.
17044 */
17045 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
17046
17047 /*
17048 * Adjust the constraint for scanrel so that it matches this
17049 * partition's attribute numbers.
17050 */
17051 thisPartConstraint =
17052 map_partition_varattnos(partConstraint, 1,
17053 part_rel, scanrel);
17054
17055 QueuePartitionConstraintValidation(wqueue, part_rel,
17056 thisPartConstraint,
17057 validate_default);
17058 table_close(part_rel, NoLock); /* keep lock till commit */
17059 }
17060 }
17061 }
17062
17063 /*
17064 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
17065 *
17066 * Return the address of the newly attached partition.
17067 */
17068 static ObjectAddress
ATExecAttachPartition(List ** wqueue,Relation rel,PartitionCmd * cmd,AlterTableUtilityContext * context)17069 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
17070 AlterTableUtilityContext *context)
17071 {
17072 Relation attachrel,
17073 catalog;
17074 List *attachrel_children;
17075 List *partConstraint;
17076 SysScanDesc scan;
17077 ScanKeyData skey;
17078 AttrNumber attno;
17079 int natts;
17080 TupleDesc tupleDesc;
17081 ObjectAddress address;
17082 const char *trigger_name;
17083 Oid defaultPartOid;
17084 List *partBoundConstraint;
17085 ParseState *pstate = make_parsestate(NULL);
17086
17087 pstate->p_sourcetext = context->queryString;
17088
17089 /*
17090 * We must lock the default partition if one exists, because attaching a
17091 * new partition will change its partition constraint.
17092 */
17093 defaultPartOid =
17094 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
17095 if (OidIsValid(defaultPartOid))
17096 LockRelationOid(defaultPartOid, AccessExclusiveLock);
17097
17098 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
17099
17100 /*
17101 * XXX I think it'd be a good idea to grab locks on all tables referenced
17102 * by FKs at this point also.
17103 */
17104
17105 /*
17106 * Must be owner of both parent and source table -- parent was checked by
17107 * ATSimplePermissions call in ATPrepCmd
17108 */
17109 ATSimplePermissions(attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
17110
17111 /* A partition can only have one parent */
17112 if (attachrel->rd_rel->relispartition)
17113 ereport(ERROR,
17114 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17115 errmsg("\"%s\" is already a partition",
17116 RelationGetRelationName(attachrel))));
17117
17118 if (OidIsValid(attachrel->rd_rel->reloftype))
17119 ereport(ERROR,
17120 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17121 errmsg("cannot attach a typed table as partition")));
17122
17123 /*
17124 * Table being attached should not already be part of inheritance; either
17125 * as a child table...
17126 */
17127 catalog = table_open(InheritsRelationId, AccessShareLock);
17128 ScanKeyInit(&skey,
17129 Anum_pg_inherits_inhrelid,
17130 BTEqualStrategyNumber, F_OIDEQ,
17131 ObjectIdGetDatum(RelationGetRelid(attachrel)));
17132 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
17133 NULL, 1, &skey);
17134 if (HeapTupleIsValid(systable_getnext(scan)))
17135 ereport(ERROR,
17136 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17137 errmsg("cannot attach inheritance child as partition")));
17138 systable_endscan(scan);
17139
17140 /* ...or as a parent table (except the case when it is partitioned) */
17141 ScanKeyInit(&skey,
17142 Anum_pg_inherits_inhparent,
17143 BTEqualStrategyNumber, F_OIDEQ,
17144 ObjectIdGetDatum(RelationGetRelid(attachrel)));
17145 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
17146 1, &skey);
17147 if (HeapTupleIsValid(systable_getnext(scan)) &&
17148 attachrel->rd_rel->relkind == RELKIND_RELATION)
17149 ereport(ERROR,
17150 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17151 errmsg("cannot attach inheritance parent as partition")));
17152 systable_endscan(scan);
17153 table_close(catalog, AccessShareLock);
17154
17155 /*
17156 * Prevent circularity by seeing if rel is a partition of attachrel. (In
17157 * particular, this disallows making a rel a partition of itself.)
17158 *
17159 * We do that by checking if rel is a member of the list of attachrel's
17160 * partitions provided the latter is partitioned at all. We want to avoid
17161 * having to construct this list again, so we request the strongest lock
17162 * on all partitions. We need the strongest lock, because we may decide
17163 * to scan them if we find out that the table being attached (or its leaf
17164 * partitions) may contain rows that violate the partition constraint. If
17165 * the table has a constraint that would prevent such rows, which by
17166 * definition is present in all the partitions, we need not scan the
17167 * table, nor its partitions. But we cannot risk a deadlock by taking a
17168 * weaker lock now and the stronger one only when needed.
17169 */
17170 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
17171 AccessExclusiveLock, NULL);
17172 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
17173 ereport(ERROR,
17174 (errcode(ERRCODE_DUPLICATE_TABLE),
17175 errmsg("circular inheritance not allowed"),
17176 errdetail("\"%s\" is already a child of \"%s\".",
17177 RelationGetRelationName(rel),
17178 RelationGetRelationName(attachrel))));
17179
17180 /* If the parent is permanent, so must be all of its partitions. */
17181 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
17182 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
17183 ereport(ERROR,
17184 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17185 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
17186 RelationGetRelationName(rel))));
17187
17188 /* Temp parent cannot have a partition that is itself not a temp */
17189 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17190 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17191 ereport(ERROR,
17192 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17193 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
17194 RelationGetRelationName(rel))));
17195
17196 /* If the parent is temp, it must belong to this session */
17197 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17198 !rel->rd_islocaltemp)
17199 ereport(ERROR,
17200 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17201 errmsg("cannot attach as partition of temporary relation of another session")));
17202
17203 /* Ditto for the partition */
17204 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17205 !attachrel->rd_islocaltemp)
17206 ereport(ERROR,
17207 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17208 errmsg("cannot attach temporary relation of another session as partition")));
17209
17210 /* Check if there are any columns in attachrel that aren't in the parent */
17211 tupleDesc = RelationGetDescr(attachrel);
17212 natts = tupleDesc->natts;
17213 for (attno = 1; attno <= natts; attno++)
17214 {
17215 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
17216 char *attributeName = NameStr(attribute->attname);
17217
17218 /* Ignore dropped */
17219 if (attribute->attisdropped)
17220 continue;
17221
17222 /* Try to find the column in parent (matching on column name) */
17223 if (!SearchSysCacheExists2(ATTNAME,
17224 ObjectIdGetDatum(RelationGetRelid(rel)),
17225 CStringGetDatum(attributeName)))
17226 ereport(ERROR,
17227 (errcode(ERRCODE_DATATYPE_MISMATCH),
17228 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
17229 RelationGetRelationName(attachrel), attributeName,
17230 RelationGetRelationName(rel)),
17231 errdetail("The new partition may contain only the columns present in parent.")));
17232 }
17233
17234 /*
17235 * If child_rel has row-level triggers with transition tables, we
17236 * currently don't allow it to become a partition. See also prohibitions
17237 * in ATExecAddInherit() and CreateTrigger().
17238 */
17239 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
17240 if (trigger_name != NULL)
17241 ereport(ERROR,
17242 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17243 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
17244 trigger_name, RelationGetRelationName(attachrel)),
17245 errdetail("ROW triggers with transition tables are not supported on partitions")));
17246
17247 /*
17248 * Check that the new partition's bound is valid and does not overlap any
17249 * of existing partitions of the parent - note that it does not return on
17250 * error.
17251 */
17252 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
17253 cmd->bound, pstate);
17254
17255 /* OK to create inheritance. Rest of the checks performed there */
17256 CreateInheritance(attachrel, rel);
17257
17258 /* Update the pg_class entry. */
17259 StorePartitionBound(attachrel, rel, cmd->bound);
17260
17261 /* Ensure there exists a correct set of indexes in the partition. */
17262 AttachPartitionEnsureIndexes(rel, attachrel);
17263
17264 /* and triggers */
17265 CloneRowTriggersToPartition(rel, attachrel);
17266
17267 /*
17268 * Clone foreign key constraints. Callee is responsible for setting up
17269 * for phase 3 constraint verification.
17270 */
17271 CloneForeignKeyConstraints(wqueue, rel, attachrel);
17272
17273 /*
17274 * Generate partition constraint from the partition bound specification.
17275 * If the parent itself is a partition, make sure to include its
17276 * constraint as well.
17277 */
17278 partBoundConstraint = get_qual_from_partbound(attachrel, rel, cmd->bound);
17279 partConstraint = list_concat(partBoundConstraint,
17280 RelationGetPartitionQual(rel));
17281
17282 /* Skip validation if there are no constraints to validate. */
17283 if (partConstraint)
17284 {
17285 /*
17286 * Run the partition quals through const-simplification similar to
17287 * check constraints. We skip canonicalize_qual, though, because
17288 * partition quals should be in canonical form already.
17289 */
17290 partConstraint =
17291 (List *) eval_const_expressions(NULL,
17292 (Node *) partConstraint);
17293
17294 /* XXX this sure looks wrong */
17295 partConstraint = list_make1(make_ands_explicit(partConstraint));
17296
17297 /*
17298 * Adjust the generated constraint to match this partition's attribute
17299 * numbers.
17300 */
17301 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
17302 rel);
17303
17304 /* Validate partition constraints against the table being attached. */
17305 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
17306 false);
17307 }
17308
17309 /*
17310 * If we're attaching a partition other than the default partition and a
17311 * default one exists, then that partition's partition constraint changes,
17312 * so add an entry to the work queue to validate it, too. (We must not do
17313 * this when the partition being attached is the default one; we already
17314 * did it above!)
17315 */
17316 if (OidIsValid(defaultPartOid))
17317 {
17318 Relation defaultrel;
17319 List *defPartConstraint;
17320
17321 Assert(!cmd->bound->is_default);
17322
17323 /* we already hold a lock on the default partition */
17324 defaultrel = table_open(defaultPartOid, NoLock);
17325 defPartConstraint =
17326 get_proposed_default_constraint(partBoundConstraint);
17327
17328 /*
17329 * Map the Vars in the constraint expression from rel's attnos to
17330 * defaultrel's.
17331 */
17332 defPartConstraint =
17333 map_partition_varattnos(defPartConstraint,
17334 1, defaultrel, rel);
17335 QueuePartitionConstraintValidation(wqueue, defaultrel,
17336 defPartConstraint, true);
17337
17338 /* keep our lock until commit. */
17339 table_close(defaultrel, NoLock);
17340 }
17341
17342 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
17343
17344 /*
17345 * If the partition we just attached is partitioned itself, invalidate
17346 * relcache for all descendent partitions too to ensure that their
17347 * rd_partcheck expression trees are rebuilt; partitions already locked
17348 * at the beginning of this function.
17349 */
17350 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17351 {
17352 ListCell *l;
17353
17354 foreach(l, attachrel_children)
17355 {
17356 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
17357 }
17358 }
17359
17360 /* keep our lock until commit */
17361 table_close(attachrel, NoLock);
17362
17363 return address;
17364 }
17365
17366 /*
17367 * AttachPartitionEnsureIndexes
17368 * subroutine for ATExecAttachPartition to create/match indexes
17369 *
17370 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
17371 * PARTITION: every partition must have an index attached to each index on the
17372 * partitioned table.
17373 */
17374 static void
AttachPartitionEnsureIndexes(Relation rel,Relation attachrel)17375 AttachPartitionEnsureIndexes(Relation rel, Relation attachrel)
17376 {
17377 List *idxes;
17378 List *attachRelIdxs;
17379 Relation *attachrelIdxRels;
17380 IndexInfo **attachInfos;
17381 int i;
17382 ListCell *cell;
17383 MemoryContext cxt;
17384 MemoryContext oldcxt;
17385
17386 cxt = AllocSetContextCreate(CurrentMemoryContext,
17387 "AttachPartitionEnsureIndexes",
17388 ALLOCSET_DEFAULT_SIZES);
17389 oldcxt = MemoryContextSwitchTo(cxt);
17390
17391 idxes = RelationGetIndexList(rel);
17392 attachRelIdxs = RelationGetIndexList(attachrel);
17393 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
17394 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
17395
17396 /* Build arrays of all existing indexes and their IndexInfos */
17397 i = 0;
17398 foreach(cell, attachRelIdxs)
17399 {
17400 Oid cldIdxId = lfirst_oid(cell);
17401
17402 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
17403 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
17404 i++;
17405 }
17406
17407 /*
17408 * If we're attaching a foreign table, we must fail if any of the indexes
17409 * is a constraint index; otherwise, there's nothing to do here. Do this
17410 * before starting work, to avoid wasting the effort of building a few
17411 * non-unique indexes before coming across a unique one.
17412 */
17413 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
17414 {
17415 foreach(cell, idxes)
17416 {
17417 Oid idx = lfirst_oid(cell);
17418 Relation idxRel = index_open(idx, AccessShareLock);
17419
17420 if (idxRel->rd_index->indisunique ||
17421 idxRel->rd_index->indisprimary)
17422 ereport(ERROR,
17423 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17424 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
17425 RelationGetRelationName(attachrel),
17426 RelationGetRelationName(rel)),
17427 errdetail("Partitioned table \"%s\" contains unique indexes.",
17428 RelationGetRelationName(rel))));
17429 index_close(idxRel, AccessShareLock);
17430 }
17431
17432 goto out;
17433 }
17434
17435 /*
17436 * For each index on the partitioned table, find a matching one in the
17437 * partition-to-be; if one is not found, create one.
17438 */
17439 foreach(cell, idxes)
17440 {
17441 Oid idx = lfirst_oid(cell);
17442 Relation idxRel = index_open(idx, AccessShareLock);
17443 IndexInfo *info;
17444 AttrMap *attmap;
17445 bool found = false;
17446 Oid constraintOid;
17447
17448 /*
17449 * Ignore indexes in the partitioned table other than partitioned
17450 * indexes.
17451 */
17452 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
17453 {
17454 index_close(idxRel, AccessShareLock);
17455 continue;
17456 }
17457
17458 /* construct an indexinfo to compare existing indexes against */
17459 info = BuildIndexInfo(idxRel);
17460 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
17461 RelationGetDescr(rel));
17462 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
17463
17464 /*
17465 * Scan the list of existing indexes in the partition-to-be, and mark
17466 * the first matching, unattached one we find, if any, as partition of
17467 * the parent index. If we find one, we're done.
17468 */
17469 for (i = 0; i < list_length(attachRelIdxs); i++)
17470 {
17471 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
17472 Oid cldConstrOid = InvalidOid;
17473
17474 /* does this index have a parent? if so, can't use it */
17475 if (attachrelIdxRels[i]->rd_rel->relispartition)
17476 continue;
17477
17478 if (CompareIndexInfo(attachInfos[i], info,
17479 attachrelIdxRels[i]->rd_indcollation,
17480 idxRel->rd_indcollation,
17481 attachrelIdxRels[i]->rd_opfamily,
17482 idxRel->rd_opfamily,
17483 attmap))
17484 {
17485 /*
17486 * If this index is being created in the parent because of a
17487 * constraint, then the child needs to have a constraint also,
17488 * so look for one. If there is no such constraint, this
17489 * index is no good, so keep looking.
17490 */
17491 if (OidIsValid(constraintOid))
17492 {
17493 cldConstrOid =
17494 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
17495 cldIdxId);
17496 /* no dice */
17497 if (!OidIsValid(cldConstrOid))
17498 continue;
17499 }
17500
17501 /* bingo. */
17502 IndexSetParentIndex(attachrelIdxRels[i], idx);
17503 if (OidIsValid(constraintOid))
17504 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
17505 RelationGetRelid(attachrel));
17506 found = true;
17507
17508 CommandCounterIncrement();
17509 break;
17510 }
17511 }
17512
17513 /*
17514 * If no suitable index was found in the partition-to-be, create one
17515 * now.
17516 */
17517 if (!found)
17518 {
17519 IndexStmt *stmt;
17520 Oid constraintOid;
17521
17522 stmt = generateClonedIndexStmt(NULL,
17523 idxRel, attmap,
17524 &constraintOid);
17525 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
17526 RelationGetRelid(idxRel),
17527 constraintOid,
17528 true, false, false, false, false);
17529 }
17530
17531 index_close(idxRel, AccessShareLock);
17532 }
17533
17534 out:
17535 /* Clean up. */
17536 for (i = 0; i < list_length(attachRelIdxs); i++)
17537 index_close(attachrelIdxRels[i], AccessShareLock);
17538 MemoryContextSwitchTo(oldcxt);
17539 MemoryContextDelete(cxt);
17540 }
17541
17542 /*
17543 * CloneRowTriggersToPartition
17544 * subroutine for ATExecAttachPartition/DefineRelation to create row
17545 * triggers on partitions
17546 */
17547 static void
CloneRowTriggersToPartition(Relation parent,Relation partition)17548 CloneRowTriggersToPartition(Relation parent, Relation partition)
17549 {
17550 Relation pg_trigger;
17551 ScanKeyData key;
17552 SysScanDesc scan;
17553 HeapTuple tuple;
17554 MemoryContext perTupCxt;
17555
17556 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
17557 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
17558 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
17559 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
17560 true, NULL, 1, &key);
17561
17562 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
17563 "clone trig", ALLOCSET_SMALL_SIZES);
17564
17565 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17566 {
17567 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
17568 CreateTrigStmt *trigStmt;
17569 Node *qual = NULL;
17570 Datum value;
17571 bool isnull;
17572 List *cols = NIL;
17573 List *trigargs = NIL;
17574 MemoryContext oldcxt;
17575
17576 /*
17577 * Ignore statement-level triggers; those are not cloned.
17578 */
17579 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
17580 continue;
17581
17582 /*
17583 * Internal triggers require careful examination. Ideally, we don't
17584 * clone them. However, if our parent is itself a partition, there
17585 * might be internal triggers that must not be skipped; for example,
17586 * triggers on our parent that are in turn clones from its parent (our
17587 * grandparent) are marked internal, yet they are to be cloned.
17588 *
17589 * Note we dare not verify that the other trigger belongs to an
17590 * ancestor relation of our parent, because that creates deadlock
17591 * opportunities.
17592 */
17593 if (trigForm->tgisinternal &&
17594 (!parent->rd_rel->relispartition ||
17595 !OidIsValid(trigForm->tgparentid)))
17596 continue;
17597
17598 /*
17599 * Complain if we find an unexpected trigger type.
17600 */
17601 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
17602 !TRIGGER_FOR_AFTER(trigForm->tgtype))
17603 elog(ERROR, "unexpected trigger \"%s\" found",
17604 NameStr(trigForm->tgname));
17605
17606 /* Use short-lived context for CREATE TRIGGER */
17607 oldcxt = MemoryContextSwitchTo(perTupCxt);
17608
17609 /*
17610 * If there is a WHEN clause, generate a 'cooked' version of it that's
17611 * appropriate for the partition.
17612 */
17613 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
17614 RelationGetDescr(pg_trigger), &isnull);
17615 if (!isnull)
17616 {
17617 qual = stringToNode(TextDatumGetCString(value));
17618 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
17619 partition, parent);
17620 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
17621 partition, parent);
17622 }
17623
17624 /*
17625 * If there is a column list, transform it to a list of column names.
17626 * Note we don't need to map this list in any way ...
17627 */
17628 if (trigForm->tgattr.dim1 > 0)
17629 {
17630 int i;
17631
17632 for (i = 0; i < trigForm->tgattr.dim1; i++)
17633 {
17634 Form_pg_attribute col;
17635
17636 col = TupleDescAttr(parent->rd_att,
17637 trigForm->tgattr.values[i] - 1);
17638 cols = lappend(cols,
17639 makeString(pstrdup(NameStr(col->attname))));
17640 }
17641 }
17642
17643 /* Reconstruct trigger arguments list. */
17644 if (trigForm->tgnargs > 0)
17645 {
17646 char *p;
17647
17648 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
17649 RelationGetDescr(pg_trigger), &isnull);
17650 if (isnull)
17651 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
17652 NameStr(trigForm->tgname), RelationGetRelationName(partition));
17653
17654 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
17655
17656 for (int i = 0; i < trigForm->tgnargs; i++)
17657 {
17658 trigargs = lappend(trigargs, makeString(pstrdup(p)));
17659 p += strlen(p) + 1;
17660 }
17661 }
17662
17663 trigStmt = makeNode(CreateTrigStmt);
17664 trigStmt->replace = false;
17665 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
17666 trigStmt->trigname = NameStr(trigForm->tgname);
17667 trigStmt->relation = NULL;
17668 trigStmt->funcname = NULL; /* passed separately */
17669 trigStmt->args = trigargs;
17670 trigStmt->row = true;
17671 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
17672 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
17673 trigStmt->columns = cols;
17674 trigStmt->whenClause = NULL; /* passed separately */
17675 trigStmt->transitionRels = NIL; /* not supported at present */
17676 trigStmt->deferrable = trigForm->tgdeferrable;
17677 trigStmt->initdeferred = trigForm->tginitdeferred;
17678 trigStmt->constrrel = NULL; /* passed separately */
17679
17680 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
17681 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
17682 trigForm->tgfoid, trigForm->oid, qual,
17683 false, true, trigForm->tgenabled);
17684
17685 MemoryContextSwitchTo(oldcxt);
17686 MemoryContextReset(perTupCxt);
17687 }
17688
17689 MemoryContextDelete(perTupCxt);
17690
17691 systable_endscan(scan);
17692 table_close(pg_trigger, RowExclusiveLock);
17693 }
17694
17695 /*
17696 * ALTER TABLE DETACH PARTITION
17697 *
17698 * Return the address of the relation that is no longer a partition of rel.
17699 *
17700 * If concurrent mode is requested, we run in two transactions. A side-
17701 * effect is that this command cannot run in a multi-part ALTER TABLE.
17702 * Currently, that's enforced by the grammar.
17703 *
17704 * The strategy for concurrency is to first modify the partition's
17705 * pg_inherit catalog row to make it visible to everyone that the
17706 * partition is detached, lock the partition against writes, and commit
17707 * the transaction; anyone who requests the partition descriptor from
17708 * that point onwards has to ignore such a partition. In a second
17709 * transaction, we wait until all transactions that could have seen the
17710 * partition as attached are gone, then we remove the rest of partition
17711 * metadata (pg_inherits and pg_class.relpartbounds).
17712 */
17713 static ObjectAddress
ATExecDetachPartition(List ** wqueue,AlteredTableInfo * tab,Relation rel,RangeVar * name,bool concurrent)17714 ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
17715 RangeVar *name, bool concurrent)
17716 {
17717 Relation partRel;
17718 ObjectAddress address;
17719 Oid defaultPartOid;
17720
17721 /*
17722 * We must lock the default partition, because detaching this partition
17723 * will change its partition constraint.
17724 */
17725 defaultPartOid =
17726 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
17727 if (OidIsValid(defaultPartOid))
17728 {
17729 /*
17730 * Concurrent detaching when a default partition exists is not
17731 * supported. The main problem is that the default partition
17732 * constraint would change. And there's a definitional problem: what
17733 * should happen to the tuples that are being inserted that belong to
17734 * the partition being detached? Putting them on the partition being
17735 * detached would be wrong, since they'd become "lost" after the but
17736 * we cannot put them in the default partition either until we alter
17737 * its partition constraint.
17738 *
17739 * I think we could solve this problem if we effected the constraint
17740 * change before committing the first transaction. But the lock would
17741 * have to remain AEL and it would cause concurrent query planning to
17742 * be blocked, so changing it that way would be even worse.
17743 */
17744 if (concurrent)
17745 ereport(ERROR,
17746 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17747 errmsg("cannot detach partitions concurrently when a default partition exists")));
17748 LockRelationOid(defaultPartOid, AccessExclusiveLock);
17749 }
17750
17751 /*
17752 * In concurrent mode, the partition is locked with share-update-exclusive
17753 * in the first transaction. This allows concurrent transactions to be
17754 * doing DML to the partition.
17755 */
17756 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
17757 AccessExclusiveLock);
17758
17759 /*
17760 * Check inheritance conditions and either delete the pg_inherits row (in
17761 * non-concurrent mode) or just set the inhdetachpending flag.
17762 */
17763 if (!concurrent)
17764 RemoveInheritance(partRel, rel, false);
17765 else
17766 MarkInheritDetached(partRel, rel);
17767
17768 /*
17769 * Ensure that foreign keys still hold after this detach. This keeps
17770 * locks on the referencing tables, which prevents concurrent transactions
17771 * from adding rows that we wouldn't see. For this to work in concurrent
17772 * mode, it is critical that the partition appears as no longer attached
17773 * for the RI queries as soon as the first transaction commits.
17774 */
17775 ATDetachCheckNoForeignKeyRefs(partRel);
17776
17777 /*
17778 * Concurrent mode has to work harder; first we add a new constraint to
17779 * the partition that matches the partition constraint. Then we close our
17780 * existing transaction, and in a new one wait for all processes to catch
17781 * up on the catalog updates we've done so far; at that point we can
17782 * complete the operation.
17783 */
17784 if (concurrent)
17785 {
17786 Oid partrelid,
17787 parentrelid;
17788 LOCKTAG tag;
17789 char *parentrelname;
17790 char *partrelname;
17791
17792 /*
17793 * Add a new constraint to the partition being detached, which
17794 * supplants the partition constraint (unless there is one already).
17795 */
17796 DetachAddConstraintIfNeeded(wqueue, partRel);
17797
17798 /*
17799 * We're almost done now; the only traces that remain are the
17800 * pg_inherits tuple and the partition's relpartbounds. Before we can
17801 * remove those, we need to wait until all transactions that know that
17802 * this is a partition are gone.
17803 */
17804
17805 /*
17806 * Remember relation OIDs to re-acquire them later; and relation names
17807 * too, for error messages if something is dropped in between.
17808 */
17809 partrelid = RelationGetRelid(partRel);
17810 parentrelid = RelationGetRelid(rel);
17811 parentrelname = MemoryContextStrdup(PortalContext,
17812 RelationGetRelationName(rel));
17813 partrelname = MemoryContextStrdup(PortalContext,
17814 RelationGetRelationName(partRel));
17815
17816 /* Invalidate relcache entries for the parent -- must be before close */
17817 CacheInvalidateRelcache(rel);
17818
17819 table_close(partRel, NoLock);
17820 table_close(rel, NoLock);
17821 tab->rel = NULL;
17822
17823 /* Make updated catalog entry visible */
17824 PopActiveSnapshot();
17825 CommitTransactionCommand();
17826
17827 StartTransactionCommand();
17828
17829 /*
17830 * Now wait. This ensures that all queries that were planned
17831 * including the partition are finished before we remove the rest of
17832 * catalog entries. We don't need or indeed want to acquire this
17833 * lock, though -- that would block later queries.
17834 *
17835 * We don't need to concern ourselves with waiting for a lock on the
17836 * partition itself, since we will acquire AccessExclusiveLock below.
17837 */
17838 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
17839 WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
17840
17841 /*
17842 * Now acquire locks in both relations again. Note they may have been
17843 * removed in the meantime, so care is required.
17844 */
17845 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
17846 partRel = try_relation_open(partrelid, AccessExclusiveLock);
17847
17848 /* If the relations aren't there, something bad happened; bail out */
17849 if (rel == NULL)
17850 {
17851 if (partRel != NULL) /* shouldn't happen */
17852 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
17853 partrelname);
17854 ereport(ERROR,
17855 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17856 errmsg("partitioned table \"%s\" was removed concurrently",
17857 parentrelname)));
17858 }
17859 if (partRel == NULL)
17860 ereport(ERROR,
17861 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17862 errmsg("partition \"%s\" was removed concurrently", partrelname)));
17863
17864 tab->rel = rel;
17865 }
17866
17867 /* Do the final part of detaching */
17868 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
17869
17870 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
17871
17872 /* keep our lock until commit */
17873 table_close(partRel, NoLock);
17874
17875 return address;
17876 }
17877
17878 /*
17879 * Second part of ALTER TABLE .. DETACH.
17880 *
17881 * This is separate so that it can be run independently when the second
17882 * transaction of the concurrent algorithm fails (crash or abort).
17883 */
17884 static void
DetachPartitionFinalize(Relation rel,Relation partRel,bool concurrent,Oid defaultPartOid)17885 DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
17886 Oid defaultPartOid)
17887 {
17888 Relation classRel;
17889 List *fks;
17890 ListCell *cell;
17891 List *indexes;
17892 Datum new_val[Natts_pg_class];
17893 bool new_null[Natts_pg_class],
17894 new_repl[Natts_pg_class];
17895 HeapTuple tuple,
17896 newtuple;
17897
17898 if (concurrent)
17899 {
17900 /*
17901 * We can remove the pg_inherits row now. (In the non-concurrent case,
17902 * this was already done).
17903 */
17904 RemoveInheritance(partRel, rel, true);
17905 }
17906
17907 /* Drop any triggers that were cloned on creation/attach. */
17908 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
17909
17910 /*
17911 * Detach any foreign keys that are inherited. This includes creating
17912 * additional action triggers.
17913 */
17914 fks = copyObject(RelationGetFKeyList(partRel));
17915 foreach(cell, fks)
17916 {
17917 ForeignKeyCacheInfo *fk = lfirst(cell);
17918 HeapTuple contup;
17919 Form_pg_constraint conform;
17920 Constraint *fkconstraint;
17921
17922 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
17923 if (!HeapTupleIsValid(contup))
17924 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
17925 conform = (Form_pg_constraint) GETSTRUCT(contup);
17926
17927 /* consider only the inherited foreign keys */
17928 if (conform->contype != CONSTRAINT_FOREIGN ||
17929 !OidIsValid(conform->conparentid))
17930 {
17931 ReleaseSysCache(contup);
17932 continue;
17933 }
17934
17935 /* unset conparentid and adjust conislocal, coninhcount, etc. */
17936 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
17937
17938 /*
17939 * Make the action triggers on the referenced relation. When this was
17940 * a partition the action triggers pointed to the parent rel (they
17941 * still do), but now we need separate ones of our own.
17942 */
17943 fkconstraint = makeNode(Constraint);
17944 fkconstraint->conname = pstrdup(NameStr(conform->conname));
17945 fkconstraint->fk_upd_action = conform->confupdtype;
17946 fkconstraint->fk_del_action = conform->confdeltype;
17947 fkconstraint->deferrable = conform->condeferrable;
17948 fkconstraint->initdeferred = conform->condeferred;
17949
17950 createForeignKeyActionTriggers(partRel, conform->confrelid,
17951 fkconstraint, fk->conoid,
17952 conform->conindid);
17953
17954 ReleaseSysCache(contup);
17955 }
17956 list_free_deep(fks);
17957
17958 /*
17959 * Any sub-constraints that are in the referenced-side of a larger
17960 * constraint have to be removed. This partition is no longer part of the
17961 * key space of the constraint.
17962 */
17963 foreach(cell, GetParentedForeignKeyRefs(partRel))
17964 {
17965 Oid constrOid = lfirst_oid(cell);
17966 ObjectAddress constraint;
17967
17968 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
17969 deleteDependencyRecordsForClass(ConstraintRelationId,
17970 constrOid,
17971 ConstraintRelationId,
17972 DEPENDENCY_INTERNAL);
17973 CommandCounterIncrement();
17974
17975 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
17976 performDeletion(&constraint, DROP_RESTRICT, 0);
17977 }
17978
17979 /* Now we can detach indexes */
17980 indexes = RelationGetIndexList(partRel);
17981 foreach(cell, indexes)
17982 {
17983 Oid idxid = lfirst_oid(cell);
17984 Relation idx;
17985 Oid constrOid;
17986
17987 if (!has_superclass(idxid))
17988 continue;
17989
17990 Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
17991 RelationGetRelid(rel)));
17992
17993 idx = index_open(idxid, AccessExclusiveLock);
17994 IndexSetParentIndex(idx, InvalidOid);
17995
17996 /* If there's a constraint associated with the index, detach it too */
17997 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
17998 idxid);
17999 if (OidIsValid(constrOid))
18000 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
18001
18002 index_close(idx, NoLock);
18003 }
18004
18005 /* Update pg_class tuple */
18006 classRel = table_open(RelationRelationId, RowExclusiveLock);
18007 tuple = SearchSysCacheCopy1(RELOID,
18008 ObjectIdGetDatum(RelationGetRelid(partRel)));
18009 if (!HeapTupleIsValid(tuple))
18010 elog(ERROR, "cache lookup failed for relation %u",
18011 RelationGetRelid(partRel));
18012 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
18013
18014 /* Clear relpartbound and reset relispartition */
18015 memset(new_val, 0, sizeof(new_val));
18016 memset(new_null, false, sizeof(new_null));
18017 memset(new_repl, false, sizeof(new_repl));
18018 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
18019 new_null[Anum_pg_class_relpartbound - 1] = true;
18020 new_repl[Anum_pg_class_relpartbound - 1] = true;
18021 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
18022 new_val, new_null, new_repl);
18023
18024 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
18025 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
18026 heap_freetuple(newtuple);
18027 table_close(classRel, RowExclusiveLock);
18028
18029 if (OidIsValid(defaultPartOid))
18030 {
18031 /*
18032 * If the relation being detached is the default partition itself,
18033 * remove it from the parent's pg_partitioned_table entry.
18034 *
18035 * If not, we must invalidate default partition's relcache entry, as
18036 * in StorePartitionBound: its partition constraint depends on every
18037 * other partition's partition constraint.
18038 */
18039 if (RelationGetRelid(partRel) == defaultPartOid)
18040 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
18041 else
18042 CacheInvalidateRelcacheByRelid(defaultPartOid);
18043 }
18044
18045 /*
18046 * Invalidate the parent's relcache so that the partition is no longer
18047 * included in its partition descriptor.
18048 */
18049 CacheInvalidateRelcache(rel);
18050
18051 /*
18052 * If the partition we just detached is partitioned itself, invalidate
18053 * relcache for all descendent partitions too to ensure that their
18054 * rd_partcheck expression trees are rebuilt; must lock partitions
18055 * before doing so, using the same lockmode as what partRel has been
18056 * locked with by the caller.
18057 */
18058 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18059 {
18060 List *children;
18061
18062 children = find_all_inheritors(RelationGetRelid(partRel),
18063 AccessExclusiveLock, NULL);
18064 foreach(cell, children)
18065 {
18066 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
18067 }
18068 }
18069 }
18070
18071 /*
18072 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
18073 *
18074 * To use when a DETACH PARTITION command previously did not run to
18075 * completion; this completes the detaching process.
18076 */
18077 static ObjectAddress
ATExecDetachPartitionFinalize(Relation rel,RangeVar * name)18078 ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
18079 {
18080 Relation partRel;
18081 ObjectAddress address;
18082 Snapshot snap = GetActiveSnapshot();
18083
18084 partRel = table_openrv(name, AccessExclusiveLock);
18085
18086 /*
18087 * Wait until existing snapshots are gone. This is important if the
18088 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
18089 * user could immediately run DETACH FINALIZE without actually waiting for
18090 * existing transactions. We must not complete the detach action until
18091 * all such queries are complete (otherwise we would present them with an
18092 * inconsistent view of catalogs).
18093 */
18094 WaitForOlderSnapshots(snap->xmin, false);
18095
18096 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
18097
18098 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
18099
18100 table_close(partRel, NoLock);
18101
18102 return address;
18103 }
18104
18105 /*
18106 * DetachAddConstraintIfNeeded
18107 * Subroutine for ATExecDetachPartition. Create a constraint that
18108 * takes the place of the partition constraint, but avoid creating
18109 * a dupe if an constraint already exists which implies the needed
18110 * constraint.
18111 */
18112 static void
DetachAddConstraintIfNeeded(List ** wqueue,Relation partRel)18113 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
18114 {
18115 List *constraintExpr;
18116
18117 constraintExpr = RelationGetPartitionQual(partRel);
18118 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
18119
18120 /*
18121 * Avoid adding a new constraint if the needed constraint is implied by an
18122 * existing constraint
18123 */
18124 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
18125 {
18126 AlteredTableInfo *tab;
18127 Constraint *n;
18128
18129 tab = ATGetQueueEntry(wqueue, partRel);
18130
18131 /* Add constraint on partition, equivalent to the partition constraint */
18132 n = makeNode(Constraint);
18133 n->contype = CONSTR_CHECK;
18134 n->conname = NULL;
18135 n->location = -1;
18136 n->is_no_inherit = false;
18137 n->raw_expr = NULL;
18138 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
18139 n->initially_valid = true;
18140 n->skip_validation = true;
18141 /* It's a re-add, since it nominally already exists */
18142 ATAddCheckConstraint(wqueue, tab, partRel, n,
18143 true, false, true, ShareUpdateExclusiveLock);
18144 }
18145 }
18146
18147 /*
18148 * DropClonedTriggersFromPartition
18149 * subroutine for ATExecDetachPartition to remove any triggers that were
18150 * cloned to the partition when it was created-as-partition or attached.
18151 * This undoes what CloneRowTriggersToPartition did.
18152 */
18153 static void
DropClonedTriggersFromPartition(Oid partitionId)18154 DropClonedTriggersFromPartition(Oid partitionId)
18155 {
18156 ScanKeyData skey;
18157 SysScanDesc scan;
18158 HeapTuple trigtup;
18159 Relation tgrel;
18160 ObjectAddresses *objects;
18161
18162 objects = new_object_addresses();
18163
18164 /*
18165 * Scan pg_trigger to search for all triggers on this rel.
18166 */
18167 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
18168 F_OIDEQ, ObjectIdGetDatum(partitionId));
18169 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
18170 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
18171 true, NULL, 1, &skey);
18172 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
18173 {
18174 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
18175 ObjectAddress trig;
18176
18177 /* Ignore triggers that weren't cloned */
18178 if (!OidIsValid(pg_trigger->tgparentid))
18179 continue;
18180
18181 /*
18182 * This is ugly, but necessary: remove the dependency markings on the
18183 * trigger so that it can be removed.
18184 */
18185 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
18186 TriggerRelationId,
18187 DEPENDENCY_PARTITION_PRI);
18188 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
18189 RelationRelationId,
18190 DEPENDENCY_PARTITION_SEC);
18191
18192 /* remember this trigger to remove it below */
18193 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
18194 add_exact_object_address(&trig, objects);
18195 }
18196
18197 /* make the dependency removal visible to the deletion below */
18198 CommandCounterIncrement();
18199 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
18200
18201 /* done */
18202 free_object_addresses(objects);
18203 systable_endscan(scan);
18204 table_close(tgrel, RowExclusiveLock);
18205 }
18206
18207 /*
18208 * Before acquiring lock on an index, acquire the same lock on the owning
18209 * table.
18210 */
18211 struct AttachIndexCallbackState
18212 {
18213 Oid partitionOid;
18214 Oid parentTblOid;
18215 bool lockedParentTbl;
18216 };
18217
18218 static void
RangeVarCallbackForAttachIndex(const RangeVar * rv,Oid relOid,Oid oldRelOid,void * arg)18219 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
18220 void *arg)
18221 {
18222 struct AttachIndexCallbackState *state;
18223 Form_pg_class classform;
18224 HeapTuple tuple;
18225
18226 state = (struct AttachIndexCallbackState *) arg;
18227
18228 if (!state->lockedParentTbl)
18229 {
18230 LockRelationOid(state->parentTblOid, AccessShareLock);
18231 state->lockedParentTbl = true;
18232 }
18233
18234 /*
18235 * If we previously locked some other heap, and the name we're looking up
18236 * no longer refers to an index on that relation, release the now-useless
18237 * lock. XXX maybe we should do *after* we verify whether the index does
18238 * not actually belong to the same relation ...
18239 */
18240 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
18241 {
18242 UnlockRelationOid(state->partitionOid, AccessShareLock);
18243 state->partitionOid = InvalidOid;
18244 }
18245
18246 /* Didn't find a relation, so no need for locking or permission checks. */
18247 if (!OidIsValid(relOid))
18248 return;
18249
18250 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
18251 if (!HeapTupleIsValid(tuple))
18252 return; /* concurrently dropped, so nothing to do */
18253 classform = (Form_pg_class) GETSTRUCT(tuple);
18254 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
18255 classform->relkind != RELKIND_INDEX)
18256 ereport(ERROR,
18257 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18258 errmsg("\"%s\" is not an index", rv->relname)));
18259 ReleaseSysCache(tuple);
18260
18261 /*
18262 * Since we need only examine the heap's tupledesc, an access share lock
18263 * on it (preventing any DDL) is sufficient.
18264 */
18265 state->partitionOid = IndexGetRelation(relOid, false);
18266 LockRelationOid(state->partitionOid, AccessShareLock);
18267 }
18268
18269 /*
18270 * ALTER INDEX i1 ATTACH PARTITION i2
18271 */
18272 static ObjectAddress
ATExecAttachPartitionIdx(List ** wqueue,Relation parentIdx,RangeVar * name)18273 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
18274 {
18275 Relation partIdx;
18276 Relation partTbl;
18277 Relation parentTbl;
18278 ObjectAddress address;
18279 Oid partIdxId;
18280 Oid currParent;
18281 struct AttachIndexCallbackState state;
18282
18283 /*
18284 * We need to obtain lock on the index 'name' to modify it, but we also
18285 * need to read its owning table's tuple descriptor -- so we need to lock
18286 * both. To avoid deadlocks, obtain lock on the table before doing so on
18287 * the index. Furthermore, we need to examine the parent table of the
18288 * partition, so lock that one too.
18289 */
18290 state.partitionOid = InvalidOid;
18291 state.parentTblOid = parentIdx->rd_index->indrelid;
18292 state.lockedParentTbl = false;
18293 partIdxId =
18294 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
18295 RangeVarCallbackForAttachIndex,
18296 (void *) &state);
18297 /* Not there? */
18298 if (!OidIsValid(partIdxId))
18299 ereport(ERROR,
18300 (errcode(ERRCODE_UNDEFINED_OBJECT),
18301 errmsg("index \"%s\" does not exist", name->relname)));
18302
18303 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
18304 partIdx = relation_open(partIdxId, AccessExclusiveLock);
18305
18306 /* we already hold locks on both tables, so this is safe: */
18307 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
18308 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
18309
18310 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
18311
18312 /* Silently do nothing if already in the right state */
18313 currParent = partIdx->rd_rel->relispartition ?
18314 get_partition_parent(partIdxId, false) : InvalidOid;
18315 if (currParent != RelationGetRelid(parentIdx))
18316 {
18317 IndexInfo *childInfo;
18318 IndexInfo *parentInfo;
18319 AttrMap *attmap;
18320 bool found;
18321 int i;
18322 PartitionDesc partDesc;
18323 Oid constraintOid,
18324 cldConstrId = InvalidOid;
18325
18326 /*
18327 * If this partition already has an index attached, refuse the
18328 * operation.
18329 */
18330 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
18331
18332 if (OidIsValid(currParent))
18333 ereport(ERROR,
18334 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18335 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
18336 RelationGetRelationName(partIdx),
18337 RelationGetRelationName(parentIdx)),
18338 errdetail("Index \"%s\" is already attached to another index.",
18339 RelationGetRelationName(partIdx))));
18340
18341 /* Make sure it indexes a partition of the other index's table */
18342 partDesc = RelationGetPartitionDesc(parentTbl, true);
18343 found = false;
18344 for (i = 0; i < partDesc->nparts; i++)
18345 {
18346 if (partDesc->oids[i] == state.partitionOid)
18347 {
18348 found = true;
18349 break;
18350 }
18351 }
18352 if (!found)
18353 ereport(ERROR,
18354 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18355 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
18356 RelationGetRelationName(partIdx),
18357 RelationGetRelationName(parentIdx)),
18358 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
18359 RelationGetRelationName(partIdx),
18360 RelationGetRelationName(parentTbl))));
18361
18362 /* Ensure the indexes are compatible */
18363 childInfo = BuildIndexInfo(partIdx);
18364 parentInfo = BuildIndexInfo(parentIdx);
18365 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
18366 RelationGetDescr(parentTbl));
18367 if (!CompareIndexInfo(childInfo, parentInfo,
18368 partIdx->rd_indcollation,
18369 parentIdx->rd_indcollation,
18370 partIdx->rd_opfamily,
18371 parentIdx->rd_opfamily,
18372 attmap))
18373 ereport(ERROR,
18374 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18375 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
18376 RelationGetRelationName(partIdx),
18377 RelationGetRelationName(parentIdx)),
18378 errdetail("The index definitions do not match.")));
18379
18380 /*
18381 * If there is a constraint in the parent, make sure there is one in
18382 * the child too.
18383 */
18384 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
18385 RelationGetRelid(parentIdx));
18386
18387 if (OidIsValid(constraintOid))
18388 {
18389 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
18390 partIdxId);
18391 if (!OidIsValid(cldConstrId))
18392 ereport(ERROR,
18393 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18394 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
18395 RelationGetRelationName(partIdx),
18396 RelationGetRelationName(parentIdx)),
18397 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
18398 RelationGetRelationName(parentIdx),
18399 RelationGetRelationName(parentTbl),
18400 RelationGetRelationName(partIdx))));
18401 }
18402
18403 /* All good -- do it */
18404 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
18405 if (OidIsValid(constraintOid))
18406 ConstraintSetParentConstraint(cldConstrId, constraintOid,
18407 RelationGetRelid(partTbl));
18408
18409 free_attrmap(attmap);
18410
18411 validatePartitionedIndex(parentIdx, parentTbl);
18412 }
18413
18414 relation_close(parentTbl, AccessShareLock);
18415 /* keep these locks till commit */
18416 relation_close(partTbl, NoLock);
18417 relation_close(partIdx, NoLock);
18418
18419 return address;
18420 }
18421
18422 /*
18423 * Verify whether the given partition already contains an index attached
18424 * to the given partitioned index. If so, raise an error.
18425 */
18426 static void
refuseDupeIndexAttach(Relation parentIdx,Relation partIdx,Relation partitionTbl)18427 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
18428 {
18429 Oid existingIdx;
18430
18431 existingIdx = index_get_partition(partitionTbl,
18432 RelationGetRelid(parentIdx));
18433 if (OidIsValid(existingIdx))
18434 ereport(ERROR,
18435 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18436 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
18437 RelationGetRelationName(partIdx),
18438 RelationGetRelationName(parentIdx)),
18439 errdetail("Another index is already attached for partition \"%s\".",
18440 RelationGetRelationName(partitionTbl))));
18441 }
18442
18443 /*
18444 * Verify whether the set of attached partition indexes to a parent index on
18445 * a partitioned table is complete. If it is, mark the parent index valid.
18446 *
18447 * This should be called each time a partition index is attached.
18448 */
18449 static void
validatePartitionedIndex(Relation partedIdx,Relation partedTbl)18450 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
18451 {
18452 Relation inheritsRel;
18453 SysScanDesc scan;
18454 ScanKeyData key;
18455 int tuples = 0;
18456 HeapTuple inhTup;
18457 bool updated = false;
18458
18459 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
18460
18461 /*
18462 * Scan pg_inherits for this parent index. Count each valid index we find
18463 * (verifying the pg_index entry for each), and if we reach the total
18464 * amount we expect, we can mark this parent index as valid.
18465 */
18466 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
18467 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
18468 BTEqualStrategyNumber, F_OIDEQ,
18469 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
18470 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
18471 NULL, 1, &key);
18472 while ((inhTup = systable_getnext(scan)) != NULL)
18473 {
18474 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
18475 HeapTuple indTup;
18476 Form_pg_index indexForm;
18477
18478 indTup = SearchSysCache1(INDEXRELID,
18479 ObjectIdGetDatum(inhForm->inhrelid));
18480 if (!HeapTupleIsValid(indTup))
18481 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
18482 indexForm = (Form_pg_index) GETSTRUCT(indTup);
18483 if (indexForm->indisvalid)
18484 tuples += 1;
18485 ReleaseSysCache(indTup);
18486 }
18487
18488 /* Done with pg_inherits */
18489 systable_endscan(scan);
18490 table_close(inheritsRel, AccessShareLock);
18491
18492 /*
18493 * If we found as many inherited indexes as the partitioned table has
18494 * partitions, we're good; update pg_index to set indisvalid.
18495 */
18496 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
18497 {
18498 Relation idxRel;
18499 HeapTuple newtup;
18500
18501 idxRel = table_open(IndexRelationId, RowExclusiveLock);
18502
18503 newtup = heap_copytuple(partedIdx->rd_indextuple);
18504 ((Form_pg_index) GETSTRUCT(newtup))->indisvalid = true;
18505 updated = true;
18506
18507 CatalogTupleUpdate(idxRel, &partedIdx->rd_indextuple->t_self, newtup);
18508
18509 table_close(idxRel, RowExclusiveLock);
18510 }
18511
18512 /*
18513 * If this index is in turn a partition of a larger index, validating it
18514 * might cause the parent to become valid also. Try that.
18515 */
18516 if (updated && partedIdx->rd_rel->relispartition)
18517 {
18518 Oid parentIdxId,
18519 parentTblId;
18520 Relation parentIdx,
18521 parentTbl;
18522
18523 /* make sure we see the validation we just did */
18524 CommandCounterIncrement();
18525
18526 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
18527 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
18528 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
18529 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
18530 Assert(!parentIdx->rd_index->indisvalid);
18531
18532 validatePartitionedIndex(parentIdx, parentTbl);
18533
18534 relation_close(parentIdx, AccessExclusiveLock);
18535 relation_close(parentTbl, AccessExclusiveLock);
18536 }
18537 }
18538
18539 /*
18540 * Return an OID list of constraints that reference the given relation
18541 * that are marked as having a parent constraints.
18542 */
18543 static List *
GetParentedForeignKeyRefs(Relation partition)18544 GetParentedForeignKeyRefs(Relation partition)
18545 {
18546 Relation pg_constraint;
18547 HeapTuple tuple;
18548 SysScanDesc scan;
18549 ScanKeyData key[2];
18550 List *constraints = NIL;
18551
18552 /*
18553 * If no indexes, or no columns are referenceable by FKs, we can avoid the
18554 * scan.
18555 */
18556 if (RelationGetIndexList(partition) == NIL ||
18557 bms_is_empty(RelationGetIndexAttrBitmap(partition,
18558 INDEX_ATTR_BITMAP_KEY)))
18559 return NIL;
18560
18561 /* Search for constraints referencing this table */
18562 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18563 ScanKeyInit(&key[0],
18564 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
18565 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
18566 ScanKeyInit(&key[1],
18567 Anum_pg_constraint_contype, BTEqualStrategyNumber,
18568 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
18569
18570 /* XXX This is a seqscan, as we don't have a usable index */
18571 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
18572 while ((tuple = systable_getnext(scan)) != NULL)
18573 {
18574 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
18575
18576 /*
18577 * We only need to process constraints that are part of larger ones.
18578 */
18579 if (!OidIsValid(constrForm->conparentid))
18580 continue;
18581
18582 constraints = lappend_oid(constraints, constrForm->oid);
18583 }
18584
18585 systable_endscan(scan);
18586 table_close(pg_constraint, AccessShareLock);
18587
18588 return constraints;
18589 }
18590
18591 /*
18592 * During DETACH PARTITION, verify that any foreign keys pointing to the
18593 * partitioned table would not become invalid. An error is raised if any
18594 * referenced values exist.
18595 */
18596 static void
ATDetachCheckNoForeignKeyRefs(Relation partition)18597 ATDetachCheckNoForeignKeyRefs(Relation partition)
18598 {
18599 List *constraints;
18600 ListCell *cell;
18601
18602 constraints = GetParentedForeignKeyRefs(partition);
18603
18604 foreach(cell, constraints)
18605 {
18606 Oid constrOid = lfirst_oid(cell);
18607 HeapTuple tuple;
18608 Form_pg_constraint constrForm;
18609 Relation rel;
18610 Trigger trig;
18611
18612 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
18613 if (!HeapTupleIsValid(tuple))
18614 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
18615 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
18616
18617 Assert(OidIsValid(constrForm->conparentid));
18618 Assert(constrForm->confrelid == RelationGetRelid(partition));
18619
18620 /* prevent data changes into the referencing table until commit */
18621 rel = table_open(constrForm->conrelid, ShareLock);
18622
18623 MemSet(&trig, 0, sizeof(trig));
18624 trig.tgoid = InvalidOid;
18625 trig.tgname = NameStr(constrForm->conname);
18626 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
18627 trig.tgisinternal = true;
18628 trig.tgconstrrelid = RelationGetRelid(partition);
18629 trig.tgconstrindid = constrForm->conindid;
18630 trig.tgconstraint = constrForm->oid;
18631 trig.tgdeferrable = false;
18632 trig.tginitdeferred = false;
18633 /* we needn't fill in remaining fields */
18634
18635 RI_PartitionRemove_Check(&trig, rel, partition);
18636
18637 ReleaseSysCache(tuple);
18638
18639 table_close(rel, NoLock);
18640 }
18641 }
18642
18643 /*
18644 * resolve column compression specification to compression method.
18645 */
18646 static char
GetAttributeCompression(Oid atttypid,char * compression)18647 GetAttributeCompression(Oid atttypid, char *compression)
18648 {
18649 char cmethod;
18650
18651 if (compression == NULL || strcmp(compression, "default") == 0)
18652 return InvalidCompressionMethod;
18653
18654 /*
18655 * To specify a nondefault method, the column data type must be toastable.
18656 * Note this says nothing about whether the column's attstorage setting
18657 * permits compression; we intentionally allow attstorage and
18658 * attcompression to be independent. But with a non-toastable type,
18659 * attstorage could not be set to a value that would permit compression.
18660 *
18661 * We don't actually need to enforce this, since nothing bad would happen
18662 * if attcompression were non-default; it would never be consulted. But
18663 * it seems more user-friendly to complain about a certainly-useless
18664 * attempt to set the property.
18665 */
18666 if (!TypeIsToastable(atttypid))
18667 ereport(ERROR,
18668 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18669 errmsg("column data type %s does not support compression",
18670 format_type_be(atttypid))));
18671
18672 cmethod = CompressionNameToMethod(compression);
18673 if (!CompressionMethodIsValid(cmethod))
18674 ereport(ERROR,
18675 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
18676 errmsg("invalid compression method \"%s\"", compression)));
18677
18678 return cmethod;
18679 }
18680