1 /*-------------------------------------------------------------------------
2 *
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
5 *
6 * Portions Copyright (c) 1996-2020, 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/xact.h"
27 #include "access/xlog.h"
28 #include "catalog/catalog.h"
29 #include "catalog/dependency.h"
30 #include "catalog/heap.h"
31 #include "catalog/index.h"
32 #include "catalog/indexing.h"
33 #include "catalog/namespace.h"
34 #include "catalog/objectaccess.h"
35 #include "catalog/partition.h"
36 #include "catalog/pg_am.h"
37 #include "catalog/pg_collation.h"
38 #include "catalog/pg_constraint.h"
39 #include "catalog/pg_depend.h"
40 #include "catalog/pg_foreign_table.h"
41 #include "catalog/pg_inherits.h"
42 #include "catalog/pg_namespace.h"
43 #include "catalog/pg_opclass.h"
44 #include "catalog/pg_tablespace.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/foreign.h"
63 #include "miscadmin.h"
64 #include "nodes/makefuncs.h"
65 #include "nodes/nodeFuncs.h"
66 #include "nodes/parsenodes.h"
67 #include "optimizer/optimizer.h"
68 #include "parser/parse_clause.h"
69 #include "parser/parse_coerce.h"
70 #include "parser/parse_collate.h"
71 #include "parser/parse_expr.h"
72 #include "parser/parse_oper.h"
73 #include "parser/parse_relation.h"
74 #include "parser/parse_type.h"
75 #include "parser/parse_utilcmd.h"
76 #include "parser/parser.h"
77 #include "partitioning/partbounds.h"
78 #include "partitioning/partdesc.h"
79 #include "pgstat.h"
80 #include "rewrite/rewriteDefine.h"
81 #include "rewrite/rewriteHandler.h"
82 #include "rewrite/rewriteManip.h"
83 #include "storage/bufmgr.h"
84 #include "storage/lmgr.h"
85 #include "storage/lock.h"
86 #include "storage/predicate.h"
87 #include "storage/smgr.h"
88 #include "tcop/utility.h"
89 #include "utils/acl.h"
90 #include "utils/builtins.h"
91 #include "utils/fmgroids.h"
92 #include "utils/inval.h"
93 #include "utils/lsyscache.h"
94 #include "utils/memutils.h"
95 #include "utils/partcache.h"
96 #include "utils/relcache.h"
97 #include "utils/ruleutils.h"
98 #include "utils/snapmgr.h"
99 #include "utils/syscache.h"
100 #include "utils/timestamp.h"
101 #include "utils/typcache.h"
102
103 /*
104 * ON COMMIT action list
105 */
106 typedef struct OnCommitItem
107 {
108 Oid relid; /* relid of relation */
109 OnCommitAction oncommit; /* what to do at end of xact */
110
111 /*
112 * If this entry was created during the current transaction,
113 * creating_subid is the ID of the creating subxact; if created in a prior
114 * transaction, creating_subid is zero. If deleted during the current
115 * transaction, deleting_subid is the ID of the deleting subxact; if no
116 * deletion request is pending, deleting_subid is zero.
117 */
118 SubTransactionId creating_subid;
119 SubTransactionId deleting_subid;
120 } OnCommitItem;
121
122 static List *on_commits = NIL;
123
124
125 /*
126 * State information for ALTER TABLE
127 *
128 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
129 * structs, one for each table modified by the operation (the named table
130 * plus any child tables that are affected). We save lists of subcommands
131 * to apply to this table (possibly modified by parse transformation steps);
132 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
133 * necessary information is stored in the constraints and newvals lists.
134 *
135 * Phase 2 is divided into multiple passes; subcommands are executed in
136 * a pass determined by subcommand type.
137 */
138
139 #define AT_PASS_UNSET -1 /* UNSET will cause ERROR */
140 #define AT_PASS_DROP 0 /* DROP (all flavors) */
141 #define AT_PASS_ALTER_TYPE 1 /* ALTER COLUMN TYPE */
142 #define AT_PASS_OLD_INDEX 2 /* re-add existing indexes */
143 #define AT_PASS_OLD_CONSTR 3 /* re-add existing constraints */
144 /* We could support a RENAME COLUMN pass here, but not currently used */
145 #define AT_PASS_ADD_COL 4 /* ADD COLUMN */
146 #define AT_PASS_ADD_CONSTR 5 /* ADD constraints (initial examination) */
147 #define AT_PASS_COL_ATTRS 6 /* set column attributes, eg NOT NULL */
148 #define AT_PASS_ADD_INDEXCONSTR 7 /* ADD index-based constraints */
149 #define AT_PASS_ADD_INDEX 8 /* ADD indexes */
150 #define AT_PASS_ADD_OTHERCONSTR 9 /* ADD other constraints, defaults */
151 #define AT_PASS_MISC 10 /* other stuff */
152 #define AT_NUM_PASSES 11
153
154 typedef struct AlteredTableInfo
155 {
156 /* Information saved before any work commences: */
157 Oid relid; /* Relation to work on */
158 char relkind; /* Its relkind */
159 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
160 /* Information saved by Phase 1 for Phase 2: */
161 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
162 /* Information saved by Phases 1/2 for Phase 3: */
163 List *constraints; /* List of NewConstraint */
164 List *newvals; /* List of NewColumnValue */
165 List *afterStmts; /* List of utility command parsetrees */
166 bool verify_new_notnull; /* T if we should recheck NOT NULL */
167 int rewrite; /* Reason for forced rewrite, if any */
168 Oid newTableSpace; /* new tablespace; 0 means no change */
169 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
170 char newrelpersistence; /* if above is true */
171 Expr *partition_constraint; /* for attach partition validation */
172 /* true, if validating default due to some other attach/detach */
173 bool validate_default;
174 /* Objects to rebuild after completing ALTER TYPE operations */
175 List *changedConstraintOids; /* OIDs of constraints to rebuild */
176 List *changedConstraintDefs; /* string definitions of same */
177 List *changedIndexOids; /* OIDs of indexes to rebuild */
178 List *changedIndexDefs; /* string definitions of same */
179 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
180 char *clusterOnIndex; /* index to use for CLUSTER */
181 } AlteredTableInfo;
182
183 /* Struct describing one new constraint to check in Phase 3 scan */
184 /* Note: new NOT NULL constraints are handled elsewhere */
185 typedef struct NewConstraint
186 {
187 char *name; /* Constraint name, or NULL if none */
188 ConstrType contype; /* CHECK or FOREIGN */
189 Oid refrelid; /* PK rel, if FOREIGN */
190 Oid refindid; /* OID of PK's index, if FOREIGN */
191 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
192 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
193 ExprState *qualstate; /* Execution state for CHECK expr */
194 } NewConstraint;
195
196 /*
197 * Struct describing one new column value that needs to be computed during
198 * Phase 3 copy (this could be either a new column with a non-null default, or
199 * a column that we're changing the type of). Columns without such an entry
200 * are just copied from the old table during ATRewriteTable. Note that the
201 * expr is an expression over *old* table values, except when is_generated
202 * is true; then it is an expression over columns of the *new* tuple.
203 */
204 typedef struct NewColumnValue
205 {
206 AttrNumber attnum; /* which column */
207 Expr *expr; /* expression to compute */
208 ExprState *exprstate; /* execution state */
209 bool is_generated; /* is it a GENERATED expression? */
210 } NewColumnValue;
211
212 /*
213 * Error-reporting support for RemoveRelations
214 */
215 struct dropmsgstrings
216 {
217 char kind;
218 int nonexistent_code;
219 const char *nonexistent_msg;
220 const char *skipping_msg;
221 const char *nota_msg;
222 const char *drophint_msg;
223 };
224
225 static const struct dropmsgstrings dropmsgstringarray[] = {
226 {RELKIND_RELATION,
227 ERRCODE_UNDEFINED_TABLE,
228 gettext_noop("table \"%s\" does not exist"),
229 gettext_noop("table \"%s\" does not exist, skipping"),
230 gettext_noop("\"%s\" is not a table"),
231 gettext_noop("Use DROP TABLE to remove a table.")},
232 {RELKIND_SEQUENCE,
233 ERRCODE_UNDEFINED_TABLE,
234 gettext_noop("sequence \"%s\" does not exist"),
235 gettext_noop("sequence \"%s\" does not exist, skipping"),
236 gettext_noop("\"%s\" is not a sequence"),
237 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
238 {RELKIND_VIEW,
239 ERRCODE_UNDEFINED_TABLE,
240 gettext_noop("view \"%s\" does not exist"),
241 gettext_noop("view \"%s\" does not exist, skipping"),
242 gettext_noop("\"%s\" is not a view"),
243 gettext_noop("Use DROP VIEW to remove a view.")},
244 {RELKIND_MATVIEW,
245 ERRCODE_UNDEFINED_TABLE,
246 gettext_noop("materialized view \"%s\" does not exist"),
247 gettext_noop("materialized view \"%s\" does not exist, skipping"),
248 gettext_noop("\"%s\" is not a materialized view"),
249 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
250 {RELKIND_INDEX,
251 ERRCODE_UNDEFINED_OBJECT,
252 gettext_noop("index \"%s\" does not exist"),
253 gettext_noop("index \"%s\" does not exist, skipping"),
254 gettext_noop("\"%s\" is not an index"),
255 gettext_noop("Use DROP INDEX to remove an index.")},
256 {RELKIND_COMPOSITE_TYPE,
257 ERRCODE_UNDEFINED_OBJECT,
258 gettext_noop("type \"%s\" does not exist"),
259 gettext_noop("type \"%s\" does not exist, skipping"),
260 gettext_noop("\"%s\" is not a type"),
261 gettext_noop("Use DROP TYPE to remove a type.")},
262 {RELKIND_FOREIGN_TABLE,
263 ERRCODE_UNDEFINED_OBJECT,
264 gettext_noop("foreign table \"%s\" does not exist"),
265 gettext_noop("foreign table \"%s\" does not exist, skipping"),
266 gettext_noop("\"%s\" is not a foreign table"),
267 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
268 {RELKIND_PARTITIONED_TABLE,
269 ERRCODE_UNDEFINED_TABLE,
270 gettext_noop("table \"%s\" does not exist"),
271 gettext_noop("table \"%s\" does not exist, skipping"),
272 gettext_noop("\"%s\" is not a table"),
273 gettext_noop("Use DROP TABLE to remove a table.")},
274 {RELKIND_PARTITIONED_INDEX,
275 ERRCODE_UNDEFINED_OBJECT,
276 gettext_noop("index \"%s\" does not exist"),
277 gettext_noop("index \"%s\" does not exist, skipping"),
278 gettext_noop("\"%s\" is not an index"),
279 gettext_noop("Use DROP INDEX to remove an index.")},
280 {'\0', 0, NULL, NULL, NULL, NULL}
281 };
282
283 struct DropRelationCallbackState
284 {
285 char relkind;
286 Oid heapOid;
287 Oid partParentOid;
288 bool concurrent;
289 };
290
291 /* Alter table target-type flags for ATSimplePermissions */
292 #define ATT_TABLE 0x0001
293 #define ATT_VIEW 0x0002
294 #define ATT_MATVIEW 0x0004
295 #define ATT_INDEX 0x0008
296 #define ATT_COMPOSITE_TYPE 0x0010
297 #define ATT_FOREIGN_TABLE 0x0020
298 #define ATT_PARTITIONED_INDEX 0x0040
299
300 /*
301 * Partition tables are expected to be dropped when the parent partitioned
302 * table gets dropped. Hence for partitioning we use AUTO dependency.
303 * Otherwise, for regular inheritance use NORMAL dependency.
304 */
305 #define child_dependency_type(child_is_partition) \
306 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
307
308 static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
309 static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
310 static void truncate_check_activity(Relation rel);
311 static void RangeVarCallbackForTruncate(const RangeVar *relation,
312 Oid relId, Oid oldRelId, void *arg);
313 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
314 bool is_partition, List **supconstr);
315 static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
316 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
317 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
318 static void StoreCatalogInheritance(Oid relationId, List *supers,
319 bool child_is_partition);
320 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
321 int32 seqNumber, Relation inhRelation,
322 bool child_is_partition);
323 static int findAttrByName(const char *attributeName, List *schema);
324 static void AlterIndexNamespaces(Relation classRel, Relation rel,
325 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
326 static void AlterSeqNamespaces(Relation classRel, Relation rel,
327 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
328 LOCKMODE lockmode);
329 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
330 bool recurse, bool recursing, LOCKMODE lockmode);
331 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
332 Relation rel, HeapTuple contuple, List **otherrelids,
333 LOCKMODE lockmode);
334 static ObjectAddress ATExecValidateConstraint(List **wqueue,
335 Relation rel, char *constrName,
336 bool recurse, bool recursing, LOCKMODE lockmode);
337 static int transformColumnNameList(Oid relId, List *colList,
338 int16 *attnums, Oid *atttypids);
339 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
340 List **attnamelist,
341 int16 *attnums, Oid *atttypids,
342 Oid *opclasses);
343 static Oid transformFkeyCheckAttrs(Relation pkrel,
344 int numattrs, int16 *attnums,
345 Oid *opclasses);
346 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
347 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
348 Oid *funcid);
349 static void validateForeignKeyConstraint(char *conname,
350 Relation rel, Relation pkrel,
351 Oid pkindOid, Oid constraintOid);
352 static void ATController(AlterTableStmt *parsetree,
353 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
354 AlterTableUtilityContext *context);
355 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
356 bool recurse, bool recursing, LOCKMODE lockmode,
357 AlterTableUtilityContext *context);
358 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
359 AlterTableUtilityContext *context);
360 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
361 AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
362 AlterTableUtilityContext *context);
363 static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
364 Relation rel, AlterTableCmd *cmd,
365 bool recurse, LOCKMODE lockmode,
366 int cur_pass,
367 AlterTableUtilityContext *context);
368 static void ATRewriteTables(AlterTableStmt *parsetree,
369 List **wqueue, LOCKMODE lockmode,
370 AlterTableUtilityContext *context);
371 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
372 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
373 static void ATSimplePermissions(Relation rel, int allowed_targets);
374 static void ATWrongRelkindError(Relation rel, int allowed_targets);
375 static void ATSimpleRecursion(List **wqueue, Relation rel,
376 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
377 AlterTableUtilityContext *context);
378 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
379 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
380 LOCKMODE lockmode,
381 AlterTableUtilityContext *context);
382 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
383 DropBehavior behavior);
384 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
385 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
386 AlterTableUtilityContext *context);
387 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
388 Relation rel, AlterTableCmd **cmd,
389 bool recurse, bool recursing,
390 LOCKMODE lockmode, int cur_pass,
391 AlterTableUtilityContext *context);
392 static bool check_for_column_name_collision(Relation rel, const char *colname,
393 bool if_not_exists);
394 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
395 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
396 static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
397 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
398 static void ATPrepSetNotNull(List **wqueue, Relation rel,
399 AlterTableCmd *cmd, bool recurse, bool recursing,
400 LOCKMODE lockmode,
401 AlterTableUtilityContext *context);
402 static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
403 const char *colName, LOCKMODE lockmode);
404 static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
405 const char *colName, LOCKMODE lockmode);
406 static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
407 static bool ConstraintImpliedByRelConstraint(Relation scanrel,
408 List *testConstraint, List *provenConstraint);
409 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
410 Node *newDefault, LOCKMODE lockmode);
411 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
412 Node *newDefault);
413 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
414 Node *def, LOCKMODE lockmode);
415 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
416 Node *def, LOCKMODE lockmode);
417 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
418 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
419 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
420 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
421 Node *newValue, LOCKMODE lockmode);
422 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
423 Node *options, bool isReset, LOCKMODE lockmode);
424 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
425 Node *newValue, LOCKMODE lockmode);
426 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
427 AlterTableCmd *cmd, LOCKMODE lockmode,
428 AlterTableUtilityContext *context);
429 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
430 DropBehavior behavior,
431 bool recurse, bool recursing,
432 bool missing_ok, LOCKMODE lockmode,
433 ObjectAddresses *addrs);
434 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
435 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
436 static ObjectAddress ATExecAddConstraint(List **wqueue,
437 AlteredTableInfo *tab, Relation rel,
438 Constraint *newConstraint, bool recurse, bool is_readd,
439 LOCKMODE lockmode);
440 static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
441 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
442 IndexStmt *stmt, LOCKMODE lockmode);
443 static ObjectAddress ATAddCheckConstraint(List **wqueue,
444 AlteredTableInfo *tab, Relation rel,
445 Constraint *constr,
446 bool recurse, bool recursing, bool is_readd,
447 LOCKMODE lockmode);
448 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
449 Relation rel, Constraint *fkconstraint, Oid parentConstr,
450 bool recurse, bool recursing,
451 LOCKMODE lockmode);
452 static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
453 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
454 int numfks, int16 *pkattnum, int16 *fkattnum,
455 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
456 bool old_check_ok);
457 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
458 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
459 int numfks, int16 *pkattnum, int16 *fkattnum,
460 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
461 bool old_check_ok, LOCKMODE lockmode);
462 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
463 Relation partitionRel);
464 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
465 static void CloneFkReferencing(List **wqueue, Relation parentRel,
466 Relation partRel);
467 static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
468 Constraint *fkconstraint, Oid constraintOid,
469 Oid indexOid);
470 static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
471 Constraint *fkconstraint, Oid constraintOid,
472 Oid indexOid);
473 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
474 Oid partRelid,
475 Oid parentConstrOid, int numfks,
476 AttrNumber *mapped_conkey, AttrNumber *confkey,
477 Oid *conpfeqop);
478 static void ATExecDropConstraint(Relation rel, const char *constrName,
479 DropBehavior behavior,
480 bool recurse, bool recursing,
481 bool missing_ok, LOCKMODE lockmode);
482 static void ATPrepAlterColumnType(List **wqueue,
483 AlteredTableInfo *tab, Relation rel,
484 bool recurse, bool recursing,
485 AlterTableCmd *cmd, LOCKMODE lockmode,
486 AlterTableUtilityContext *context);
487 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
488 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
489 AlterTableCmd *cmd, LOCKMODE lockmode);
490 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
491 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
492 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
493 LOCKMODE lockmode);
494 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
495 char *cmd, List **wqueue, LOCKMODE lockmode,
496 bool rewrite);
497 static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
498 Oid objid, Relation rel, List *domname,
499 const char *conname);
500 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
501 static void TryReuseForeignKey(Oid oldId, Constraint *con);
502 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
503 List *options, LOCKMODE lockmode);
504 static void change_owner_fix_column_acls(Oid relationOid,
505 Oid oldOwnerId, Oid newOwnerId);
506 static void change_owner_recurse_to_sequences(Oid relationOid,
507 Oid newOwnerId, LOCKMODE lockmode);
508 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
509 LOCKMODE lockmode);
510 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
511 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
512 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
513 const char *tablespacename, LOCKMODE lockmode);
514 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
515 static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
516 static void ATExecSetRelOptions(Relation rel, List *defList,
517 AlterTableType operation,
518 LOCKMODE lockmode);
519 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
520 char fires_when, bool skip_system, LOCKMODE lockmode);
521 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
522 char fires_when, LOCKMODE lockmode);
523 static void ATPrepAddInherit(Relation child_rel);
524 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
525 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
526 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
527 DependencyType deptype);
528 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
529 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
530 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
531 static void ATExecGenericOptions(Relation rel, List *options);
532 static void ATExecEnableRowSecurity(Relation rel);
533 static void ATExecDisableRowSecurity(Relation rel);
534 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
535
536 static void index_copy_data(Relation rel, RelFileNode newrnode);
537 static const char *storage_name(char c);
538
539 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
540 Oid oldRelOid, void *arg);
541 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
542 Oid oldrelid, void *arg);
543 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
544 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
545 List **partexprs, Oid *partopclass, Oid *partcollation, char strategy);
546 static void CreateInheritance(Relation child_rel, Relation parent_rel);
547 static void RemoveInheritance(Relation child_rel, Relation parent_rel);
548 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
549 PartitionCmd *cmd);
550 static void AttachPartitionEnsureIndexes(Relation rel, Relation attachrel);
551 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
552 List *partConstraint,
553 bool validate_default);
554 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
555 static void DropClonedTriggersFromPartition(Oid partitionId);
556 static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
557 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation rel,
558 RangeVar *name);
559 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
560 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
561 Relation partitionTbl);
562 static List *GetParentedForeignKeyRefs(Relation partition);
563 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
564
565
566 /* ----------------------------------------------------------------
567 * DefineRelation
568 * Creates a new relation.
569 *
570 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
571 * The other arguments are used to extend the behavior for other cases:
572 * relkind: relkind to assign to the new relation
573 * ownerId: if not InvalidOid, use this as the new relation's owner.
574 * typaddress: if not null, it's set to the pg_type entry's address.
575 * queryString: for error reporting
576 *
577 * Note that permissions checks are done against current user regardless of
578 * ownerId. A nonzero ownerId is used when someone is creating a relation
579 * "on behalf of" someone else, so we still want to see that the current user
580 * has permissions to do it.
581 *
582 * If successful, returns the address of the new relation.
583 * ----------------------------------------------------------------
584 */
585 ObjectAddress
DefineRelation(CreateStmt * stmt,char relkind,Oid ownerId,ObjectAddress * typaddress,const char * queryString)586 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
587 ObjectAddress *typaddress, const char *queryString)
588 {
589 char relname[NAMEDATALEN];
590 Oid namespaceId;
591 Oid relationId;
592 Oid tablespaceId;
593 Relation rel;
594 TupleDesc descriptor;
595 List *inheritOids;
596 List *old_constraints;
597 List *rawDefaults;
598 List *cookedDefaults;
599 Datum reloptions;
600 ListCell *listptr;
601 AttrNumber attnum;
602 bool partitioned;
603 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
604 Oid ofTypeId;
605 ObjectAddress address;
606 LOCKMODE parentLockmode;
607 const char *accessMethod = NULL;
608 Oid accessMethodId = InvalidOid;
609
610 /*
611 * Truncate relname to appropriate length (probably a waste of time, as
612 * parser should have done this already).
613 */
614 StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
615
616 /*
617 * Check consistency of arguments
618 */
619 if (stmt->oncommit != ONCOMMIT_NOOP
620 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
621 ereport(ERROR,
622 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
623 errmsg("ON COMMIT can only be used on temporary tables")));
624
625 if (stmt->partspec != NULL)
626 {
627 if (relkind != RELKIND_RELATION)
628 elog(ERROR, "unexpected relkind: %d", (int) relkind);
629
630 relkind = RELKIND_PARTITIONED_TABLE;
631 partitioned = true;
632 }
633 else
634 partitioned = false;
635
636 /*
637 * Look up the namespace in which we are supposed to create the relation,
638 * check we have permission to create there, lock it against concurrent
639 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
640 * namespace is selected.
641 */
642 namespaceId =
643 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
644
645 /*
646 * Security check: disallow creating temp tables from security-restricted
647 * code. This is needed because calling code might not expect untrusted
648 * tables to appear in pg_temp at the front of its search path.
649 */
650 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
651 && InSecurityRestrictedOperation())
652 ereport(ERROR,
653 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
654 errmsg("cannot create temporary table within security-restricted operation")));
655
656 /*
657 * Determine the lockmode to use when scanning parents. A self-exclusive
658 * lock is needed here.
659 *
660 * For regular inheritance, if two backends attempt to add children to the
661 * same parent simultaneously, and that parent has no pre-existing
662 * children, then both will attempt to update the parent's relhassubclass
663 * field, leading to a "tuple concurrently updated" error. Also, this
664 * interlocks against a concurrent ANALYZE on the parent table, which
665 * might otherwise be attempting to clear the parent's relhassubclass
666 * field, if its previous children were recently dropped.
667 *
668 * If the child table is a partition, then we instead grab an exclusive
669 * lock on the parent because its partition descriptor will be changed by
670 * addition of the new partition.
671 */
672 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
673 ShareUpdateExclusiveLock);
674
675 /* Determine the list of OIDs of the parents. */
676 inheritOids = NIL;
677 foreach(listptr, stmt->inhRelations)
678 {
679 RangeVar *rv = (RangeVar *) lfirst(listptr);
680 Oid parentOid;
681
682 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
683
684 /*
685 * Reject duplications in the list of parents.
686 */
687 if (list_member_oid(inheritOids, parentOid))
688 ereport(ERROR,
689 (errcode(ERRCODE_DUPLICATE_TABLE),
690 errmsg("relation \"%s\" would be inherited from more than once",
691 get_rel_name(parentOid))));
692
693 inheritOids = lappend_oid(inheritOids, parentOid);
694 }
695
696 /*
697 * Select tablespace to use: an explicitly indicated one, or (in the case
698 * of a partitioned table) the parent's, if it has one.
699 */
700 if (stmt->tablespacename)
701 {
702 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
703
704 if (partitioned && tablespaceId == MyDatabaseTableSpace)
705 ereport(ERROR,
706 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
707 errmsg("cannot specify default tablespace for partitioned relations")));
708 }
709 else if (stmt->partbound)
710 {
711 /*
712 * For partitions, when no other tablespace is specified, we default
713 * the tablespace to the parent partitioned table's.
714 */
715 Assert(list_length(inheritOids) == 1);
716 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
717 }
718 else
719 tablespaceId = InvalidOid;
720
721 /* still nothing? use the default */
722 if (!OidIsValid(tablespaceId))
723 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
724 partitioned);
725
726 /* Check permissions except when using database's default */
727 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
728 {
729 AclResult aclresult;
730
731 aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
732 ACL_CREATE);
733 if (aclresult != ACLCHECK_OK)
734 aclcheck_error(aclresult, OBJECT_TABLESPACE,
735 get_tablespace_name(tablespaceId));
736 }
737
738 /* In all cases disallow placing user relations in pg_global */
739 if (tablespaceId == GLOBALTABLESPACE_OID)
740 ereport(ERROR,
741 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
742 errmsg("only shared relations can be placed in pg_global tablespace")));
743
744 /* Identify user ID that will own the table */
745 if (!OidIsValid(ownerId))
746 ownerId = GetUserId();
747
748 /*
749 * Parse and validate reloptions, if any.
750 */
751 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
752 true, false);
753
754 switch (relkind)
755 {
756 case RELKIND_VIEW:
757 (void) view_reloptions(reloptions, true);
758 break;
759 case RELKIND_PARTITIONED_TABLE:
760 (void) partitioned_table_reloptions(reloptions, true);
761 break;
762 default:
763 (void) heap_reloptions(relkind, reloptions, true);
764 }
765
766 if (stmt->ofTypename)
767 {
768 AclResult aclresult;
769
770 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
771
772 aclresult = pg_type_aclcheck(ofTypeId, GetUserId(), ACL_USAGE);
773 if (aclresult != ACLCHECK_OK)
774 aclcheck_error_type(aclresult, ofTypeId);
775 }
776 else
777 ofTypeId = InvalidOid;
778
779 /*
780 * Look up inheritance ancestors and generate relation schema, including
781 * inherited attributes. (Note that stmt->tableElts is destructively
782 * modified by MergeAttributes.)
783 */
784 stmt->tableElts =
785 MergeAttributes(stmt->tableElts, inheritOids,
786 stmt->relation->relpersistence,
787 stmt->partbound != NULL,
788 &old_constraints);
789
790 /*
791 * Create a tuple descriptor from the relation schema. Note that this
792 * deals with column names, types, and NOT NULL constraints, but not
793 * default values or CHECK constraints; we handle those below.
794 */
795 descriptor = BuildDescForRelation(stmt->tableElts);
796
797 /*
798 * Find columns with default values and prepare for insertion of the
799 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
800 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
801 * while raw defaults go into a list of RawColumnDefault structs that will
802 * be processed by AddRelationNewConstraints. (We can't deal with raw
803 * expressions until we can do transformExpr.)
804 *
805 * We can set the atthasdef flags now in the tuple descriptor; this just
806 * saves StoreAttrDefault from having to do an immediate update of the
807 * pg_attribute rows.
808 */
809 rawDefaults = NIL;
810 cookedDefaults = NIL;
811 attnum = 0;
812
813 foreach(listptr, stmt->tableElts)
814 {
815 ColumnDef *colDef = lfirst(listptr);
816 Form_pg_attribute attr;
817
818 attnum++;
819 attr = TupleDescAttr(descriptor, attnum - 1);
820
821 if (colDef->raw_default != NULL)
822 {
823 RawColumnDefault *rawEnt;
824
825 Assert(colDef->cooked_default == NULL);
826
827 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
828 rawEnt->attnum = attnum;
829 rawEnt->raw_default = colDef->raw_default;
830 rawEnt->missingMode = false;
831 rawEnt->generated = colDef->generated;
832 rawDefaults = lappend(rawDefaults, rawEnt);
833 attr->atthasdef = true;
834 }
835 else if (colDef->cooked_default != NULL)
836 {
837 CookedConstraint *cooked;
838
839 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
840 cooked->contype = CONSTR_DEFAULT;
841 cooked->conoid = InvalidOid; /* until created */
842 cooked->name = NULL;
843 cooked->attnum = attnum;
844 cooked->expr = colDef->cooked_default;
845 cooked->skip_validation = false;
846 cooked->is_local = true; /* not used for defaults */
847 cooked->inhcount = 0; /* ditto */
848 cooked->is_no_inherit = false;
849 cookedDefaults = lappend(cookedDefaults, cooked);
850 attr->atthasdef = true;
851 }
852
853 if (colDef->identity)
854 attr->attidentity = colDef->identity;
855
856 if (colDef->generated)
857 attr->attgenerated = colDef->generated;
858 }
859
860 /*
861 * If the statement hasn't specified an access method, but we're defining
862 * a type of relation that needs one, use the default.
863 */
864 if (stmt->accessMethod != NULL)
865 {
866 accessMethod = stmt->accessMethod;
867
868 if (partitioned)
869 ereport(ERROR,
870 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
871 errmsg("specifying a table access method is not supported on a partitioned table")));
872
873 }
874 else if (relkind == RELKIND_RELATION ||
875 relkind == RELKIND_TOASTVALUE ||
876 relkind == RELKIND_MATVIEW)
877 accessMethod = default_table_access_method;
878
879 /* look up the access method, verify it is for a table */
880 if (accessMethod != NULL)
881 accessMethodId = get_table_am_oid(accessMethod, false);
882
883 /*
884 * Create the relation. Inherited defaults and constraints are passed in
885 * for immediate handling --- since they don't need parsing, they can be
886 * stored immediately.
887 */
888 relationId = heap_create_with_catalog(relname,
889 namespaceId,
890 tablespaceId,
891 InvalidOid,
892 InvalidOid,
893 ofTypeId,
894 ownerId,
895 accessMethodId,
896 descriptor,
897 list_concat(cookedDefaults,
898 old_constraints),
899 relkind,
900 stmt->relation->relpersistence,
901 false,
902 false,
903 stmt->oncommit,
904 reloptions,
905 true,
906 allowSystemTableMods,
907 false,
908 InvalidOid,
909 typaddress);
910
911 /*
912 * We must bump the command counter to make the newly-created relation
913 * tuple visible for opening.
914 */
915 CommandCounterIncrement();
916
917 /*
918 * Open the new relation and acquire exclusive lock on it. This isn't
919 * really necessary for locking out other backends (since they can't see
920 * the new rel anyway until we commit), but it keeps the lock manager from
921 * complaining about deadlock risks.
922 */
923 rel = relation_open(relationId, AccessExclusiveLock);
924
925 /*
926 * Now add any newly specified column default and generation expressions
927 * to the new relation. These are passed to us in the form of raw
928 * parsetrees; we need to transform them to executable expression trees
929 * before they can be added. The most convenient way to do that is to
930 * apply the parser's transformExpr routine, but transformExpr doesn't
931 * work unless we have a pre-existing relation. So, the transformation has
932 * to be postponed to this final step of CREATE TABLE.
933 *
934 * This needs to be before processing the partitioning clauses because
935 * those could refer to generated columns.
936 */
937 if (rawDefaults)
938 AddRelationNewConstraints(rel, rawDefaults, NIL,
939 true, true, false, queryString);
940
941 /*
942 * Make column generation expressions visible for use by partitioning.
943 */
944 CommandCounterIncrement();
945
946 /* Process and store partition bound, if any. */
947 if (stmt->partbound)
948 {
949 PartitionBoundSpec *bound;
950 ParseState *pstate;
951 Oid parentId = linitial_oid(inheritOids),
952 defaultPartOid;
953 Relation parent,
954 defaultRel = NULL;
955 ParseNamespaceItem *nsitem;
956
957 /* Already have strong enough lock on the parent */
958 parent = table_open(parentId, NoLock);
959
960 /*
961 * We are going to try to validate the partition bound specification
962 * against the partition key of parentRel, so it better have one.
963 */
964 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
965 ereport(ERROR,
966 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
967 errmsg("\"%s\" is not partitioned",
968 RelationGetRelationName(parent))));
969
970 /*
971 * The partition constraint of the default partition depends on the
972 * partition bounds of every other partition. It is possible that
973 * another backend might be about to execute a query on the default
974 * partition table, and that the query relies on previously cached
975 * default partition constraints. We must therefore take a table lock
976 * strong enough to prevent all queries on the default partition from
977 * proceeding until we commit and send out a shared-cache-inval notice
978 * that will make them update their index lists.
979 *
980 * Order of locking: The relation being added won't be visible to
981 * other backends until it is committed, hence here in
982 * DefineRelation() the order of locking the default partition and the
983 * relation being added does not matter. But at all other places we
984 * need to lock the default relation before we lock the relation being
985 * added or removed i.e. we should take the lock in same order at all
986 * the places such that lock parent, lock default partition and then
987 * lock the partition so as to avoid a deadlock.
988 */
989 defaultPartOid =
990 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent));
991 if (OidIsValid(defaultPartOid))
992 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
993
994 /* Transform the bound values */
995 pstate = make_parsestate(NULL);
996 pstate->p_sourcetext = queryString;
997
998 /*
999 * Add an nsitem containing this relation, so that transformExpr
1000 * called on partition bound expressions is able to report errors
1001 * using a proper context.
1002 */
1003 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1004 NULL, false, false);
1005 addNSItemToQuery(pstate, nsitem, false, true, true);
1006
1007 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1008
1009 /*
1010 * Check first that the new partition's bound is valid and does not
1011 * overlap with any of existing partitions of the parent.
1012 */
1013 check_new_partition_bound(relname, parent, bound);
1014
1015 /*
1016 * If the default partition exists, its partition constraints will
1017 * change after the addition of this new partition such that it won't
1018 * allow any row that qualifies for this new partition. So, check that
1019 * the existing data in the default partition satisfies the constraint
1020 * as it will exist after adding this partition.
1021 */
1022 if (OidIsValid(defaultPartOid))
1023 {
1024 check_default_partition_contents(parent, defaultRel, bound);
1025 /* Keep the lock until commit. */
1026 table_close(defaultRel, NoLock);
1027 }
1028
1029 /* Update the pg_class entry. */
1030 StorePartitionBound(rel, parent, bound);
1031
1032 table_close(parent, NoLock);
1033 }
1034
1035 /* Store inheritance information for new rel. */
1036 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1037
1038 /*
1039 * Process the partitioning specification (if any) and store the partition
1040 * key information into the catalog.
1041 */
1042 if (partitioned)
1043 {
1044 ParseState *pstate;
1045 char strategy;
1046 int partnatts;
1047 AttrNumber partattrs[PARTITION_MAX_KEYS];
1048 Oid partopclass[PARTITION_MAX_KEYS];
1049 Oid partcollation[PARTITION_MAX_KEYS];
1050 List *partexprs = NIL;
1051
1052 pstate = make_parsestate(NULL);
1053 pstate->p_sourcetext = queryString;
1054
1055 partnatts = list_length(stmt->partspec->partParams);
1056
1057 /* Protect fixed-size arrays here and in executor */
1058 if (partnatts > PARTITION_MAX_KEYS)
1059 ereport(ERROR,
1060 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1061 errmsg("cannot partition using more than %d columns",
1062 PARTITION_MAX_KEYS)));
1063
1064 /*
1065 * We need to transform the raw parsetrees corresponding to partition
1066 * expressions into executable expression trees. Like column defaults
1067 * and CHECK constraints, we could not have done the transformation
1068 * earlier.
1069 */
1070 stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
1071 &strategy);
1072
1073 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1074 partattrs, &partexprs, partopclass,
1075 partcollation, strategy);
1076
1077 StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
1078 partopclass, partcollation);
1079
1080 /* make it all visible */
1081 CommandCounterIncrement();
1082 }
1083
1084 /*
1085 * If we're creating a partition, create now all the indexes, triggers,
1086 * FKs defined in the parent.
1087 *
1088 * We can't do it earlier, because DefineIndex wants to know the partition
1089 * key which we just stored.
1090 */
1091 if (stmt->partbound)
1092 {
1093 Oid parentId = linitial_oid(inheritOids);
1094 Relation parent;
1095 List *idxlist;
1096 ListCell *cell;
1097
1098 /* Already have strong enough lock on the parent */
1099 parent = table_open(parentId, NoLock);
1100 idxlist = RelationGetIndexList(parent);
1101
1102 /*
1103 * For each index in the parent table, create one in the partition
1104 */
1105 foreach(cell, idxlist)
1106 {
1107 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1108 AttrMap *attmap;
1109 IndexStmt *idxstmt;
1110 Oid constraintOid;
1111
1112 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1113 {
1114 if (idxRel->rd_index->indisunique)
1115 ereport(ERROR,
1116 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1117 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1118 RelationGetRelationName(parent)),
1119 errdetail("Table \"%s\" contains indexes that are unique.",
1120 RelationGetRelationName(parent))));
1121 else
1122 {
1123 index_close(idxRel, AccessShareLock);
1124 continue;
1125 }
1126 }
1127
1128 attmap = build_attrmap_by_name(RelationGetDescr(rel),
1129 RelationGetDescr(parent));
1130 idxstmt =
1131 generateClonedIndexStmt(NULL, idxRel,
1132 attmap, &constraintOid);
1133 DefineIndex(RelationGetRelid(rel),
1134 idxstmt,
1135 InvalidOid,
1136 RelationGetRelid(idxRel),
1137 constraintOid,
1138 false, false, false, false, false);
1139
1140 index_close(idxRel, AccessShareLock);
1141 }
1142
1143 list_free(idxlist);
1144
1145 /*
1146 * If there are any row-level triggers, clone them to the new
1147 * partition.
1148 */
1149 if (parent->trigdesc != NULL)
1150 CloneRowTriggersToPartition(parent, rel);
1151
1152 /*
1153 * And foreign keys too. Note that because we're freshly creating the
1154 * table, there is no need to verify these new constraints.
1155 */
1156 CloneForeignKeyConstraints(NULL, parent, rel);
1157
1158 table_close(parent, NoLock);
1159 }
1160
1161 /*
1162 * Now add any newly specified CHECK constraints to the new relation. Same
1163 * as for defaults above, but these need to come after partitioning is set
1164 * up.
1165 */
1166 if (stmt->constraints)
1167 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1168 true, true, false, queryString);
1169
1170 ObjectAddressSet(address, RelationRelationId, relationId);
1171
1172 /*
1173 * Clean up. We keep lock on new relation (although it shouldn't be
1174 * visible to anyone else anyway, until commit).
1175 */
1176 relation_close(rel, NoLock);
1177
1178 return address;
1179 }
1180
1181 /*
1182 * Emit the right error or warning message for a "DROP" command issued on a
1183 * non-existent relation
1184 */
1185 static void
DropErrorMsgNonExistent(RangeVar * rel,char rightkind,bool missing_ok)1186 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1187 {
1188 const struct dropmsgstrings *rentry;
1189
1190 if (rel->schemaname != NULL &&
1191 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1192 {
1193 if (!missing_ok)
1194 {
1195 ereport(ERROR,
1196 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1197 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1198 }
1199 else
1200 {
1201 ereport(NOTICE,
1202 (errmsg("schema \"%s\" does not exist, skipping",
1203 rel->schemaname)));
1204 }
1205 return;
1206 }
1207
1208 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1209 {
1210 if (rentry->kind == rightkind)
1211 {
1212 if (!missing_ok)
1213 {
1214 ereport(ERROR,
1215 (errcode(rentry->nonexistent_code),
1216 errmsg(rentry->nonexistent_msg, rel->relname)));
1217 }
1218 else
1219 {
1220 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1221 break;
1222 }
1223 }
1224 }
1225
1226 Assert(rentry->kind != '\0'); /* Should be impossible */
1227 }
1228
1229 /*
1230 * Emit the right error message for a "DROP" command issued on a
1231 * relation of the wrong type
1232 */
1233 static void
DropErrorMsgWrongType(const char * relname,char wrongkind,char rightkind)1234 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1235 {
1236 const struct dropmsgstrings *rentry;
1237 const struct dropmsgstrings *wentry;
1238
1239 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1240 if (rentry->kind == rightkind)
1241 break;
1242 Assert(rentry->kind != '\0');
1243
1244 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1245 if (wentry->kind == wrongkind)
1246 break;
1247 /* wrongkind could be something we don't have in our table... */
1248
1249 ereport(ERROR,
1250 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1251 errmsg(rentry->nota_msg, relname),
1252 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1253 }
1254
1255 /*
1256 * RemoveRelations
1257 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1258 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1259 */
1260 void
RemoveRelations(DropStmt * drop)1261 RemoveRelations(DropStmt *drop)
1262 {
1263 ObjectAddresses *objects;
1264 char relkind;
1265 ListCell *cell;
1266 int flags = 0;
1267 LOCKMODE lockmode = AccessExclusiveLock;
1268
1269 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1270 if (drop->concurrent)
1271 {
1272 /*
1273 * Note that for temporary relations this lock may get upgraded later
1274 * on, but as no other session can access a temporary relation, this
1275 * is actually fine.
1276 */
1277 lockmode = ShareUpdateExclusiveLock;
1278 Assert(drop->removeType == OBJECT_INDEX);
1279 if (list_length(drop->objects) != 1)
1280 ereport(ERROR,
1281 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1282 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1283 if (drop->behavior == DROP_CASCADE)
1284 ereport(ERROR,
1285 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1286 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1287 }
1288
1289 /*
1290 * First we identify all the relations, then we delete them in a single
1291 * performMultipleDeletions() call. This is to avoid unwanted DROP
1292 * RESTRICT errors if one of the relations depends on another.
1293 */
1294
1295 /* Determine required relkind */
1296 switch (drop->removeType)
1297 {
1298 case OBJECT_TABLE:
1299 relkind = RELKIND_RELATION;
1300 break;
1301
1302 case OBJECT_INDEX:
1303 relkind = RELKIND_INDEX;
1304 break;
1305
1306 case OBJECT_SEQUENCE:
1307 relkind = RELKIND_SEQUENCE;
1308 break;
1309
1310 case OBJECT_VIEW:
1311 relkind = RELKIND_VIEW;
1312 break;
1313
1314 case OBJECT_MATVIEW:
1315 relkind = RELKIND_MATVIEW;
1316 break;
1317
1318 case OBJECT_FOREIGN_TABLE:
1319 relkind = RELKIND_FOREIGN_TABLE;
1320 break;
1321
1322 default:
1323 elog(ERROR, "unrecognized drop object type: %d",
1324 (int) drop->removeType);
1325 relkind = 0; /* keep compiler quiet */
1326 break;
1327 }
1328
1329 /* Lock and validate each relation; build a list of object addresses */
1330 objects = new_object_addresses();
1331
1332 foreach(cell, drop->objects)
1333 {
1334 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1335 Oid relOid;
1336 ObjectAddress obj;
1337 struct DropRelationCallbackState state;
1338
1339 /*
1340 * These next few steps are a great deal like relation_openrv, but we
1341 * don't bother building a relcache entry since we don't need it.
1342 *
1343 * Check for shared-cache-inval messages before trying to access the
1344 * relation. This is needed to cover the case where the name
1345 * identifies a rel that has been dropped and recreated since the
1346 * start of our transaction: if we don't flush the old syscache entry,
1347 * then we'll latch onto that entry and suffer an error later.
1348 */
1349 AcceptInvalidationMessages();
1350
1351 /* Look up the appropriate relation using namespace search. */
1352 state.relkind = relkind;
1353 state.heapOid = InvalidOid;
1354 state.partParentOid = InvalidOid;
1355 state.concurrent = drop->concurrent;
1356 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1357 RangeVarCallbackForDropRelation,
1358 (void *) &state);
1359
1360 /* Not there? */
1361 if (!OidIsValid(relOid))
1362 {
1363 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1364 continue;
1365 }
1366
1367 /*
1368 * Decide if concurrent mode needs to be used here or not. The
1369 * relation persistence cannot be known without its OID.
1370 */
1371 if (drop->concurrent &&
1372 get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
1373 {
1374 Assert(list_length(drop->objects) == 1 &&
1375 drop->removeType == OBJECT_INDEX);
1376 flags |= PERFORM_DELETION_CONCURRENTLY;
1377 }
1378
1379 /*
1380 * Concurrent index drop cannot be used with partitioned indexes,
1381 * either.
1382 */
1383 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1384 get_rel_relkind(relOid) == RELKIND_PARTITIONED_INDEX)
1385 ereport(ERROR,
1386 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1387 errmsg("cannot drop partitioned index \"%s\" concurrently",
1388 rel->relname)));
1389
1390 /* OK, we're ready to delete this one */
1391 obj.classId = RelationRelationId;
1392 obj.objectId = relOid;
1393 obj.objectSubId = 0;
1394
1395 add_exact_object_address(&obj, objects);
1396 }
1397
1398 performMultipleDeletions(objects, drop->behavior, flags);
1399
1400 free_object_addresses(objects);
1401 }
1402
1403 /*
1404 * Before acquiring a table lock, check whether we have sufficient rights.
1405 * In the case of DROP INDEX, also try to lock the table before the index.
1406 * Also, if the table to be dropped is a partition, we try to lock the parent
1407 * first.
1408 */
1409 static void
RangeVarCallbackForDropRelation(const RangeVar * rel,Oid relOid,Oid oldRelOid,void * arg)1410 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1411 void *arg)
1412 {
1413 HeapTuple tuple;
1414 struct DropRelationCallbackState *state;
1415 char relkind;
1416 char expected_relkind;
1417 bool is_partition;
1418 Form_pg_class classform;
1419 LOCKMODE heap_lockmode;
1420 bool invalid_system_index = false;
1421
1422 state = (struct DropRelationCallbackState *) arg;
1423 relkind = state->relkind;
1424 heap_lockmode = state->concurrent ?
1425 ShareUpdateExclusiveLock : AccessExclusiveLock;
1426
1427 /*
1428 * If we previously locked some other index's heap, and the name we're
1429 * looking up no longer refers to that relation, release the now-useless
1430 * lock.
1431 */
1432 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1433 {
1434 UnlockRelationOid(state->heapOid, heap_lockmode);
1435 state->heapOid = InvalidOid;
1436 }
1437
1438 /*
1439 * Similarly, if we previously locked some other partition's heap, and the
1440 * name we're looking up no longer refers to that relation, release the
1441 * now-useless lock.
1442 */
1443 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1444 {
1445 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1446 state->partParentOid = InvalidOid;
1447 }
1448
1449 /* Didn't find a relation, so no need for locking or permission checks. */
1450 if (!OidIsValid(relOid))
1451 return;
1452
1453 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1454 if (!HeapTupleIsValid(tuple))
1455 return; /* concurrently dropped, so nothing to do */
1456 classform = (Form_pg_class) GETSTRUCT(tuple);
1457 is_partition = classform->relispartition;
1458
1459 /*
1460 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1461 * but RemoveRelations() can only pass one relkind for a given relation.
1462 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1463 * That means we must be careful before giving the wrong type error when
1464 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1465 * exists with indexes.
1466 */
1467 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1468 expected_relkind = RELKIND_RELATION;
1469 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1470 expected_relkind = RELKIND_INDEX;
1471 else
1472 expected_relkind = classform->relkind;
1473
1474 if (relkind != expected_relkind)
1475 DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
1476
1477 /* Allow DROP to either table owner or schema owner */
1478 if (!pg_class_ownercheck(relOid, GetUserId()) &&
1479 !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
1480 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
1481 rel->relname);
1482
1483 /*
1484 * Check the case of a system index that might have been invalidated by a
1485 * failed concurrent process and allow its drop. For the time being, this
1486 * only concerns indexes of toast relations that became invalid during a
1487 * REINDEX CONCURRENTLY process.
1488 */
1489 if (IsSystemClass(relOid, classform) && relkind == RELKIND_INDEX)
1490 {
1491 HeapTuple locTuple;
1492 Form_pg_index indexform;
1493 bool indisvalid;
1494
1495 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1496 if (!HeapTupleIsValid(locTuple))
1497 {
1498 ReleaseSysCache(tuple);
1499 return;
1500 }
1501
1502 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1503 indisvalid = indexform->indisvalid;
1504 ReleaseSysCache(locTuple);
1505
1506 /* Mark object as being an invalid index of system catalogs */
1507 if (!indisvalid)
1508 invalid_system_index = true;
1509 }
1510
1511 /* In the case of an invalid index, it is fine to bypass this check */
1512 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1513 ereport(ERROR,
1514 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1515 errmsg("permission denied: \"%s\" is a system catalog",
1516 rel->relname)));
1517
1518 ReleaseSysCache(tuple);
1519
1520 /*
1521 * In DROP INDEX, attempt to acquire lock on the parent table before
1522 * locking the index. index_drop() will need this anyway, and since
1523 * regular queries lock tables before their indexes, we risk deadlock if
1524 * we do it the other way around. No error if we don't find a pg_index
1525 * entry, though --- the relation may have been dropped.
1526 */
1527 if ((relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX) &&
1528 relOid != oldRelOid)
1529 {
1530 state->heapOid = IndexGetRelation(relOid, true);
1531 if (OidIsValid(state->heapOid))
1532 LockRelationOid(state->heapOid, heap_lockmode);
1533 }
1534
1535 /*
1536 * Similarly, if the relation is a partition, we must acquire lock on its
1537 * parent before locking the partition. That's because queries lock the
1538 * parent before its partitions, so we risk deadlock it we do it the other
1539 * way around.
1540 */
1541 if (is_partition && relOid != oldRelOid)
1542 {
1543 state->partParentOid = get_partition_parent(relOid);
1544 if (OidIsValid(state->partParentOid))
1545 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1546 }
1547 }
1548
1549 /*
1550 * ExecuteTruncate
1551 * Executes a TRUNCATE command.
1552 *
1553 * This is a multi-relation truncate. We first open and grab exclusive
1554 * lock on all relations involved, checking permissions and otherwise
1555 * verifying that the relation is OK for truncation. In CASCADE mode,
1556 * relations having FK references to the targeted relations are automatically
1557 * added to the group; in RESTRICT mode, we check that all FK references are
1558 * internal to the group that's being truncated. Finally all the relations
1559 * are truncated and reindexed.
1560 */
1561 void
ExecuteTruncate(TruncateStmt * stmt)1562 ExecuteTruncate(TruncateStmt *stmt)
1563 {
1564 List *rels = NIL;
1565 List *relids = NIL;
1566 List *relids_logged = NIL;
1567 ListCell *cell;
1568
1569 /*
1570 * Open, exclusive-lock, and check all the explicitly-specified relations
1571 */
1572 foreach(cell, stmt->relations)
1573 {
1574 RangeVar *rv = lfirst(cell);
1575 Relation rel;
1576 bool recurse = rv->inh;
1577 Oid myrelid;
1578 LOCKMODE lockmode = AccessExclusiveLock;
1579
1580 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1581 0, RangeVarCallbackForTruncate,
1582 NULL);
1583
1584 /* open the relation, we already hold a lock on it */
1585 rel = table_open(myrelid, NoLock);
1586
1587 /* don't throw error for "TRUNCATE foo, foo" */
1588 if (list_member_oid(relids, myrelid))
1589 {
1590 table_close(rel, lockmode);
1591 continue;
1592 }
1593
1594 /*
1595 * RangeVarGetRelidExtended() has done most checks with its callback,
1596 * but other checks with the now-opened Relation remain.
1597 */
1598 truncate_check_activity(rel);
1599
1600 rels = lappend(rels, rel);
1601 relids = lappend_oid(relids, myrelid);
1602 /* Log this relation only if needed for logical decoding */
1603 if (RelationIsLogicallyLogged(rel))
1604 relids_logged = lappend_oid(relids_logged, myrelid);
1605
1606 if (recurse)
1607 {
1608 ListCell *child;
1609 List *children;
1610
1611 children = find_all_inheritors(myrelid, lockmode, NULL);
1612
1613 foreach(child, children)
1614 {
1615 Oid childrelid = lfirst_oid(child);
1616
1617 if (list_member_oid(relids, childrelid))
1618 continue;
1619
1620 /* find_all_inheritors already got lock */
1621 rel = table_open(childrelid, NoLock);
1622
1623 /*
1624 * It is possible that the parent table has children that are
1625 * temp tables of other backends. We cannot safely access
1626 * such tables (because of buffering issues), and the best
1627 * thing to do is to silently ignore them. Note that this
1628 * check is the same as one of the checks done in
1629 * truncate_check_activity() called below, still it is kept
1630 * here for simplicity.
1631 */
1632 if (RELATION_IS_OTHER_TEMP(rel))
1633 {
1634 table_close(rel, lockmode);
1635 continue;
1636 }
1637
1638 /*
1639 * Inherited TRUNCATE commands perform access permission
1640 * checks on the parent table only. So we skip checking the
1641 * children's permissions and don't call
1642 * truncate_check_perms() here.
1643 */
1644 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1645 truncate_check_activity(rel);
1646
1647 rels = lappend(rels, rel);
1648 relids = lappend_oid(relids, childrelid);
1649 /* Log this relation only if needed for logical decoding */
1650 if (RelationIsLogicallyLogged(rel))
1651 relids_logged = lappend_oid(relids_logged, childrelid);
1652 }
1653 }
1654 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1655 ereport(ERROR,
1656 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1657 errmsg("cannot truncate only a partitioned table"),
1658 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1659 }
1660
1661 ExecuteTruncateGuts(rels, relids, relids_logged,
1662 stmt->behavior, stmt->restart_seqs);
1663
1664 /* And close the rels */
1665 foreach(cell, rels)
1666 {
1667 Relation rel = (Relation) lfirst(cell);
1668
1669 table_close(rel, NoLock);
1670 }
1671 }
1672
1673 /*
1674 * ExecuteTruncateGuts
1675 *
1676 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1677 * command (see above) as well as replication subscribers that execute a
1678 * replicated TRUNCATE action.
1679 *
1680 * explicit_rels is the list of Relations to truncate that the command
1681 * specified. relids is the list of Oids corresponding to explicit_rels.
1682 * relids_logged is the list of Oids (a subset of relids) that require
1683 * WAL-logging. This is all a bit redundant, but the existing callers have
1684 * this information handy in this form.
1685 */
1686 void
ExecuteTruncateGuts(List * explicit_rels,List * relids,List * relids_logged,DropBehavior behavior,bool restart_seqs)1687 ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
1688 DropBehavior behavior, bool restart_seqs)
1689 {
1690 List *rels;
1691 List *seq_relids = NIL;
1692 EState *estate;
1693 ResultRelInfo *resultRelInfos;
1694 ResultRelInfo *resultRelInfo;
1695 SubTransactionId mySubid;
1696 ListCell *cell;
1697 Oid *logrelids;
1698
1699 /*
1700 * Check the explicitly-specified relations.
1701 *
1702 * In CASCADE mode, suck in all referencing relations as well. This
1703 * requires multiple iterations to find indirectly-dependent relations. At
1704 * each phase, we need to exclusive-lock new rels before looking for their
1705 * dependencies, else we might miss something. Also, we check each rel as
1706 * soon as we open it, to avoid a faux pas such as holding lock for a long
1707 * time on a rel we have no permissions for.
1708 */
1709 rels = list_copy(explicit_rels);
1710 if (behavior == DROP_CASCADE)
1711 {
1712 for (;;)
1713 {
1714 List *newrelids;
1715
1716 newrelids = heap_truncate_find_FKs(relids);
1717 if (newrelids == NIL)
1718 break; /* nothing else to add */
1719
1720 foreach(cell, newrelids)
1721 {
1722 Oid relid = lfirst_oid(cell);
1723 Relation rel;
1724
1725 rel = table_open(relid, AccessExclusiveLock);
1726 ereport(NOTICE,
1727 (errmsg("truncate cascades to table \"%s\"",
1728 RelationGetRelationName(rel))));
1729 truncate_check_rel(relid, rel->rd_rel);
1730 truncate_check_perms(relid, rel->rd_rel);
1731 truncate_check_activity(rel);
1732 rels = lappend(rels, rel);
1733 relids = lappend_oid(relids, relid);
1734 /* Log this relation only if needed for logical decoding */
1735 if (RelationIsLogicallyLogged(rel))
1736 relids_logged = lappend_oid(relids_logged, relid);
1737 }
1738 }
1739 }
1740
1741 /*
1742 * Check foreign key references. In CASCADE mode, this should be
1743 * unnecessary since we just pulled in all the references; but as a
1744 * cross-check, do it anyway if in an Assert-enabled build.
1745 */
1746 #ifdef USE_ASSERT_CHECKING
1747 heap_truncate_check_FKs(rels, false);
1748 #else
1749 if (behavior == DROP_RESTRICT)
1750 heap_truncate_check_FKs(rels, false);
1751 #endif
1752
1753 /*
1754 * If we are asked to restart sequences, find all the sequences, lock them
1755 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1756 * We want to do this early since it's pointless to do all the truncation
1757 * work only to fail on sequence permissions.
1758 */
1759 if (restart_seqs)
1760 {
1761 foreach(cell, rels)
1762 {
1763 Relation rel = (Relation) lfirst(cell);
1764 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
1765 ListCell *seqcell;
1766
1767 foreach(seqcell, seqlist)
1768 {
1769 Oid seq_relid = lfirst_oid(seqcell);
1770 Relation seq_rel;
1771
1772 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1773
1774 /* This check must match AlterSequence! */
1775 if (!pg_class_ownercheck(seq_relid, GetUserId()))
1776 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
1777 RelationGetRelationName(seq_rel));
1778
1779 seq_relids = lappend_oid(seq_relids, seq_relid);
1780
1781 relation_close(seq_rel, NoLock);
1782 }
1783 }
1784 }
1785
1786 /* Prepare to catch AFTER triggers. */
1787 AfterTriggerBeginQuery();
1788
1789 /*
1790 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1791 * each relation. We don't need to call ExecOpenIndices, though.
1792 */
1793 estate = CreateExecutorState();
1794 resultRelInfos = (ResultRelInfo *)
1795 palloc(list_length(rels) * sizeof(ResultRelInfo));
1796 resultRelInfo = resultRelInfos;
1797 foreach(cell, rels)
1798 {
1799 Relation rel = (Relation) lfirst(cell);
1800
1801 InitResultRelInfo(resultRelInfo,
1802 rel,
1803 0, /* dummy rangetable index */
1804 NULL,
1805 0);
1806 resultRelInfo++;
1807 }
1808 estate->es_result_relations = resultRelInfos;
1809 estate->es_num_result_relations = list_length(rels);
1810
1811 /*
1812 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
1813 * truncating (this is because one of them might throw an error). Also, if
1814 * we were to allow them to prevent statement execution, that would need
1815 * to be handled here.
1816 */
1817 resultRelInfo = resultRelInfos;
1818 foreach(cell, rels)
1819 {
1820 estate->es_result_relation_info = resultRelInfo;
1821 ExecBSTruncateTriggers(estate, resultRelInfo);
1822 resultRelInfo++;
1823 }
1824
1825 /*
1826 * OK, truncate each table.
1827 */
1828 mySubid = GetCurrentSubTransactionId();
1829
1830 foreach(cell, rels)
1831 {
1832 Relation rel = (Relation) lfirst(cell);
1833
1834 /* Skip partitioned tables as there is nothing to do */
1835 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1836 continue;
1837
1838 /*
1839 * Normally, we need a transaction-safe truncation here. However, if
1840 * the table was either created in the current (sub)transaction or has
1841 * a new relfilenode in the current (sub)transaction, then we can just
1842 * truncate it in-place, because a rollback would cause the whole
1843 * table or the current physical file to be thrown away anyway.
1844 */
1845 if (rel->rd_createSubid == mySubid ||
1846 rel->rd_newRelfilenodeSubid == mySubid)
1847 {
1848 /* Immediate, non-rollbackable truncation is OK */
1849 heap_truncate_one_rel(rel);
1850 }
1851 else
1852 {
1853 Oid heap_relid;
1854 Oid toast_relid;
1855
1856 /*
1857 * This effectively deletes all rows in the table, and may be done
1858 * in a serializable transaction. In that case we must record a
1859 * rw-conflict in to this transaction from each transaction
1860 * holding a predicate lock on the table.
1861 */
1862 CheckTableForSerializableConflictIn(rel);
1863
1864 /*
1865 * Need the full transaction-safe pushups.
1866 *
1867 * Create a new empty storage file for the relation, and assign it
1868 * as the relfilenode value. The old storage file is scheduled for
1869 * deletion at commit.
1870 */
1871 RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence);
1872
1873 heap_relid = RelationGetRelid(rel);
1874
1875 /*
1876 * The same for the toast table, if any.
1877 */
1878 toast_relid = rel->rd_rel->reltoastrelid;
1879 if (OidIsValid(toast_relid))
1880 {
1881 Relation toastrel = relation_open(toast_relid,
1882 AccessExclusiveLock);
1883
1884 RelationSetNewRelfilenode(toastrel,
1885 toastrel->rd_rel->relpersistence);
1886 table_close(toastrel, NoLock);
1887 }
1888
1889 /*
1890 * Reconstruct the indexes to match, and we're done.
1891 */
1892 reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, 0);
1893 }
1894
1895 pgstat_count_truncate(rel);
1896 }
1897
1898 /*
1899 * Restart owned sequences if we were asked to.
1900 */
1901 foreach(cell, seq_relids)
1902 {
1903 Oid seq_relid = lfirst_oid(cell);
1904
1905 ResetSequence(seq_relid);
1906 }
1907
1908 /*
1909 * Write a WAL record to allow this set of actions to be logically
1910 * decoded.
1911 *
1912 * Assemble an array of relids so we can write a single WAL record for the
1913 * whole action.
1914 */
1915 if (list_length(relids_logged) > 0)
1916 {
1917 xl_heap_truncate xlrec;
1918 int i = 0;
1919
1920 /* should only get here if wal_level >= logical */
1921 Assert(XLogLogicalInfoActive());
1922
1923 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
1924 foreach(cell, relids_logged)
1925 logrelids[i++] = lfirst_oid(cell);
1926
1927 xlrec.dbId = MyDatabaseId;
1928 xlrec.nrelids = list_length(relids_logged);
1929 xlrec.flags = 0;
1930 if (behavior == DROP_CASCADE)
1931 xlrec.flags |= XLH_TRUNCATE_CASCADE;
1932 if (restart_seqs)
1933 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
1934
1935 XLogBeginInsert();
1936 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
1937 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
1938
1939 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
1940
1941 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
1942 }
1943
1944 /*
1945 * Process all AFTER STATEMENT TRUNCATE triggers.
1946 */
1947 resultRelInfo = resultRelInfos;
1948 foreach(cell, rels)
1949 {
1950 estate->es_result_relation_info = resultRelInfo;
1951 ExecASTruncateTriggers(estate, resultRelInfo);
1952 resultRelInfo++;
1953 }
1954
1955 /* Handle queued AFTER triggers */
1956 AfterTriggerEndQuery(estate);
1957
1958 /* We can clean up the EState now */
1959 FreeExecutorState(estate);
1960
1961 /*
1962 * Close any rels opened by CASCADE (can't do this while EState still
1963 * holds refs)
1964 */
1965 rels = list_difference_ptr(rels, explicit_rels);
1966 foreach(cell, rels)
1967 {
1968 Relation rel = (Relation) lfirst(cell);
1969
1970 table_close(rel, NoLock);
1971 }
1972 }
1973
1974 /*
1975 * Check that a given relation is safe to truncate. Subroutine for
1976 * ExecuteTruncate() and RangeVarCallbackForTruncate().
1977 */
1978 static void
truncate_check_rel(Oid relid,Form_pg_class reltuple)1979 truncate_check_rel(Oid relid, Form_pg_class reltuple)
1980 {
1981 char *relname = NameStr(reltuple->relname);
1982
1983 /*
1984 * Only allow truncate on regular tables and partitioned tables (although,
1985 * the latter are only being included here for the following checks; no
1986 * physical truncation will occur in their case.)
1987 */
1988 if (reltuple->relkind != RELKIND_RELATION &&
1989 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
1990 ereport(ERROR,
1991 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1992 errmsg("\"%s\" is not a table", relname)));
1993
1994 if (!allowSystemTableMods && IsSystemClass(relid, reltuple))
1995 ereport(ERROR,
1996 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1997 errmsg("permission denied: \"%s\" is a system catalog",
1998 relname)));
1999
2000 InvokeObjectTruncateHook(relid);
2001 }
2002
2003 /*
2004 * Check that current user has the permission to truncate given relation.
2005 */
2006 static void
truncate_check_perms(Oid relid,Form_pg_class reltuple)2007 truncate_check_perms(Oid relid, Form_pg_class reltuple)
2008 {
2009 char *relname = NameStr(reltuple->relname);
2010 AclResult aclresult;
2011
2012 /* Permissions checks */
2013 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2014 if (aclresult != ACLCHECK_OK)
2015 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2016 relname);
2017 }
2018
2019 /*
2020 * Set of extra sanity checks to check if a given relation is safe to
2021 * truncate. This is split with truncate_check_rel() as
2022 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2023 */
2024 static void
truncate_check_activity(Relation rel)2025 truncate_check_activity(Relation rel)
2026 {
2027 /*
2028 * Don't allow truncate on temp tables of other backends ... their local
2029 * buffer manager is not going to cope.
2030 */
2031 if (RELATION_IS_OTHER_TEMP(rel))
2032 ereport(ERROR,
2033 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2034 errmsg("cannot truncate temporary tables of other sessions")));
2035
2036 /*
2037 * Also check for active uses of the relation in the current transaction,
2038 * including open scans and pending AFTER trigger events.
2039 */
2040 CheckTableNotInUse(rel, "TRUNCATE");
2041 }
2042
2043 /*
2044 * storage_name
2045 * returns the name corresponding to a typstorage/attstorage enum value
2046 */
2047 static const char *
storage_name(char c)2048 storage_name(char c)
2049 {
2050 switch (c)
2051 {
2052 case TYPSTORAGE_PLAIN:
2053 return "PLAIN";
2054 case TYPSTORAGE_EXTERNAL:
2055 return "EXTERNAL";
2056 case TYPSTORAGE_EXTENDED:
2057 return "EXTENDED";
2058 case TYPSTORAGE_MAIN:
2059 return "MAIN";
2060 default:
2061 return "???";
2062 }
2063 }
2064
2065 /*----------
2066 * MergeAttributes
2067 * Returns new schema given initial schema and superclasses.
2068 *
2069 * Input arguments:
2070 * 'schema' is the column/attribute definition for the table. (It's a list
2071 * of ColumnDef's.) It is destructively changed.
2072 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2073 * 'relpersistence' is the persistence type of the table.
2074 * 'is_partition' tells if the table is a partition.
2075 *
2076 * Output arguments:
2077 * 'supconstr' receives a list of constraints belonging to the parents,
2078 * updated as necessary to be valid for the child.
2079 *
2080 * Return value:
2081 * Completed schema list.
2082 *
2083 * Notes:
2084 * The order in which the attributes are inherited is very important.
2085 * Intuitively, the inherited attributes should come first. If a table
2086 * inherits from multiple parents, the order of those attributes are
2087 * according to the order of the parents specified in CREATE TABLE.
2088 *
2089 * Here's an example:
2090 *
2091 * create table person (name text, age int4, location point);
2092 * create table emp (salary int4, manager text) inherits(person);
2093 * create table student (gpa float8) inherits (person);
2094 * create table stud_emp (percent int4) inherits (emp, student);
2095 *
2096 * The order of the attributes of stud_emp is:
2097 *
2098 * person {1:name, 2:age, 3:location}
2099 * / \
2100 * {6:gpa} student emp {4:salary, 5:manager}
2101 * \ /
2102 * stud_emp {7:percent}
2103 *
2104 * If the same attribute name appears multiple times, then it appears
2105 * in the result table in the proper location for its first appearance.
2106 *
2107 * Constraints (including NOT NULL constraints) for the child table
2108 * are the union of all relevant constraints, from both the child schema
2109 * and parent tables.
2110 *
2111 * The default value for a child column is defined as:
2112 * (1) If the child schema specifies a default, that value is used.
2113 * (2) If neither the child nor any parent specifies a default, then
2114 * the column will not have a default.
2115 * (3) If conflicting defaults are inherited from different parents
2116 * (and not overridden by the child), an error is raised.
2117 * (4) Otherwise the inherited default is used.
2118 * Rule (3) is new in Postgres 7.1; in earlier releases you got a
2119 * rather arbitrary choice of which parent default to use.
2120 *----------
2121 */
2122 static List *
MergeAttributes(List * schema,List * supers,char relpersistence,bool is_partition,List ** supconstr)2123 MergeAttributes(List *schema, List *supers, char relpersistence,
2124 bool is_partition, List **supconstr)
2125 {
2126 List *inhSchema = NIL;
2127 List *constraints = NIL;
2128 bool have_bogus_defaults = false;
2129 int child_attno;
2130 static Node bogus_marker = {0}; /* marks conflicting defaults */
2131 List *saved_schema = NIL;
2132 ListCell *entry;
2133
2134 /*
2135 * Check for and reject tables with too many columns. We perform this
2136 * check relatively early for two reasons: (a) we don't run the risk of
2137 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2138 * okay if we're processing <= 1600 columns, but could take minutes to
2139 * execute if the user attempts to create a table with hundreds of
2140 * thousands of columns.
2141 *
2142 * Note that we also need to check that we do not exceed this figure after
2143 * including columns from inherited relations.
2144 */
2145 if (list_length(schema) > MaxHeapAttributeNumber)
2146 ereport(ERROR,
2147 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2148 errmsg("tables can have at most %d columns",
2149 MaxHeapAttributeNumber)));
2150
2151 /*
2152 * Check for duplicate names in the explicit list of attributes.
2153 *
2154 * Although we might consider merging such entries in the same way that we
2155 * handle name conflicts for inherited attributes, it seems to make more
2156 * sense to assume such conflicts are errors.
2157 *
2158 * We don't use foreach() here because we have two nested loops over the
2159 * schema list, with possible element deletions in the inner one. If we
2160 * used foreach_delete_current() it could only fix up the state of one of
2161 * the loops, so it seems cleaner to use looping over list indexes for
2162 * both loops. Note that any deletion will happen beyond where the outer
2163 * loop is, so its index never needs adjustment.
2164 */
2165 for (int coldefpos = 0; coldefpos < list_length(schema); coldefpos++)
2166 {
2167 ColumnDef *coldef = list_nth_node(ColumnDef, schema, coldefpos);
2168
2169 if (!is_partition && coldef->typeName == NULL)
2170 {
2171 /*
2172 * Typed table column option that does not belong to a column from
2173 * the type. This works because the columns from the type come
2174 * first in the list. (We omit this check for partition column
2175 * lists; those are processed separately below.)
2176 */
2177 ereport(ERROR,
2178 (errcode(ERRCODE_UNDEFINED_COLUMN),
2179 errmsg("column \"%s\" does not exist",
2180 coldef->colname)));
2181 }
2182
2183 /* restpos scans all entries beyond coldef; incr is in loop body */
2184 for (int restpos = coldefpos + 1; restpos < list_length(schema);)
2185 {
2186 ColumnDef *restdef = list_nth_node(ColumnDef, schema, restpos);
2187
2188 if (strcmp(coldef->colname, restdef->colname) == 0)
2189 {
2190 if (coldef->is_from_type)
2191 {
2192 /*
2193 * merge the column options into the column from the type
2194 */
2195 coldef->is_not_null = restdef->is_not_null;
2196 coldef->raw_default = restdef->raw_default;
2197 coldef->cooked_default = restdef->cooked_default;
2198 coldef->constraints = restdef->constraints;
2199 coldef->is_from_type = false;
2200 schema = list_delete_nth_cell(schema, restpos);
2201 }
2202 else
2203 ereport(ERROR,
2204 (errcode(ERRCODE_DUPLICATE_COLUMN),
2205 errmsg("column \"%s\" specified more than once",
2206 coldef->colname)));
2207 }
2208 else
2209 restpos++;
2210 }
2211 }
2212
2213 /*
2214 * In case of a partition, there are no new column definitions, only dummy
2215 * ColumnDefs created for column constraints. Set them aside for now and
2216 * process them at the end.
2217 */
2218 if (is_partition)
2219 {
2220 saved_schema = schema;
2221 schema = NIL;
2222 }
2223
2224 /*
2225 * Scan the parents left-to-right, and merge their attributes to form a
2226 * list of inherited attributes (inhSchema). Also check to see if we need
2227 * to inherit an OID column.
2228 */
2229 child_attno = 0;
2230 foreach(entry, supers)
2231 {
2232 Oid parent = lfirst_oid(entry);
2233 Relation relation;
2234 TupleDesc tupleDesc;
2235 TupleConstr *constr;
2236 AttrMap *newattmap;
2237 List *inherited_defaults;
2238 List *cols_with_defaults;
2239 AttrNumber parent_attno;
2240 ListCell *lc1;
2241 ListCell *lc2;
2242
2243 /* caller already got lock */
2244 relation = table_open(parent, NoLock);
2245
2246 /*
2247 * Check for active uses of the parent partitioned table in the
2248 * current transaction, such as being used in some manner by an
2249 * enclosing command.
2250 */
2251 if (is_partition)
2252 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2253
2254 /*
2255 * We do not allow partitioned tables and partitions to participate in
2256 * regular inheritance.
2257 */
2258 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2259 !is_partition)
2260 ereport(ERROR,
2261 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2262 errmsg("cannot inherit from partitioned table \"%s\"",
2263 RelationGetRelationName(relation))));
2264 if (relation->rd_rel->relispartition && !is_partition)
2265 ereport(ERROR,
2266 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2267 errmsg("cannot inherit from partition \"%s\"",
2268 RelationGetRelationName(relation))));
2269
2270 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2271 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2272 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2273 ereport(ERROR,
2274 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2275 errmsg("inherited relation \"%s\" is not a table or foreign table",
2276 RelationGetRelationName(relation))));
2277
2278 /*
2279 * If the parent is permanent, so must be all of its partitions. Note
2280 * that inheritance allows that case.
2281 */
2282 if (is_partition &&
2283 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2284 relpersistence == RELPERSISTENCE_TEMP)
2285 ereport(ERROR,
2286 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2287 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2288 RelationGetRelationName(relation))));
2289
2290 /* Permanent rels cannot inherit from temporary ones */
2291 if (relpersistence != RELPERSISTENCE_TEMP &&
2292 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2293 ereport(ERROR,
2294 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2295 errmsg(!is_partition
2296 ? "cannot inherit from temporary relation \"%s\""
2297 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2298 RelationGetRelationName(relation))));
2299
2300 /* If existing rel is temp, it must belong to this session */
2301 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2302 !relation->rd_islocaltemp)
2303 ereport(ERROR,
2304 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2305 errmsg(!is_partition
2306 ? "cannot inherit from temporary relation of another session"
2307 : "cannot create as partition of temporary relation of another session")));
2308
2309 /*
2310 * We should have an UNDER permission flag for this, but for now,
2311 * demand that creator of a child table own the parent.
2312 */
2313 if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
2314 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2315 RelationGetRelationName(relation));
2316
2317 tupleDesc = RelationGetDescr(relation);
2318 constr = tupleDesc->constr;
2319
2320 /*
2321 * newattmap->attnums[] will contain the child-table attribute numbers
2322 * for the attributes of this parent table. (They are not the same
2323 * for parents after the first one, nor if we have dropped columns.)
2324 */
2325 newattmap = make_attrmap(tupleDesc->natts);
2326
2327 /* We can't process inherited defaults until newattmap is complete. */
2328 inherited_defaults = cols_with_defaults = NIL;
2329
2330 for (parent_attno = 1; parent_attno <= tupleDesc->natts;
2331 parent_attno++)
2332 {
2333 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2334 parent_attno - 1);
2335 char *attributeName = NameStr(attribute->attname);
2336 int exist_attno;
2337 ColumnDef *def;
2338
2339 /*
2340 * Ignore dropped columns in the parent.
2341 */
2342 if (attribute->attisdropped)
2343 continue; /* leave newattmap->attnums entry as zero */
2344
2345 /*
2346 * Does it conflict with some previously inherited column?
2347 */
2348 exist_attno = findAttrByName(attributeName, inhSchema);
2349 if (exist_attno > 0)
2350 {
2351 Oid defTypeId;
2352 int32 deftypmod;
2353 Oid defCollId;
2354
2355 /*
2356 * Yes, try to merge the two column definitions. They must
2357 * have the same type, typmod, and collation.
2358 */
2359 ereport(NOTICE,
2360 (errmsg("merging multiple inherited definitions of column \"%s\"",
2361 attributeName)));
2362 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2363 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2364 if (defTypeId != attribute->atttypid ||
2365 deftypmod != attribute->atttypmod)
2366 ereport(ERROR,
2367 (errcode(ERRCODE_DATATYPE_MISMATCH),
2368 errmsg("inherited column \"%s\" has a type conflict",
2369 attributeName),
2370 errdetail("%s versus %s",
2371 format_type_with_typemod(defTypeId,
2372 deftypmod),
2373 format_type_with_typemod(attribute->atttypid,
2374 attribute->atttypmod))));
2375 defCollId = GetColumnDefCollation(NULL, def, defTypeId);
2376 if (defCollId != attribute->attcollation)
2377 ereport(ERROR,
2378 (errcode(ERRCODE_COLLATION_MISMATCH),
2379 errmsg("inherited column \"%s\" has a collation conflict",
2380 attributeName),
2381 errdetail("\"%s\" versus \"%s\"",
2382 get_collation_name(defCollId),
2383 get_collation_name(attribute->attcollation))));
2384
2385 /* Copy/check storage parameter */
2386 if (def->storage == 0)
2387 def->storage = attribute->attstorage;
2388 else if (def->storage != attribute->attstorage)
2389 ereport(ERROR,
2390 (errcode(ERRCODE_DATATYPE_MISMATCH),
2391 errmsg("inherited column \"%s\" has a storage parameter conflict",
2392 attributeName),
2393 errdetail("%s versus %s",
2394 storage_name(def->storage),
2395 storage_name(attribute->attstorage))));
2396
2397 def->inhcount++;
2398 /* Merge of NOT NULL constraints = OR 'em together */
2399 def->is_not_null |= attribute->attnotnull;
2400 /* Default and other constraints are handled below */
2401 newattmap->attnums[parent_attno - 1] = exist_attno;
2402
2403 /* Check for GENERATED conflicts */
2404 if (def->generated != attribute->attgenerated)
2405 ereport(ERROR,
2406 (errcode(ERRCODE_DATATYPE_MISMATCH),
2407 errmsg("inherited column \"%s\" has a generation conflict",
2408 attributeName)));
2409 }
2410 else
2411 {
2412 /*
2413 * No, create a new inherited column
2414 */
2415 def = makeNode(ColumnDef);
2416 def->colname = pstrdup(attributeName);
2417 def->typeName = makeTypeNameFromOid(attribute->atttypid,
2418 attribute->atttypmod);
2419 def->inhcount = 1;
2420 def->is_local = false;
2421 def->is_not_null = attribute->attnotnull;
2422 def->is_from_type = false;
2423 def->storage = attribute->attstorage;
2424 def->raw_default = NULL;
2425 def->cooked_default = NULL;
2426 def->generated = attribute->attgenerated;
2427 def->collClause = NULL;
2428 def->collOid = attribute->attcollation;
2429 def->constraints = NIL;
2430 def->location = -1;
2431 inhSchema = lappend(inhSchema, def);
2432 newattmap->attnums[parent_attno - 1] = ++child_attno;
2433 }
2434
2435 /*
2436 * Locate default if any
2437 */
2438 if (attribute->atthasdef)
2439 {
2440 Node *this_default = NULL;
2441 AttrDefault *attrdef;
2442 int i;
2443
2444 /* Find default in constraint structure */
2445 Assert(constr != NULL);
2446 attrdef = constr->defval;
2447 for (i = 0; i < constr->num_defval; i++)
2448 {
2449 if (attrdef[i].adnum == parent_attno)
2450 {
2451 this_default = stringToNode(attrdef[i].adbin);
2452 break;
2453 }
2454 }
2455 Assert(this_default != NULL);
2456
2457 /*
2458 * If it's a GENERATED default, it might contain Vars that
2459 * need to be mapped to the inherited column(s)' new numbers.
2460 * We can't do that till newattmap is ready, so just remember
2461 * all the inherited default expressions for the moment.
2462 */
2463 inherited_defaults = lappend(inherited_defaults, this_default);
2464 cols_with_defaults = lappend(cols_with_defaults, def);
2465 }
2466 }
2467
2468 /*
2469 * Now process any inherited default expressions, adjusting attnos
2470 * using the completed newattmap map.
2471 */
2472 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2473 {
2474 Node *this_default = (Node *) lfirst(lc1);
2475 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2476 bool found_whole_row;
2477
2478 /* Adjust Vars to match new table's column numbering */
2479 this_default = map_variable_attnos(this_default,
2480 1, 0,
2481 newattmap,
2482 InvalidOid, &found_whole_row);
2483
2484 /*
2485 * For the moment we have to reject whole-row variables. We could
2486 * convert them, if we knew the new table's rowtype OID, but that
2487 * hasn't been assigned yet. (A variable could only appear in a
2488 * generation expression, so the error message is correct.)
2489 */
2490 if (found_whole_row)
2491 ereport(ERROR,
2492 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2493 errmsg("cannot convert whole-row table reference"),
2494 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2495 def->colname,
2496 RelationGetRelationName(relation))));
2497
2498 /*
2499 * If we already had a default from some prior parent, check to
2500 * see if they are the same. If so, no problem; if not, mark the
2501 * column as having a bogus default. Below, we will complain if
2502 * the bogus default isn't overridden by the child schema.
2503 */
2504 Assert(def->raw_default == NULL);
2505 if (def->cooked_default == NULL)
2506 def->cooked_default = this_default;
2507 else if (!equal(def->cooked_default, this_default))
2508 {
2509 def->cooked_default = &bogus_marker;
2510 have_bogus_defaults = true;
2511 }
2512 }
2513
2514 /*
2515 * Now copy the CHECK constraints of this parent, adjusting attnos
2516 * using the completed newattmap map. Identically named constraints
2517 * are merged if possible, else we throw error.
2518 */
2519 if (constr && constr->num_check > 0)
2520 {
2521 ConstrCheck *check = constr->check;
2522 int i;
2523
2524 for (i = 0; i < constr->num_check; i++)
2525 {
2526 char *name = check[i].ccname;
2527 Node *expr;
2528 bool found_whole_row;
2529
2530 /* ignore if the constraint is non-inheritable */
2531 if (check[i].ccnoinherit)
2532 continue;
2533
2534 /* Adjust Vars to match new table's column numbering */
2535 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2536 1, 0,
2537 newattmap,
2538 InvalidOid, &found_whole_row);
2539
2540 /*
2541 * For the moment we have to reject whole-row variables. We
2542 * could convert them, if we knew the new table's rowtype OID,
2543 * but that hasn't been assigned yet.
2544 */
2545 if (found_whole_row)
2546 ereport(ERROR,
2547 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2548 errmsg("cannot convert whole-row table reference"),
2549 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2550 name,
2551 RelationGetRelationName(relation))));
2552
2553 /* check for duplicate */
2554 if (!MergeCheckConstraint(constraints, name, expr))
2555 {
2556 /* nope, this is a new one */
2557 CookedConstraint *cooked;
2558
2559 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
2560 cooked->contype = CONSTR_CHECK;
2561 cooked->conoid = InvalidOid; /* until created */
2562 cooked->name = pstrdup(name);
2563 cooked->attnum = 0; /* not used for constraints */
2564 cooked->expr = expr;
2565 cooked->skip_validation = false;
2566 cooked->is_local = false;
2567 cooked->inhcount = 1;
2568 cooked->is_no_inherit = false;
2569 constraints = lappend(constraints, cooked);
2570 }
2571 }
2572 }
2573
2574 free_attrmap(newattmap);
2575
2576 /*
2577 * Close the parent rel, but keep our lock on it until xact commit.
2578 * That will prevent someone else from deleting or ALTERing the parent
2579 * before the child is committed.
2580 */
2581 table_close(relation, NoLock);
2582 }
2583
2584 /*
2585 * If we had no inherited attributes, the result schema is just the
2586 * explicitly declared columns. Otherwise, we need to merge the declared
2587 * columns into the inherited schema list. Although, we never have any
2588 * explicitly declared columns if the table is a partition.
2589 */
2590 if (inhSchema != NIL)
2591 {
2592 int schema_attno = 0;
2593
2594 foreach(entry, schema)
2595 {
2596 ColumnDef *newdef = lfirst(entry);
2597 char *attributeName = newdef->colname;
2598 int exist_attno;
2599
2600 schema_attno++;
2601
2602 /*
2603 * Does it conflict with some previously inherited column?
2604 */
2605 exist_attno = findAttrByName(attributeName, inhSchema);
2606 if (exist_attno > 0)
2607 {
2608 ColumnDef *def;
2609 Oid defTypeId,
2610 newTypeId;
2611 int32 deftypmod,
2612 newtypmod;
2613 Oid defcollid,
2614 newcollid;
2615
2616 /*
2617 * Partitions have only one parent and have no column
2618 * definitions of their own, so conflict should never occur.
2619 */
2620 Assert(!is_partition);
2621
2622 /*
2623 * Yes, try to merge the two column definitions. They must
2624 * have the same type, typmod, and collation.
2625 */
2626 if (exist_attno == schema_attno)
2627 ereport(NOTICE,
2628 (errmsg("merging column \"%s\" with inherited definition",
2629 attributeName)));
2630 else
2631 ereport(NOTICE,
2632 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
2633 errdetail("User-specified column moved to the position of the inherited column.")));
2634 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2635 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2636 typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
2637 if (defTypeId != newTypeId || deftypmod != newtypmod)
2638 ereport(ERROR,
2639 (errcode(ERRCODE_DATATYPE_MISMATCH),
2640 errmsg("column \"%s\" has a type conflict",
2641 attributeName),
2642 errdetail("%s versus %s",
2643 format_type_with_typemod(defTypeId,
2644 deftypmod),
2645 format_type_with_typemod(newTypeId,
2646 newtypmod))));
2647 defcollid = GetColumnDefCollation(NULL, def, defTypeId);
2648 newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
2649 if (defcollid != newcollid)
2650 ereport(ERROR,
2651 (errcode(ERRCODE_COLLATION_MISMATCH),
2652 errmsg("column \"%s\" has a collation conflict",
2653 attributeName),
2654 errdetail("\"%s\" versus \"%s\"",
2655 get_collation_name(defcollid),
2656 get_collation_name(newcollid))));
2657
2658 /*
2659 * Identity is never inherited. The new column can have an
2660 * identity definition, so we always just take that one.
2661 */
2662 def->identity = newdef->identity;
2663
2664 /* Copy storage parameter */
2665 if (def->storage == 0)
2666 def->storage = newdef->storage;
2667 else if (newdef->storage != 0 && def->storage != newdef->storage)
2668 ereport(ERROR,
2669 (errcode(ERRCODE_DATATYPE_MISMATCH),
2670 errmsg("column \"%s\" has a storage parameter conflict",
2671 attributeName),
2672 errdetail("%s versus %s",
2673 storage_name(def->storage),
2674 storage_name(newdef->storage))));
2675
2676 /* Mark the column as locally defined */
2677 def->is_local = true;
2678 /* Merge of NOT NULL constraints = OR 'em together */
2679 def->is_not_null |= newdef->is_not_null;
2680
2681 /*
2682 * Check for conflicts related to generated columns.
2683 *
2684 * If the parent column is generated, the child column must be
2685 * unadorned and will be made a generated column. (We could
2686 * in theory allow the child column definition specifying the
2687 * exact same generation expression, but that's a bit
2688 * complicated to implement and doesn't seem very useful.) We
2689 * also check that the child column doesn't specify a default
2690 * value or identity, which matches the rules for a single
2691 * column in parse_util.c.
2692 */
2693 if (def->generated)
2694 {
2695 if (newdef->generated)
2696 ereport(ERROR,
2697 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2698 errmsg("child column \"%s\" specifies generation expression",
2699 def->colname),
2700 errhint("Omit the generation expression in the definition of the child table column to inherit the generation expression from the parent table.")));
2701 if (newdef->raw_default && !newdef->generated)
2702 ereport(ERROR,
2703 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2704 errmsg("column \"%s\" inherits from generated column but specifies default",
2705 def->colname)));
2706 if (newdef->identity)
2707 ereport(ERROR,
2708 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2709 errmsg("column \"%s\" inherits from generated column but specifies identity",
2710 def->colname)));
2711 }
2712
2713 /*
2714 * If the parent column is not generated, then take whatever
2715 * the child column definition says.
2716 */
2717 else
2718 {
2719 if (newdef->generated)
2720 def->generated = newdef->generated;
2721 }
2722
2723 /* If new def has a default, override previous default */
2724 if (newdef->raw_default != NULL)
2725 {
2726 def->raw_default = newdef->raw_default;
2727 def->cooked_default = newdef->cooked_default;
2728 }
2729 }
2730 else
2731 {
2732 /*
2733 * No, attach new column to result schema
2734 */
2735 inhSchema = lappend(inhSchema, newdef);
2736 }
2737 }
2738
2739 schema = inhSchema;
2740
2741 /*
2742 * Check that we haven't exceeded the legal # of columns after merging
2743 * in inherited columns.
2744 */
2745 if (list_length(schema) > MaxHeapAttributeNumber)
2746 ereport(ERROR,
2747 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2748 errmsg("tables can have at most %d columns",
2749 MaxHeapAttributeNumber)));
2750 }
2751
2752 /*
2753 * Now that we have the column definition list for a partition, we can
2754 * check whether the columns referenced in the column constraint specs
2755 * actually exist. Also, we merge NOT NULL and defaults into each
2756 * corresponding column definition.
2757 */
2758 if (is_partition)
2759 {
2760 foreach(entry, saved_schema)
2761 {
2762 ColumnDef *restdef = lfirst(entry);
2763 bool found = false;
2764 ListCell *l;
2765
2766 foreach(l, schema)
2767 {
2768 ColumnDef *coldef = lfirst(l);
2769
2770 if (strcmp(coldef->colname, restdef->colname) == 0)
2771 {
2772 found = true;
2773 coldef->is_not_null |= restdef->is_not_null;
2774
2775 /*
2776 * Override the parent's default value for this column
2777 * (coldef->cooked_default) with the partition's local
2778 * definition (restdef->raw_default), if there's one. It
2779 * should be physically impossible to get a cooked default
2780 * in the local definition or a raw default in the
2781 * inherited definition, but make sure they're nulls, for
2782 * future-proofing.
2783 */
2784 Assert(restdef->cooked_default == NULL);
2785 Assert(coldef->raw_default == NULL);
2786 if (restdef->raw_default)
2787 {
2788 coldef->raw_default = restdef->raw_default;
2789 coldef->cooked_default = NULL;
2790 }
2791 }
2792 }
2793
2794 /* complain for constraints on columns not in parent */
2795 if (!found)
2796 ereport(ERROR,
2797 (errcode(ERRCODE_UNDEFINED_COLUMN),
2798 errmsg("column \"%s\" does not exist",
2799 restdef->colname)));
2800 }
2801 }
2802
2803 /*
2804 * If we found any conflicting parent default values, check to make sure
2805 * they were overridden by the child.
2806 */
2807 if (have_bogus_defaults)
2808 {
2809 foreach(entry, schema)
2810 {
2811 ColumnDef *def = lfirst(entry);
2812
2813 if (def->cooked_default == &bogus_marker)
2814 {
2815 if (def->generated)
2816 ereport(ERROR,
2817 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2818 errmsg("column \"%s\" inherits conflicting generation expressions",
2819 def->colname)));
2820 else
2821 ereport(ERROR,
2822 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2823 errmsg("column \"%s\" inherits conflicting default values",
2824 def->colname),
2825 errhint("To resolve the conflict, specify a default explicitly.")));
2826 }
2827 }
2828 }
2829
2830 *supconstr = constraints;
2831 return schema;
2832 }
2833
2834
2835 /*
2836 * MergeCheckConstraint
2837 * Try to merge an inherited CHECK constraint with previous ones
2838 *
2839 * If we inherit identically-named constraints from multiple parents, we must
2840 * merge them, or throw an error if they don't have identical definitions.
2841 *
2842 * constraints is a list of CookedConstraint structs for previous constraints.
2843 *
2844 * Returns true if merged (constraint is a duplicate), or false if it's
2845 * got a so-far-unique name, or throws error if conflict.
2846 */
2847 static bool
MergeCheckConstraint(List * constraints,char * name,Node * expr)2848 MergeCheckConstraint(List *constraints, char *name, Node *expr)
2849 {
2850 ListCell *lc;
2851
2852 foreach(lc, constraints)
2853 {
2854 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
2855
2856 Assert(ccon->contype == CONSTR_CHECK);
2857
2858 /* Non-matching names never conflict */
2859 if (strcmp(ccon->name, name) != 0)
2860 continue;
2861
2862 if (equal(expr, ccon->expr))
2863 {
2864 /* OK to merge */
2865 ccon->inhcount++;
2866 return true;
2867 }
2868
2869 ereport(ERROR,
2870 (errcode(ERRCODE_DUPLICATE_OBJECT),
2871 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
2872 name)));
2873 }
2874
2875 return false;
2876 }
2877
2878
2879 /*
2880 * StoreCatalogInheritance
2881 * Updates the system catalogs with proper inheritance information.
2882 *
2883 * supers is a list of the OIDs of the new relation's direct ancestors.
2884 */
2885 static void
StoreCatalogInheritance(Oid relationId,List * supers,bool child_is_partition)2886 StoreCatalogInheritance(Oid relationId, List *supers,
2887 bool child_is_partition)
2888 {
2889 Relation relation;
2890 int32 seqNumber;
2891 ListCell *entry;
2892
2893 /*
2894 * sanity checks
2895 */
2896 AssertArg(OidIsValid(relationId));
2897
2898 if (supers == NIL)
2899 return;
2900
2901 /*
2902 * Store INHERITS information in pg_inherits using direct ancestors only.
2903 * Also enter dependencies on the direct ancestors, and make sure they are
2904 * marked with relhassubclass = true.
2905 *
2906 * (Once upon a time, both direct and indirect ancestors were found here
2907 * and then entered into pg_ipl. Since that catalog doesn't exist
2908 * anymore, there's no need to look for indirect ancestors.)
2909 */
2910 relation = table_open(InheritsRelationId, RowExclusiveLock);
2911
2912 seqNumber = 1;
2913 foreach(entry, supers)
2914 {
2915 Oid parentOid = lfirst_oid(entry);
2916
2917 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
2918 child_is_partition);
2919 seqNumber++;
2920 }
2921
2922 table_close(relation, RowExclusiveLock);
2923 }
2924
2925 /*
2926 * Make catalog entries showing relationId as being an inheritance child
2927 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
2928 */
2929 static void
StoreCatalogInheritance1(Oid relationId,Oid parentOid,int32 seqNumber,Relation inhRelation,bool child_is_partition)2930 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
2931 int32 seqNumber, Relation inhRelation,
2932 bool child_is_partition)
2933 {
2934 ObjectAddress childobject,
2935 parentobject;
2936
2937 /* store the pg_inherits row */
2938 StoreSingleInheritance(relationId, parentOid, seqNumber);
2939
2940 /*
2941 * Store a dependency too
2942 */
2943 parentobject.classId = RelationRelationId;
2944 parentobject.objectId = parentOid;
2945 parentobject.objectSubId = 0;
2946 childobject.classId = RelationRelationId;
2947 childobject.objectId = relationId;
2948 childobject.objectSubId = 0;
2949
2950 recordDependencyOn(&childobject, &parentobject,
2951 child_dependency_type(child_is_partition));
2952
2953 /*
2954 * Post creation hook of this inheritance. Since object_access_hook
2955 * doesn't take multiple object identifiers, we relay oid of parent
2956 * relation using auxiliary_id argument.
2957 */
2958 InvokeObjectPostAlterHookArg(InheritsRelationId,
2959 relationId, 0,
2960 parentOid, false);
2961
2962 /*
2963 * Mark the parent as having subclasses.
2964 */
2965 SetRelationHasSubclass(parentOid, true);
2966 }
2967
2968 /*
2969 * Look for an existing schema entry with the given name.
2970 *
2971 * Returns the index (starting with 1) if attribute already exists in schema,
2972 * 0 if it doesn't.
2973 */
2974 static int
findAttrByName(const char * attributeName,List * schema)2975 findAttrByName(const char *attributeName, List *schema)
2976 {
2977 ListCell *s;
2978 int i = 1;
2979
2980 foreach(s, schema)
2981 {
2982 ColumnDef *def = lfirst(s);
2983
2984 if (strcmp(attributeName, def->colname) == 0)
2985 return i;
2986
2987 i++;
2988 }
2989 return 0;
2990 }
2991
2992
2993 /*
2994 * SetRelationHasSubclass
2995 * Set the value of the relation's relhassubclass field in pg_class.
2996 *
2997 * NOTE: caller must be holding an appropriate lock on the relation.
2998 * ShareUpdateExclusiveLock is sufficient.
2999 *
3000 * NOTE: an important side-effect of this operation is that an SI invalidation
3001 * message is sent out to all backends --- including me --- causing plans
3002 * referencing the relation to be rebuilt with the new list of children.
3003 * This must happen even if we find that no change is needed in the pg_class
3004 * row.
3005 */
3006 void
SetRelationHasSubclass(Oid relationId,bool relhassubclass)3007 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3008 {
3009 Relation relationRelation;
3010 HeapTuple tuple;
3011 Form_pg_class classtuple;
3012
3013 /*
3014 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3015 */
3016 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3017 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3018 if (!HeapTupleIsValid(tuple))
3019 elog(ERROR, "cache lookup failed for relation %u", relationId);
3020 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3021
3022 if (classtuple->relhassubclass != relhassubclass)
3023 {
3024 classtuple->relhassubclass = relhassubclass;
3025 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3026 }
3027 else
3028 {
3029 /* no need to change tuple, but force relcache rebuild anyway */
3030 CacheInvalidateRelcacheByTuple(tuple);
3031 }
3032
3033 heap_freetuple(tuple);
3034 table_close(relationRelation, RowExclusiveLock);
3035 }
3036
3037 /*
3038 * renameatt_check - basic sanity checks before attribute rename
3039 */
3040 static void
renameatt_check(Oid myrelid,Form_pg_class classform,bool recursing)3041 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3042 {
3043 char relkind = classform->relkind;
3044
3045 if (classform->reloftype && !recursing)
3046 ereport(ERROR,
3047 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3048 errmsg("cannot rename column of typed table")));
3049
3050 /*
3051 * Renaming the columns of sequences or toast tables doesn't actually
3052 * break anything from the system's point of view, since internal
3053 * references are by attnum. But it doesn't seem right to allow users to
3054 * change names that are hardcoded into the system, hence the following
3055 * restriction.
3056 */
3057 if (relkind != RELKIND_RELATION &&
3058 relkind != RELKIND_VIEW &&
3059 relkind != RELKIND_MATVIEW &&
3060 relkind != RELKIND_COMPOSITE_TYPE &&
3061 relkind != RELKIND_INDEX &&
3062 relkind != RELKIND_PARTITIONED_INDEX &&
3063 relkind != RELKIND_FOREIGN_TABLE &&
3064 relkind != RELKIND_PARTITIONED_TABLE)
3065 ereport(ERROR,
3066 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3067 errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table",
3068 NameStr(classform->relname))));
3069
3070 /*
3071 * permissions checking. only the owner of a class can change its schema.
3072 */
3073 if (!pg_class_ownercheck(myrelid, GetUserId()))
3074 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3075 NameStr(classform->relname));
3076 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3077 ereport(ERROR,
3078 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3079 errmsg("permission denied: \"%s\" is a system catalog",
3080 NameStr(classform->relname))));
3081 }
3082
3083 /*
3084 * renameatt_internal - workhorse for renameatt
3085 *
3086 * Return value is the attribute number in the 'myrelid' relation.
3087 */
3088 static AttrNumber
renameatt_internal(Oid myrelid,const char * oldattname,const char * newattname,bool recurse,bool recursing,int expected_parents,DropBehavior behavior)3089 renameatt_internal(Oid myrelid,
3090 const char *oldattname,
3091 const char *newattname,
3092 bool recurse,
3093 bool recursing,
3094 int expected_parents,
3095 DropBehavior behavior)
3096 {
3097 Relation targetrelation;
3098 Relation attrelation;
3099 HeapTuple atttup;
3100 Form_pg_attribute attform;
3101 AttrNumber attnum;
3102
3103 /*
3104 * Grab an exclusive lock on the target table, which we will NOT release
3105 * until end of transaction.
3106 */
3107 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3108 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3109
3110 /*
3111 * if the 'recurse' flag is set then we are supposed to rename this
3112 * attribute in all classes that inherit from 'relname' (as well as in
3113 * 'relname').
3114 *
3115 * any permissions or problems with duplicate attributes will cause the
3116 * whole transaction to abort, which is what we want -- all or nothing.
3117 */
3118 if (recurse)
3119 {
3120 List *child_oids,
3121 *child_numparents;
3122 ListCell *lo,
3123 *li;
3124
3125 /*
3126 * we need the number of parents for each child so that the recursive
3127 * calls to renameatt() can determine whether there are any parents
3128 * outside the inheritance hierarchy being processed.
3129 */
3130 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3131 &child_numparents);
3132
3133 /*
3134 * find_all_inheritors does the recursive search of the inheritance
3135 * hierarchy, so all we have to do is process all of the relids in the
3136 * list that it returns.
3137 */
3138 forboth(lo, child_oids, li, child_numparents)
3139 {
3140 Oid childrelid = lfirst_oid(lo);
3141 int numparents = lfirst_int(li);
3142
3143 if (childrelid == myrelid)
3144 continue;
3145 /* note we need not recurse again */
3146 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3147 }
3148 }
3149 else
3150 {
3151 /*
3152 * If we are told not to recurse, there had better not be any child
3153 * tables; else the rename would put them out of step.
3154 *
3155 * expected_parents will only be 0 if we are not already recursing.
3156 */
3157 if (expected_parents == 0 &&
3158 find_inheritance_children(myrelid, NoLock) != NIL)
3159 ereport(ERROR,
3160 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3161 errmsg("inherited column \"%s\" must be renamed in child tables too",
3162 oldattname)));
3163 }
3164
3165 /* rename attributes in typed tables of composite type */
3166 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3167 {
3168 List *child_oids;
3169 ListCell *lo;
3170
3171 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3172 RelationGetRelationName(targetrelation),
3173 behavior);
3174
3175 foreach(lo, child_oids)
3176 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3177 }
3178
3179 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3180
3181 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3182 if (!HeapTupleIsValid(atttup))
3183 ereport(ERROR,
3184 (errcode(ERRCODE_UNDEFINED_COLUMN),
3185 errmsg("column \"%s\" does not exist",
3186 oldattname)));
3187 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3188
3189 attnum = attform->attnum;
3190 if (attnum <= 0)
3191 ereport(ERROR,
3192 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3193 errmsg("cannot rename system column \"%s\"",
3194 oldattname)));
3195
3196 /*
3197 * if the attribute is inherited, forbid the renaming. if this is a
3198 * top-level call to renameatt(), then expected_parents will be 0, so the
3199 * effect of this code will be to prohibit the renaming if the attribute
3200 * is inherited at all. if this is a recursive call to renameatt(),
3201 * expected_parents will be the number of parents the current relation has
3202 * within the inheritance hierarchy being processed, so we'll prohibit the
3203 * renaming only if there are additional parents from elsewhere.
3204 */
3205 if (attform->attinhcount > expected_parents)
3206 ereport(ERROR,
3207 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3208 errmsg("cannot rename inherited column \"%s\"",
3209 oldattname)));
3210
3211 /* new name should not already exist */
3212 (void) check_for_column_name_collision(targetrelation, newattname, false);
3213
3214 /* apply the update */
3215 namestrcpy(&(attform->attname), newattname);
3216
3217 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3218
3219 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3220
3221 heap_freetuple(atttup);
3222
3223 table_close(attrelation, RowExclusiveLock);
3224
3225 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3226
3227 return attnum;
3228 }
3229
3230 /*
3231 * Perform permissions and integrity checks before acquiring a relation lock.
3232 */
3233 static void
RangeVarCallbackForRenameAttribute(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)3234 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3235 void *arg)
3236 {
3237 HeapTuple tuple;
3238 Form_pg_class form;
3239
3240 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3241 if (!HeapTupleIsValid(tuple))
3242 return; /* concurrently dropped */
3243 form = (Form_pg_class) GETSTRUCT(tuple);
3244 renameatt_check(relid, form, false);
3245 ReleaseSysCache(tuple);
3246 }
3247
3248 /*
3249 * renameatt - changes the name of an attribute in a relation
3250 *
3251 * The returned ObjectAddress is that of the renamed column.
3252 */
3253 ObjectAddress
renameatt(RenameStmt * stmt)3254 renameatt(RenameStmt *stmt)
3255 {
3256 Oid relid;
3257 AttrNumber attnum;
3258 ObjectAddress address;
3259
3260 /* lock level taken here should match renameatt_internal */
3261 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3262 stmt->missing_ok ? RVR_MISSING_OK : 0,
3263 RangeVarCallbackForRenameAttribute,
3264 NULL);
3265
3266 if (!OidIsValid(relid))
3267 {
3268 ereport(NOTICE,
3269 (errmsg("relation \"%s\" does not exist, skipping",
3270 stmt->relation->relname)));
3271 return InvalidObjectAddress;
3272 }
3273
3274 attnum =
3275 renameatt_internal(relid,
3276 stmt->subname, /* old att name */
3277 stmt->newname, /* new att name */
3278 stmt->relation->inh, /* recursive? */
3279 false, /* recursing? */
3280 0, /* expected inhcount */
3281 stmt->behavior);
3282
3283 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3284
3285 return address;
3286 }
3287
3288 /*
3289 * same logic as renameatt_internal
3290 */
3291 static ObjectAddress
rename_constraint_internal(Oid myrelid,Oid mytypid,const char * oldconname,const char * newconname,bool recurse,bool recursing,int expected_parents)3292 rename_constraint_internal(Oid myrelid,
3293 Oid mytypid,
3294 const char *oldconname,
3295 const char *newconname,
3296 bool recurse,
3297 bool recursing,
3298 int expected_parents)
3299 {
3300 Relation targetrelation = NULL;
3301 Oid constraintOid;
3302 HeapTuple tuple;
3303 Form_pg_constraint con;
3304 ObjectAddress address;
3305
3306 AssertArg(!myrelid || !mytypid);
3307
3308 if (mytypid)
3309 {
3310 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3311 }
3312 else
3313 {
3314 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3315
3316 /*
3317 * don't tell it whether we're recursing; we allow changing typed
3318 * tables here
3319 */
3320 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3321
3322 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3323 }
3324
3325 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3326 if (!HeapTupleIsValid(tuple))
3327 elog(ERROR, "cache lookup failed for constraint %u",
3328 constraintOid);
3329 con = (Form_pg_constraint) GETSTRUCT(tuple);
3330
3331 if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
3332 {
3333 if (recurse)
3334 {
3335 List *child_oids,
3336 *child_numparents;
3337 ListCell *lo,
3338 *li;
3339
3340 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3341 &child_numparents);
3342
3343 forboth(lo, child_oids, li, child_numparents)
3344 {
3345 Oid childrelid = lfirst_oid(lo);
3346 int numparents = lfirst_int(li);
3347
3348 if (childrelid == myrelid)
3349 continue;
3350
3351 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3352 }
3353 }
3354 else
3355 {
3356 if (expected_parents == 0 &&
3357 find_inheritance_children(myrelid, NoLock) != NIL)
3358 ereport(ERROR,
3359 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3360 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3361 oldconname)));
3362 }
3363
3364 if (con->coninhcount > expected_parents)
3365 ereport(ERROR,
3366 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3367 errmsg("cannot rename inherited constraint \"%s\"",
3368 oldconname)));
3369 }
3370
3371 if (con->conindid
3372 && (con->contype == CONSTRAINT_PRIMARY
3373 || con->contype == CONSTRAINT_UNIQUE
3374 || con->contype == CONSTRAINT_EXCLUSION))
3375 /* rename the index; this renames the constraint as well */
3376 RenameRelationInternal(con->conindid, newconname, false, true);
3377 else
3378 RenameConstraintById(constraintOid, newconname);
3379
3380 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
3381
3382 ReleaseSysCache(tuple);
3383
3384 if (targetrelation)
3385 {
3386 /*
3387 * Invalidate relcache so as others can see the new constraint name.
3388 */
3389 CacheInvalidateRelcache(targetrelation);
3390
3391 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3392 }
3393
3394 return address;
3395 }
3396
3397 ObjectAddress
RenameConstraint(RenameStmt * stmt)3398 RenameConstraint(RenameStmt *stmt)
3399 {
3400 Oid relid = InvalidOid;
3401 Oid typid = InvalidOid;
3402
3403 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
3404 {
3405 Relation rel;
3406 HeapTuple tup;
3407
3408 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
3409 rel = table_open(TypeRelationId, RowExclusiveLock);
3410 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
3411 if (!HeapTupleIsValid(tup))
3412 elog(ERROR, "cache lookup failed for type %u", typid);
3413 checkDomainOwner(tup);
3414 ReleaseSysCache(tup);
3415 table_close(rel, NoLock);
3416 }
3417 else
3418 {
3419 /* lock level taken here should match rename_constraint_internal */
3420 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3421 stmt->missing_ok ? RVR_MISSING_OK : 0,
3422 RangeVarCallbackForRenameAttribute,
3423 NULL);
3424 if (!OidIsValid(relid))
3425 {
3426 ereport(NOTICE,
3427 (errmsg("relation \"%s\" does not exist, skipping",
3428 stmt->relation->relname)));
3429 return InvalidObjectAddress;
3430 }
3431 }
3432
3433 return
3434 rename_constraint_internal(relid, typid,
3435 stmt->subname,
3436 stmt->newname,
3437 (stmt->relation &&
3438 stmt->relation->inh), /* recursive? */
3439 false, /* recursing? */
3440 0 /* expected inhcount */ );
3441
3442 }
3443
3444 /*
3445 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
3446 * RENAME
3447 */
3448 ObjectAddress
RenameRelation(RenameStmt * stmt)3449 RenameRelation(RenameStmt *stmt)
3450 {
3451 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
3452 Oid relid;
3453 ObjectAddress address;
3454
3455 /*
3456 * Grab an exclusive lock on the target table, index, sequence, view,
3457 * materialized view, or foreign table, which we will NOT release until
3458 * end of transaction.
3459 *
3460 * Lock level used here should match RenameRelationInternal, to avoid lock
3461 * escalation. However, because ALTER INDEX can be used with any relation
3462 * type, we mustn't believe without verification.
3463 */
3464 for (;;)
3465 {
3466 LOCKMODE lockmode;
3467 char relkind;
3468 bool obj_is_index;
3469
3470 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
3471
3472 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
3473 stmt->missing_ok ? RVR_MISSING_OK : 0,
3474 RangeVarCallbackForAlterRelation,
3475 (void *) stmt);
3476
3477 if (!OidIsValid(relid))
3478 {
3479 ereport(NOTICE,
3480 (errmsg("relation \"%s\" does not exist, skipping",
3481 stmt->relation->relname)));
3482 return InvalidObjectAddress;
3483 }
3484
3485 /*
3486 * We allow mismatched statement and object types (e.g., ALTER INDEX
3487 * to rename a table), but we might've used the wrong lock level. If
3488 * that happens, retry with the correct lock level. We don't bother
3489 * if we already acquired AccessExclusiveLock with an index, however.
3490 */
3491 relkind = get_rel_relkind(relid);
3492 obj_is_index = (relkind == RELKIND_INDEX ||
3493 relkind == RELKIND_PARTITIONED_INDEX);
3494 if (obj_is_index || is_index_stmt == obj_is_index)
3495 break;
3496
3497 UnlockRelationOid(relid, lockmode);
3498 is_index_stmt = obj_is_index;
3499 }
3500
3501 /* Do the work */
3502 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
3503
3504 ObjectAddressSet(address, RelationRelationId, relid);
3505
3506 return address;
3507 }
3508
3509 /*
3510 * RenameRelationInternal - change the name of a relation
3511 */
3512 void
RenameRelationInternal(Oid myrelid,const char * newrelname,bool is_internal,bool is_index)3513 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
3514 {
3515 Relation targetrelation;
3516 Relation relrelation; /* for RELATION relation */
3517 HeapTuple reltup;
3518 Form_pg_class relform;
3519 Oid namespaceId;
3520
3521 /*
3522 * Grab a lock on the target relation, which we will NOT release until end
3523 * of transaction. We need at least a self-exclusive lock so that
3524 * concurrent DDL doesn't overwrite the rename if they start updating
3525 * while still seeing the old version. The lock also guards against
3526 * triggering relcache reloads in concurrent sessions, which might not
3527 * handle this information changing under them. For indexes, we can use a
3528 * reduced lock level because RelationReloadIndexInfo() handles indexes
3529 * specially.
3530 */
3531 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
3532 namespaceId = RelationGetNamespace(targetrelation);
3533
3534 /*
3535 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
3536 */
3537 relrelation = table_open(RelationRelationId, RowExclusiveLock);
3538
3539 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
3540 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
3541 elog(ERROR, "cache lookup failed for relation %u", myrelid);
3542 relform = (Form_pg_class) GETSTRUCT(reltup);
3543
3544 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
3545 ereport(ERROR,
3546 (errcode(ERRCODE_DUPLICATE_TABLE),
3547 errmsg("relation \"%s\" already exists",
3548 newrelname)));
3549
3550 /*
3551 * RenameRelation is careful not to believe the caller's idea of the
3552 * relation kind being handled. We don't have to worry about this, but
3553 * let's not be totally oblivious to it. We can process an index as
3554 * not-an-index, but not the other way around.
3555 */
3556 Assert(!is_index ||
3557 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
3558 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
3559
3560 /*
3561 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
3562 * because it's a copy...)
3563 */
3564 namestrcpy(&(relform->relname), newrelname);
3565
3566 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
3567
3568 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
3569 InvalidOid, is_internal);
3570
3571 heap_freetuple(reltup);
3572 table_close(relrelation, RowExclusiveLock);
3573
3574 /*
3575 * Also rename the associated type, if any.
3576 */
3577 if (OidIsValid(targetrelation->rd_rel->reltype))
3578 RenameTypeInternal(targetrelation->rd_rel->reltype,
3579 newrelname, namespaceId);
3580
3581 /*
3582 * Also rename the associated constraint, if any.
3583 */
3584 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
3585 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
3586 {
3587 Oid constraintId = get_index_constraint(myrelid);
3588
3589 if (OidIsValid(constraintId))
3590 RenameConstraintById(constraintId, newrelname);
3591 }
3592
3593 /*
3594 * Close rel, but keep lock!
3595 */
3596 relation_close(targetrelation, NoLock);
3597 }
3598
3599 /*
3600 * ResetRelRewrite - reset relrewrite
3601 */
3602 void
ResetRelRewrite(Oid myrelid)3603 ResetRelRewrite(Oid myrelid)
3604 {
3605 Relation relrelation; /* for RELATION relation */
3606 HeapTuple reltup;
3607 Form_pg_class relform;
3608
3609 /*
3610 * Find relation's pg_class tuple.
3611 */
3612 relrelation = table_open(RelationRelationId, RowExclusiveLock);
3613
3614 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
3615 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
3616 elog(ERROR, "cache lookup failed for relation %u", myrelid);
3617 relform = (Form_pg_class) GETSTRUCT(reltup);
3618
3619 /*
3620 * Update pg_class tuple.
3621 */
3622 relform->relrewrite = InvalidOid;
3623
3624 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
3625
3626 heap_freetuple(reltup);
3627 table_close(relrelation, RowExclusiveLock);
3628 }
3629
3630 /*
3631 * Disallow ALTER TABLE (and similar commands) when the current backend has
3632 * any open reference to the target table besides the one just acquired by
3633 * the calling command; this implies there's an open cursor or active plan.
3634 * We need this check because our lock doesn't protect us against stomping
3635 * on our own foot, only other people's feet!
3636 *
3637 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
3638 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
3639 * possibly be relaxed to only error out for certain types of alterations.
3640 * But the use-case for allowing any of these things is not obvious, so we
3641 * won't work hard at it for now.
3642 *
3643 * We also reject these commands if there are any pending AFTER trigger events
3644 * for the rel. This is certainly necessary for the rewriting variants of
3645 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
3646 * events would try to fetch the wrong tuples. It might be overly cautious
3647 * in other cases, but again it seems better to err on the side of paranoia.
3648 *
3649 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
3650 * we are worried about active indexscans on the index. The trigger-event
3651 * check can be skipped, since we are doing no damage to the parent table.
3652 *
3653 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
3654 */
3655 void
CheckTableNotInUse(Relation rel,const char * stmt)3656 CheckTableNotInUse(Relation rel, const char *stmt)
3657 {
3658 int expected_refcnt;
3659
3660 expected_refcnt = rel->rd_isnailed ? 2 : 1;
3661 if (rel->rd_refcnt != expected_refcnt)
3662 ereport(ERROR,
3663 (errcode(ERRCODE_OBJECT_IN_USE),
3664 /* translator: first %s is a SQL command, eg ALTER TABLE */
3665 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
3666 stmt, RelationGetRelationName(rel))));
3667
3668 if (rel->rd_rel->relkind != RELKIND_INDEX &&
3669 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
3670 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
3671 ereport(ERROR,
3672 (errcode(ERRCODE_OBJECT_IN_USE),
3673 /* translator: first %s is a SQL command, eg ALTER TABLE */
3674 errmsg("cannot %s \"%s\" because it has pending trigger events",
3675 stmt, RelationGetRelationName(rel))));
3676 }
3677
3678 /*
3679 * AlterTableLookupRelation
3680 * Look up, and lock, the OID for the relation named by an alter table
3681 * statement.
3682 */
3683 Oid
AlterTableLookupRelation(AlterTableStmt * stmt,LOCKMODE lockmode)3684 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
3685 {
3686 return RangeVarGetRelidExtended(stmt->relation, lockmode,
3687 stmt->missing_ok ? RVR_MISSING_OK : 0,
3688 RangeVarCallbackForAlterRelation,
3689 (void *) stmt);
3690 }
3691
3692 /*
3693 * AlterTable
3694 * Execute ALTER TABLE, which can be a list of subcommands
3695 *
3696 * ALTER TABLE is performed in three phases:
3697 * 1. Examine subcommands and perform pre-transformation checking.
3698 * 2. Validate and transform subcommands, and update system catalogs.
3699 * 3. Scan table(s) to check new constraints, and optionally recopy
3700 * the data into new table(s).
3701 * Phase 3 is not performed unless one or more of the subcommands requires
3702 * it. The intention of this design is to allow multiple independent
3703 * updates of the table schema to be performed with only one pass over the
3704 * data.
3705 *
3706 * ATPrepCmd performs phase 1. A "work queue" entry is created for
3707 * each table to be affected (there may be multiple affected tables if the
3708 * commands traverse a table inheritance hierarchy). Also we do preliminary
3709 * validation of the subcommands. Because earlier subcommands may change
3710 * the catalog state seen by later commands, there are limits to what can
3711 * be done in this phase. Generally, this phase acquires table locks,
3712 * checks permissions and relkind, and recurses to find child tables.
3713 *
3714 * ATRewriteCatalogs performs phase 2 for each affected table. (Note that
3715 * phases 2 and 3 normally do no explicit recursion, since phase 1 already
3716 * did it --- although some subcommands have to recurse in phase 2 instead.)
3717 * Certain subcommands need to be performed before others to avoid
3718 * unnecessary conflicts; for example, DROP COLUMN should come before
3719 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
3720 * lists, one for each logical "pass" of phase 2.
3721 *
3722 * ATRewriteTables performs phase 3 for those tables that need it.
3723 *
3724 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
3725 * the whole operation; we don't have to do anything special to clean up.
3726 *
3727 * The caller must lock the relation, with an appropriate lock level
3728 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
3729 * or higher. We pass the lock level down
3730 * so that we can apply it recursively to inherited tables. Note that the
3731 * lock level we want as we recurse might well be higher than required for
3732 * that specific subcommand. So we pass down the overall lock requirement,
3733 * rather than reassess it at lower levels.
3734 *
3735 * The caller also provides a "context" which is to be passed back to
3736 * utility.c when we need to execute a subcommand such as CREATE INDEX.
3737 * Some of the fields therein, such as the relid, are used here as well.
3738 */
3739 void
AlterTable(AlterTableStmt * stmt,LOCKMODE lockmode,AlterTableUtilityContext * context)3740 AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
3741 AlterTableUtilityContext *context)
3742 {
3743 Relation rel;
3744
3745 /* Caller is required to provide an adequate lock. */
3746 rel = relation_open(context->relid, NoLock);
3747
3748 CheckTableNotInUse(rel, "ALTER TABLE");
3749
3750 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
3751 }
3752
3753 /*
3754 * AlterTableInternal
3755 *
3756 * ALTER TABLE with target specified by OID
3757 *
3758 * We do not reject if the relation is already open, because it's quite
3759 * likely that one or more layers of caller have it open. That means it
3760 * is unsafe to use this entry point for alterations that could break
3761 * existing query plans. On the assumption it's not used for such, we
3762 * don't have to reject pending AFTER triggers, either.
3763 *
3764 * Also, since we don't have an AlterTableUtilityContext, this cannot be
3765 * used for any subcommand types that require parse transformation or
3766 * could generate subcommands that have to be passed to ProcessUtility.
3767 */
3768 void
AlterTableInternal(Oid relid,List * cmds,bool recurse)3769 AlterTableInternal(Oid relid, List *cmds, bool recurse)
3770 {
3771 Relation rel;
3772 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
3773
3774 rel = relation_open(relid, lockmode);
3775
3776 EventTriggerAlterTableRelid(relid);
3777
3778 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
3779 }
3780
3781 /*
3782 * AlterTableGetLockLevel
3783 *
3784 * Sets the overall lock level required for the supplied list of subcommands.
3785 * Policy for doing this set according to needs of AlterTable(), see
3786 * comments there for overall explanation.
3787 *
3788 * Function is called before and after parsing, so it must give same
3789 * answer each time it is called. Some subcommands are transformed
3790 * into other subcommand types, so the transform must never be made to a
3791 * lower lock level than previously assigned. All transforms are noted below.
3792 *
3793 * Since this is called before we lock the table we cannot use table metadata
3794 * to influence the type of lock we acquire.
3795 *
3796 * There should be no lockmodes hardcoded into the subcommand functions. All
3797 * lockmode decisions for ALTER TABLE are made here only. The one exception is
3798 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
3799 * and does not travel through this section of code and cannot be combined with
3800 * any of the subcommands given here.
3801 *
3802 * Note that Hot Standby only knows about AccessExclusiveLocks on the master
3803 * so any changes that might affect SELECTs running on standbys need to use
3804 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
3805 * have a solution for that also.
3806 *
3807 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
3808 * that takes a lock less than AccessExclusiveLock can change object definitions
3809 * while pg_dump is running. Be careful to check that the appropriate data is
3810 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
3811 * otherwise we might end up with an inconsistent dump that can't restore.
3812 */
3813 LOCKMODE
AlterTableGetLockLevel(List * cmds)3814 AlterTableGetLockLevel(List *cmds)
3815 {
3816 /*
3817 * This only works if we read catalog tables using MVCC snapshots.
3818 */
3819 ListCell *lcmd;
3820 LOCKMODE lockmode = ShareUpdateExclusiveLock;
3821
3822 foreach(lcmd, cmds)
3823 {
3824 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3825 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
3826
3827 switch (cmd->subtype)
3828 {
3829 /*
3830 * These subcommands rewrite the heap, so require full locks.
3831 */
3832 case AT_AddColumn: /* may rewrite heap, in some cases and visible
3833 * to SELECT */
3834 case AT_SetTableSpace: /* must rewrite heap */
3835 case AT_AlterColumnType: /* must rewrite heap */
3836 cmd_lockmode = AccessExclusiveLock;
3837 break;
3838
3839 /*
3840 * These subcommands may require addition of toast tables. If
3841 * we add a toast table to a table currently being scanned, we
3842 * might miss data added to the new toast table by concurrent
3843 * insert transactions.
3844 */
3845 case AT_SetStorage: /* may add toast tables, see
3846 * ATRewriteCatalogs() */
3847 cmd_lockmode = AccessExclusiveLock;
3848 break;
3849
3850 /*
3851 * Removing constraints can affect SELECTs that have been
3852 * optimized assuming the constraint holds true. See also
3853 * CloneFkReferenced.
3854 */
3855 case AT_DropConstraint: /* as DROP INDEX */
3856 case AT_DropNotNull: /* may change some SQL plans */
3857 cmd_lockmode = AccessExclusiveLock;
3858 break;
3859
3860 /*
3861 * Subcommands that may be visible to concurrent SELECTs
3862 */
3863 case AT_DropColumn: /* change visible to SELECT */
3864 case AT_AddColumnToView: /* CREATE VIEW */
3865 case AT_DropOids: /* used to equiv to DropColumn */
3866 case AT_EnableAlwaysRule: /* may change SELECT rules */
3867 case AT_EnableReplicaRule: /* may change SELECT rules */
3868 case AT_EnableRule: /* may change SELECT rules */
3869 case AT_DisableRule: /* may change SELECT rules */
3870 cmd_lockmode = AccessExclusiveLock;
3871 break;
3872
3873 /*
3874 * Changing owner may remove implicit SELECT privileges
3875 */
3876 case AT_ChangeOwner: /* change visible to SELECT */
3877 cmd_lockmode = AccessExclusiveLock;
3878 break;
3879
3880 /*
3881 * Changing foreign table options may affect optimization.
3882 */
3883 case AT_GenericOptions:
3884 case AT_AlterColumnGenericOptions:
3885 cmd_lockmode = AccessExclusiveLock;
3886 break;
3887
3888 /*
3889 * These subcommands affect write operations only.
3890 */
3891 case AT_EnableTrig:
3892 case AT_EnableAlwaysTrig:
3893 case AT_EnableReplicaTrig:
3894 case AT_EnableTrigAll:
3895 case AT_EnableTrigUser:
3896 case AT_DisableTrig:
3897 case AT_DisableTrigAll:
3898 case AT_DisableTrigUser:
3899 cmd_lockmode = ShareRowExclusiveLock;
3900 break;
3901
3902 /*
3903 * These subcommands affect write operations only. XXX
3904 * Theoretically, these could be ShareRowExclusiveLock.
3905 */
3906 case AT_ColumnDefault:
3907 case AT_CookedColumnDefault:
3908 case AT_AlterConstraint:
3909 case AT_AddIndex: /* from ADD CONSTRAINT */
3910 case AT_AddIndexConstraint:
3911 case AT_ReplicaIdentity:
3912 case AT_SetNotNull:
3913 case AT_EnableRowSecurity:
3914 case AT_DisableRowSecurity:
3915 case AT_ForceRowSecurity:
3916 case AT_NoForceRowSecurity:
3917 case AT_AddIdentity:
3918 case AT_DropIdentity:
3919 case AT_SetIdentity:
3920 case AT_DropExpression:
3921 cmd_lockmode = AccessExclusiveLock;
3922 break;
3923
3924 case AT_AddConstraint:
3925 case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
3926 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
3927 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
3928 if (IsA(cmd->def, Constraint))
3929 {
3930 Constraint *con = (Constraint *) cmd->def;
3931
3932 switch (con->contype)
3933 {
3934 case CONSTR_EXCLUSION:
3935 case CONSTR_PRIMARY:
3936 case CONSTR_UNIQUE:
3937
3938 /*
3939 * Cases essentially the same as CREATE INDEX. We
3940 * could reduce the lock strength to ShareLock if
3941 * we can work out how to allow concurrent catalog
3942 * updates. XXX Might be set down to
3943 * ShareRowExclusiveLock but requires further
3944 * analysis.
3945 */
3946 cmd_lockmode = AccessExclusiveLock;
3947 break;
3948 case CONSTR_FOREIGN:
3949
3950 /*
3951 * We add triggers to both tables when we add a
3952 * Foreign Key, so the lock level must be at least
3953 * as strong as CREATE TRIGGER.
3954 */
3955 cmd_lockmode = ShareRowExclusiveLock;
3956 break;
3957
3958 default:
3959 cmd_lockmode = AccessExclusiveLock;
3960 }
3961 }
3962 break;
3963
3964 /*
3965 * These subcommands affect inheritance behaviour. Queries
3966 * started before us will continue to see the old inheritance
3967 * behaviour, while queries started after we commit will see
3968 * new behaviour. No need to prevent reads or writes to the
3969 * subtable while we hook it up though. Changing the TupDesc
3970 * may be a problem, so keep highest lock.
3971 */
3972 case AT_AddInherit:
3973 case AT_DropInherit:
3974 cmd_lockmode = AccessExclusiveLock;
3975 break;
3976
3977 /*
3978 * These subcommands affect implicit row type conversion. They
3979 * have affects similar to CREATE/DROP CAST on queries. don't
3980 * provide for invalidating parse trees as a result of such
3981 * changes, so we keep these at AccessExclusiveLock.
3982 */
3983 case AT_AddOf:
3984 case AT_DropOf:
3985 cmd_lockmode = AccessExclusiveLock;
3986 break;
3987
3988 /*
3989 * Only used by CREATE OR REPLACE VIEW which must conflict
3990 * with an SELECTs currently using the view.
3991 */
3992 case AT_ReplaceRelOptions:
3993 cmd_lockmode = AccessExclusiveLock;
3994 break;
3995
3996 /*
3997 * These subcommands affect general strategies for performance
3998 * and maintenance, though don't change the semantic results
3999 * from normal data reads and writes. Delaying an ALTER TABLE
4000 * behind currently active writes only delays the point where
4001 * the new strategy begins to take effect, so there is no
4002 * benefit in waiting. In this case the minimum restriction
4003 * applies: we don't currently allow concurrent catalog
4004 * updates.
4005 */
4006 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4007 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4008 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4009 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4010 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4011 cmd_lockmode = ShareUpdateExclusiveLock;
4012 break;
4013
4014 case AT_SetLogged:
4015 case AT_SetUnLogged:
4016 cmd_lockmode = AccessExclusiveLock;
4017 break;
4018
4019 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4020 cmd_lockmode = ShareUpdateExclusiveLock;
4021 break;
4022
4023 /*
4024 * Rel options are more complex than first appears. Options
4025 * are set here for tables, views and indexes; for historical
4026 * reasons these can all be used with ALTER TABLE, so we can't
4027 * decide between them using the basic grammar.
4028 */
4029 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4030 * getTables() */
4031 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4032 * getTables() */
4033 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4034 break;
4035
4036 case AT_AttachPartition:
4037 cmd_lockmode = ShareUpdateExclusiveLock;
4038 break;
4039
4040 case AT_DetachPartition:
4041 cmd_lockmode = AccessExclusiveLock;
4042 break;
4043
4044 case AT_CheckNotNull:
4045
4046 /*
4047 * This only examines the table's schema; but lock must be
4048 * strong enough to prevent concurrent DROP NOT NULL.
4049 */
4050 cmd_lockmode = AccessShareLock;
4051 break;
4052
4053 default: /* oops */
4054 elog(ERROR, "unrecognized alter table type: %d",
4055 (int) cmd->subtype);
4056 break;
4057 }
4058
4059 /*
4060 * Take the greatest lockmode from any subcommand
4061 */
4062 if (cmd_lockmode > lockmode)
4063 lockmode = cmd_lockmode;
4064 }
4065
4066 return lockmode;
4067 }
4068
4069 /*
4070 * ATController provides top level control over the phases.
4071 *
4072 * parsetree is passed in to allow it to be passed to event triggers
4073 * when requested.
4074 */
4075 static void
ATController(AlterTableStmt * parsetree,Relation rel,List * cmds,bool recurse,LOCKMODE lockmode,AlterTableUtilityContext * context)4076 ATController(AlterTableStmt *parsetree,
4077 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4078 AlterTableUtilityContext *context)
4079 {
4080 List *wqueue = NIL;
4081 ListCell *lcmd;
4082
4083 /* Phase 1: preliminary examination of commands, create work queue */
4084 foreach(lcmd, cmds)
4085 {
4086 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4087
4088 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4089 }
4090
4091 /* Close the relation, but keep lock until commit */
4092 relation_close(rel, NoLock);
4093
4094 /* Phase 2: update system catalogs */
4095 ATRewriteCatalogs(&wqueue, lockmode, context);
4096
4097 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4098 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4099 }
4100
4101 /*
4102 * ATPrepCmd
4103 *
4104 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4105 * recursion and permission checks.
4106 *
4107 * Caller must have acquired appropriate lock type on relation already.
4108 * This lock should be held until commit.
4109 */
4110 static void
ATPrepCmd(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode,AlterTableUtilityContext * context)4111 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4112 bool recurse, bool recursing, LOCKMODE lockmode,
4113 AlterTableUtilityContext *context)
4114 {
4115 AlteredTableInfo *tab;
4116 int pass = AT_PASS_UNSET;
4117
4118 /* Find or create work queue entry for this table */
4119 tab = ATGetQueueEntry(wqueue, rel);
4120
4121 /*
4122 * Copy the original subcommand for each table. This avoids conflicts
4123 * when different child tables need to make different parse
4124 * transformations (for example, the same column may have different column
4125 * numbers in different children). It also ensures that we don't corrupt
4126 * the original parse tree, in case it is saved in plancache.
4127 */
4128 cmd = copyObject(cmd);
4129
4130 /*
4131 * Do permissions and relkind checking, recursion to child tables if
4132 * needed, and any additional phase-1 processing needed. (But beware of
4133 * adding any processing that looks at table details that another
4134 * subcommand could change. In some cases we reject multiple subcommands
4135 * that could try to change the same state in contrary ways.)
4136 */
4137 switch (cmd->subtype)
4138 {
4139 case AT_AddColumn: /* ADD COLUMN */
4140 ATSimplePermissions(rel,
4141 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4142 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4143 lockmode, context);
4144 /* Recursion occurs during execution phase */
4145 pass = AT_PASS_ADD_COL;
4146 break;
4147 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4148 ATSimplePermissions(rel, ATT_VIEW);
4149 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4150 lockmode, context);
4151 /* Recursion occurs during execution phase */
4152 pass = AT_PASS_ADD_COL;
4153 break;
4154 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4155
4156 /*
4157 * We allow defaults on views so that INSERT into a view can have
4158 * default-ish behavior. This works because the rewriter
4159 * substitutes default values into INSERTs before it expands
4160 * rules.
4161 */
4162 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4163 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4164 /* No command-specific prep needed */
4165 pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4166 break;
4167 case AT_CookedColumnDefault: /* add a pre-cooked default */
4168 /* This is currently used only in CREATE TABLE */
4169 /* (so the permission check really isn't necessary) */
4170 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4171 /* This command never recurses */
4172 pass = AT_PASS_ADD_OTHERCONSTR;
4173 break;
4174 case AT_AddIdentity:
4175 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4176 /* This command never recurses */
4177 pass = AT_PASS_ADD_OTHERCONSTR;
4178 break;
4179 case AT_SetIdentity:
4180 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4181 /* This command never recurses */
4182 /* This should run after AddIdentity, so do it in MISC pass */
4183 pass = AT_PASS_MISC;
4184 break;
4185 case AT_DropIdentity:
4186 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4187 /* This command never recurses */
4188 pass = AT_PASS_DROP;
4189 break;
4190 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4191 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4192 ATPrepDropNotNull(rel, recurse, recursing);
4193 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4194 pass = AT_PASS_DROP;
4195 break;
4196 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4197 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4198 /* Need command-specific recursion decision */
4199 ATPrepSetNotNull(wqueue, rel, cmd, recurse, recursing,
4200 lockmode, context);
4201 pass = AT_PASS_COL_ATTRS;
4202 break;
4203 case AT_CheckNotNull: /* check column is already marked NOT NULL */
4204 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4205 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4206 /* No command-specific prep needed */
4207 pass = AT_PASS_COL_ATTRS;
4208 break;
4209 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4210 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4211 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4212 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4213 pass = AT_PASS_DROP;
4214 break;
4215 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4216 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4217 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4218 /* No command-specific prep needed */
4219 pass = AT_PASS_MISC;
4220 break;
4221 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4222 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4223 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4224 /* This command never recurses */
4225 pass = AT_PASS_MISC;
4226 break;
4227 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4228 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4229 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4230 /* No command-specific prep needed */
4231 pass = AT_PASS_MISC;
4232 break;
4233 case AT_DropColumn: /* DROP COLUMN */
4234 ATSimplePermissions(rel,
4235 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4236 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4237 lockmode, context);
4238 /* Recursion occurs during execution phase */
4239 pass = AT_PASS_DROP;
4240 break;
4241 case AT_AddIndex: /* ADD INDEX */
4242 ATSimplePermissions(rel, ATT_TABLE);
4243 /* This command never recurses */
4244 /* No command-specific prep needed */
4245 pass = AT_PASS_ADD_INDEX;
4246 break;
4247 case AT_AddConstraint: /* ADD CONSTRAINT */
4248 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4249 /* Recursion occurs during execution phase */
4250 /* No command-specific prep needed except saving recurse flag */
4251 if (recurse)
4252 cmd->subtype = AT_AddConstraintRecurse;
4253 pass = AT_PASS_ADD_CONSTR;
4254 break;
4255 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4256 ATSimplePermissions(rel, ATT_TABLE);
4257 /* This command never recurses */
4258 /* No command-specific prep needed */
4259 pass = AT_PASS_ADD_INDEXCONSTR;
4260 break;
4261 case AT_DropConstraint: /* DROP CONSTRAINT */
4262 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4263 ATCheckPartitionsNotInUse(rel, lockmode);
4264 /* Other recursion occurs during execution phase */
4265 /* No command-specific prep needed except saving recurse flag */
4266 if (recurse)
4267 cmd->subtype = AT_DropConstraintRecurse;
4268 pass = AT_PASS_DROP;
4269 break;
4270 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4271 ATSimplePermissions(rel,
4272 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4273 /* See comments for ATPrepAlterColumnType */
4274 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4275 AT_PASS_UNSET, context);
4276 Assert(cmd != NULL);
4277 /* Performs own recursion */
4278 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4279 lockmode, context);
4280 pass = AT_PASS_ALTER_TYPE;
4281 break;
4282 case AT_AlterColumnGenericOptions:
4283 ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
4284 /* This command never recurses */
4285 /* No command-specific prep needed */
4286 pass = AT_PASS_MISC;
4287 break;
4288 case AT_ChangeOwner: /* ALTER OWNER */
4289 /* This command never recurses */
4290 /* No command-specific prep needed */
4291 pass = AT_PASS_MISC;
4292 break;
4293 case AT_ClusterOn: /* CLUSTER ON */
4294 case AT_DropCluster: /* SET WITHOUT CLUSTER */
4295 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
4296 /* These commands never recurse */
4297 /* No command-specific prep needed */
4298 pass = AT_PASS_MISC;
4299 break;
4300 case AT_SetLogged: /* SET LOGGED */
4301 ATSimplePermissions(rel, ATT_TABLE);
4302 if (tab->chgPersistence)
4303 ereport(ERROR,
4304 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4305 errmsg("cannot change persistence setting twice")));
4306 tab->chgPersistence = ATPrepChangePersistence(rel, true);
4307 /* force rewrite if necessary; see comment in ATRewriteTables */
4308 if (tab->chgPersistence)
4309 {
4310 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4311 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4312 }
4313 pass = AT_PASS_MISC;
4314 break;
4315 case AT_SetUnLogged: /* SET UNLOGGED */
4316 ATSimplePermissions(rel, ATT_TABLE);
4317 if (tab->chgPersistence)
4318 ereport(ERROR,
4319 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4320 errmsg("cannot change persistence setting twice")));
4321 tab->chgPersistence = ATPrepChangePersistence(rel, false);
4322 /* force rewrite if necessary; see comment in ATRewriteTables */
4323 if (tab->chgPersistence)
4324 {
4325 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4326 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
4327 }
4328 pass = AT_PASS_MISC;
4329 break;
4330 case AT_DropOids: /* SET WITHOUT OIDS */
4331 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4332 pass = AT_PASS_DROP;
4333 break;
4334 case AT_SetTableSpace: /* SET TABLESPACE */
4335 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
4336 ATT_PARTITIONED_INDEX);
4337 /* This command never recurses */
4338 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
4339 pass = AT_PASS_MISC; /* doesn't actually matter */
4340 break;
4341 case AT_SetRelOptions: /* SET (...) */
4342 case AT_ResetRelOptions: /* RESET (...) */
4343 case AT_ReplaceRelOptions: /* reset them all, then set just these */
4344 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
4345 /* This command never recurses */
4346 /* No command-specific prep needed */
4347 pass = AT_PASS_MISC;
4348 break;
4349 case AT_AddInherit: /* INHERIT */
4350 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4351 /* This command never recurses */
4352 ATPrepAddInherit(rel);
4353 pass = AT_PASS_MISC;
4354 break;
4355 case AT_DropInherit: /* NO INHERIT */
4356 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4357 /* This command never recurses */
4358 /* No command-specific prep needed */
4359 pass = AT_PASS_MISC;
4360 break;
4361 case AT_AlterConstraint: /* ALTER CONSTRAINT */
4362 ATSimplePermissions(rel, ATT_TABLE);
4363 /* Recursion occurs during execution phase */
4364 pass = AT_PASS_MISC;
4365 break;
4366 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
4367 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4368 /* Recursion occurs during execution phase */
4369 /* No command-specific prep needed except saving recurse flag */
4370 if (recurse)
4371 cmd->subtype = AT_ValidateConstraintRecurse;
4372 pass = AT_PASS_MISC;
4373 break;
4374 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
4375 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
4376 pass = AT_PASS_MISC;
4377 /* This command never recurses */
4378 /* No command-specific prep needed */
4379 break;
4380 case AT_EnableTrig: /* ENABLE TRIGGER variants */
4381 case AT_EnableAlwaysTrig:
4382 case AT_EnableReplicaTrig:
4383 case AT_EnableTrigAll:
4384 case AT_EnableTrigUser:
4385 case AT_DisableTrig: /* DISABLE TRIGGER variants */
4386 case AT_DisableTrigAll:
4387 case AT_DisableTrigUser:
4388 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4389 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4390 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4391 pass = AT_PASS_MISC;
4392 break;
4393 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
4394 case AT_EnableAlwaysRule:
4395 case AT_EnableReplicaRule:
4396 case AT_DisableRule:
4397 case AT_AddOf: /* OF */
4398 case AT_DropOf: /* NOT OF */
4399 case AT_EnableRowSecurity:
4400 case AT_DisableRowSecurity:
4401 case AT_ForceRowSecurity:
4402 case AT_NoForceRowSecurity:
4403 ATSimplePermissions(rel, ATT_TABLE);
4404 /* These commands never recurse */
4405 /* No command-specific prep needed */
4406 pass = AT_PASS_MISC;
4407 break;
4408 case AT_GenericOptions:
4409 ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
4410 /* No command-specific prep needed */
4411 pass = AT_PASS_MISC;
4412 break;
4413 case AT_AttachPartition:
4414 ATSimplePermissions(rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
4415 /* No command-specific prep needed */
4416 pass = AT_PASS_MISC;
4417 break;
4418 case AT_DetachPartition:
4419 ATSimplePermissions(rel, ATT_TABLE);
4420 /* No command-specific prep needed */
4421 pass = AT_PASS_MISC;
4422 break;
4423 default: /* oops */
4424 elog(ERROR, "unrecognized alter table type: %d",
4425 (int) cmd->subtype);
4426 pass = AT_PASS_UNSET; /* keep compiler quiet */
4427 break;
4428 }
4429 Assert(pass > AT_PASS_UNSET);
4430
4431 /* Add the subcommand to the appropriate list for phase 2 */
4432 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
4433 }
4434
4435 /*
4436 * ATRewriteCatalogs
4437 *
4438 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
4439 * dispatched in a "safe" execution order (designed to avoid unnecessary
4440 * conflicts).
4441 */
4442 static void
ATRewriteCatalogs(List ** wqueue,LOCKMODE lockmode,AlterTableUtilityContext * context)4443 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
4444 AlterTableUtilityContext *context)
4445 {
4446 int pass;
4447 ListCell *ltab;
4448
4449 /*
4450 * We process all the tables "in parallel", one pass at a time. This is
4451 * needed because we may have to propagate work from one table to another
4452 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
4453 * re-adding of the foreign key constraint to the other table). Work can
4454 * only be propagated into later passes, however.
4455 */
4456 for (pass = 0; pass < AT_NUM_PASSES; pass++)
4457 {
4458 /* Go through each table that needs to be processed */
4459 foreach(ltab, *wqueue)
4460 {
4461 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4462 List *subcmds = tab->subcmds[pass];
4463 Relation rel;
4464 ListCell *lcmd;
4465
4466 if (subcmds == NIL)
4467 continue;
4468
4469 /*
4470 * Appropriate lock was obtained by phase 1, needn't get it again
4471 */
4472 rel = relation_open(tab->relid, NoLock);
4473
4474 foreach(lcmd, subcmds)
4475 ATExecCmd(wqueue, tab, rel,
4476 castNode(AlterTableCmd, lfirst(lcmd)),
4477 lockmode, pass, context);
4478
4479 /*
4480 * After the ALTER TYPE pass, do cleanup work (this is not done in
4481 * ATExecAlterColumnType since it should be done only once if
4482 * multiple columns of a table are altered).
4483 */
4484 if (pass == AT_PASS_ALTER_TYPE)
4485 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
4486
4487 relation_close(rel, NoLock);
4488 }
4489 }
4490
4491 /* Check to see if a toast table must be added. */
4492 foreach(ltab, *wqueue)
4493 {
4494 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4495
4496 /*
4497 * If the table is source table of ATTACH PARTITION command, we did
4498 * not modify anything about it that will change its toasting
4499 * requirement, so no need to check.
4500 */
4501 if (((tab->relkind == RELKIND_RELATION ||
4502 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
4503 tab->partition_constraint == NULL) ||
4504 tab->relkind == RELKIND_MATVIEW)
4505 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
4506 }
4507 }
4508
4509 /*
4510 * ATExecCmd: dispatch a subcommand to appropriate execution routine
4511 */
4512 static void
ATExecCmd(List ** wqueue,AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode,int cur_pass,AlterTableUtilityContext * context)4513 ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
4514 AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
4515 AlterTableUtilityContext *context)
4516 {
4517 ObjectAddress address = InvalidObjectAddress;
4518
4519 switch (cmd->subtype)
4520 {
4521 case AT_AddColumn: /* ADD COLUMN */
4522 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4523 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
4524 false, false,
4525 lockmode, cur_pass, context);
4526 break;
4527 case AT_AddColumnRecurse:
4528 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
4529 true, false,
4530 lockmode, cur_pass, context);
4531 break;
4532 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4533 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
4534 break;
4535 case AT_CookedColumnDefault: /* add a pre-cooked default */
4536 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
4537 break;
4538 case AT_AddIdentity:
4539 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
4540 cur_pass, context);
4541 Assert(cmd != NULL);
4542 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
4543 break;
4544 case AT_SetIdentity:
4545 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
4546 cur_pass, context);
4547 Assert(cmd != NULL);
4548 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
4549 break;
4550 case AT_DropIdentity:
4551 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
4552 break;
4553 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4554 address = ATExecDropNotNull(rel, cmd->name, lockmode);
4555 break;
4556 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4557 address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
4558 break;
4559 case AT_CheckNotNull: /* check column is already marked NOT NULL */
4560 ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
4561 break;
4562 case AT_DropExpression:
4563 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
4564 break;
4565 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4566 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
4567 break;
4568 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4569 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
4570 break;
4571 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4572 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
4573 break;
4574 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4575 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
4576 break;
4577 case AT_DropColumn: /* DROP COLUMN */
4578 address = ATExecDropColumn(wqueue, rel, cmd->name,
4579 cmd->behavior, false, false,
4580 cmd->missing_ok, lockmode,
4581 NULL);
4582 break;
4583 case AT_DropColumnRecurse: /* DROP COLUMN with recursion */
4584 address = ATExecDropColumn(wqueue, rel, cmd->name,
4585 cmd->behavior, true, false,
4586 cmd->missing_ok, lockmode,
4587 NULL);
4588 break;
4589 case AT_AddIndex: /* ADD INDEX */
4590 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
4591 lockmode);
4592 break;
4593 case AT_ReAddIndex: /* ADD INDEX */
4594 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
4595 lockmode);
4596 break;
4597 case AT_AddConstraint: /* ADD CONSTRAINT */
4598 /* Transform the command only during initial examination */
4599 if (cur_pass == AT_PASS_ADD_CONSTR)
4600 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
4601 false, lockmode,
4602 cur_pass, context);
4603 /* Depending on constraint type, might be no more work to do now */
4604 if (cmd != NULL)
4605 address =
4606 ATExecAddConstraint(wqueue, tab, rel,
4607 (Constraint *) cmd->def,
4608 false, false, lockmode);
4609 break;
4610 case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
4611 /* Transform the command only during initial examination */
4612 if (cur_pass == AT_PASS_ADD_CONSTR)
4613 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
4614 true, lockmode,
4615 cur_pass, context);
4616 /* Depending on constraint type, might be no more work to do now */
4617 if (cmd != NULL)
4618 address =
4619 ATExecAddConstraint(wqueue, tab, rel,
4620 (Constraint *) cmd->def,
4621 true, false, lockmode);
4622 break;
4623 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
4624 address =
4625 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
4626 true, true, lockmode);
4627 break;
4628 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
4629 * constraint */
4630 address =
4631 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
4632 ((AlterDomainStmt *) cmd->def)->def,
4633 NULL);
4634 break;
4635 case AT_ReAddComment: /* Re-add existing comment */
4636 address = CommentObject((CommentStmt *) cmd->def);
4637 break;
4638 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4639 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
4640 lockmode);
4641 break;
4642 case AT_AlterConstraint: /* ALTER CONSTRAINT */
4643 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
4644 break;
4645 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
4646 address = ATExecValidateConstraint(wqueue, rel, cmd->name, false,
4647 false, lockmode);
4648 break;
4649 case AT_ValidateConstraintRecurse: /* VALIDATE CONSTRAINT with
4650 * recursion */
4651 address = ATExecValidateConstraint(wqueue, rel, cmd->name, true,
4652 false, lockmode);
4653 break;
4654 case AT_DropConstraint: /* DROP CONSTRAINT */
4655 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
4656 false, false,
4657 cmd->missing_ok, lockmode);
4658 break;
4659 case AT_DropConstraintRecurse: /* DROP CONSTRAINT with recursion */
4660 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
4661 true, false,
4662 cmd->missing_ok, lockmode);
4663 break;
4664 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4665 /* parse transformation was done earlier */
4666 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
4667 break;
4668 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
4669 address =
4670 ATExecAlterColumnGenericOptions(rel, cmd->name,
4671 (List *) cmd->def, lockmode);
4672 break;
4673 case AT_ChangeOwner: /* ALTER OWNER */
4674 ATExecChangeOwner(RelationGetRelid(rel),
4675 get_rolespec_oid(cmd->newowner, false),
4676 false, lockmode);
4677 break;
4678 case AT_ClusterOn: /* CLUSTER ON */
4679 address = ATExecClusterOn(rel, cmd->name, lockmode);
4680 break;
4681 case AT_DropCluster: /* SET WITHOUT CLUSTER */
4682 ATExecDropCluster(rel, lockmode);
4683 break;
4684 case AT_SetLogged: /* SET LOGGED */
4685 case AT_SetUnLogged: /* SET UNLOGGED */
4686 break;
4687 case AT_DropOids: /* SET WITHOUT OIDS */
4688 /* nothing to do here, oid columns don't exist anymore */
4689 break;
4690 case AT_SetTableSpace: /* SET TABLESPACE */
4691
4692 /*
4693 * Only do this for partitioned tables and indexes, for which this
4694 * is just a catalog change. Other relation types which have
4695 * storage are handled by Phase 3.
4696 */
4697 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
4698 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4699 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
4700
4701 break;
4702 case AT_SetRelOptions: /* SET (...) */
4703 case AT_ResetRelOptions: /* RESET (...) */
4704 case AT_ReplaceRelOptions: /* replace entire option list */
4705 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
4706 break;
4707 case AT_EnableTrig: /* ENABLE TRIGGER name */
4708 ATExecEnableDisableTrigger(rel, cmd->name,
4709 TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
4710 break;
4711 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
4712 ATExecEnableDisableTrigger(rel, cmd->name,
4713 TRIGGER_FIRES_ALWAYS, false, lockmode);
4714 break;
4715 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
4716 ATExecEnableDisableTrigger(rel, cmd->name,
4717 TRIGGER_FIRES_ON_REPLICA, false, lockmode);
4718 break;
4719 case AT_DisableTrig: /* DISABLE TRIGGER name */
4720 ATExecEnableDisableTrigger(rel, cmd->name,
4721 TRIGGER_DISABLED, false, lockmode);
4722 break;
4723 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
4724 ATExecEnableDisableTrigger(rel, NULL,
4725 TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
4726 break;
4727 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
4728 ATExecEnableDisableTrigger(rel, NULL,
4729 TRIGGER_DISABLED, false, lockmode);
4730 break;
4731 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
4732 ATExecEnableDisableTrigger(rel, NULL,
4733 TRIGGER_FIRES_ON_ORIGIN, true, lockmode);
4734 break;
4735 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
4736 ATExecEnableDisableTrigger(rel, NULL,
4737 TRIGGER_DISABLED, true, lockmode);
4738 break;
4739
4740 case AT_EnableRule: /* ENABLE RULE name */
4741 ATExecEnableDisableRule(rel, cmd->name,
4742 RULE_FIRES_ON_ORIGIN, lockmode);
4743 break;
4744 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
4745 ATExecEnableDisableRule(rel, cmd->name,
4746 RULE_FIRES_ALWAYS, lockmode);
4747 break;
4748 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
4749 ATExecEnableDisableRule(rel, cmd->name,
4750 RULE_FIRES_ON_REPLICA, lockmode);
4751 break;
4752 case AT_DisableRule: /* DISABLE RULE name */
4753 ATExecEnableDisableRule(rel, cmd->name,
4754 RULE_DISABLED, lockmode);
4755 break;
4756
4757 case AT_AddInherit:
4758 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
4759 break;
4760 case AT_DropInherit:
4761 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
4762 break;
4763 case AT_AddOf:
4764 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
4765 break;
4766 case AT_DropOf:
4767 ATExecDropOf(rel, lockmode);
4768 break;
4769 case AT_ReplicaIdentity:
4770 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
4771 break;
4772 case AT_EnableRowSecurity:
4773 ATExecEnableRowSecurity(rel);
4774 break;
4775 case AT_DisableRowSecurity:
4776 ATExecDisableRowSecurity(rel);
4777 break;
4778 case AT_ForceRowSecurity:
4779 ATExecForceNoForceRowSecurity(rel, true);
4780 break;
4781 case AT_NoForceRowSecurity:
4782 ATExecForceNoForceRowSecurity(rel, false);
4783 break;
4784 case AT_GenericOptions:
4785 ATExecGenericOptions(rel, (List *) cmd->def);
4786 break;
4787 case AT_AttachPartition:
4788 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
4789 cur_pass, context);
4790 Assert(cmd != NULL);
4791 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4792 ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
4793 else
4794 ATExecAttachPartitionIdx(wqueue, rel,
4795 ((PartitionCmd *) cmd->def)->name);
4796 break;
4797 case AT_DetachPartition:
4798 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
4799 cur_pass, context);
4800 Assert(cmd != NULL);
4801 /* ATPrepCmd ensures it must be a table */
4802 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
4803 ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
4804 break;
4805 default: /* oops */
4806 elog(ERROR, "unrecognized alter table type: %d",
4807 (int) cmd->subtype);
4808 break;
4809 }
4810
4811 /*
4812 * Report the subcommand to interested event triggers.
4813 */
4814 if (cmd)
4815 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
4816
4817 /*
4818 * Bump the command counter to ensure the next subcommand in the sequence
4819 * can see the changes so far
4820 */
4821 CommandCounterIncrement();
4822 }
4823
4824 /*
4825 * ATParseTransformCmd: perform parse transformation for one subcommand
4826 *
4827 * Returns the transformed subcommand tree, if there is one, else NULL.
4828 *
4829 * The parser may hand back additional AlterTableCmd(s) and/or other
4830 * utility statements, either before or after the original subcommand.
4831 * Other AlterTableCmds are scheduled into the appropriate slot of the
4832 * AlteredTableInfo (they had better be for later passes than the current one).
4833 * Utility statements that are supposed to happen before the AlterTableCmd
4834 * are executed immediately. Those that are supposed to happen afterwards
4835 * are added to the tab->afterStmts list to be done at the very end.
4836 */
4837 static AlterTableCmd *
ATParseTransformCmd(List ** wqueue,AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,bool recurse,LOCKMODE lockmode,int cur_pass,AlterTableUtilityContext * context)4838 ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
4839 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
4840 int cur_pass, AlterTableUtilityContext *context)
4841 {
4842 AlterTableCmd *newcmd = NULL;
4843 AlterTableStmt *atstmt = makeNode(AlterTableStmt);
4844 List *beforeStmts;
4845 List *afterStmts;
4846 ListCell *lc;
4847
4848 /* Gin up an AlterTableStmt with just this subcommand and this table */
4849 atstmt->relation =
4850 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
4851 pstrdup(RelationGetRelationName(rel)),
4852 -1);
4853 atstmt->relation->inh = recurse;
4854 atstmt->cmds = list_make1(cmd);
4855 atstmt->relkind = OBJECT_TABLE; /* needn't be picky here */
4856 atstmt->missing_ok = false;
4857
4858 /* Transform the AlterTableStmt */
4859 atstmt = transformAlterTableStmt(RelationGetRelid(rel),
4860 atstmt,
4861 context->queryString,
4862 &beforeStmts,
4863 &afterStmts);
4864
4865 /* Execute any statements that should happen before these subcommand(s) */
4866 foreach(lc, beforeStmts)
4867 {
4868 Node *stmt = (Node *) lfirst(lc);
4869
4870 ProcessUtilityForAlterTable(stmt, context);
4871 CommandCounterIncrement();
4872 }
4873
4874 /* Examine the transformed subcommands and schedule them appropriately */
4875 foreach(lc, atstmt->cmds)
4876 {
4877 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
4878 int pass;
4879
4880 /*
4881 * This switch need only cover the subcommand types that can be added
4882 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
4883 * executing the subcommand immediately, as a substitute for the
4884 * original subcommand. (Note, however, that this does cause
4885 * AT_AddConstraint subcommands to be rescheduled into later passes,
4886 * which is important for index and foreign key constraints.)
4887 *
4888 * We assume we needn't do any phase-1 checks for added subcommands.
4889 */
4890 switch (cmd2->subtype)
4891 {
4892 case AT_SetNotNull:
4893 /* Need command-specific recursion decision */
4894 ATPrepSetNotNull(wqueue, rel, cmd2,
4895 recurse, false,
4896 lockmode, context);
4897 pass = AT_PASS_COL_ATTRS;
4898 break;
4899 case AT_AddIndex:
4900 /* This command never recurses */
4901 /* No command-specific prep needed */
4902 pass = AT_PASS_ADD_INDEX;
4903 break;
4904 case AT_AddIndexConstraint:
4905 /* This command never recurses */
4906 /* No command-specific prep needed */
4907 pass = AT_PASS_ADD_INDEXCONSTR;
4908 break;
4909 case AT_AddConstraint:
4910 /* Recursion occurs during execution phase */
4911 if (recurse)
4912 cmd2->subtype = AT_AddConstraintRecurse;
4913 switch (castNode(Constraint, cmd2->def)->contype)
4914 {
4915 case CONSTR_PRIMARY:
4916 case CONSTR_UNIQUE:
4917 case CONSTR_EXCLUSION:
4918 pass = AT_PASS_ADD_INDEXCONSTR;
4919 break;
4920 default:
4921 pass = AT_PASS_ADD_OTHERCONSTR;
4922 break;
4923 }
4924 break;
4925 case AT_AlterColumnGenericOptions:
4926 /* This command never recurses */
4927 /* No command-specific prep needed */
4928 pass = AT_PASS_MISC;
4929 break;
4930 default:
4931 pass = cur_pass;
4932 break;
4933 }
4934
4935 if (pass < cur_pass)
4936 {
4937 /* Cannot schedule into a pass we already finished */
4938 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
4939 pass);
4940 }
4941 else if (pass > cur_pass)
4942 {
4943 /* OK, queue it up for later */
4944 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
4945 }
4946 else
4947 {
4948 /*
4949 * We should see at most one subcommand for the current pass,
4950 * which is the transformed version of the original subcommand.
4951 */
4952 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
4953 {
4954 /* Found the transformed version of our subcommand */
4955 newcmd = cmd2;
4956 }
4957 else
4958 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
4959 pass);
4960 }
4961 }
4962
4963 /* Queue up any after-statements to happen at the end */
4964 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
4965
4966 return newcmd;
4967 }
4968
4969 /*
4970 * ATRewriteTables: ALTER TABLE phase 3
4971 */
4972 static void
ATRewriteTables(AlterTableStmt * parsetree,List ** wqueue,LOCKMODE lockmode,AlterTableUtilityContext * context)4973 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
4974 AlterTableUtilityContext *context)
4975 {
4976 ListCell *ltab;
4977
4978 /* Go through each table that needs to be checked or rewritten */
4979 foreach(ltab, *wqueue)
4980 {
4981 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4982
4983 /* Relations without storage may be ignored here */
4984 if (!RELKIND_HAS_STORAGE(tab->relkind))
4985 continue;
4986
4987 /*
4988 * If we change column data types or add/remove OIDs, the operation
4989 * has to be propagated to tables that use this table's rowtype as a
4990 * column type. tab->newvals will also be non-NULL in the case where
4991 * we're adding a column with a default. We choose to forbid that
4992 * case as well, since composite types might eventually support
4993 * defaults.
4994 *
4995 * (Eventually we'll probably need to check for composite type
4996 * dependencies even when we're just scanning the table without a
4997 * rewrite, but at the moment a composite type does not enforce any
4998 * constraints, so it's not necessary/appropriate to enforce them just
4999 * during ALTER.)
5000 */
5001 if (tab->newvals != NIL || tab->rewrite > 0)
5002 {
5003 Relation rel;
5004
5005 rel = table_open(tab->relid, NoLock);
5006 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5007 table_close(rel, NoLock);
5008 }
5009
5010 /*
5011 * We only need to rewrite the table if at least one column needs to
5012 * be recomputed, we are adding/removing the OID column, or we are
5013 * changing its persistence.
5014 *
5015 * There are two reasons for requiring a rewrite when changing
5016 * persistence: on one hand, we need to ensure that the buffers
5017 * belonging to each of the two relations are marked with or without
5018 * BM_PERMANENT properly. On the other hand, since rewriting creates
5019 * and assigns a new relfilenode, we automatically create or drop an
5020 * init fork for the relation as appropriate.
5021 */
5022 if (tab->rewrite > 0)
5023 {
5024 /* Build a temporary relation and copy data */
5025 Relation OldHeap;
5026 Oid OIDNewHeap;
5027 Oid NewTableSpace;
5028 char persistence;
5029
5030 OldHeap = table_open(tab->relid, NoLock);
5031
5032 /*
5033 * We don't support rewriting of system catalogs; there are too
5034 * many corner cases and too little benefit. In particular this
5035 * is certainly not going to work for mapped catalogs.
5036 */
5037 if (IsSystemRelation(OldHeap))
5038 ereport(ERROR,
5039 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5040 errmsg("cannot rewrite system relation \"%s\"",
5041 RelationGetRelationName(OldHeap))));
5042
5043 if (RelationIsUsedAsCatalogTable(OldHeap))
5044 ereport(ERROR,
5045 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5046 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5047 RelationGetRelationName(OldHeap))));
5048
5049 /*
5050 * Don't allow rewrite on temp tables of other backends ... their
5051 * local buffer manager is not going to cope.
5052 */
5053 if (RELATION_IS_OTHER_TEMP(OldHeap))
5054 ereport(ERROR,
5055 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5056 errmsg("cannot rewrite temporary tables of other sessions")));
5057
5058 /*
5059 * Select destination tablespace (same as original unless user
5060 * requested a change)
5061 */
5062 if (tab->newTableSpace)
5063 NewTableSpace = tab->newTableSpace;
5064 else
5065 NewTableSpace = OldHeap->rd_rel->reltablespace;
5066
5067 /*
5068 * Select persistence of transient table (same as original unless
5069 * user requested a change)
5070 */
5071 persistence = tab->chgPersistence ?
5072 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5073
5074 table_close(OldHeap, NoLock);
5075
5076 /*
5077 * Fire off an Event Trigger now, before actually rewriting the
5078 * table.
5079 *
5080 * We don't support Event Trigger for nested commands anywhere,
5081 * here included, and parsetree is given NULL when coming from
5082 * AlterTableInternal.
5083 *
5084 * And fire it only once.
5085 */
5086 if (parsetree)
5087 EventTriggerTableRewrite((Node *) parsetree,
5088 tab->relid,
5089 tab->rewrite);
5090
5091 /*
5092 * Create transient table that will receive the modified data.
5093 *
5094 * Ensure it is marked correctly as logged or unlogged. We have
5095 * to do this here so that buffers for the new relfilenode will
5096 * have the right persistence set, and at the same time ensure
5097 * that the original filenode's buffers will get read in with the
5098 * correct setting (i.e. the original one). Otherwise a rollback
5099 * after the rewrite would possibly result with buffers for the
5100 * original filenode having the wrong persistence setting.
5101 *
5102 * NB: This relies on swap_relation_files() also swapping the
5103 * persistence. That wouldn't work for pg_class, but that can't be
5104 * unlogged anyway.
5105 */
5106 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
5107 lockmode);
5108
5109 /*
5110 * Copy the heap data into the new table with the desired
5111 * modifications, and test the current data within the table
5112 * against new constraints generated by ALTER TABLE commands.
5113 */
5114 ATRewriteTable(tab, OIDNewHeap, lockmode);
5115
5116 /*
5117 * Swap the physical files of the old and new heaps, then rebuild
5118 * indexes and discard the old heap. We can use RecentXmin for
5119 * the table's new relfrozenxid because we rewrote all the tuples
5120 * in ATRewriteTable, so no older Xid remains in the table. Also,
5121 * we never try to swap toast tables by content, since we have no
5122 * interest in letting this code work on system catalogs.
5123 */
5124 finish_heap_swap(tab->relid, OIDNewHeap,
5125 false, false, true,
5126 !OidIsValid(tab->newTableSpace),
5127 RecentXmin,
5128 ReadNextMultiXactId(),
5129 persistence);
5130 }
5131 else
5132 {
5133 /*
5134 * If required, test the current data within the table against new
5135 * constraints generated by ALTER TABLE commands, but don't
5136 * rebuild data.
5137 */
5138 if (tab->constraints != NIL || tab->verify_new_notnull ||
5139 tab->partition_constraint != NULL)
5140 ATRewriteTable(tab, InvalidOid, lockmode);
5141
5142 /*
5143 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5144 * just do a block-by-block copy.
5145 */
5146 if (tab->newTableSpace)
5147 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5148 }
5149 }
5150
5151 /*
5152 * Foreign key constraints are checked in a final pass, since (a) it's
5153 * generally best to examine each one separately, and (b) it's at least
5154 * theoretically possible that we have changed both relations of the
5155 * foreign key, and we'd better have finished both rewrites before we try
5156 * to read the tables.
5157 */
5158 foreach(ltab, *wqueue)
5159 {
5160 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5161 Relation rel = NULL;
5162 ListCell *lcon;
5163
5164 /* Relations without storage may be ignored here too */
5165 if (!RELKIND_HAS_STORAGE(tab->relkind))
5166 continue;
5167
5168 foreach(lcon, tab->constraints)
5169 {
5170 NewConstraint *con = lfirst(lcon);
5171
5172 if (con->contype == CONSTR_FOREIGN)
5173 {
5174 Constraint *fkconstraint = (Constraint *) con->qual;
5175 Relation refrel;
5176
5177 if (rel == NULL)
5178 {
5179 /* Long since locked, no need for another */
5180 rel = table_open(tab->relid, NoLock);
5181 }
5182
5183 refrel = table_open(con->refrelid, RowShareLock);
5184
5185 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5186 con->refindid,
5187 con->conid);
5188
5189 /*
5190 * No need to mark the constraint row as validated, we did
5191 * that when we inserted the row earlier.
5192 */
5193
5194 table_close(refrel, NoLock);
5195 }
5196 }
5197
5198 if (rel)
5199 table_close(rel, NoLock);
5200 }
5201
5202 /* Finally, run any afterStmts that were queued up */
5203 foreach(ltab, *wqueue)
5204 {
5205 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5206 ListCell *lc;
5207
5208 foreach(lc, tab->afterStmts)
5209 {
5210 Node *stmt = (Node *) lfirst(lc);
5211
5212 ProcessUtilityForAlterTable(stmt, context);
5213 CommandCounterIncrement();
5214 }
5215 }
5216 }
5217
5218 /*
5219 * ATRewriteTable: scan or rewrite one table
5220 *
5221 * OIDNewHeap is InvalidOid if we don't need to rewrite
5222 */
5223 static void
ATRewriteTable(AlteredTableInfo * tab,Oid OIDNewHeap,LOCKMODE lockmode)5224 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
5225 {
5226 Relation oldrel;
5227 Relation newrel;
5228 TupleDesc oldTupDesc;
5229 TupleDesc newTupDesc;
5230 bool needscan = false;
5231 List *notnull_attrs;
5232 int i;
5233 ListCell *l;
5234 EState *estate;
5235 CommandId mycid;
5236 BulkInsertState bistate;
5237 int ti_options;
5238 ExprState *partqualstate = NULL;
5239
5240 /*
5241 * Open the relation(s). We have surely already locked the existing
5242 * table.
5243 */
5244 oldrel = table_open(tab->relid, NoLock);
5245 oldTupDesc = tab->oldDesc;
5246 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
5247
5248 if (OidIsValid(OIDNewHeap))
5249 newrel = table_open(OIDNewHeap, lockmode);
5250 else
5251 newrel = NULL;
5252
5253 /*
5254 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
5255 * is empty, so don't bother using it.
5256 */
5257 if (newrel)
5258 {
5259 mycid = GetCurrentCommandId(true);
5260 bistate = GetBulkInsertState();
5261 ti_options = TABLE_INSERT_SKIP_FSM;
5262 }
5263 else
5264 {
5265 /* keep compiler quiet about using these uninitialized */
5266 mycid = 0;
5267 bistate = NULL;
5268 ti_options = 0;
5269 }
5270
5271 /*
5272 * Generate the constraint and default execution states
5273 */
5274
5275 estate = CreateExecutorState();
5276
5277 /* Build the needed expression execution states */
5278 foreach(l, tab->constraints)
5279 {
5280 NewConstraint *con = lfirst(l);
5281
5282 switch (con->contype)
5283 {
5284 case CONSTR_CHECK:
5285 needscan = true;
5286 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
5287 break;
5288 case CONSTR_FOREIGN:
5289 /* Nothing to do here */
5290 break;
5291 default:
5292 elog(ERROR, "unrecognized constraint type: %d",
5293 (int) con->contype);
5294 }
5295 }
5296
5297 /* Build expression execution states for partition check quals */
5298 if (tab->partition_constraint)
5299 {
5300 needscan = true;
5301 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
5302 }
5303
5304 foreach(l, tab->newvals)
5305 {
5306 NewColumnValue *ex = lfirst(l);
5307
5308 /* expr already planned */
5309 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
5310 }
5311
5312 notnull_attrs = NIL;
5313 if (newrel || tab->verify_new_notnull)
5314 {
5315 /*
5316 * If we are rebuilding the tuples OR if we added any new but not
5317 * verified NOT NULL constraints, check all not-null constraints. This
5318 * is a bit of overkill but it minimizes risk of bugs, and
5319 * heap_attisnull is a pretty cheap test anyway.
5320 */
5321 for (i = 0; i < newTupDesc->natts; i++)
5322 {
5323 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
5324
5325 if (attr->attnotnull && !attr->attisdropped)
5326 notnull_attrs = lappend_int(notnull_attrs, i);
5327 }
5328 if (notnull_attrs)
5329 needscan = true;
5330 }
5331
5332 if (newrel || needscan)
5333 {
5334 ExprContext *econtext;
5335 TupleTableSlot *oldslot;
5336 TupleTableSlot *newslot;
5337 TableScanDesc scan;
5338 MemoryContext oldCxt;
5339 List *dropped_attrs = NIL;
5340 ListCell *lc;
5341 Snapshot snapshot;
5342
5343 if (newrel)
5344 ereport(DEBUG1,
5345 (errmsg("rewriting table \"%s\"",
5346 RelationGetRelationName(oldrel))));
5347 else
5348 ereport(DEBUG1,
5349 (errmsg("verifying table \"%s\"",
5350 RelationGetRelationName(oldrel))));
5351
5352 if (newrel)
5353 {
5354 /*
5355 * All predicate locks on the tuples or pages are about to be made
5356 * invalid, because we move tuples around. Promote them to
5357 * relation locks.
5358 */
5359 TransferPredicateLocksToHeapRelation(oldrel);
5360 }
5361
5362 econtext = GetPerTupleExprContext(estate);
5363
5364 /*
5365 * Create necessary tuple slots. When rewriting, two slots are needed,
5366 * otherwise one suffices. In the case where one slot suffices, we
5367 * need to use the new tuple descriptor, otherwise some constraints
5368 * can't be evaluated. Note that even when the tuple layout is the
5369 * same and no rewrite is required, the tupDescs might not be
5370 * (consider ADD COLUMN without a default).
5371 */
5372 if (tab->rewrite)
5373 {
5374 Assert(newrel != NULL);
5375 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
5376 table_slot_callbacks(oldrel));
5377 newslot = MakeSingleTupleTableSlot(newTupDesc,
5378 table_slot_callbacks(newrel));
5379
5380 /*
5381 * Set all columns in the new slot to NULL initially, to ensure
5382 * columns added as part of the rewrite are initialized to NULL.
5383 * That is necessary as tab->newvals will not contain an
5384 * expression for columns with a NULL default, e.g. when adding a
5385 * column without a default together with a column with a default
5386 * requiring an actual rewrite.
5387 */
5388 ExecStoreAllNullTuple(newslot);
5389 }
5390 else
5391 {
5392 oldslot = MakeSingleTupleTableSlot(newTupDesc,
5393 table_slot_callbacks(oldrel));
5394 newslot = NULL;
5395 }
5396
5397 /*
5398 * Any attributes that are dropped according to the new tuple
5399 * descriptor can be set to NULL. We precompute the list of dropped
5400 * attributes to avoid needing to do so in the per-tuple loop.
5401 */
5402 for (i = 0; i < newTupDesc->natts; i++)
5403 {
5404 if (TupleDescAttr(newTupDesc, i)->attisdropped)
5405 dropped_attrs = lappend_int(dropped_attrs, i);
5406 }
5407
5408 /*
5409 * Scan through the rows, generating a new row if needed and then
5410 * checking all the constraints.
5411 */
5412 snapshot = RegisterSnapshot(GetLatestSnapshot());
5413 scan = table_beginscan(oldrel, snapshot, 0, NULL);
5414
5415 /*
5416 * Switch to per-tuple memory context and reset it for each tuple
5417 * produced, so we don't leak memory.
5418 */
5419 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
5420
5421 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
5422 {
5423 TupleTableSlot *insertslot;
5424
5425 if (tab->rewrite > 0)
5426 {
5427 /* Extract data from old tuple */
5428 slot_getallattrs(oldslot);
5429 ExecClearTuple(newslot);
5430
5431 /* copy attributes */
5432 memcpy(newslot->tts_values, oldslot->tts_values,
5433 sizeof(Datum) * oldslot->tts_nvalid);
5434 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
5435 sizeof(bool) * oldslot->tts_nvalid);
5436
5437 /* Set dropped attributes to null in new tuple */
5438 foreach(lc, dropped_attrs)
5439 newslot->tts_isnull[lfirst_int(lc)] = true;
5440
5441 /*
5442 * Constraints and GENERATED expressions might reference the
5443 * tableoid column, so fill tts_tableOid with the desired
5444 * value. (We must do this each time, because it gets
5445 * overwritten with newrel's OID during storing.)
5446 */
5447 newslot->tts_tableOid = RelationGetRelid(oldrel);
5448
5449 /*
5450 * Process supplied expressions to replace selected columns.
5451 *
5452 * First, evaluate expressions whose inputs come from the old
5453 * tuple.
5454 */
5455 econtext->ecxt_scantuple = oldslot;
5456
5457 foreach(l, tab->newvals)
5458 {
5459 NewColumnValue *ex = lfirst(l);
5460
5461 if (ex->is_generated)
5462 continue;
5463
5464 newslot->tts_values[ex->attnum - 1]
5465 = ExecEvalExpr(ex->exprstate,
5466 econtext,
5467 &newslot->tts_isnull[ex->attnum - 1]);
5468 }
5469
5470 ExecStoreVirtualTuple(newslot);
5471
5472 /*
5473 * Now, evaluate any expressions whose inputs come from the
5474 * new tuple. We assume these columns won't reference each
5475 * other, so that there's no ordering dependency.
5476 */
5477 econtext->ecxt_scantuple = newslot;
5478
5479 foreach(l, tab->newvals)
5480 {
5481 NewColumnValue *ex = lfirst(l);
5482
5483 if (!ex->is_generated)
5484 continue;
5485
5486 newslot->tts_values[ex->attnum - 1]
5487 = ExecEvalExpr(ex->exprstate,
5488 econtext,
5489 &newslot->tts_isnull[ex->attnum - 1]);
5490 }
5491
5492 insertslot = newslot;
5493 }
5494 else
5495 {
5496 /*
5497 * If there's no rewrite, old and new table are guaranteed to
5498 * have the same AM, so we can just use the old slot to verify
5499 * new constraints etc.
5500 */
5501 insertslot = oldslot;
5502 }
5503
5504 /* Now check any constraints on the possibly-changed tuple */
5505 econtext->ecxt_scantuple = insertslot;
5506
5507 foreach(l, notnull_attrs)
5508 {
5509 int attn = lfirst_int(l);
5510
5511 if (slot_attisnull(insertslot, attn + 1))
5512 {
5513 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
5514
5515 ereport(ERROR,
5516 (errcode(ERRCODE_NOT_NULL_VIOLATION),
5517 errmsg("column \"%s\" of relation \"%s\" contains null values",
5518 NameStr(attr->attname),
5519 RelationGetRelationName(oldrel)),
5520 errtablecol(oldrel, attn + 1)));
5521 }
5522 }
5523
5524 foreach(l, tab->constraints)
5525 {
5526 NewConstraint *con = lfirst(l);
5527
5528 switch (con->contype)
5529 {
5530 case CONSTR_CHECK:
5531 if (!ExecCheck(con->qualstate, econtext))
5532 ereport(ERROR,
5533 (errcode(ERRCODE_CHECK_VIOLATION),
5534 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
5535 con->name,
5536 RelationGetRelationName(oldrel)),
5537 errtableconstraint(oldrel, con->name)));
5538 break;
5539 case CONSTR_FOREIGN:
5540 /* Nothing to do here */
5541 break;
5542 default:
5543 elog(ERROR, "unrecognized constraint type: %d",
5544 (int) con->contype);
5545 }
5546 }
5547
5548 if (partqualstate && !ExecCheck(partqualstate, econtext))
5549 {
5550 if (tab->validate_default)
5551 ereport(ERROR,
5552 (errcode(ERRCODE_CHECK_VIOLATION),
5553 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
5554 RelationGetRelationName(oldrel)),
5555 errtable(oldrel)));
5556 else
5557 ereport(ERROR,
5558 (errcode(ERRCODE_CHECK_VIOLATION),
5559 errmsg("partition constraint of relation \"%s\" is violated by some row",
5560 RelationGetRelationName(oldrel)),
5561 errtable(oldrel)));
5562 }
5563
5564 /* Write the tuple out to the new relation */
5565 if (newrel)
5566 table_tuple_insert(newrel, insertslot, mycid,
5567 ti_options, bistate);
5568
5569 ResetExprContext(econtext);
5570
5571 CHECK_FOR_INTERRUPTS();
5572 }
5573
5574 MemoryContextSwitchTo(oldCxt);
5575 table_endscan(scan);
5576 UnregisterSnapshot(snapshot);
5577
5578 ExecDropSingleTupleTableSlot(oldslot);
5579 if (newslot)
5580 ExecDropSingleTupleTableSlot(newslot);
5581 }
5582
5583 FreeExecutorState(estate);
5584
5585 table_close(oldrel, NoLock);
5586 if (newrel)
5587 {
5588 FreeBulkInsertState(bistate);
5589
5590 table_finish_bulk_insert(newrel, ti_options);
5591
5592 table_close(newrel, NoLock);
5593 }
5594 }
5595
5596 /*
5597 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
5598 */
5599 static AlteredTableInfo *
ATGetQueueEntry(List ** wqueue,Relation rel)5600 ATGetQueueEntry(List **wqueue, Relation rel)
5601 {
5602 Oid relid = RelationGetRelid(rel);
5603 AlteredTableInfo *tab;
5604 ListCell *ltab;
5605
5606 foreach(ltab, *wqueue)
5607 {
5608 tab = (AlteredTableInfo *) lfirst(ltab);
5609 if (tab->relid == relid)
5610 return tab;
5611 }
5612
5613 /*
5614 * Not there, so add it. Note that we make a copy of the relation's
5615 * existing descriptor before anything interesting can happen to it.
5616 */
5617 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
5618 tab->relid = relid;
5619 tab->relkind = rel->rd_rel->relkind;
5620 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
5621 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5622 tab->chgPersistence = false;
5623
5624 *wqueue = lappend(*wqueue, tab);
5625
5626 return tab;
5627 }
5628
5629 /*
5630 * ATSimplePermissions
5631 *
5632 * - Ensure that it is a relation (or possibly a view)
5633 * - Ensure this user is the owner
5634 * - Ensure that it is not a system table
5635 */
5636 static void
ATSimplePermissions(Relation rel,int allowed_targets)5637 ATSimplePermissions(Relation rel, int allowed_targets)
5638 {
5639 int actual_target;
5640
5641 switch (rel->rd_rel->relkind)
5642 {
5643 case RELKIND_RELATION:
5644 case RELKIND_PARTITIONED_TABLE:
5645 actual_target = ATT_TABLE;
5646 break;
5647 case RELKIND_VIEW:
5648 actual_target = ATT_VIEW;
5649 break;
5650 case RELKIND_MATVIEW:
5651 actual_target = ATT_MATVIEW;
5652 break;
5653 case RELKIND_INDEX:
5654 actual_target = ATT_INDEX;
5655 break;
5656 case RELKIND_PARTITIONED_INDEX:
5657 actual_target = ATT_PARTITIONED_INDEX;
5658 break;
5659 case RELKIND_COMPOSITE_TYPE:
5660 actual_target = ATT_COMPOSITE_TYPE;
5661 break;
5662 case RELKIND_FOREIGN_TABLE:
5663 actual_target = ATT_FOREIGN_TABLE;
5664 break;
5665 default:
5666 actual_target = 0;
5667 break;
5668 }
5669
5670 /* Wrong target type? */
5671 if ((actual_target & allowed_targets) == 0)
5672 ATWrongRelkindError(rel, allowed_targets);
5673
5674 /* Permissions checks */
5675 if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
5676 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
5677 RelationGetRelationName(rel));
5678
5679 if (!allowSystemTableMods && IsSystemRelation(rel))
5680 ereport(ERROR,
5681 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
5682 errmsg("permission denied: \"%s\" is a system catalog",
5683 RelationGetRelationName(rel))));
5684 }
5685
5686 /*
5687 * ATWrongRelkindError
5688 *
5689 * Throw an error when a relation has been determined to be of the wrong
5690 * type.
5691 */
5692 static void
ATWrongRelkindError(Relation rel,int allowed_targets)5693 ATWrongRelkindError(Relation rel, int allowed_targets)
5694 {
5695 char *msg;
5696
5697 switch (allowed_targets)
5698 {
5699 case ATT_TABLE:
5700 msg = _("\"%s\" is not a table");
5701 break;
5702 case ATT_TABLE | ATT_VIEW:
5703 msg = _("\"%s\" is not a table or view");
5704 break;
5705 case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
5706 msg = _("\"%s\" is not a table, view, or foreign table");
5707 break;
5708 case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
5709 msg = _("\"%s\" is not a table, view, materialized view, or index");
5710 break;
5711 case ATT_TABLE | ATT_MATVIEW:
5712 msg = _("\"%s\" is not a table or materialized view");
5713 break;
5714 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX:
5715 msg = _("\"%s\" is not a table, materialized view, or index");
5716 break;
5717 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX:
5718 msg = _("\"%s\" is not a table, materialized view, index, or partitioned index");
5719 break;
5720 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE:
5721 msg = _("\"%s\" is not a table, materialized view, index, partitioned index, or foreign table");
5722 break;
5723 case ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE:
5724 msg = _("\"%s\" is not a table, materialized view, or foreign table");
5725 break;
5726 case ATT_TABLE | ATT_FOREIGN_TABLE:
5727 msg = _("\"%s\" is not a table or foreign table");
5728 break;
5729 case ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE:
5730 msg = _("\"%s\" is not a table, composite type, or foreign table");
5731 break;
5732 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE:
5733 msg = _("\"%s\" is not a table, materialized view, index, or foreign table");
5734 break;
5735 case ATT_TABLE | ATT_PARTITIONED_INDEX:
5736 msg = _("\"%s\" is not a table or partitioned index");
5737 break;
5738 case ATT_VIEW:
5739 msg = _("\"%s\" is not a view");
5740 break;
5741 case ATT_FOREIGN_TABLE:
5742 msg = _("\"%s\" is not a foreign table");
5743 break;
5744 default:
5745 /* shouldn't get here, add all necessary cases above */
5746 msg = _("\"%s\" is of the wrong type");
5747 break;
5748 }
5749
5750 ereport(ERROR,
5751 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5752 errmsg(msg, RelationGetRelationName(rel))));
5753 }
5754
5755 /*
5756 * ATSimpleRecursion
5757 *
5758 * Simple table recursion sufficient for most ALTER TABLE operations.
5759 * All direct and indirect children are processed in an unspecified order.
5760 * Note that if a child inherits from the original table via multiple
5761 * inheritance paths, it will be visited just once.
5762 */
5763 static void
ATSimpleRecursion(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,LOCKMODE lockmode,AlterTableUtilityContext * context)5764 ATSimpleRecursion(List **wqueue, Relation rel,
5765 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5766 AlterTableUtilityContext *context)
5767 {
5768 /*
5769 * Propagate to children, if desired and if there are (or might be) any
5770 * children.
5771 */
5772 if (recurse && rel->rd_rel->relhassubclass)
5773 {
5774 Oid relid = RelationGetRelid(rel);
5775 ListCell *child;
5776 List *children;
5777
5778 children = find_all_inheritors(relid, lockmode, NULL);
5779
5780 /*
5781 * find_all_inheritors does the recursive search of the inheritance
5782 * hierarchy, so all we have to do is process all of the relids in the
5783 * list that it returns.
5784 */
5785 foreach(child, children)
5786 {
5787 Oid childrelid = lfirst_oid(child);
5788 Relation childrel;
5789
5790 if (childrelid == relid)
5791 continue;
5792 /* find_all_inheritors already got lock */
5793 childrel = relation_open(childrelid, NoLock);
5794 CheckTableNotInUse(childrel, "ALTER TABLE");
5795 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
5796 relation_close(childrel, NoLock);
5797 }
5798 }
5799 }
5800
5801 /*
5802 * Obtain list of partitions of the given table, locking them all at the given
5803 * lockmode and ensuring that they all pass CheckTableNotInUse.
5804 *
5805 * This function is a no-op if the given relation is not a partitioned table;
5806 * in particular, nothing is done if it's a legacy inheritance parent.
5807 */
5808 static void
ATCheckPartitionsNotInUse(Relation rel,LOCKMODE lockmode)5809 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
5810 {
5811 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5812 {
5813 List *inh;
5814 ListCell *cell;
5815
5816 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5817 /* first element is the parent rel; must ignore it */
5818 for_each_from(cell, inh, 1)
5819 {
5820 Relation childrel;
5821
5822 /* find_all_inheritors already got lock */
5823 childrel = table_open(lfirst_oid(cell), NoLock);
5824 CheckTableNotInUse(childrel, "ALTER TABLE");
5825 table_close(childrel, NoLock);
5826 }
5827 list_free(inh);
5828 }
5829 }
5830
5831 /*
5832 * ATTypedTableRecursion
5833 *
5834 * Propagate ALTER TYPE operations to the typed tables of that type.
5835 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
5836 * recursion to inheritance children of the typed tables.
5837 */
5838 static void
ATTypedTableRecursion(List ** wqueue,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode,AlterTableUtilityContext * context)5839 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
5840 LOCKMODE lockmode, AlterTableUtilityContext *context)
5841 {
5842 ListCell *child;
5843 List *children;
5844
5845 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
5846
5847 children = find_typed_table_dependencies(rel->rd_rel->reltype,
5848 RelationGetRelationName(rel),
5849 cmd->behavior);
5850
5851 foreach(child, children)
5852 {
5853 Oid childrelid = lfirst_oid(child);
5854 Relation childrel;
5855
5856 childrel = relation_open(childrelid, lockmode);
5857 CheckTableNotInUse(childrel, "ALTER TABLE");
5858 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
5859 relation_close(childrel, NoLock);
5860 }
5861 }
5862
5863
5864 /*
5865 * find_composite_type_dependencies
5866 *
5867 * Check to see if the type "typeOid" is being used as a column in some table
5868 * (possibly nested several levels deep in composite types, arrays, etc!).
5869 * Eventually, we'd like to propagate the check or rewrite operation
5870 * into such tables, but for now, just error out if we find any.
5871 *
5872 * Caller should provide either the associated relation of a rowtype,
5873 * or a type name (not both) for use in the error message, if any.
5874 *
5875 * Note that "typeOid" is not necessarily a composite type; it could also be
5876 * another container type such as an array or range, or a domain over one of
5877 * these things. The name of this function is therefore somewhat historical,
5878 * but it's not worth changing.
5879 *
5880 * We assume that functions and views depending on the type are not reasons
5881 * to reject the ALTER. (How safe is this really?)
5882 */
5883 void
find_composite_type_dependencies(Oid typeOid,Relation origRelation,const char * origTypeName)5884 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
5885 const char *origTypeName)
5886 {
5887 Relation depRel;
5888 ScanKeyData key[2];
5889 SysScanDesc depScan;
5890 HeapTuple depTup;
5891
5892 /* since this function recurses, it could be driven to stack overflow */
5893 check_stack_depth();
5894
5895 /*
5896 * We scan pg_depend to find those things that depend on the given type.
5897 * (We assume we can ignore refobjsubid for a type.)
5898 */
5899 depRel = table_open(DependRelationId, AccessShareLock);
5900
5901 ScanKeyInit(&key[0],
5902 Anum_pg_depend_refclassid,
5903 BTEqualStrategyNumber, F_OIDEQ,
5904 ObjectIdGetDatum(TypeRelationId));
5905 ScanKeyInit(&key[1],
5906 Anum_pg_depend_refobjid,
5907 BTEqualStrategyNumber, F_OIDEQ,
5908 ObjectIdGetDatum(typeOid));
5909
5910 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
5911 NULL, 2, key);
5912
5913 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
5914 {
5915 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
5916 Relation rel;
5917 Form_pg_attribute att;
5918
5919 /* Check for directly dependent types */
5920 if (pg_depend->classid == TypeRelationId)
5921 {
5922 /*
5923 * This must be an array, domain, or range containing the given
5924 * type, so recursively check for uses of this type. Note that
5925 * any error message will mention the original type not the
5926 * container; this is intentional.
5927 */
5928 find_composite_type_dependencies(pg_depend->objid,
5929 origRelation, origTypeName);
5930 continue;
5931 }
5932
5933 /* Else, ignore dependees that aren't user columns of relations */
5934 /* (we assume system columns are never of interesting types) */
5935 if (pg_depend->classid != RelationRelationId ||
5936 pg_depend->objsubid <= 0)
5937 continue;
5938
5939 rel = relation_open(pg_depend->objid, AccessShareLock);
5940 att = TupleDescAttr(rel->rd_att, pg_depend->objsubid - 1);
5941
5942 if (rel->rd_rel->relkind == RELKIND_RELATION ||
5943 rel->rd_rel->relkind == RELKIND_MATVIEW ||
5944 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5945 {
5946 if (origTypeName)
5947 ereport(ERROR,
5948 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5949 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
5950 origTypeName,
5951 RelationGetRelationName(rel),
5952 NameStr(att->attname))));
5953 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
5954 ereport(ERROR,
5955 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5956 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
5957 RelationGetRelationName(origRelation),
5958 RelationGetRelationName(rel),
5959 NameStr(att->attname))));
5960 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
5961 ereport(ERROR,
5962 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5963 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
5964 RelationGetRelationName(origRelation),
5965 RelationGetRelationName(rel),
5966 NameStr(att->attname))));
5967 else
5968 ereport(ERROR,
5969 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5970 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
5971 RelationGetRelationName(origRelation),
5972 RelationGetRelationName(rel),
5973 NameStr(att->attname))));
5974 }
5975 else if (OidIsValid(rel->rd_rel->reltype))
5976 {
5977 /*
5978 * A view or composite type itself isn't a problem, but we must
5979 * recursively check for indirect dependencies via its rowtype.
5980 */
5981 find_composite_type_dependencies(rel->rd_rel->reltype,
5982 origRelation, origTypeName);
5983 }
5984
5985 relation_close(rel, AccessShareLock);
5986 }
5987
5988 systable_endscan(depScan);
5989
5990 relation_close(depRel, AccessShareLock);
5991 }
5992
5993
5994 /*
5995 * find_typed_table_dependencies
5996 *
5997 * Check to see if a composite type is being used as the type of a
5998 * typed table. Abort if any are found and behavior is RESTRICT.
5999 * Else return the list of tables.
6000 */
6001 static List *
find_typed_table_dependencies(Oid typeOid,const char * typeName,DropBehavior behavior)6002 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6003 {
6004 Relation classRel;
6005 ScanKeyData key[1];
6006 TableScanDesc scan;
6007 HeapTuple tuple;
6008 List *result = NIL;
6009
6010 classRel = table_open(RelationRelationId, AccessShareLock);
6011
6012 ScanKeyInit(&key[0],
6013 Anum_pg_class_reloftype,
6014 BTEqualStrategyNumber, F_OIDEQ,
6015 ObjectIdGetDatum(typeOid));
6016
6017 scan = table_beginscan_catalog(classRel, 1, key);
6018
6019 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6020 {
6021 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6022
6023 if (behavior == DROP_RESTRICT)
6024 ereport(ERROR,
6025 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6026 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6027 typeName),
6028 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6029 else
6030 result = lappend_oid(result, classform->oid);
6031 }
6032
6033 table_endscan(scan);
6034 table_close(classRel, AccessShareLock);
6035
6036 return result;
6037 }
6038
6039
6040 /*
6041 * check_of_type
6042 *
6043 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6044 * isn't suitable, throw an error. Currently, we require that the type
6045 * originated with CREATE TYPE AS. We could support any row type, but doing so
6046 * would require handling a number of extra corner cases in the DDL commands.
6047 * (Also, allowing domain-over-composite would open up a can of worms about
6048 * whether and how the domain's constraints should apply to derived tables.)
6049 */
6050 void
check_of_type(HeapTuple typetuple)6051 check_of_type(HeapTuple typetuple)
6052 {
6053 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6054 bool typeOk = false;
6055
6056 if (typ->typtype == TYPTYPE_COMPOSITE)
6057 {
6058 Relation typeRelation;
6059
6060 Assert(OidIsValid(typ->typrelid));
6061 typeRelation = relation_open(typ->typrelid, AccessShareLock);
6062 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6063
6064 /*
6065 * Close the parent rel, but keep our AccessShareLock on it until xact
6066 * commit. That will prevent someone else from deleting or ALTERing
6067 * the type before the typed table creation/conversion commits.
6068 */
6069 relation_close(typeRelation, NoLock);
6070 }
6071 if (!typeOk)
6072 ereport(ERROR,
6073 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6074 errmsg("type %s is not a composite type",
6075 format_type_be(typ->oid))));
6076 }
6077
6078
6079 /*
6080 * ALTER TABLE ADD COLUMN
6081 *
6082 * Adds an additional attribute to a relation making the assumption that
6083 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6084 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6085 * AlterTableCmd's.
6086 *
6087 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6088 * have to decide at runtime whether to recurse or not depending on whether we
6089 * actually add a column or merely merge with an existing column. (We can't
6090 * check this in a static pre-pass because it won't handle multiple inheritance
6091 * situations correctly.)
6092 */
6093 static void
ATPrepAddColumn(List ** wqueue,Relation rel,bool recurse,bool recursing,bool is_view,AlterTableCmd * cmd,LOCKMODE lockmode,AlterTableUtilityContext * context)6094 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6095 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
6096 AlterTableUtilityContext *context)
6097 {
6098 if (rel->rd_rel->reloftype && !recursing)
6099 ereport(ERROR,
6100 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6101 errmsg("cannot add column to typed table")));
6102
6103 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6104 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
6105
6106 if (recurse && !is_view)
6107 cmd->subtype = AT_AddColumnRecurse;
6108 }
6109
6110 /*
6111 * Add a column to a table. The return value is the address of the
6112 * new column in the parent relation.
6113 *
6114 * cmd is pass-by-ref so that we can replace it with the parse-transformed
6115 * copy (but that happens only after we check for IF NOT EXISTS).
6116 */
6117 static ObjectAddress
ATExecAddColumn(List ** wqueue,AlteredTableInfo * tab,Relation rel,AlterTableCmd ** cmd,bool recurse,bool recursing,LOCKMODE lockmode,int cur_pass,AlterTableUtilityContext * context)6118 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
6119 AlterTableCmd **cmd,
6120 bool recurse, bool recursing,
6121 LOCKMODE lockmode, int cur_pass,
6122 AlterTableUtilityContext *context)
6123 {
6124 Oid myrelid = RelationGetRelid(rel);
6125 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
6126 bool if_not_exists = (*cmd)->missing_ok;
6127 Relation pgclass,
6128 attrdesc;
6129 HeapTuple reltup;
6130 FormData_pg_attribute attribute;
6131 int newattnum;
6132 char relkind;
6133 HeapTuple typeTuple;
6134 Oid typeOid;
6135 int32 typmod;
6136 Oid collOid;
6137 Form_pg_type tform;
6138 Expr *defval;
6139 List *children;
6140 ListCell *child;
6141 AlterTableCmd *childcmd;
6142 AclResult aclresult;
6143 ObjectAddress address;
6144
6145 /* At top level, permission check was done in ATPrepCmd, else do it */
6146 if (recursing)
6147 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6148
6149 if (rel->rd_rel->relispartition && !recursing)
6150 ereport(ERROR,
6151 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6152 errmsg("cannot add column to a partition")));
6153
6154 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
6155
6156 /*
6157 * Are we adding the column to a recursion child? If so, check whether to
6158 * merge with an existing definition for the column. If we do merge, we
6159 * must not recurse. Children will already have the column, and recursing
6160 * into them would mess up attinhcount.
6161 */
6162 if (colDef->inhcount > 0)
6163 {
6164 HeapTuple tuple;
6165
6166 /* Does child already have a column by this name? */
6167 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
6168 if (HeapTupleIsValid(tuple))
6169 {
6170 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
6171 Oid ctypeId;
6172 int32 ctypmod;
6173 Oid ccollid;
6174
6175 /* Child column must match on type, typmod, and collation */
6176 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
6177 if (ctypeId != childatt->atttypid ||
6178 ctypmod != childatt->atttypmod)
6179 ereport(ERROR,
6180 (errcode(ERRCODE_DATATYPE_MISMATCH),
6181 errmsg("child table \"%s\" has different type for column \"%s\"",
6182 RelationGetRelationName(rel), colDef->colname)));
6183 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
6184 if (ccollid != childatt->attcollation)
6185 ereport(ERROR,
6186 (errcode(ERRCODE_COLLATION_MISMATCH),
6187 errmsg("child table \"%s\" has different collation for column \"%s\"",
6188 RelationGetRelationName(rel), colDef->colname),
6189 errdetail("\"%s\" versus \"%s\"",
6190 get_collation_name(ccollid),
6191 get_collation_name(childatt->attcollation))));
6192
6193 /* Bump the existing child att's inhcount */
6194 childatt->attinhcount++;
6195 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
6196
6197 heap_freetuple(tuple);
6198
6199 /* Inform the user about the merge */
6200 ereport(NOTICE,
6201 (errmsg("merging definition of column \"%s\" for child \"%s\"",
6202 colDef->colname, RelationGetRelationName(rel))));
6203
6204 table_close(attrdesc, RowExclusiveLock);
6205 return InvalidObjectAddress;
6206 }
6207 }
6208
6209 /* skip if the name already exists and if_not_exists is true */
6210 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
6211 {
6212 table_close(attrdesc, RowExclusiveLock);
6213 return InvalidObjectAddress;
6214 }
6215
6216 /*
6217 * Okay, we need to add the column, so go ahead and do parse
6218 * transformation. This can result in queueing up, or even immediately
6219 * executing, subsidiary operations (such as creation of unique indexes);
6220 * so we mustn't do it until we have made the if_not_exists check.
6221 *
6222 * When recursing, the command was already transformed and we needn't do
6223 * so again. Also, if context isn't given we can't transform. (That
6224 * currently happens only for AT_AddColumnToView; we expect that view.c
6225 * passed us a ColumnDef that doesn't need work.)
6226 */
6227 if (context != NULL && !recursing)
6228 {
6229 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
6230 cur_pass, context);
6231 Assert(*cmd != NULL);
6232 colDef = castNode(ColumnDef, (*cmd)->def);
6233 }
6234
6235 /*
6236 * Cannot add identity column if table has children, because identity does
6237 * not inherit. (Adding column and identity separately will work.)
6238 */
6239 if (colDef->identity &&
6240 recurse &&
6241 find_inheritance_children(myrelid, NoLock) != NIL)
6242 ereport(ERROR,
6243 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6244 errmsg("cannot recursively add identity column to table that has child tables")));
6245
6246 pgclass = table_open(RelationRelationId, RowExclusiveLock);
6247
6248 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
6249 if (!HeapTupleIsValid(reltup))
6250 elog(ERROR, "cache lookup failed for relation %u", myrelid);
6251 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
6252
6253 /* Determine the new attribute's number */
6254 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
6255 if (newattnum > MaxHeapAttributeNumber)
6256 ereport(ERROR,
6257 (errcode(ERRCODE_TOO_MANY_COLUMNS),
6258 errmsg("tables can have at most %d columns",
6259 MaxHeapAttributeNumber)));
6260
6261 typeTuple = typenameType(NULL, colDef->typeName, &typmod);
6262 tform = (Form_pg_type) GETSTRUCT(typeTuple);
6263 typeOid = tform->oid;
6264
6265 aclresult = pg_type_aclcheck(typeOid, GetUserId(), ACL_USAGE);
6266 if (aclresult != ACLCHECK_OK)
6267 aclcheck_error_type(aclresult, typeOid);
6268
6269 collOid = GetColumnDefCollation(NULL, colDef, typeOid);
6270
6271 /* make sure datatype is legal for a column */
6272 CheckAttributeType(colDef->colname, typeOid, collOid,
6273 list_make1_oid(rel->rd_rel->reltype),
6274 0);
6275
6276 /* construct new attribute's pg_attribute entry */
6277 attribute.attrelid = myrelid;
6278 namestrcpy(&(attribute.attname), colDef->colname);
6279 attribute.atttypid = typeOid;
6280 attribute.attstattarget = (newattnum > 0) ? -1 : 0;
6281 attribute.attlen = tform->typlen;
6282 attribute.atttypmod = typmod;
6283 attribute.attnum = newattnum;
6284 attribute.attbyval = tform->typbyval;
6285 attribute.attndims = list_length(colDef->typeName->arrayBounds);
6286 attribute.attstorage = tform->typstorage;
6287 attribute.attalign = tform->typalign;
6288 attribute.attnotnull = colDef->is_not_null;
6289 attribute.atthasdef = false;
6290 attribute.atthasmissing = false;
6291 attribute.attidentity = colDef->identity;
6292 attribute.attgenerated = colDef->generated;
6293 attribute.attisdropped = false;
6294 attribute.attislocal = colDef->is_local;
6295 attribute.attinhcount = colDef->inhcount;
6296 attribute.attcollation = collOid;
6297 /* attribute.attacl is handled by InsertPgAttributeTuple */
6298
6299 ReleaseSysCache(typeTuple);
6300
6301 InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL);
6302
6303 table_close(attrdesc, RowExclusiveLock);
6304
6305 /*
6306 * Update pg_class tuple as appropriate
6307 */
6308 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
6309
6310 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
6311
6312 heap_freetuple(reltup);
6313
6314 /* Post creation hook for new attribute */
6315 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
6316
6317 table_close(pgclass, RowExclusiveLock);
6318
6319 /* Make the attribute's catalog entry visible */
6320 CommandCounterIncrement();
6321
6322 /*
6323 * Store the DEFAULT, if any, in the catalogs
6324 */
6325 if (colDef->raw_default)
6326 {
6327 RawColumnDefault *rawEnt;
6328
6329 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
6330 rawEnt->attnum = attribute.attnum;
6331 rawEnt->raw_default = copyObject(colDef->raw_default);
6332
6333 /*
6334 * Attempt to skip a complete table rewrite by storing the specified
6335 * DEFAULT value outside of the heap. This may be disabled inside
6336 * AddRelationNewConstraints if the optimization cannot be applied.
6337 */
6338 rawEnt->missingMode = (!colDef->generated);
6339
6340 rawEnt->generated = colDef->generated;
6341
6342 /*
6343 * This function is intended for CREATE TABLE, so it processes a
6344 * _list_ of defaults, but we just do one.
6345 */
6346 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
6347 false, true, false, NULL);
6348
6349 /* Make the additional catalog changes visible */
6350 CommandCounterIncrement();
6351
6352 /*
6353 * Did the request for a missing value work? If not we'll have to do a
6354 * rewrite
6355 */
6356 if (!rawEnt->missingMode)
6357 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
6358 }
6359
6360 /*
6361 * Tell Phase 3 to fill in the default expression, if there is one.
6362 *
6363 * If there is no default, Phase 3 doesn't have to do anything, because
6364 * that effectively means that the default is NULL. The heap tuple access
6365 * routines always check for attnum > # of attributes in tuple, and return
6366 * NULL if so, so without any modification of the tuple data we will get
6367 * the effect of NULL values in the new column.
6368 *
6369 * An exception occurs when the new column is of a domain type: the domain
6370 * might have a NOT NULL constraint, or a check constraint that indirectly
6371 * rejects nulls. If there are any domain constraints then we construct
6372 * an explicit NULL default value that will be passed through
6373 * CoerceToDomain processing. (This is a tad inefficient, since it causes
6374 * rewriting the table which we really don't have to do, but the present
6375 * design of domain processing doesn't offer any simple way of checking
6376 * the constraints more directly.)
6377 *
6378 * Note: we use build_column_default, and not just the cooked default
6379 * returned by AddRelationNewConstraints, so that the right thing happens
6380 * when a datatype's default applies.
6381 *
6382 * Note: it might seem that this should happen at the end of Phase 2, so
6383 * that the effects of subsequent subcommands can be taken into account.
6384 * It's intentional that we do it now, though. The new column should be
6385 * filled according to what is said in the ADD COLUMN subcommand, so that
6386 * the effects are the same as if this subcommand had been run by itself
6387 * and the later subcommands had been issued in new ALTER TABLE commands.
6388 *
6389 * We can skip this entirely for relations without storage, since Phase 3
6390 * is certainly not going to touch them. System attributes don't have
6391 * interesting defaults, either.
6392 */
6393 if (RELKIND_HAS_STORAGE(relkind) && attribute.attnum > 0)
6394 {
6395 /*
6396 * For an identity column, we can't use build_column_default(),
6397 * because the sequence ownership isn't set yet. So do it manually.
6398 */
6399 if (colDef->identity)
6400 {
6401 NextValueExpr *nve = makeNode(NextValueExpr);
6402
6403 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
6404 nve->typeId = typeOid;
6405
6406 defval = (Expr *) nve;
6407
6408 /* must do a rewrite for identity columns */
6409 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
6410 }
6411 else
6412 defval = (Expr *) build_column_default(rel, attribute.attnum);
6413
6414 if (!defval && DomainHasConstraints(typeOid))
6415 {
6416 Oid baseTypeId;
6417 int32 baseTypeMod;
6418 Oid baseTypeColl;
6419
6420 baseTypeMod = typmod;
6421 baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
6422 baseTypeColl = get_typcollation(baseTypeId);
6423 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
6424 defval = (Expr *) coerce_to_target_type(NULL,
6425 (Node *) defval,
6426 baseTypeId,
6427 typeOid,
6428 typmod,
6429 COERCION_ASSIGNMENT,
6430 COERCE_IMPLICIT_CAST,
6431 -1);
6432 if (defval == NULL) /* should not happen */
6433 elog(ERROR, "failed to coerce base type to domain");
6434 }
6435
6436 if (defval)
6437 {
6438 NewColumnValue *newval;
6439
6440 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
6441 newval->attnum = attribute.attnum;
6442 newval->expr = expression_planner(defval);
6443 newval->is_generated = (colDef->generated != '\0');
6444
6445 tab->newvals = lappend(tab->newvals, newval);
6446 }
6447
6448 if (DomainHasConstraints(typeOid))
6449 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
6450
6451 if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing)
6452 {
6453 /*
6454 * If the new column is NOT NULL, and there is no missing value,
6455 * tell Phase 3 it needs to check for NULLs.
6456 */
6457 tab->verify_new_notnull |= colDef->is_not_null;
6458 }
6459 }
6460
6461 /*
6462 * Add needed dependency entries for the new column.
6463 */
6464 add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
6465 add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
6466
6467 /*
6468 * Propagate to children as appropriate. Unlike most other ALTER
6469 * routines, we have to do this one level of recursion at a time; we can't
6470 * use find_all_inheritors to do it in one pass.
6471 */
6472 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
6473
6474 /*
6475 * If we are told not to recurse, there had better not be any child
6476 * tables; else the addition would put them out of step.
6477 */
6478 if (children && !recurse)
6479 ereport(ERROR,
6480 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6481 errmsg("column must be added to child tables too")));
6482
6483 /* Children should see column as singly inherited */
6484 if (!recursing)
6485 {
6486 childcmd = copyObject(*cmd);
6487 colDef = castNode(ColumnDef, childcmd->def);
6488 colDef->inhcount = 1;
6489 colDef->is_local = false;
6490 }
6491 else
6492 childcmd = *cmd; /* no need to copy again */
6493
6494 foreach(child, children)
6495 {
6496 Oid childrelid = lfirst_oid(child);
6497 Relation childrel;
6498 AlteredTableInfo *childtab;
6499
6500 /* find_inheritance_children already got lock */
6501 childrel = table_open(childrelid, NoLock);
6502 CheckTableNotInUse(childrel, "ALTER TABLE");
6503
6504 /* Find or create work queue entry for this table */
6505 childtab = ATGetQueueEntry(wqueue, childrel);
6506
6507 /* Recurse to child; return value is ignored */
6508 ATExecAddColumn(wqueue, childtab, childrel,
6509 &childcmd, recurse, true,
6510 lockmode, cur_pass, context);
6511
6512 table_close(childrel, NoLock);
6513 }
6514
6515 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
6516 return address;
6517 }
6518
6519 /*
6520 * If a new or renamed column will collide with the name of an existing
6521 * column and if_not_exists is false then error out, else do nothing.
6522 */
6523 static bool
check_for_column_name_collision(Relation rel,const char * colname,bool if_not_exists)6524 check_for_column_name_collision(Relation rel, const char *colname,
6525 bool if_not_exists)
6526 {
6527 HeapTuple attTuple;
6528 int attnum;
6529
6530 /*
6531 * this test is deliberately not attisdropped-aware, since if one tries to
6532 * add a column matching a dropped column name, it's gonna fail anyway.
6533 */
6534 attTuple = SearchSysCache2(ATTNAME,
6535 ObjectIdGetDatum(RelationGetRelid(rel)),
6536 PointerGetDatum(colname));
6537 if (!HeapTupleIsValid(attTuple))
6538 return true;
6539
6540 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
6541 ReleaseSysCache(attTuple);
6542
6543 /*
6544 * We throw a different error message for conflicts with system column
6545 * names, since they are normally not shown and the user might otherwise
6546 * be confused about the reason for the conflict.
6547 */
6548 if (attnum <= 0)
6549 ereport(ERROR,
6550 (errcode(ERRCODE_DUPLICATE_COLUMN),
6551 errmsg("column name \"%s\" conflicts with a system column name",
6552 colname)));
6553 else
6554 {
6555 if (if_not_exists)
6556 {
6557 ereport(NOTICE,
6558 (errcode(ERRCODE_DUPLICATE_COLUMN),
6559 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
6560 colname, RelationGetRelationName(rel))));
6561 return false;
6562 }
6563
6564 ereport(ERROR,
6565 (errcode(ERRCODE_DUPLICATE_COLUMN),
6566 errmsg("column \"%s\" of relation \"%s\" already exists",
6567 colname, RelationGetRelationName(rel))));
6568 }
6569
6570 return true;
6571 }
6572
6573 /*
6574 * Install a column's dependency on its datatype.
6575 */
6576 static void
add_column_datatype_dependency(Oid relid,int32 attnum,Oid typid)6577 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
6578 {
6579 ObjectAddress myself,
6580 referenced;
6581
6582 myself.classId = RelationRelationId;
6583 myself.objectId = relid;
6584 myself.objectSubId = attnum;
6585 referenced.classId = TypeRelationId;
6586 referenced.objectId = typid;
6587 referenced.objectSubId = 0;
6588 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
6589 }
6590
6591 /*
6592 * Install a column's dependency on its collation.
6593 */
6594 static void
add_column_collation_dependency(Oid relid,int32 attnum,Oid collid)6595 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
6596 {
6597 ObjectAddress myself,
6598 referenced;
6599
6600 /* We know the default collation is pinned, so don't bother recording it */
6601 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
6602 {
6603 myself.classId = RelationRelationId;
6604 myself.objectId = relid;
6605 myself.objectSubId = attnum;
6606 referenced.classId = CollationRelationId;
6607 referenced.objectId = collid;
6608 referenced.objectSubId = 0;
6609 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
6610 }
6611 }
6612
6613 /*
6614 * ALTER TABLE ALTER COLUMN DROP NOT NULL
6615 */
6616
6617 static void
ATPrepDropNotNull(Relation rel,bool recurse,bool recursing)6618 ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
6619 {
6620 /*
6621 * If the parent is a partitioned table, like check constraints, we do not
6622 * support removing the NOT NULL while partitions exist.
6623 */
6624 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6625 {
6626 PartitionDesc partdesc = RelationGetPartitionDesc(rel);
6627
6628 Assert(partdesc != NULL);
6629 if (partdesc->nparts > 0 && !recurse && !recursing)
6630 ereport(ERROR,
6631 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6632 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
6633 errhint("Do not specify the ONLY keyword.")));
6634 }
6635 }
6636
6637 /*
6638 * Return the address of the modified column. If the column was already
6639 * nullable, InvalidObjectAddress is returned.
6640 */
6641 static ObjectAddress
ATExecDropNotNull(Relation rel,const char * colName,LOCKMODE lockmode)6642 ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
6643 {
6644 HeapTuple tuple;
6645 Form_pg_attribute attTup;
6646 AttrNumber attnum;
6647 Relation attr_rel;
6648 List *indexoidlist;
6649 ListCell *indexoidscan;
6650 ObjectAddress address;
6651
6652 /*
6653 * lookup the attribute
6654 */
6655 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
6656
6657 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6658 if (!HeapTupleIsValid(tuple))
6659 ereport(ERROR,
6660 (errcode(ERRCODE_UNDEFINED_COLUMN),
6661 errmsg("column \"%s\" of relation \"%s\" does not exist",
6662 colName, RelationGetRelationName(rel))));
6663 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6664 attnum = attTup->attnum;
6665
6666 /* Prevent them from altering a system attribute */
6667 if (attnum <= 0)
6668 ereport(ERROR,
6669 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6670 errmsg("cannot alter system column \"%s\"",
6671 colName)));
6672
6673 if (attTup->attidentity)
6674 ereport(ERROR,
6675 (errcode(ERRCODE_SYNTAX_ERROR),
6676 errmsg("column \"%s\" of relation \"%s\" is an identity column",
6677 colName, RelationGetRelationName(rel))));
6678
6679 /*
6680 * Check that the attribute is not in a primary key
6681 *
6682 * Note: we'll throw error even if the pkey index is not valid.
6683 */
6684
6685 /* Loop over all indexes on the relation */
6686 indexoidlist = RelationGetIndexList(rel);
6687
6688 foreach(indexoidscan, indexoidlist)
6689 {
6690 Oid indexoid = lfirst_oid(indexoidscan);
6691 HeapTuple indexTuple;
6692 Form_pg_index indexStruct;
6693 int i;
6694
6695 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
6696 if (!HeapTupleIsValid(indexTuple))
6697 elog(ERROR, "cache lookup failed for index %u", indexoid);
6698 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
6699
6700 /* If the index is not a primary key, skip the check */
6701 if (indexStruct->indisprimary)
6702 {
6703 /*
6704 * Loop over each attribute in the primary key and see if it
6705 * matches the to-be-altered attribute
6706 */
6707 for (i = 0; i < indexStruct->indnkeyatts; i++)
6708 {
6709 if (indexStruct->indkey.values[i] == attnum)
6710 ereport(ERROR,
6711 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6712 errmsg("column \"%s\" is in a primary key",
6713 colName)));
6714 }
6715 }
6716
6717 ReleaseSysCache(indexTuple);
6718 }
6719
6720 list_free(indexoidlist);
6721
6722 /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
6723 if (rel->rd_rel->relispartition)
6724 {
6725 Oid parentId = get_partition_parent(RelationGetRelid(rel));
6726 Relation parent = table_open(parentId, AccessShareLock);
6727 TupleDesc tupDesc = RelationGetDescr(parent);
6728 AttrNumber parent_attnum;
6729
6730 parent_attnum = get_attnum(parentId, colName);
6731 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
6732 ereport(ERROR,
6733 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6734 errmsg("column \"%s\" is marked NOT NULL in parent table",
6735 colName)));
6736 table_close(parent, AccessShareLock);
6737 }
6738
6739 /*
6740 * Okay, actually perform the catalog change ... if needed
6741 */
6742 if (attTup->attnotnull)
6743 {
6744 attTup->attnotnull = false;
6745
6746 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
6747
6748 ObjectAddressSubSet(address, RelationRelationId,
6749 RelationGetRelid(rel), attnum);
6750 }
6751 else
6752 address = InvalidObjectAddress;
6753
6754 InvokeObjectPostAlterHook(RelationRelationId,
6755 RelationGetRelid(rel), attnum);
6756
6757 table_close(attr_rel, RowExclusiveLock);
6758
6759 return address;
6760 }
6761
6762 /*
6763 * ALTER TABLE ALTER COLUMN SET NOT NULL
6764 */
6765
6766 static void
ATPrepSetNotNull(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode,AlterTableUtilityContext * context)6767 ATPrepSetNotNull(List **wqueue, Relation rel,
6768 AlterTableCmd *cmd, bool recurse, bool recursing,
6769 LOCKMODE lockmode, AlterTableUtilityContext *context)
6770 {
6771 /*
6772 * If we're already recursing, there's nothing to do; the topmost
6773 * invocation of ATSimpleRecursion already visited all children.
6774 */
6775 if (recursing)
6776 return;
6777
6778 /*
6779 * If the target column is already marked NOT NULL, we can skip recursing
6780 * to children, because their columns should already be marked NOT NULL as
6781 * well. But there's no point in checking here unless the relation has
6782 * some children; else we can just wait till execution to check. (If it
6783 * does have children, however, this can save taking per-child locks
6784 * unnecessarily. This greatly improves concurrency in some parallel
6785 * restore scenarios.)
6786 *
6787 * Unfortunately, we can only apply this optimization to partitioned
6788 * tables, because traditional inheritance doesn't enforce that child
6789 * columns be NOT NULL when their parent is. (That's a bug that should
6790 * get fixed someday.)
6791 */
6792 if (rel->rd_rel->relhassubclass &&
6793 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6794 {
6795 HeapTuple tuple;
6796 bool attnotnull;
6797
6798 tuple = SearchSysCacheAttName(RelationGetRelid(rel), cmd->name);
6799
6800 /* Might as well throw the error now, if name is bad */
6801 if (!HeapTupleIsValid(tuple))
6802 ereport(ERROR,
6803 (errcode(ERRCODE_UNDEFINED_COLUMN),
6804 errmsg("column \"%s\" of relation \"%s\" does not exist",
6805 cmd->name, RelationGetRelationName(rel))));
6806
6807 attnotnull = ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull;
6808 ReleaseSysCache(tuple);
6809 if (attnotnull)
6810 return;
6811 }
6812
6813 /*
6814 * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
6815 * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use
6816 * normal recursion logic.
6817 */
6818 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
6819 !recurse)
6820 {
6821 AlterTableCmd *newcmd = makeNode(AlterTableCmd);
6822
6823 newcmd->subtype = AT_CheckNotNull;
6824 newcmd->name = pstrdup(cmd->name);
6825 ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context);
6826 }
6827 else
6828 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
6829 }
6830
6831 /*
6832 * Return the address of the modified column. If the column was already NOT
6833 * NULL, InvalidObjectAddress is returned.
6834 */
6835 static ObjectAddress
ATExecSetNotNull(AlteredTableInfo * tab,Relation rel,const char * colName,LOCKMODE lockmode)6836 ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
6837 const char *colName, LOCKMODE lockmode)
6838 {
6839 HeapTuple tuple;
6840 AttrNumber attnum;
6841 Relation attr_rel;
6842 ObjectAddress address;
6843
6844 /*
6845 * lookup the attribute
6846 */
6847 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
6848
6849 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6850
6851 if (!HeapTupleIsValid(tuple))
6852 ereport(ERROR,
6853 (errcode(ERRCODE_UNDEFINED_COLUMN),
6854 errmsg("column \"%s\" of relation \"%s\" does not exist",
6855 colName, RelationGetRelationName(rel))));
6856
6857 attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
6858
6859 /* Prevent them from altering a system attribute */
6860 if (attnum <= 0)
6861 ereport(ERROR,
6862 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6863 errmsg("cannot alter system column \"%s\"",
6864 colName)));
6865
6866 /*
6867 * Okay, actually perform the catalog change ... if needed
6868 */
6869 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
6870 {
6871 ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = true;
6872
6873 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
6874
6875 /*
6876 * Ordinarily phase 3 must ensure that no NULLs exist in columns that
6877 * are set NOT NULL; however, if we can find a constraint which proves
6878 * this then we can skip that. We needn't bother looking if we've
6879 * already found that we must verify some other NOT NULL constraint.
6880 */
6881 if (!tab->verify_new_notnull &&
6882 !NotNullImpliedByRelConstraints(rel, (Form_pg_attribute) GETSTRUCT(tuple)))
6883 {
6884 /* Tell Phase 3 it needs to test the constraint */
6885 tab->verify_new_notnull = true;
6886 }
6887
6888 ObjectAddressSubSet(address, RelationRelationId,
6889 RelationGetRelid(rel), attnum);
6890 }
6891 else
6892 address = InvalidObjectAddress;
6893
6894 InvokeObjectPostAlterHook(RelationRelationId,
6895 RelationGetRelid(rel), attnum);
6896
6897 table_close(attr_rel, RowExclusiveLock);
6898
6899 return address;
6900 }
6901
6902 /*
6903 * ALTER TABLE ALTER COLUMN CHECK NOT NULL
6904 *
6905 * This doesn't exist in the grammar, but we generate AT_CheckNotNull
6906 * commands against the partitions of a partitioned table if the user
6907 * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
6908 * or tries to create a primary key on it (which internally creates
6909 * AT_SetNotNull on the partitioned table). Such a command doesn't
6910 * allow us to actually modify any partition, but we want to let it
6911 * go through if the partitions are already properly marked.
6912 *
6913 * In future, this might need to adjust the child table's state, likely
6914 * by incrementing an inheritance count for the attnotnull constraint.
6915 * For now we need only check for the presence of the flag.
6916 */
6917 static void
ATExecCheckNotNull(AlteredTableInfo * tab,Relation rel,const char * colName,LOCKMODE lockmode)6918 ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
6919 const char *colName, LOCKMODE lockmode)
6920 {
6921 HeapTuple tuple;
6922
6923 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
6924
6925 if (!HeapTupleIsValid(tuple))
6926 ereport(ERROR,
6927 (errcode(ERRCODE_UNDEFINED_COLUMN),
6928 errmsg("column \"%s\" of relation \"%s\" does not exist",
6929 colName, RelationGetRelationName(rel))));
6930
6931 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
6932 ereport(ERROR,
6933 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6934 errmsg("constraint must be added to child tables too"),
6935 errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
6936 colName, RelationGetRelationName(rel)),
6937 errhint("Do not specify the ONLY keyword.")));
6938
6939 ReleaseSysCache(tuple);
6940 }
6941
6942 /*
6943 * NotNullImpliedByRelConstraints
6944 * Does rel's existing constraints imply NOT NULL for the given attribute?
6945 */
6946 static bool
NotNullImpliedByRelConstraints(Relation rel,Form_pg_attribute attr)6947 NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
6948 {
6949 NullTest *nnulltest = makeNode(NullTest);
6950
6951 nnulltest->arg = (Expr *) makeVar(1,
6952 attr->attnum,
6953 attr->atttypid,
6954 attr->atttypmod,
6955 attr->attcollation,
6956 0);
6957 nnulltest->nulltesttype = IS_NOT_NULL;
6958
6959 /*
6960 * argisrow = false is correct even for a composite column, because
6961 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
6962 * case, just IS DISTINCT FROM NULL.
6963 */
6964 nnulltest->argisrow = false;
6965 nnulltest->location = -1;
6966
6967 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
6968 {
6969 ereport(DEBUG1,
6970 (errmsg("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
6971 RelationGetRelationName(rel), NameStr(attr->attname))));
6972 return true;
6973 }
6974
6975 return false;
6976 }
6977
6978 /*
6979 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
6980 *
6981 * Return the address of the affected column.
6982 */
6983 static ObjectAddress
ATExecColumnDefault(Relation rel,const char * colName,Node * newDefault,LOCKMODE lockmode)6984 ATExecColumnDefault(Relation rel, const char *colName,
6985 Node *newDefault, LOCKMODE lockmode)
6986 {
6987 TupleDesc tupdesc = RelationGetDescr(rel);
6988 AttrNumber attnum;
6989 ObjectAddress address;
6990
6991 /*
6992 * get the number of the attribute
6993 */
6994 attnum = get_attnum(RelationGetRelid(rel), colName);
6995 if (attnum == InvalidAttrNumber)
6996 ereport(ERROR,
6997 (errcode(ERRCODE_UNDEFINED_COLUMN),
6998 errmsg("column \"%s\" of relation \"%s\" does not exist",
6999 colName, RelationGetRelationName(rel))));
7000
7001 /* Prevent them from altering a system attribute */
7002 if (attnum <= 0)
7003 ereport(ERROR,
7004 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7005 errmsg("cannot alter system column \"%s\"",
7006 colName)));
7007
7008 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7009 ereport(ERROR,
7010 (errcode(ERRCODE_SYNTAX_ERROR),
7011 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7012 colName, RelationGetRelationName(rel)),
7013 newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
7014
7015 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7016 ereport(ERROR,
7017 (errcode(ERRCODE_SYNTAX_ERROR),
7018 errmsg("column \"%s\" of relation \"%s\" is a generated column",
7019 colName, RelationGetRelationName(rel)),
7020 newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
7021 errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.")));
7022
7023 /*
7024 * Remove any old default for the column. We use RESTRICT here for
7025 * safety, but at present we do not expect anything to depend on the
7026 * default.
7027 *
7028 * We treat removing the existing default as an internal operation when it
7029 * is preparatory to adding a new default, but as a user-initiated
7030 * operation when the user asked for a drop.
7031 */
7032 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7033 newDefault == NULL ? false : true);
7034
7035 if (newDefault)
7036 {
7037 /* SET DEFAULT */
7038 RawColumnDefault *rawEnt;
7039
7040 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7041 rawEnt->attnum = attnum;
7042 rawEnt->raw_default = newDefault;
7043 rawEnt->missingMode = false;
7044 rawEnt->generated = '\0';
7045
7046 /*
7047 * This function is intended for CREATE TABLE, so it processes a
7048 * _list_ of defaults, but we just do one.
7049 */
7050 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7051 false, true, false, NULL);
7052 }
7053
7054 ObjectAddressSubSet(address, RelationRelationId,
7055 RelationGetRelid(rel), attnum);
7056 return address;
7057 }
7058
7059 /*
7060 * Add a pre-cooked default expression.
7061 *
7062 * Return the address of the affected column.
7063 */
7064 static ObjectAddress
ATExecCookedColumnDefault(Relation rel,AttrNumber attnum,Node * newDefault)7065 ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
7066 Node *newDefault)
7067 {
7068 ObjectAddress address;
7069
7070 /* We assume no checking is required */
7071
7072 /*
7073 * Remove any old default for the column. We use RESTRICT here for
7074 * safety, but at present we do not expect anything to depend on the
7075 * default. (In ordinary cases, there could not be a default in place
7076 * anyway, but it's possible when combining LIKE with inheritance.)
7077 */
7078 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7079 true);
7080
7081 (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
7082
7083 ObjectAddressSubSet(address, RelationRelationId,
7084 RelationGetRelid(rel), attnum);
7085 return address;
7086 }
7087
7088 /*
7089 * ALTER TABLE ALTER COLUMN ADD IDENTITY
7090 *
7091 * Return the address of the affected column.
7092 */
7093 static ObjectAddress
ATExecAddIdentity(Relation rel,const char * colName,Node * def,LOCKMODE lockmode)7094 ATExecAddIdentity(Relation rel, const char *colName,
7095 Node *def, LOCKMODE lockmode)
7096 {
7097 Relation attrelation;
7098 HeapTuple tuple;
7099 Form_pg_attribute attTup;
7100 AttrNumber attnum;
7101 ObjectAddress address;
7102 ColumnDef *cdef = castNode(ColumnDef, def);
7103
7104 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7105
7106 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7107 if (!HeapTupleIsValid(tuple))
7108 ereport(ERROR,
7109 (errcode(ERRCODE_UNDEFINED_COLUMN),
7110 errmsg("column \"%s\" of relation \"%s\" does not exist",
7111 colName, RelationGetRelationName(rel))));
7112 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7113 attnum = attTup->attnum;
7114
7115 /* Can't alter a system attribute */
7116 if (attnum <= 0)
7117 ereport(ERROR,
7118 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7119 errmsg("cannot alter system column \"%s\"",
7120 colName)));
7121
7122 /*
7123 * Creating a column as identity implies NOT NULL, so adding the identity
7124 * to an existing column that is not NOT NULL would create a state that
7125 * cannot be reproduced without contortions.
7126 */
7127 if (!attTup->attnotnull)
7128 ereport(ERROR,
7129 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7130 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
7131 colName, RelationGetRelationName(rel))));
7132
7133 if (attTup->attidentity)
7134 ereport(ERROR,
7135 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7136 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
7137 colName, RelationGetRelationName(rel))));
7138
7139 if (attTup->atthasdef)
7140 ereport(ERROR,
7141 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7142 errmsg("column \"%s\" of relation \"%s\" already has a default value",
7143 colName, RelationGetRelationName(rel))));
7144
7145 attTup->attidentity = cdef->identity;
7146 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7147
7148 InvokeObjectPostAlterHook(RelationRelationId,
7149 RelationGetRelid(rel),
7150 attTup->attnum);
7151 ObjectAddressSubSet(address, RelationRelationId,
7152 RelationGetRelid(rel), attnum);
7153 heap_freetuple(tuple);
7154
7155 table_close(attrelation, RowExclusiveLock);
7156
7157 return address;
7158 }
7159
7160 /*
7161 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
7162 *
7163 * Return the address of the affected column.
7164 */
7165 static ObjectAddress
ATExecSetIdentity(Relation rel,const char * colName,Node * def,LOCKMODE lockmode)7166 ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
7167 {
7168 ListCell *option;
7169 DefElem *generatedEl = NULL;
7170 HeapTuple tuple;
7171 Form_pg_attribute attTup;
7172 AttrNumber attnum;
7173 Relation attrelation;
7174 ObjectAddress address;
7175
7176 foreach(option, castNode(List, def))
7177 {
7178 DefElem *defel = lfirst_node(DefElem, option);
7179
7180 if (strcmp(defel->defname, "generated") == 0)
7181 {
7182 if (generatedEl)
7183 ereport(ERROR,
7184 (errcode(ERRCODE_SYNTAX_ERROR),
7185 errmsg("conflicting or redundant options")));
7186 generatedEl = defel;
7187 }
7188 else
7189 elog(ERROR, "option \"%s\" not recognized",
7190 defel->defname);
7191 }
7192
7193 /*
7194 * Even if there is nothing to change here, we run all the checks. There
7195 * will be a subsequent ALTER SEQUENCE that relies on everything being
7196 * there.
7197 */
7198
7199 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7200 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7201 if (!HeapTupleIsValid(tuple))
7202 ereport(ERROR,
7203 (errcode(ERRCODE_UNDEFINED_COLUMN),
7204 errmsg("column \"%s\" of relation \"%s\" does not exist",
7205 colName, RelationGetRelationName(rel))));
7206
7207 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7208 attnum = attTup->attnum;
7209
7210 if (attnum <= 0)
7211 ereport(ERROR,
7212 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7213 errmsg("cannot alter system column \"%s\"",
7214 colName)));
7215
7216 if (!attTup->attidentity)
7217 ereport(ERROR,
7218 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7219 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
7220 colName, RelationGetRelationName(rel))));
7221
7222 if (generatedEl)
7223 {
7224 attTup->attidentity = defGetInt32(generatedEl);
7225 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7226
7227 InvokeObjectPostAlterHook(RelationRelationId,
7228 RelationGetRelid(rel),
7229 attTup->attnum);
7230 ObjectAddressSubSet(address, RelationRelationId,
7231 RelationGetRelid(rel), attnum);
7232 }
7233 else
7234 address = InvalidObjectAddress;
7235
7236 heap_freetuple(tuple);
7237 table_close(attrelation, RowExclusiveLock);
7238
7239 return address;
7240 }
7241
7242 /*
7243 * ALTER TABLE ALTER COLUMN DROP IDENTITY
7244 *
7245 * Return the address of the affected column.
7246 */
7247 static ObjectAddress
ATExecDropIdentity(Relation rel,const char * colName,bool missing_ok,LOCKMODE lockmode)7248 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
7249 {
7250 HeapTuple tuple;
7251 Form_pg_attribute attTup;
7252 AttrNumber attnum;
7253 Relation attrelation;
7254 ObjectAddress address;
7255 Oid seqid;
7256 ObjectAddress seqaddress;
7257
7258 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7259 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7260 if (!HeapTupleIsValid(tuple))
7261 ereport(ERROR,
7262 (errcode(ERRCODE_UNDEFINED_COLUMN),
7263 errmsg("column \"%s\" of relation \"%s\" does not exist",
7264 colName, RelationGetRelationName(rel))));
7265
7266 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7267 attnum = attTup->attnum;
7268
7269 if (attnum <= 0)
7270 ereport(ERROR,
7271 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7272 errmsg("cannot alter system column \"%s\"",
7273 colName)));
7274
7275 if (!attTup->attidentity)
7276 {
7277 if (!missing_ok)
7278 ereport(ERROR,
7279 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7280 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
7281 colName, RelationGetRelationName(rel))));
7282 else
7283 {
7284 ereport(NOTICE,
7285 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
7286 colName, RelationGetRelationName(rel))));
7287 heap_freetuple(tuple);
7288 table_close(attrelation, RowExclusiveLock);
7289 return InvalidObjectAddress;
7290 }
7291 }
7292
7293 attTup->attidentity = '\0';
7294 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7295
7296 InvokeObjectPostAlterHook(RelationRelationId,
7297 RelationGetRelid(rel),
7298 attTup->attnum);
7299 ObjectAddressSubSet(address, RelationRelationId,
7300 RelationGetRelid(rel), attnum);
7301 heap_freetuple(tuple);
7302
7303 table_close(attrelation, RowExclusiveLock);
7304
7305 /* drop the internal sequence */
7306 seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
7307 deleteDependencyRecordsForClass(RelationRelationId, seqid,
7308 RelationRelationId, DEPENDENCY_INTERNAL);
7309 CommandCounterIncrement();
7310 seqaddress.classId = RelationRelationId;
7311 seqaddress.objectId = seqid;
7312 seqaddress.objectSubId = 0;
7313 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
7314
7315 return address;
7316 }
7317
7318 /*
7319 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
7320 */
7321 static void
ATPrepDropExpression(Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode)7322 ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
7323 {
7324 /*
7325 * Reject ONLY if there are child tables. We could implement this, but it
7326 * is a bit complicated. GENERATED clauses must be attached to the column
7327 * definition and cannot be added later like DEFAULT, so if a child table
7328 * has a generation expression that the parent does not have, the child
7329 * column will necessarily be an attlocal column. So to implement ONLY
7330 * here, we'd need extra code to update attislocal of the direct child
7331 * tables, somewhat similar to how DROP COLUMN does it, so that the
7332 * resulting state can be properly dumped and restored.
7333 */
7334 if (!recurse &&
7335 find_inheritance_children(RelationGetRelid(rel), lockmode))
7336 ereport(ERROR,
7337 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7338 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
7339
7340 /*
7341 * Cannot drop generation expression from inherited columns.
7342 */
7343 if (!recursing)
7344 {
7345 HeapTuple tuple;
7346 Form_pg_attribute attTup;
7347
7348 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
7349 if (!HeapTupleIsValid(tuple))
7350 ereport(ERROR,
7351 (errcode(ERRCODE_UNDEFINED_COLUMN),
7352 errmsg("column \"%s\" of relation \"%s\" does not exist",
7353 cmd->name, RelationGetRelationName(rel))));
7354
7355 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7356
7357 if (attTup->attinhcount > 0)
7358 ereport(ERROR,
7359 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7360 errmsg("cannot drop generation expression from inherited column")));
7361 }
7362 }
7363
7364 /*
7365 * Return the address of the affected column.
7366 */
7367 static ObjectAddress
ATExecDropExpression(Relation rel,const char * colName,bool missing_ok,LOCKMODE lockmode)7368 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
7369 {
7370 HeapTuple tuple;
7371 Form_pg_attribute attTup;
7372 AttrNumber attnum;
7373 Relation attrelation;
7374 ObjectAddress address;
7375
7376 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7377 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7378 if (!HeapTupleIsValid(tuple))
7379 ereport(ERROR,
7380 (errcode(ERRCODE_UNDEFINED_COLUMN),
7381 errmsg("column \"%s\" of relation \"%s\" does not exist",
7382 colName, RelationGetRelationName(rel))));
7383
7384 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7385 attnum = attTup->attnum;
7386
7387 if (attnum <= 0)
7388 ereport(ERROR,
7389 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7390 errmsg("cannot alter system column \"%s\"",
7391 colName)));
7392
7393 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
7394 {
7395 if (!missing_ok)
7396 ereport(ERROR,
7397 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7398 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
7399 colName, RelationGetRelationName(rel))));
7400 else
7401 {
7402 ereport(NOTICE,
7403 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
7404 colName, RelationGetRelationName(rel))));
7405 heap_freetuple(tuple);
7406 table_close(attrelation, RowExclusiveLock);
7407 return InvalidObjectAddress;
7408 }
7409 }
7410
7411 attTup->attgenerated = '\0';
7412 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7413
7414 InvokeObjectPostAlterHook(RelationRelationId,
7415 RelationGetRelid(rel),
7416 attTup->attnum);
7417 ObjectAddressSubSet(address, RelationRelationId,
7418 RelationGetRelid(rel), attnum);
7419 heap_freetuple(tuple);
7420
7421 table_close(attrelation, RowExclusiveLock);
7422
7423 CommandCounterIncrement();
7424
7425 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false, false);
7426
7427 /*
7428 * Remove all dependencies of this (formerly generated) column on other
7429 * columns in the same table. (See StoreAttrDefault() for which
7430 * dependencies are created.) We don't expect there to be dependencies
7431 * between columns of the same table for other reasons, so it's okay to
7432 * remove all of them.
7433 */
7434 {
7435 Relation depRel;
7436 ScanKeyData key[3];
7437 SysScanDesc scan;
7438 HeapTuple tup;
7439
7440 depRel = table_open(DependRelationId, RowExclusiveLock);
7441
7442 ScanKeyInit(&key[0],
7443 Anum_pg_depend_classid,
7444 BTEqualStrategyNumber, F_OIDEQ,
7445 ObjectIdGetDatum(RelationRelationId));
7446 ScanKeyInit(&key[1],
7447 Anum_pg_depend_objid,
7448 BTEqualStrategyNumber, F_OIDEQ,
7449 ObjectIdGetDatum(RelationGetRelid(rel)));
7450 ScanKeyInit(&key[2],
7451 Anum_pg_depend_objsubid,
7452 BTEqualStrategyNumber, F_INT4EQ,
7453 Int32GetDatum(attnum));
7454
7455 scan = systable_beginscan(depRel, DependDependerIndexId, true,
7456 NULL, 3, key);
7457
7458 while (HeapTupleIsValid(tup = systable_getnext(scan)))
7459 {
7460 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
7461
7462 if (depform->refclassid == RelationRelationId &&
7463 depform->refobjid == RelationGetRelid(rel) &&
7464 depform->refobjsubid != 0 &&
7465 depform->deptype == DEPENDENCY_AUTO)
7466 {
7467 CatalogTupleDelete(depRel, &tup->t_self);
7468 }
7469 }
7470
7471 systable_endscan(scan);
7472
7473 table_close(depRel, RowExclusiveLock);
7474 }
7475
7476 return address;
7477 }
7478
7479 /*
7480 * ALTER TABLE ALTER COLUMN SET STATISTICS
7481 *
7482 * Return value is the address of the modified column
7483 */
7484 static ObjectAddress
ATExecSetStatistics(Relation rel,const char * colName,int16 colNum,Node * newValue,LOCKMODE lockmode)7485 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
7486 {
7487 int newtarget;
7488 Relation attrelation;
7489 HeapTuple tuple;
7490 Form_pg_attribute attrtuple;
7491 AttrNumber attnum;
7492 ObjectAddress address;
7493
7494 /*
7495 * We allow referencing columns by numbers only for indexes, since table
7496 * column numbers could contain gaps if columns are later dropped.
7497 */
7498 if (rel->rd_rel->relkind != RELKIND_INDEX &&
7499 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
7500 !colName)
7501 ereport(ERROR,
7502 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7503 errmsg("cannot refer to non-index column by number")));
7504
7505 Assert(IsA(newValue, Integer));
7506 newtarget = intVal(newValue);
7507
7508 /*
7509 * Limit target to a sane range
7510 */
7511 if (newtarget < -1)
7512 {
7513 ereport(ERROR,
7514 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
7515 errmsg("statistics target %d is too low",
7516 newtarget)));
7517 }
7518 else if (newtarget > 10000)
7519 {
7520 newtarget = 10000;
7521 ereport(WARNING,
7522 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
7523 errmsg("lowering statistics target to %d",
7524 newtarget)));
7525 }
7526
7527 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7528
7529 if (colName)
7530 {
7531 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7532
7533 if (!HeapTupleIsValid(tuple))
7534 ereport(ERROR,
7535 (errcode(ERRCODE_UNDEFINED_COLUMN),
7536 errmsg("column \"%s\" of relation \"%s\" does not exist",
7537 colName, RelationGetRelationName(rel))));
7538 }
7539 else
7540 {
7541 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
7542
7543 if (!HeapTupleIsValid(tuple))
7544 ereport(ERROR,
7545 (errcode(ERRCODE_UNDEFINED_COLUMN),
7546 errmsg("column number %d of relation \"%s\" does not exist",
7547 colNum, RelationGetRelationName(rel))));
7548 }
7549
7550 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
7551
7552 attnum = attrtuple->attnum;
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 (rel->rd_rel->relkind == RELKIND_INDEX ||
7560 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
7561 {
7562 if (attnum > rel->rd_index->indnkeyatts)
7563 ereport(ERROR,
7564 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7565 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
7566 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
7567 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
7568 ereport(ERROR,
7569 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7570 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
7571 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
7572 errhint("Alter statistics on table column instead.")));
7573 }
7574
7575 attrtuple->attstattarget = newtarget;
7576
7577 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7578
7579 InvokeObjectPostAlterHook(RelationRelationId,
7580 RelationGetRelid(rel),
7581 attrtuple->attnum);
7582 ObjectAddressSubSet(address, RelationRelationId,
7583 RelationGetRelid(rel), attnum);
7584 heap_freetuple(tuple);
7585
7586 table_close(attrelation, RowExclusiveLock);
7587
7588 return address;
7589 }
7590
7591 /*
7592 * Return value is the address of the modified column
7593 */
7594 static ObjectAddress
ATExecSetOptions(Relation rel,const char * colName,Node * options,bool isReset,LOCKMODE lockmode)7595 ATExecSetOptions(Relation rel, const char *colName, Node *options,
7596 bool isReset, LOCKMODE lockmode)
7597 {
7598 Relation attrelation;
7599 HeapTuple tuple,
7600 newtuple;
7601 Form_pg_attribute attrtuple;
7602 AttrNumber attnum;
7603 Datum datum,
7604 newOptions;
7605 bool isnull;
7606 ObjectAddress address;
7607 Datum repl_val[Natts_pg_attribute];
7608 bool repl_null[Natts_pg_attribute];
7609 bool repl_repl[Natts_pg_attribute];
7610
7611 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7612
7613 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7614
7615 if (!HeapTupleIsValid(tuple))
7616 ereport(ERROR,
7617 (errcode(ERRCODE_UNDEFINED_COLUMN),
7618 errmsg("column \"%s\" of relation \"%s\" does not exist",
7619 colName, RelationGetRelationName(rel))));
7620 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
7621
7622 attnum = attrtuple->attnum;
7623 if (attnum <= 0)
7624 ereport(ERROR,
7625 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7626 errmsg("cannot alter system column \"%s\"",
7627 colName)));
7628
7629 /* Generate new proposed attoptions (text array) */
7630 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
7631 &isnull);
7632 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
7633 castNode(List, options), NULL, NULL,
7634 false, isReset);
7635 /* Validate new options */
7636 (void) attribute_reloptions(newOptions, true);
7637
7638 /* Build new tuple. */
7639 memset(repl_null, false, sizeof(repl_null));
7640 memset(repl_repl, false, sizeof(repl_repl));
7641 if (newOptions != (Datum) 0)
7642 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
7643 else
7644 repl_null[Anum_pg_attribute_attoptions - 1] = true;
7645 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
7646 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
7647 repl_val, repl_null, repl_repl);
7648
7649 /* Update system catalog. */
7650 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
7651
7652 InvokeObjectPostAlterHook(RelationRelationId,
7653 RelationGetRelid(rel),
7654 attrtuple->attnum);
7655 ObjectAddressSubSet(address, RelationRelationId,
7656 RelationGetRelid(rel), attnum);
7657
7658 heap_freetuple(newtuple);
7659
7660 ReleaseSysCache(tuple);
7661
7662 table_close(attrelation, RowExclusiveLock);
7663
7664 return address;
7665 }
7666
7667 /*
7668 * ALTER TABLE ALTER COLUMN SET STORAGE
7669 *
7670 * Return value is the address of the modified column
7671 */
7672 static ObjectAddress
ATExecSetStorage(Relation rel,const char * colName,Node * newValue,LOCKMODE lockmode)7673 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
7674 {
7675 char *storagemode;
7676 char newstorage;
7677 Relation attrelation;
7678 HeapTuple tuple;
7679 Form_pg_attribute attrtuple;
7680 AttrNumber attnum;
7681 ObjectAddress address;
7682 ListCell *lc;
7683
7684 Assert(IsA(newValue, String));
7685 storagemode = strVal(newValue);
7686
7687 if (pg_strcasecmp(storagemode, "plain") == 0)
7688 newstorage = TYPSTORAGE_PLAIN;
7689 else if (pg_strcasecmp(storagemode, "external") == 0)
7690 newstorage = TYPSTORAGE_EXTERNAL;
7691 else if (pg_strcasecmp(storagemode, "extended") == 0)
7692 newstorage = TYPSTORAGE_EXTENDED;
7693 else if (pg_strcasecmp(storagemode, "main") == 0)
7694 newstorage = TYPSTORAGE_MAIN;
7695 else
7696 {
7697 ereport(ERROR,
7698 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
7699 errmsg("invalid storage type \"%s\"",
7700 storagemode)));
7701 newstorage = 0; /* keep compiler quiet */
7702 }
7703
7704 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7705
7706 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7707
7708 if (!HeapTupleIsValid(tuple))
7709 ereport(ERROR,
7710 (errcode(ERRCODE_UNDEFINED_COLUMN),
7711 errmsg("column \"%s\" of relation \"%s\" does not exist",
7712 colName, RelationGetRelationName(rel))));
7713 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
7714
7715 attnum = attrtuple->attnum;
7716 if (attnum <= 0)
7717 ereport(ERROR,
7718 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7719 errmsg("cannot alter system column \"%s\"",
7720 colName)));
7721
7722 /*
7723 * safety check: do not allow toasted storage modes unless column datatype
7724 * is TOAST-aware.
7725 */
7726 if (newstorage == TYPSTORAGE_PLAIN || TypeIsToastable(attrtuple->atttypid))
7727 attrtuple->attstorage = newstorage;
7728 else
7729 ereport(ERROR,
7730 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7731 errmsg("column data type %s can only have storage PLAIN",
7732 format_type_be(attrtuple->atttypid))));
7733
7734 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7735
7736 InvokeObjectPostAlterHook(RelationRelationId,
7737 RelationGetRelid(rel),
7738 attrtuple->attnum);
7739
7740 heap_freetuple(tuple);
7741
7742 /*
7743 * Apply the change to indexes as well (only for simple index columns,
7744 * matching behavior of index.c ConstructTupleDescriptor()).
7745 */
7746 foreach(lc, RelationGetIndexList(rel))
7747 {
7748 Oid indexoid = lfirst_oid(lc);
7749 Relation indrel;
7750 AttrNumber indattnum = 0;
7751
7752 indrel = index_open(indexoid, lockmode);
7753
7754 for (int i = 0; i < indrel->rd_index->indnatts; i++)
7755 {
7756 if (indrel->rd_index->indkey.values[i] == attnum)
7757 {
7758 indattnum = i + 1;
7759 break;
7760 }
7761 }
7762
7763 if (indattnum == 0)
7764 {
7765 index_close(indrel, lockmode);
7766 continue;
7767 }
7768
7769 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
7770
7771 if (HeapTupleIsValid(tuple))
7772 {
7773 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
7774 attrtuple->attstorage = newstorage;
7775
7776 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7777
7778 InvokeObjectPostAlterHook(RelationRelationId,
7779 RelationGetRelid(rel),
7780 attrtuple->attnum);
7781
7782 heap_freetuple(tuple);
7783 }
7784
7785 index_close(indrel, lockmode);
7786 }
7787
7788 table_close(attrelation, RowExclusiveLock);
7789
7790 ObjectAddressSubSet(address, RelationRelationId,
7791 RelationGetRelid(rel), attnum);
7792 return address;
7793 }
7794
7795
7796 /*
7797 * ALTER TABLE DROP COLUMN
7798 *
7799 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
7800 * because we have to decide at runtime whether to recurse or not depending
7801 * on whether attinhcount goes to zero or not. (We can't check this in a
7802 * static pre-pass because it won't handle multiple inheritance situations
7803 * correctly.)
7804 */
7805 static void
ATPrepDropColumn(List ** wqueue,Relation rel,bool recurse,bool recursing,AlterTableCmd * cmd,LOCKMODE lockmode,AlterTableUtilityContext * context)7806 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7807 AlterTableCmd *cmd, LOCKMODE lockmode,
7808 AlterTableUtilityContext *context)
7809 {
7810 if (rel->rd_rel->reloftype && !recursing)
7811 ereport(ERROR,
7812 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7813 errmsg("cannot drop column from typed table")));
7814
7815 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7816 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7817
7818 if (recurse)
7819 cmd->subtype = AT_DropColumnRecurse;
7820 }
7821
7822 /*
7823 * Drops column 'colName' from relation 'rel' and returns the address of the
7824 * dropped column. The column is also dropped (or marked as no longer
7825 * inherited from relation) from the relation's inheritance children, if any.
7826 *
7827 * In the recursive invocations for inheritance child relations, instead of
7828 * dropping the column directly (if to be dropped at all), its object address
7829 * is added to 'addrs', which must be non-NULL in such invocations. All
7830 * columns are dropped at the same time after all the children have been
7831 * checked recursively.
7832 */
7833 static ObjectAddress
ATExecDropColumn(List ** wqueue,Relation rel,const char * colName,DropBehavior behavior,bool recurse,bool recursing,bool missing_ok,LOCKMODE lockmode,ObjectAddresses * addrs)7834 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
7835 DropBehavior behavior,
7836 bool recurse, bool recursing,
7837 bool missing_ok, LOCKMODE lockmode,
7838 ObjectAddresses *addrs)
7839 {
7840 HeapTuple tuple;
7841 Form_pg_attribute targetatt;
7842 AttrNumber attnum;
7843 List *children;
7844 ObjectAddress object;
7845 bool is_expr;
7846
7847 /* At top level, permission check was done in ATPrepCmd, else do it */
7848 if (recursing)
7849 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7850
7851 /* Initialize addrs on the first invocation */
7852 Assert(!recursing || addrs != NULL);
7853 if (!recursing)
7854 addrs = new_object_addresses();
7855
7856 /*
7857 * get the number of the attribute
7858 */
7859 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7860 if (!HeapTupleIsValid(tuple))
7861 {
7862 if (!missing_ok)
7863 {
7864 ereport(ERROR,
7865 (errcode(ERRCODE_UNDEFINED_COLUMN),
7866 errmsg("column \"%s\" of relation \"%s\" does not exist",
7867 colName, RelationGetRelationName(rel))));
7868 }
7869 else
7870 {
7871 ereport(NOTICE,
7872 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
7873 colName, RelationGetRelationName(rel))));
7874 return InvalidObjectAddress;
7875 }
7876 }
7877 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
7878
7879 attnum = targetatt->attnum;
7880
7881 /* Can't drop a system attribute */
7882 if (attnum <= 0)
7883 ereport(ERROR,
7884 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7885 errmsg("cannot drop system column \"%s\"",
7886 colName)));
7887
7888 /*
7889 * Don't drop inherited columns, unless recursing (presumably from a drop
7890 * of the parent column)
7891 */
7892 if (targetatt->attinhcount > 0 && !recursing)
7893 ereport(ERROR,
7894 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7895 errmsg("cannot drop inherited column \"%s\"",
7896 colName)));
7897
7898 /*
7899 * Don't drop columns used in the partition key, either. (If we let this
7900 * go through, the key column's dependencies would cause a cascaded drop
7901 * of the whole table, which is surely not what the user expected.)
7902 */
7903 if (has_partition_attrs(rel,
7904 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
7905 &is_expr))
7906 ereport(ERROR,
7907 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7908 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
7909 colName, RelationGetRelationName(rel))));
7910
7911 ReleaseSysCache(tuple);
7912
7913 /*
7914 * Propagate to children as appropriate. Unlike most other ALTER
7915 * routines, we have to do this one level of recursion at a time; we can't
7916 * use find_all_inheritors to do it in one pass.
7917 */
7918 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7919
7920 if (children)
7921 {
7922 Relation attr_rel;
7923 ListCell *child;
7924
7925 /*
7926 * In case of a partitioned table, the column must be dropped from the
7927 * partitions as well.
7928 */
7929 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
7930 ereport(ERROR,
7931 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7932 errmsg("cannot drop column from only the partitioned table when partitions exist"),
7933 errhint("Do not specify the ONLY keyword.")));
7934
7935 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7936 foreach(child, children)
7937 {
7938 Oid childrelid = lfirst_oid(child);
7939 Relation childrel;
7940 Form_pg_attribute childatt;
7941
7942 /* find_inheritance_children already got lock */
7943 childrel = table_open(childrelid, NoLock);
7944 CheckTableNotInUse(childrel, "ALTER TABLE");
7945
7946 tuple = SearchSysCacheCopyAttName(childrelid, colName);
7947 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
7948 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
7949 colName, childrelid);
7950 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7951
7952 if (childatt->attinhcount <= 0) /* shouldn't happen */
7953 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
7954 childrelid, colName);
7955
7956 if (recurse)
7957 {
7958 /*
7959 * If the child column has other definition sources, just
7960 * decrement its inheritance count; if not, recurse to delete
7961 * it.
7962 */
7963 if (childatt->attinhcount == 1 && !childatt->attislocal)
7964 {
7965 /* Time to delete this child column, too */
7966 ATExecDropColumn(wqueue, childrel, colName,
7967 behavior, true, true,
7968 false, lockmode, addrs);
7969 }
7970 else
7971 {
7972 /* Child column must survive my deletion */
7973 childatt->attinhcount--;
7974
7975 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7976
7977 /* Make update visible */
7978 CommandCounterIncrement();
7979 }
7980 }
7981 else
7982 {
7983 /*
7984 * If we were told to drop ONLY in this table (no recursion),
7985 * we need to mark the inheritors' attributes as locally
7986 * defined rather than inherited.
7987 */
7988 childatt->attinhcount--;
7989 childatt->attislocal = true;
7990
7991 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7992
7993 /* Make update visible */
7994 CommandCounterIncrement();
7995 }
7996
7997 heap_freetuple(tuple);
7998
7999 table_close(childrel, NoLock);
8000 }
8001 table_close(attr_rel, RowExclusiveLock);
8002 }
8003
8004 /* Add object to delete */
8005 object.classId = RelationRelationId;
8006 object.objectId = RelationGetRelid(rel);
8007 object.objectSubId = attnum;
8008 add_exact_object_address(&object, addrs);
8009
8010 if (!recursing)
8011 {
8012 /* Recursion has ended, drop everything that was collected */
8013 performMultipleDeletions(addrs, behavior, 0);
8014 free_object_addresses(addrs);
8015 }
8016
8017 return object;
8018 }
8019
8020 /*
8021 * ALTER TABLE ADD INDEX
8022 *
8023 * There is no such command in the grammar, but parse_utilcmd.c converts
8024 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
8025 * us schedule creation of the index at the appropriate time during ALTER.
8026 *
8027 * Return value is the address of the new index.
8028 */
8029 static ObjectAddress
ATExecAddIndex(AlteredTableInfo * tab,Relation rel,IndexStmt * stmt,bool is_rebuild,LOCKMODE lockmode)8030 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
8031 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
8032 {
8033 bool check_rights;
8034 bool skip_build;
8035 bool quiet;
8036 ObjectAddress address;
8037
8038 Assert(IsA(stmt, IndexStmt));
8039 Assert(!stmt->concurrent);
8040
8041 /* The IndexStmt has already been through transformIndexStmt */
8042 Assert(stmt->transformed);
8043
8044 /* suppress schema rights check when rebuilding existing index */
8045 check_rights = !is_rebuild;
8046 /* skip index build if phase 3 will do it or we're reusing an old one */
8047 skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode);
8048 /* suppress notices when rebuilding existing index */
8049 quiet = is_rebuild;
8050
8051 address = DefineIndex(RelationGetRelid(rel),
8052 stmt,
8053 InvalidOid, /* no predefined OID */
8054 InvalidOid, /* no parent index */
8055 InvalidOid, /* no parent constraint */
8056 true, /* is_alter_table */
8057 check_rights,
8058 false, /* check_not_in_use - we did it already */
8059 skip_build,
8060 quiet);
8061
8062 /*
8063 * If TryReuseIndex() stashed a relfilenode for us, we used it for the new
8064 * index instead of building from scratch. Restore associated fields.
8065 * This may store InvalidSubTransactionId in both fields, in which case
8066 * relcache.c will assume it can rebuild the relcache entry. Hence, do
8067 * this after the CCI that made catalog rows visible to any rebuild. The
8068 * DROP of the old edition of this index will have scheduled the storage
8069 * for deletion at commit, so cancel that pending deletion.
8070 */
8071 if (OidIsValid(stmt->oldNode))
8072 {
8073 Relation irel = index_open(address.objectId, NoLock);
8074
8075 irel->rd_createSubid = stmt->oldCreateSubid;
8076 irel->rd_firstRelfilenodeSubid = stmt->oldFirstRelfilenodeSubid;
8077 RelationPreserveStorage(irel->rd_node, true);
8078 index_close(irel, NoLock);
8079 }
8080
8081 return address;
8082 }
8083
8084 /*
8085 * ALTER TABLE ADD CONSTRAINT USING INDEX
8086 *
8087 * Returns the address of the new constraint.
8088 */
8089 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo * tab,Relation rel,IndexStmt * stmt,LOCKMODE lockmode)8090 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
8091 IndexStmt *stmt, LOCKMODE lockmode)
8092 {
8093 Oid index_oid = stmt->indexOid;
8094 Relation indexRel;
8095 char *indexName;
8096 IndexInfo *indexInfo;
8097 char *constraintName;
8098 char constraintType;
8099 ObjectAddress address;
8100 bits16 flags;
8101
8102 Assert(IsA(stmt, IndexStmt));
8103 Assert(OidIsValid(index_oid));
8104 Assert(stmt->isconstraint);
8105
8106 /*
8107 * Doing this on partitioned tables is not a simple feature to implement,
8108 * so let's punt for now.
8109 */
8110 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8111 ereport(ERROR,
8112 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8113 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
8114
8115 indexRel = index_open(index_oid, AccessShareLock);
8116
8117 indexName = pstrdup(RelationGetRelationName(indexRel));
8118
8119 indexInfo = BuildIndexInfo(indexRel);
8120
8121 /* this should have been checked at parse time */
8122 if (!indexInfo->ii_Unique)
8123 elog(ERROR, "index \"%s\" is not unique", indexName);
8124
8125 /*
8126 * Determine name to assign to constraint. We require a constraint to
8127 * have the same name as the underlying index; therefore, use the index's
8128 * existing name as the default constraint name, and if the user
8129 * explicitly gives some other name for the constraint, rename the index
8130 * to match.
8131 */
8132 constraintName = stmt->idxname;
8133 if (constraintName == NULL)
8134 constraintName = indexName;
8135 else if (strcmp(constraintName, indexName) != 0)
8136 {
8137 ereport(NOTICE,
8138 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
8139 indexName, constraintName)));
8140 RenameRelationInternal(index_oid, constraintName, false, true);
8141 }
8142
8143 /* Extra checks needed if making primary key */
8144 if (stmt->primary)
8145 index_check_primary_key(rel, indexInfo, true, stmt);
8146
8147 /* Note we currently don't support EXCLUSION constraints here */
8148 if (stmt->primary)
8149 constraintType = CONSTRAINT_PRIMARY;
8150 else
8151 constraintType = CONSTRAINT_UNIQUE;
8152
8153 /* Create the catalog entries for the constraint */
8154 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
8155 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
8156 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
8157 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
8158 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
8159
8160 address = index_constraint_create(rel,
8161 index_oid,
8162 InvalidOid,
8163 indexInfo,
8164 constraintName,
8165 constraintType,
8166 flags,
8167 allowSystemTableMods,
8168 false); /* is_internal */
8169
8170 index_close(indexRel, NoLock);
8171
8172 return address;
8173 }
8174
8175 /*
8176 * ALTER TABLE ADD CONSTRAINT
8177 *
8178 * Return value is the address of the new constraint; if no constraint was
8179 * added, InvalidObjectAddress is returned.
8180 */
8181 static ObjectAddress
ATExecAddConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * newConstraint,bool recurse,bool is_readd,LOCKMODE lockmode)8182 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
8183 Constraint *newConstraint, bool recurse, bool is_readd,
8184 LOCKMODE lockmode)
8185 {
8186 ObjectAddress address = InvalidObjectAddress;
8187
8188 Assert(IsA(newConstraint, Constraint));
8189
8190 /*
8191 * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
8192 * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
8193 * switch anyway to make it easier to add more code later.
8194 */
8195 switch (newConstraint->contype)
8196 {
8197 case CONSTR_CHECK:
8198 address =
8199 ATAddCheckConstraint(wqueue, tab, rel,
8200 newConstraint, recurse, false, is_readd,
8201 lockmode);
8202 break;
8203
8204 case CONSTR_FOREIGN:
8205
8206 /*
8207 * Assign or validate constraint name
8208 */
8209 if (newConstraint->conname)
8210 {
8211 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
8212 RelationGetRelid(rel),
8213 newConstraint->conname))
8214 ereport(ERROR,
8215 (errcode(ERRCODE_DUPLICATE_OBJECT),
8216 errmsg("constraint \"%s\" for relation \"%s\" already exists",
8217 newConstraint->conname,
8218 RelationGetRelationName(rel))));
8219 }
8220 else
8221 newConstraint->conname =
8222 ChooseConstraintName(RelationGetRelationName(rel),
8223 ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
8224 "fkey",
8225 RelationGetNamespace(rel),
8226 NIL);
8227
8228 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
8229 newConstraint, InvalidOid,
8230 recurse, false,
8231 lockmode);
8232 break;
8233
8234 default:
8235 elog(ERROR, "unrecognized constraint type: %d",
8236 (int) newConstraint->contype);
8237 }
8238
8239 return address;
8240 }
8241
8242 /*
8243 * Generate the column-name portion of the constraint name for a new foreign
8244 * key given the list of column names that reference the referenced
8245 * table. This will be passed to ChooseConstraintName along with the parent
8246 * table name and the "fkey" suffix.
8247 *
8248 * We know that less than NAMEDATALEN characters will actually be used, so we
8249 * can truncate the result once we've generated that many.
8250 *
8251 * XXX see also ChooseExtendedStatisticNameAddition and
8252 * ChooseIndexNameAddition.
8253 */
8254 static char *
ChooseForeignKeyConstraintNameAddition(List * colnames)8255 ChooseForeignKeyConstraintNameAddition(List *colnames)
8256 {
8257 char buf[NAMEDATALEN * 2];
8258 int buflen = 0;
8259 ListCell *lc;
8260
8261 buf[0] = '\0';
8262 foreach(lc, colnames)
8263 {
8264 const char *name = strVal(lfirst(lc));
8265
8266 if (buflen > 0)
8267 buf[buflen++] = '_'; /* insert _ between names */
8268
8269 /*
8270 * At this point we have buflen <= NAMEDATALEN. name should be less
8271 * than NAMEDATALEN already, but use strlcpy for paranoia.
8272 */
8273 strlcpy(buf + buflen, name, NAMEDATALEN);
8274 buflen += strlen(buf + buflen);
8275 if (buflen >= NAMEDATALEN)
8276 break;
8277 }
8278 return pstrdup(buf);
8279 }
8280
8281 /*
8282 * Add a check constraint to a single table and its children. Returns the
8283 * address of the constraint added to the parent relation, if one gets added,
8284 * or InvalidObjectAddress otherwise.
8285 *
8286 * Subroutine for ATExecAddConstraint.
8287 *
8288 * We must recurse to child tables during execution, rather than using
8289 * ALTER TABLE's normal prep-time recursion. The reason is that all the
8290 * constraints *must* be given the same name, else they won't be seen as
8291 * related later. If the user didn't explicitly specify a name, then
8292 * AddRelationNewConstraints would normally assign different names to the
8293 * child constraints. To fix that, we must capture the name assigned at
8294 * the parent table and pass that down.
8295 */
8296 static ObjectAddress
ATAddCheckConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * constr,bool recurse,bool recursing,bool is_readd,LOCKMODE lockmode)8297 ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
8298 Constraint *constr, bool recurse, bool recursing,
8299 bool is_readd, LOCKMODE lockmode)
8300 {
8301 List *newcons;
8302 ListCell *lcon;
8303 List *children;
8304 ListCell *child;
8305 ObjectAddress address = InvalidObjectAddress;
8306
8307 /* At top level, permission check was done in ATPrepCmd, else do it */
8308 if (recursing)
8309 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8310
8311 /*
8312 * Call AddRelationNewConstraints to do the work, making sure it works on
8313 * a copy of the Constraint so transformExpr can't modify the original. It
8314 * returns a list of cooked constraints.
8315 *
8316 * If the constraint ends up getting merged with a pre-existing one, it's
8317 * omitted from the returned list, which is what we want: we do not need
8318 * to do any validation work. That can only happen at child tables,
8319 * though, since we disallow merging at the top level.
8320 */
8321 newcons = AddRelationNewConstraints(rel, NIL,
8322 list_make1(copyObject(constr)),
8323 recursing | is_readd, /* allow_merge */
8324 !recursing, /* is_local */
8325 is_readd, /* is_internal */
8326 NULL); /* queryString not available
8327 * here */
8328
8329 /* we don't expect more than one constraint here */
8330 Assert(list_length(newcons) <= 1);
8331
8332 /* Add each to-be-validated constraint to Phase 3's queue */
8333 foreach(lcon, newcons)
8334 {
8335 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
8336
8337 if (!ccon->skip_validation)
8338 {
8339 NewConstraint *newcon;
8340
8341 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
8342 newcon->name = ccon->name;
8343 newcon->contype = ccon->contype;
8344 newcon->qual = ccon->expr;
8345
8346 tab->constraints = lappend(tab->constraints, newcon);
8347 }
8348
8349 /* Save the actually assigned name if it was defaulted */
8350 if (constr->conname == NULL)
8351 constr->conname = ccon->name;
8352
8353 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8354 }
8355
8356 /* At this point we must have a locked-down name to use */
8357 Assert(constr->conname != NULL);
8358
8359 /* Advance command counter in case same table is visited multiple times */
8360 CommandCounterIncrement();
8361
8362 /*
8363 * If the constraint got merged with an existing constraint, we're done.
8364 * We mustn't recurse to child tables in this case, because they've
8365 * already got the constraint, and visiting them again would lead to an
8366 * incorrect value for coninhcount.
8367 */
8368 if (newcons == NIL)
8369 return address;
8370
8371 /*
8372 * If adding a NO INHERIT constraint, no need to find our children.
8373 */
8374 if (constr->is_no_inherit)
8375 return address;
8376
8377 /*
8378 * Propagate to children as appropriate. Unlike most other ALTER
8379 * routines, we have to do this one level of recursion at a time; we can't
8380 * use find_all_inheritors to do it in one pass.
8381 */
8382 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8383
8384 /*
8385 * Check if ONLY was specified with ALTER TABLE. If so, allow the
8386 * constraint creation only if there are no children currently. Error out
8387 * otherwise.
8388 */
8389 if (!recurse && children != NIL)
8390 ereport(ERROR,
8391 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8392 errmsg("constraint must be added to child tables too")));
8393
8394 foreach(child, children)
8395 {
8396 Oid childrelid = lfirst_oid(child);
8397 Relation childrel;
8398 AlteredTableInfo *childtab;
8399
8400 /* find_inheritance_children already got lock */
8401 childrel = table_open(childrelid, NoLock);
8402 CheckTableNotInUse(childrel, "ALTER TABLE");
8403
8404 /* Find or create work queue entry for this table */
8405 childtab = ATGetQueueEntry(wqueue, childrel);
8406
8407 /* Recurse to child */
8408 ATAddCheckConstraint(wqueue, childtab, childrel,
8409 constr, recurse, true, is_readd, lockmode);
8410
8411 table_close(childrel, NoLock);
8412 }
8413
8414 return address;
8415 }
8416
8417 /*
8418 * Add a foreign-key constraint to a single table; return the new constraint's
8419 * address.
8420 *
8421 * Subroutine for ATExecAddConstraint. Must already hold exclusive
8422 * lock on the rel, and have done appropriate validity checks for it.
8423 * We do permissions checks here, however.
8424 *
8425 * When the referenced or referencing tables (or both) are partitioned,
8426 * multiple pg_constraint rows are required -- one for each partitioned table
8427 * and each partition on each side (fortunately, not one for every combination
8428 * thereof). We also need action triggers on each leaf partition on the
8429 * referenced side, and check triggers on each leaf partition on the
8430 * referencing side.
8431 */
8432 static ObjectAddress
ATAddForeignKeyConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * fkconstraint,Oid parentConstr,bool recurse,bool recursing,LOCKMODE lockmode)8433 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
8434 Constraint *fkconstraint, Oid parentConstr,
8435 bool recurse, bool recursing, LOCKMODE lockmode)
8436 {
8437 Relation pkrel;
8438 int16 pkattnum[INDEX_MAX_KEYS];
8439 int16 fkattnum[INDEX_MAX_KEYS];
8440 Oid pktypoid[INDEX_MAX_KEYS];
8441 Oid fktypoid[INDEX_MAX_KEYS];
8442 Oid opclasses[INDEX_MAX_KEYS];
8443 Oid pfeqoperators[INDEX_MAX_KEYS];
8444 Oid ppeqoperators[INDEX_MAX_KEYS];
8445 Oid ffeqoperators[INDEX_MAX_KEYS];
8446 int i;
8447 int numfks,
8448 numpks;
8449 Oid indexOid;
8450 bool old_check_ok;
8451 ObjectAddress address;
8452 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
8453
8454 /*
8455 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
8456 * delete rows out from under us.
8457 */
8458 if (OidIsValid(fkconstraint->old_pktable_oid))
8459 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
8460 else
8461 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
8462
8463 /*
8464 * Validity checks (permission checks wait till we have the column
8465 * numbers)
8466 */
8467 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8468 {
8469 if (!recurse)
8470 ereport(ERROR,
8471 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8472 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
8473 RelationGetRelationName(rel),
8474 RelationGetRelationName(pkrel))));
8475 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
8476 ereport(ERROR,
8477 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8478 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
8479 RelationGetRelationName(rel),
8480 RelationGetRelationName(pkrel)),
8481 errdetail("This feature is not yet supported on partitioned tables.")));
8482 }
8483
8484 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
8485 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
8486 ereport(ERROR,
8487 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8488 errmsg("referenced relation \"%s\" is not a table",
8489 RelationGetRelationName(pkrel))));
8490
8491 if (!allowSystemTableMods && IsSystemRelation(pkrel))
8492 ereport(ERROR,
8493 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
8494 errmsg("permission denied: \"%s\" is a system catalog",
8495 RelationGetRelationName(pkrel))));
8496
8497 /*
8498 * References from permanent or unlogged tables to temp tables, and from
8499 * permanent tables to unlogged tables, are disallowed because the
8500 * referenced data can vanish out from under us. References from temp
8501 * tables to any other table type are also disallowed, because other
8502 * backends might need to run the RI triggers on the perm table, but they
8503 * can't reliably see tuples in the local buffers of other backends.
8504 */
8505 switch (rel->rd_rel->relpersistence)
8506 {
8507 case RELPERSISTENCE_PERMANENT:
8508 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
8509 ereport(ERROR,
8510 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8511 errmsg("constraints on permanent tables may reference only permanent tables")));
8512 break;
8513 case RELPERSISTENCE_UNLOGGED:
8514 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT
8515 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
8516 ereport(ERROR,
8517 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8518 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
8519 break;
8520 case RELPERSISTENCE_TEMP:
8521 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
8522 ereport(ERROR,
8523 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8524 errmsg("constraints on temporary tables may reference only temporary tables")));
8525 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
8526 ereport(ERROR,
8527 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8528 errmsg("constraints on temporary tables must involve temporary tables of this session")));
8529 break;
8530 }
8531
8532 /*
8533 * Look up the referencing attributes to make sure they exist, and record
8534 * their attnums and type OIDs.
8535 */
8536 MemSet(pkattnum, 0, sizeof(pkattnum));
8537 MemSet(fkattnum, 0, sizeof(fkattnum));
8538 MemSet(pktypoid, 0, sizeof(pktypoid));
8539 MemSet(fktypoid, 0, sizeof(fktypoid));
8540 MemSet(opclasses, 0, sizeof(opclasses));
8541 MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
8542 MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
8543 MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
8544
8545 numfks = transformColumnNameList(RelationGetRelid(rel),
8546 fkconstraint->fk_attrs,
8547 fkattnum, fktypoid);
8548
8549 /*
8550 * If the attribute list for the referenced table was omitted, lookup the
8551 * definition of the primary key and use it. Otherwise, validate the
8552 * supplied attribute list. In either case, discover the index OID and
8553 * index opclasses, and the attnums and type OIDs of the attributes.
8554 */
8555 if (fkconstraint->pk_attrs == NIL)
8556 {
8557 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
8558 &fkconstraint->pk_attrs,
8559 pkattnum, pktypoid,
8560 opclasses);
8561 }
8562 else
8563 {
8564 numpks = transformColumnNameList(RelationGetRelid(pkrel),
8565 fkconstraint->pk_attrs,
8566 pkattnum, pktypoid);
8567 /* Look for an index matching the column list */
8568 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
8569 opclasses);
8570 }
8571
8572 /*
8573 * Now we can check permissions.
8574 */
8575 checkFkeyPermissions(pkrel, pkattnum, numpks);
8576
8577 /*
8578 * Check some things for generated columns.
8579 */
8580 for (i = 0; i < numfks; i++)
8581 {
8582 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
8583
8584 if (attgenerated)
8585 {
8586 /*
8587 * Check restrictions on UPDATE/DELETE actions, per SQL standard
8588 */
8589 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
8590 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
8591 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
8592 ereport(ERROR,
8593 (errcode(ERRCODE_SYNTAX_ERROR),
8594 errmsg("invalid %s action for foreign key constraint containing generated column",
8595 "ON UPDATE")));
8596 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
8597 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
8598 ereport(ERROR,
8599 (errcode(ERRCODE_SYNTAX_ERROR),
8600 errmsg("invalid %s action for foreign key constraint containing generated column",
8601 "ON DELETE")));
8602 }
8603 }
8604
8605 /*
8606 * Look up the equality operators to use in the constraint.
8607 *
8608 * Note that we have to be careful about the difference between the actual
8609 * PK column type and the opclass' declared input type, which might be
8610 * only binary-compatible with it. The declared opcintype is the right
8611 * thing to probe pg_amop with.
8612 */
8613 if (numfks != numpks)
8614 ereport(ERROR,
8615 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
8616 errmsg("number of referencing and referenced columns for foreign key disagree")));
8617
8618 /*
8619 * On the strength of a previous constraint, we might avoid scanning
8620 * tables to validate this one. See below.
8621 */
8622 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
8623 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
8624
8625 for (i = 0; i < numpks; i++)
8626 {
8627 Oid pktype = pktypoid[i];
8628 Oid fktype = fktypoid[i];
8629 Oid fktyped;
8630 HeapTuple cla_ht;
8631 Form_pg_opclass cla_tup;
8632 Oid amid;
8633 Oid opfamily;
8634 Oid opcintype;
8635 Oid pfeqop;
8636 Oid ppeqop;
8637 Oid ffeqop;
8638 int16 eqstrategy;
8639 Oid pfeqop_right;
8640
8641 /* We need several fields out of the pg_opclass entry */
8642 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
8643 if (!HeapTupleIsValid(cla_ht))
8644 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
8645 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
8646 amid = cla_tup->opcmethod;
8647 opfamily = cla_tup->opcfamily;
8648 opcintype = cla_tup->opcintype;
8649 ReleaseSysCache(cla_ht);
8650
8651 /*
8652 * Check it's a btree; currently this can never fail since no other
8653 * index AMs support unique indexes. If we ever did have other types
8654 * of unique indexes, we'd need a way to determine which operator
8655 * strategy number is equality. (Is it reasonable to insist that
8656 * every such index AM use btree's number for equality?)
8657 */
8658 if (amid != BTREE_AM_OID)
8659 elog(ERROR, "only b-tree indexes are supported for foreign keys");
8660 eqstrategy = BTEqualStrategyNumber;
8661
8662 /*
8663 * There had better be a primary equality operator for the index.
8664 * We'll use it for PK = PK comparisons.
8665 */
8666 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
8667 eqstrategy);
8668
8669 if (!OidIsValid(ppeqop))
8670 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
8671 eqstrategy, opcintype, opcintype, opfamily);
8672
8673 /*
8674 * Are there equality operators that take exactly the FK type? Assume
8675 * we should look through any domain here.
8676 */
8677 fktyped = getBaseType(fktype);
8678
8679 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
8680 eqstrategy);
8681 if (OidIsValid(pfeqop))
8682 {
8683 pfeqop_right = fktyped;
8684 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
8685 eqstrategy);
8686 }
8687 else
8688 {
8689 /* keep compiler quiet */
8690 pfeqop_right = InvalidOid;
8691 ffeqop = InvalidOid;
8692 }
8693
8694 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
8695 {
8696 /*
8697 * Otherwise, look for an implicit cast from the FK type to the
8698 * opcintype, and if found, use the primary equality operator.
8699 * This is a bit tricky because opcintype might be a polymorphic
8700 * type such as ANYARRAY or ANYENUM; so what we have to test is
8701 * whether the two actual column types can be concurrently cast to
8702 * that type. (Otherwise, we'd fail to reject combinations such
8703 * as int[] and point[].)
8704 */
8705 Oid input_typeids[2];
8706 Oid target_typeids[2];
8707
8708 input_typeids[0] = pktype;
8709 input_typeids[1] = fktype;
8710 target_typeids[0] = opcintype;
8711 target_typeids[1] = opcintype;
8712 if (can_coerce_type(2, input_typeids, target_typeids,
8713 COERCION_IMPLICIT))
8714 {
8715 pfeqop = ffeqop = ppeqop;
8716 pfeqop_right = opcintype;
8717 }
8718 }
8719
8720 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
8721 ereport(ERROR,
8722 (errcode(ERRCODE_DATATYPE_MISMATCH),
8723 errmsg("foreign key constraint \"%s\" cannot be implemented",
8724 fkconstraint->conname),
8725 errdetail("Key columns \"%s\" and \"%s\" "
8726 "are of incompatible types: %s and %s.",
8727 strVal(list_nth(fkconstraint->fk_attrs, i)),
8728 strVal(list_nth(fkconstraint->pk_attrs, i)),
8729 format_type_be(fktype),
8730 format_type_be(pktype))));
8731
8732 if (old_check_ok)
8733 {
8734 /*
8735 * When a pfeqop changes, revalidate the constraint. We could
8736 * permit intra-opfamily changes, but that adds subtle complexity
8737 * without any concrete benefit for core types. We need not
8738 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
8739 */
8740 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
8741 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
8742 old_pfeqop_item);
8743 }
8744 if (old_check_ok)
8745 {
8746 Oid old_fktype;
8747 Oid new_fktype;
8748 CoercionPathType old_pathtype;
8749 CoercionPathType new_pathtype;
8750 Oid old_castfunc;
8751 Oid new_castfunc;
8752 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
8753 fkattnum[i] - 1);
8754
8755 /*
8756 * Identify coercion pathways from each of the old and new FK-side
8757 * column types to the right (foreign) operand type of the pfeqop.
8758 * We may assume that pg_constraint.conkey is not changing.
8759 */
8760 old_fktype = attr->atttypid;
8761 new_fktype = fktype;
8762 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
8763 &old_castfunc);
8764 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
8765 &new_castfunc);
8766
8767 /*
8768 * Upon a change to the cast from the FK column to its pfeqop
8769 * operand, revalidate the constraint. For this evaluation, a
8770 * binary coercion cast is equivalent to no cast at all. While
8771 * type implementors should design implicit casts with an eye
8772 * toward consistency of operations like equality, we cannot
8773 * assume here that they have done so.
8774 *
8775 * A function with a polymorphic argument could change behavior
8776 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
8777 * when the cast destination is polymorphic, we only avoid
8778 * revalidation if the input type has not changed at all. Given
8779 * just the core data types and operator classes, this requirement
8780 * prevents no would-be optimizations.
8781 *
8782 * If the cast converts from a base type to a domain thereon, then
8783 * that domain type must be the opcintype of the unique index.
8784 * Necessarily, the primary key column must then be of the domain
8785 * type. Since the constraint was previously valid, all values on
8786 * the foreign side necessarily exist on the primary side and in
8787 * turn conform to the domain. Consequently, we need not treat
8788 * domains specially here.
8789 *
8790 * Since we require that all collations share the same notion of
8791 * equality (which they do, because texteq reduces to bitwise
8792 * equality), we don't compare collation here.
8793 *
8794 * We need not directly consider the PK type. It's necessarily
8795 * binary coercible to the opcintype of the unique index column,
8796 * and ri_triggers.c will only deal with PK datums in terms of
8797 * that opcintype. Changing the opcintype also changes pfeqop.
8798 */
8799 old_check_ok = (new_pathtype == old_pathtype &&
8800 new_castfunc == old_castfunc &&
8801 (!IsPolymorphicType(pfeqop_right) ||
8802 new_fktype == old_fktype));
8803 }
8804
8805 pfeqoperators[i] = pfeqop;
8806 ppeqoperators[i] = ppeqop;
8807 ffeqoperators[i] = ffeqop;
8808 }
8809
8810 /*
8811 * Create all the constraint and trigger objects, recursing to partitions
8812 * as necessary. First handle the referenced side.
8813 */
8814 address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
8815 indexOid,
8816 InvalidOid, /* no parent constraint */
8817 numfks,
8818 pkattnum,
8819 fkattnum,
8820 pfeqoperators,
8821 ppeqoperators,
8822 ffeqoperators,
8823 old_check_ok);
8824
8825 /* Now handle the referencing side. */
8826 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
8827 indexOid,
8828 address.objectId,
8829 numfks,
8830 pkattnum,
8831 fkattnum,
8832 pfeqoperators,
8833 ppeqoperators,
8834 ffeqoperators,
8835 old_check_ok,
8836 lockmode);
8837
8838 /*
8839 * Done. Close pk table, but keep lock until we've committed.
8840 */
8841 table_close(pkrel, NoLock);
8842
8843 return address;
8844 }
8845
8846 /*
8847 * addFkRecurseReferenced
8848 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
8849 * side of the constraint
8850 *
8851 * Create pg_constraint rows for the referenced side of the constraint,
8852 * referencing the parent of the referencing side; also create action triggers
8853 * on leaf partitions. If the table is partitioned, recurse to handle each
8854 * partition.
8855 *
8856 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
8857 * of an ALTER TABLE sequence.
8858 * fkconstraint is the constraint being added.
8859 * rel is the root referencing relation.
8860 * pkrel is the referenced relation; might be a partition, if recursing.
8861 * indexOid is the OID of the index (on pkrel) implementing this constraint.
8862 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
8863 * top-level constraint.
8864 * numfks is the number of columns in the foreign key
8865 * pkattnum is the attnum array of referenced attributes.
8866 * fkattnum is the attnum array of referencing attributes.
8867 * pf/pp/ffeqoperators are OID array of operators between columns.
8868 * old_check_ok signals that this constraint replaces an existing one that
8869 * was already validated (thus this one doesn't need validation).
8870 */
8871 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)8872 addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
8873 Relation pkrel, Oid indexOid, Oid parentConstr,
8874 int numfks,
8875 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
8876 Oid *ppeqoperators, Oid *ffeqoperators, bool old_check_ok)
8877 {
8878 ObjectAddress address;
8879 Oid constrOid;
8880 char *conname;
8881 bool conislocal;
8882 int coninhcount;
8883 bool connoinherit;
8884
8885 /*
8886 * Verify relkind for each referenced partition. At the top level, this
8887 * is redundant with a previous check, but we need it when recursing.
8888 */
8889 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
8890 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
8891 ereport(ERROR,
8892 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8893 errmsg("referenced relation \"%s\" is not a table",
8894 RelationGetRelationName(pkrel))));
8895
8896 /*
8897 * Caller supplies us with a constraint name; however, it may be used in
8898 * this partition, so come up with a different one in that case.
8899 */
8900 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
8901 RelationGetRelid(rel),
8902 fkconstraint->conname))
8903 conname = ChooseConstraintName(RelationGetRelationName(rel),
8904 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
8905 "fkey",
8906 RelationGetNamespace(rel), NIL);
8907 else
8908 conname = fkconstraint->conname;
8909
8910 if (OidIsValid(parentConstr))
8911 {
8912 conislocal = false;
8913 coninhcount = 1;
8914 connoinherit = false;
8915 }
8916 else
8917 {
8918 conislocal = true;
8919 coninhcount = 0;
8920
8921 /*
8922 * always inherit for partitioned tables, never for legacy inheritance
8923 */
8924 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
8925 }
8926
8927 /*
8928 * Record the FK constraint in pg_constraint.
8929 */
8930 constrOid = CreateConstraintEntry(conname,
8931 RelationGetNamespace(rel),
8932 CONSTRAINT_FOREIGN,
8933 fkconstraint->deferrable,
8934 fkconstraint->initdeferred,
8935 fkconstraint->initially_valid,
8936 parentConstr,
8937 RelationGetRelid(rel),
8938 fkattnum,
8939 numfks,
8940 numfks,
8941 InvalidOid, /* not a domain constraint */
8942 indexOid,
8943 RelationGetRelid(pkrel),
8944 pkattnum,
8945 pfeqoperators,
8946 ppeqoperators,
8947 ffeqoperators,
8948 numfks,
8949 fkconstraint->fk_upd_action,
8950 fkconstraint->fk_del_action,
8951 fkconstraint->fk_matchtype,
8952 NULL, /* no exclusion constraint */
8953 NULL, /* no check constraint */
8954 NULL,
8955 conislocal, /* islocal */
8956 coninhcount, /* inhcount */
8957 connoinherit, /* conNoInherit */
8958 false); /* is_internal */
8959
8960 ObjectAddressSet(address, ConstraintRelationId, constrOid);
8961
8962 /*
8963 * Mark the child constraint as part of the parent constraint; it must not
8964 * be dropped on its own. (This constraint is deleted when the partition
8965 * is detached, but a special check needs to occur that the partition
8966 * contains no referenced values.)
8967 */
8968 if (OidIsValid(parentConstr))
8969 {
8970 ObjectAddress referenced;
8971
8972 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
8973 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
8974 }
8975
8976 /* make new constraint visible, in case we add more */
8977 CommandCounterIncrement();
8978
8979 /*
8980 * If the referenced table is a plain relation, create the action triggers
8981 * that enforce the constraint.
8982 */
8983 if (pkrel->rd_rel->relkind == RELKIND_RELATION)
8984 {
8985 createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
8986 fkconstraint,
8987 constrOid, indexOid);
8988 }
8989
8990 /*
8991 * If the referenced table is partitioned, recurse on ourselves to handle
8992 * each partition. We need one pg_constraint row created for each
8993 * partition in addition to the pg_constraint row for the parent table.
8994 */
8995 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8996 {
8997 PartitionDesc pd = RelationGetPartitionDesc(pkrel);
8998
8999 for (int i = 0; i < pd->nparts; i++)
9000 {
9001 Relation partRel;
9002 AttrMap *map;
9003 AttrNumber *mapped_pkattnum;
9004 Oid partIndexId;
9005
9006 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
9007
9008 /*
9009 * Map the attribute numbers in the referenced side of the FK
9010 * definition to match the partition's column layout.
9011 */
9012 map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
9013 RelationGetDescr(pkrel));
9014 if (map)
9015 {
9016 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
9017 for (int j = 0; j < numfks; j++)
9018 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
9019 }
9020 else
9021 mapped_pkattnum = pkattnum;
9022
9023 /* do the deed */
9024 partIndexId = index_get_partition(partRel, indexOid);
9025 if (!OidIsValid(partIndexId))
9026 elog(ERROR, "index for %u not found in partition %s",
9027 indexOid, RelationGetRelationName(partRel));
9028 addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
9029 partIndexId, constrOid, numfks,
9030 mapped_pkattnum, fkattnum,
9031 pfeqoperators, ppeqoperators, ffeqoperators,
9032 old_check_ok);
9033
9034 /* Done -- clean up (but keep the lock) */
9035 table_close(partRel, NoLock);
9036 if (map)
9037 {
9038 pfree(mapped_pkattnum);
9039 free_attrmap(map);
9040 }
9041 }
9042 }
9043
9044 return address;
9045 }
9046
9047 /*
9048 * addFkRecurseReferencing
9049 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
9050 *
9051 * If the referencing relation is a plain relation, create the necessary check
9052 * triggers that implement the constraint, and set up for Phase 3 constraint
9053 * verification. If the referencing relation is a partitioned table, then
9054 * we create a pg_constraint row for it and recurse on this routine for each
9055 * partition.
9056 *
9057 * We assume that the referenced relation is locked against concurrent
9058 * deletions. If it's a partitioned relation, every partition must be so
9059 * locked.
9060 *
9061 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9062 * of an ALTER TABLE sequence.
9063 * fkconstraint is the constraint being added.
9064 * rel is the referencing relation; might be a partition, if recursing.
9065 * pkrel is the root referenced relation.
9066 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9067 * parentConstr is the OID of the parent constraint (there is always one).
9068 * numfks is the number of columns in the foreign key
9069 * pkattnum is the attnum array of referenced attributes.
9070 * fkattnum is the attnum array of referencing attributes.
9071 * pf/pp/ffeqoperators are OID array of operators between columns.
9072 * old_check_ok signals that this constraint replaces an existing one that
9073 * was already validated (thus this one doesn't need validation).
9074 * lockmode is the lockmode to acquire on partitions when recursing.
9075 */
9076 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)9077 addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
9078 Relation pkrel, Oid indexOid, Oid parentConstr,
9079 int numfks, int16 *pkattnum, int16 *fkattnum,
9080 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
9081 bool old_check_ok, LOCKMODE lockmode)
9082 {
9083 AssertArg(OidIsValid(parentConstr));
9084
9085 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
9086 ereport(ERROR,
9087 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9088 errmsg("foreign key constraints are not supported on foreign tables")));
9089
9090 /*
9091 * If the referencing relation is a plain table, add the check triggers to
9092 * it and, if necessary, schedule it to be checked in Phase 3.
9093 *
9094 * If the relation is partitioned, drill down to do it to its partitions.
9095 */
9096 if (rel->rd_rel->relkind == RELKIND_RELATION)
9097 {
9098 createForeignKeyCheckTriggers(RelationGetRelid(rel),
9099 RelationGetRelid(pkrel),
9100 fkconstraint,
9101 parentConstr,
9102 indexOid);
9103
9104 /*
9105 * Tell Phase 3 to check that the constraint is satisfied by existing
9106 * rows. We can skip this during table creation, when requested
9107 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
9108 * and when we're recreating a constraint following a SET DATA TYPE
9109 * operation that did not impugn its validity.
9110 */
9111 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
9112 {
9113 NewConstraint *newcon;
9114 AlteredTableInfo *tab;
9115
9116 tab = ATGetQueueEntry(wqueue, rel);
9117
9118 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9119 newcon->name = get_constraint_name(parentConstr);
9120 newcon->contype = CONSTR_FOREIGN;
9121 newcon->refrelid = RelationGetRelid(pkrel);
9122 newcon->refindid = indexOid;
9123 newcon->conid = parentConstr;
9124 newcon->qual = (Node *) fkconstraint;
9125
9126 tab->constraints = lappend(tab->constraints, newcon);
9127 }
9128 }
9129 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9130 {
9131 PartitionDesc pd = RelationGetPartitionDesc(rel);
9132
9133 /*
9134 * Recurse to take appropriate action on each partition; either we
9135 * find an existing constraint to reparent to ours, or we create a new
9136 * one.
9137 */
9138 for (int i = 0; i < pd->nparts; i++)
9139 {
9140 Oid partitionId = pd->oids[i];
9141 Relation partition = table_open(partitionId, lockmode);
9142 List *partFKs;
9143 AttrMap *attmap;
9144 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
9145 bool attached;
9146 char *conname;
9147 Oid constrOid;
9148 ObjectAddress address,
9149 referenced;
9150 ListCell *cell;
9151
9152 CheckTableNotInUse(partition, "ALTER TABLE");
9153
9154 attmap = build_attrmap_by_name(RelationGetDescr(partition),
9155 RelationGetDescr(rel));
9156 for (int j = 0; j < numfks; j++)
9157 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
9158
9159 /* Check whether an existing constraint can be repurposed */
9160 partFKs = copyObject(RelationGetFKeyList(partition));
9161 attached = false;
9162 foreach(cell, partFKs)
9163 {
9164 ForeignKeyCacheInfo *fk;
9165
9166 fk = lfirst_node(ForeignKeyCacheInfo, cell);
9167 if (tryAttachPartitionForeignKey(fk,
9168 partitionId,
9169 parentConstr,
9170 numfks,
9171 mapped_fkattnum,
9172 pkattnum,
9173 pfeqoperators))
9174 {
9175 attached = true;
9176 break;
9177 }
9178 }
9179 if (attached)
9180 {
9181 table_close(partition, NoLock);
9182 continue;
9183 }
9184
9185 /*
9186 * No luck finding a good constraint to reuse; create our own.
9187 */
9188 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9189 RelationGetRelid(partition),
9190 fkconstraint->conname))
9191 conname = ChooseConstraintName(RelationGetRelationName(partition),
9192 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
9193 "fkey",
9194 RelationGetNamespace(partition), NIL);
9195 else
9196 conname = fkconstraint->conname;
9197 constrOid =
9198 CreateConstraintEntry(conname,
9199 RelationGetNamespace(partition),
9200 CONSTRAINT_FOREIGN,
9201 fkconstraint->deferrable,
9202 fkconstraint->initdeferred,
9203 fkconstraint->initially_valid,
9204 parentConstr,
9205 partitionId,
9206 mapped_fkattnum,
9207 numfks,
9208 numfks,
9209 InvalidOid,
9210 indexOid,
9211 RelationGetRelid(pkrel),
9212 pkattnum,
9213 pfeqoperators,
9214 ppeqoperators,
9215 ffeqoperators,
9216 numfks,
9217 fkconstraint->fk_upd_action,
9218 fkconstraint->fk_del_action,
9219 fkconstraint->fk_matchtype,
9220 NULL,
9221 NULL,
9222 NULL,
9223 false,
9224 1,
9225 false,
9226 false);
9227
9228 /*
9229 * Give this constraint partition-type dependencies on the parent
9230 * constraint as well as the table.
9231 */
9232 ObjectAddressSet(address, ConstraintRelationId, constrOid);
9233 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
9234 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
9235 ObjectAddressSet(referenced, RelationRelationId, partitionId);
9236 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
9237
9238 /* Make all this visible before recursing */
9239 CommandCounterIncrement();
9240
9241 /* call ourselves to finalize the creation and we're done */
9242 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
9243 indexOid,
9244 constrOid,
9245 numfks,
9246 pkattnum,
9247 mapped_fkattnum,
9248 pfeqoperators,
9249 ppeqoperators,
9250 ffeqoperators,
9251 old_check_ok,
9252 lockmode);
9253
9254 table_close(partition, NoLock);
9255 }
9256 }
9257 }
9258
9259 /*
9260 * CloneForeignKeyConstraints
9261 * Clone foreign keys from a partitioned table to a newly acquired
9262 * partition.
9263 *
9264 * partitionRel is a partition of parentRel, so we can be certain that it has
9265 * the same columns with the same datatypes. The columns may be in different
9266 * order, though.
9267 *
9268 * wqueue must be passed to set up phase 3 constraint checking, unless the
9269 * referencing-side partition is known to be empty (such as in CREATE TABLE /
9270 * PARTITION OF).
9271 */
9272 static void
CloneForeignKeyConstraints(List ** wqueue,Relation parentRel,Relation partitionRel)9273 CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
9274 Relation partitionRel)
9275 {
9276 /* This only works for declarative partitioning */
9277 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
9278
9279 /*
9280 * Clone constraints for which the parent is on the referenced side.
9281 */
9282 CloneFkReferenced(parentRel, partitionRel);
9283
9284 /*
9285 * Now clone constraints where the parent is on the referencing side.
9286 */
9287 CloneFkReferencing(wqueue, parentRel, partitionRel);
9288 }
9289
9290 /*
9291 * CloneFkReferenced
9292 * Subroutine for CloneForeignKeyConstraints
9293 *
9294 * Find all the FKs that have the parent relation on the referenced side;
9295 * clone those constraints to the given partition. This is to be called
9296 * when the partition is being created or attached.
9297 *
9298 * This recurses to partitions, if the relation being attached is partitioned.
9299 * Recursion is done by calling addFkRecurseReferenced.
9300 */
9301 static void
CloneFkReferenced(Relation parentRel,Relation partitionRel)9302 CloneFkReferenced(Relation parentRel, Relation partitionRel)
9303 {
9304 Relation pg_constraint;
9305 AttrMap *attmap;
9306 ListCell *cell;
9307 SysScanDesc scan;
9308 ScanKeyData key[2];
9309 HeapTuple tuple;
9310 List *clone = NIL;
9311
9312 /*
9313 * Search for any constraints where this partition's parent is in the
9314 * referenced side. However, we must not clone any constraint whose
9315 * parent constraint is also going to be cloned, to avoid duplicates. So
9316 * do it in two steps: first construct the list of constraints to clone,
9317 * then go over that list cloning those whose parents are not in the list.
9318 * (We must not rely on the parent being seen first, since the catalog
9319 * scan could return children first.)
9320 */
9321 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
9322 ScanKeyInit(&key[0],
9323 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
9324 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
9325 ScanKeyInit(&key[1],
9326 Anum_pg_constraint_contype, BTEqualStrategyNumber,
9327 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
9328 /* This is a seqscan, as we don't have a usable index ... */
9329 scan = systable_beginscan(pg_constraint, InvalidOid, true,
9330 NULL, 2, key);
9331 while ((tuple = systable_getnext(scan)) != NULL)
9332 {
9333 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
9334
9335 clone = lappend_oid(clone, constrForm->oid);
9336 }
9337 systable_endscan(scan);
9338 table_close(pg_constraint, RowShareLock);
9339
9340 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
9341 RelationGetDescr(parentRel));
9342 foreach(cell, clone)
9343 {
9344 Oid constrOid = lfirst_oid(cell);
9345 Form_pg_constraint constrForm;
9346 Relation fkRel;
9347 Oid indexOid;
9348 Oid partIndexId;
9349 int numfks;
9350 AttrNumber conkey[INDEX_MAX_KEYS];
9351 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
9352 AttrNumber confkey[INDEX_MAX_KEYS];
9353 Oid conpfeqop[INDEX_MAX_KEYS];
9354 Oid conppeqop[INDEX_MAX_KEYS];
9355 Oid conffeqop[INDEX_MAX_KEYS];
9356 Constraint *fkconstraint;
9357
9358 tuple = SearchSysCache1(CONSTROID, constrOid);
9359 if (!HeapTupleIsValid(tuple))
9360 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
9361 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
9362
9363 /*
9364 * As explained above: don't try to clone a constraint for which we're
9365 * going to clone the parent.
9366 */
9367 if (list_member_oid(clone, constrForm->conparentid))
9368 {
9369 ReleaseSysCache(tuple);
9370 continue;
9371 }
9372
9373 /*
9374 * Because we're only expanding the key space at the referenced side,
9375 * we don't need to prevent any operation in the referencing table, so
9376 * AccessShareLock suffices (assumes that dropping the constraint
9377 * acquires AEL).
9378 */
9379 fkRel = table_open(constrForm->conrelid, AccessShareLock);
9380
9381 indexOid = constrForm->conindid;
9382 DeconstructFkConstraintRow(tuple,
9383 &numfks,
9384 conkey,
9385 confkey,
9386 conpfeqop,
9387 conppeqop,
9388 conffeqop);
9389
9390 for (int i = 0; i < numfks; i++)
9391 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
9392
9393 fkconstraint = makeNode(Constraint);
9394 /* for now this is all we need */
9395 fkconstraint->conname = NameStr(constrForm->conname);
9396 fkconstraint->fk_upd_action = constrForm->confupdtype;
9397 fkconstraint->fk_del_action = constrForm->confdeltype;
9398 fkconstraint->deferrable = constrForm->condeferrable;
9399 fkconstraint->initdeferred = constrForm->condeferred;
9400 fkconstraint->initially_valid = true;
9401 fkconstraint->fk_matchtype = constrForm->confmatchtype;
9402
9403 /* set up colnames that are used to generate the constraint name */
9404 for (int i = 0; i < numfks; i++)
9405 {
9406 Form_pg_attribute att;
9407
9408 att = TupleDescAttr(RelationGetDescr(fkRel),
9409 conkey[i] - 1);
9410 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
9411 makeString(NameStr(att->attname)));
9412 }
9413
9414 /*
9415 * Add the new foreign key constraint pointing to the new partition.
9416 * Because this new partition appears in the referenced side of the
9417 * constraint, we don't need to set up for Phase 3 check.
9418 */
9419 partIndexId = index_get_partition(partitionRel, indexOid);
9420 if (!OidIsValid(partIndexId))
9421 elog(ERROR, "index for %u not found in partition %s",
9422 indexOid, RelationGetRelationName(partitionRel));
9423 addFkRecurseReferenced(NULL,
9424 fkconstraint,
9425 fkRel,
9426 partitionRel,
9427 partIndexId,
9428 constrOid,
9429 numfks,
9430 mapped_confkey,
9431 conkey,
9432 conpfeqop,
9433 conppeqop,
9434 conffeqop,
9435 true);
9436
9437 table_close(fkRel, NoLock);
9438 ReleaseSysCache(tuple);
9439 }
9440 }
9441
9442 /*
9443 * CloneFkReferencing
9444 * Subroutine for CloneForeignKeyConstraints
9445 *
9446 * For each FK constraint of the parent relation in the given list, find an
9447 * equivalent constraint in its partition relation that can be reparented;
9448 * if one cannot be found, create a new constraint in the partition as its
9449 * child.
9450 *
9451 * If wqueue is given, it is used to set up phase-3 verification for each
9452 * cloned constraint; if omitted, we assume that such verification is not
9453 * needed (example: the partition is being created anew).
9454 */
9455 static void
CloneFkReferencing(List ** wqueue,Relation parentRel,Relation partRel)9456 CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
9457 {
9458 AttrMap *attmap;
9459 List *partFKs;
9460 List *clone = NIL;
9461 ListCell *cell;
9462
9463 /* obtain a list of constraints that we need to clone */
9464 foreach(cell, RelationGetFKeyList(parentRel))
9465 {
9466 ForeignKeyCacheInfo *fk = lfirst(cell);
9467
9468 clone = lappend_oid(clone, fk->conoid);
9469 }
9470
9471 /*
9472 * Silently do nothing if there's nothing to do. In particular, this
9473 * avoids throwing a spurious error for foreign tables.
9474 */
9475 if (clone == NIL)
9476 return;
9477
9478 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
9479 ereport(ERROR,
9480 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9481 errmsg("foreign key constraints are not supported on foreign tables")));
9482
9483 /*
9484 * The constraint key may differ, if the columns in the partition are
9485 * different. This map is used to convert them.
9486 */
9487 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
9488 RelationGetDescr(parentRel));
9489
9490 partFKs = copyObject(RelationGetFKeyList(partRel));
9491
9492 foreach(cell, clone)
9493 {
9494 Oid parentConstrOid = lfirst_oid(cell);
9495 Form_pg_constraint constrForm;
9496 Relation pkrel;
9497 HeapTuple tuple;
9498 int numfks;
9499 AttrNumber conkey[INDEX_MAX_KEYS];
9500 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
9501 AttrNumber confkey[INDEX_MAX_KEYS];
9502 Oid conpfeqop[INDEX_MAX_KEYS];
9503 Oid conppeqop[INDEX_MAX_KEYS];
9504 Oid conffeqop[INDEX_MAX_KEYS];
9505 Constraint *fkconstraint;
9506 bool attached;
9507 Oid indexOid;
9508 Oid constrOid;
9509 ObjectAddress address,
9510 referenced;
9511 ListCell *cell;
9512
9513 tuple = SearchSysCache1(CONSTROID, parentConstrOid);
9514 if (!HeapTupleIsValid(tuple))
9515 elog(ERROR, "cache lookup failed for constraint %u",
9516 parentConstrOid);
9517 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
9518
9519 /* Don't clone constraints whose parents are being cloned */
9520 if (list_member_oid(clone, constrForm->conparentid))
9521 {
9522 ReleaseSysCache(tuple);
9523 continue;
9524 }
9525
9526 /*
9527 * Need to prevent concurrent deletions. If pkrel is a partitioned
9528 * relation, that means to lock all partitions.
9529 */
9530 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
9531 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9532 (void) find_all_inheritors(RelationGetRelid(pkrel),
9533 ShareRowExclusiveLock, NULL);
9534
9535 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
9536 conpfeqop, conppeqop, conffeqop);
9537 for (int i = 0; i < numfks; i++)
9538 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
9539
9540 /*
9541 * Before creating a new constraint, see whether any existing FKs are
9542 * fit for the purpose. If one is, attach the parent constraint to
9543 * it, and don't clone anything. This way we avoid the expensive
9544 * verification step and don't end up with a duplicate FK, and we
9545 * don't need to recurse to partitions for this constraint.
9546 */
9547 attached = false;
9548 foreach(cell, partFKs)
9549 {
9550 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, cell);
9551
9552 if (tryAttachPartitionForeignKey(fk,
9553 RelationGetRelid(partRel),
9554 parentConstrOid,
9555 numfks,
9556 mapped_conkey,
9557 confkey,
9558 conpfeqop))
9559 {
9560 attached = true;
9561 table_close(pkrel, NoLock);
9562 break;
9563 }
9564 }
9565 if (attached)
9566 {
9567 ReleaseSysCache(tuple);
9568 continue;
9569 }
9570
9571 /* No dice. Set up to create our own constraint */
9572 fkconstraint = makeNode(Constraint);
9573 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9574 RelationGetRelid(partRel),
9575 NameStr(constrForm->conname)))
9576 fkconstraint->conname =
9577 ChooseConstraintName(RelationGetRelationName(partRel),
9578 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
9579 "fkey",
9580 RelationGetNamespace(partRel), NIL);
9581 else
9582 fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
9583 fkconstraint->fk_upd_action = constrForm->confupdtype;
9584 fkconstraint->fk_del_action = constrForm->confdeltype;
9585 fkconstraint->deferrable = constrForm->condeferrable;
9586 fkconstraint->initdeferred = constrForm->condeferred;
9587 fkconstraint->fk_matchtype = constrForm->confmatchtype;
9588 for (int i = 0; i < numfks; i++)
9589 {
9590 Form_pg_attribute att;
9591
9592 att = TupleDescAttr(RelationGetDescr(partRel),
9593 mapped_conkey[i] - 1);
9594 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
9595 makeString(NameStr(att->attname)));
9596 }
9597
9598 indexOid = constrForm->conindid;
9599 constrOid =
9600 CreateConstraintEntry(fkconstraint->conname,
9601 constrForm->connamespace,
9602 CONSTRAINT_FOREIGN,
9603 fkconstraint->deferrable,
9604 fkconstraint->initdeferred,
9605 constrForm->convalidated,
9606 parentConstrOid,
9607 RelationGetRelid(partRel),
9608 mapped_conkey,
9609 numfks,
9610 numfks,
9611 InvalidOid, /* not a domain constraint */
9612 indexOid,
9613 constrForm->confrelid, /* same foreign rel */
9614 confkey,
9615 conpfeqop,
9616 conppeqop,
9617 conffeqop,
9618 numfks,
9619 fkconstraint->fk_upd_action,
9620 fkconstraint->fk_del_action,
9621 fkconstraint->fk_matchtype,
9622 NULL,
9623 NULL,
9624 NULL,
9625 false, /* islocal */
9626 1, /* inhcount */
9627 false, /* conNoInherit */
9628 true);
9629
9630 /* Set up partition dependencies for the new constraint */
9631 ObjectAddressSet(address, ConstraintRelationId, constrOid);
9632 ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
9633 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
9634 ObjectAddressSet(referenced, RelationRelationId,
9635 RelationGetRelid(partRel));
9636 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
9637
9638 /* Done with the cloned constraint's tuple */
9639 ReleaseSysCache(tuple);
9640
9641 /* Make all this visible before recursing */
9642 CommandCounterIncrement();
9643
9644 addFkRecurseReferencing(wqueue,
9645 fkconstraint,
9646 partRel,
9647 pkrel,
9648 indexOid,
9649 constrOid,
9650 numfks,
9651 confkey,
9652 mapped_conkey,
9653 conpfeqop,
9654 conppeqop,
9655 conffeqop,
9656 false, /* no old check exists */
9657 AccessExclusiveLock);
9658 table_close(pkrel, NoLock);
9659 }
9660 }
9661
9662 /*
9663 * When the parent of a partition receives [the referencing side of] a foreign
9664 * key, we must propagate that foreign key to the partition. However, the
9665 * partition might already have an equivalent foreign key; this routine
9666 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
9667 * by the other parameters. If they are equivalent, create the link between
9668 * the two constraints and return true.
9669 *
9670 * If the given FK does not match the one defined by rest of the params,
9671 * return false.
9672 */
9673 static bool
tryAttachPartitionForeignKey(ForeignKeyCacheInfo * fk,Oid partRelid,Oid parentConstrOid,int numfks,AttrNumber * mapped_conkey,AttrNumber * confkey,Oid * conpfeqop)9674 tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
9675 Oid partRelid,
9676 Oid parentConstrOid,
9677 int numfks,
9678 AttrNumber *mapped_conkey,
9679 AttrNumber *confkey,
9680 Oid *conpfeqop)
9681 {
9682 HeapTuple parentConstrTup;
9683 Form_pg_constraint parentConstr;
9684 HeapTuple partcontup;
9685 Form_pg_constraint partConstr;
9686 Relation trigrel;
9687 ScanKeyData key;
9688 SysScanDesc scan;
9689 HeapTuple trigtup;
9690
9691 parentConstrTup = SearchSysCache1(CONSTROID,
9692 ObjectIdGetDatum(parentConstrOid));
9693 if (!HeapTupleIsValid(parentConstrTup))
9694 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
9695 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
9696
9697 /*
9698 * Do some quick & easy initial checks. If any of these fail, we cannot
9699 * use this constraint.
9700 */
9701 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
9702 {
9703 ReleaseSysCache(parentConstrTup);
9704 return false;
9705 }
9706 for (int i = 0; i < numfks; i++)
9707 {
9708 if (fk->conkey[i] != mapped_conkey[i] ||
9709 fk->confkey[i] != confkey[i] ||
9710 fk->conpfeqop[i] != conpfeqop[i])
9711 {
9712 ReleaseSysCache(parentConstrTup);
9713 return false;
9714 }
9715 }
9716
9717 /*
9718 * Looks good so far; do some more extensive checks. Presumably the check
9719 * for 'convalidated' could be dropped, since we don't really care about
9720 * that, but let's be careful for now.
9721 */
9722 partcontup = SearchSysCache1(CONSTROID,
9723 ObjectIdGetDatum(fk->conoid));
9724 if (!HeapTupleIsValid(partcontup))
9725 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
9726 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
9727 if (OidIsValid(partConstr->conparentid) ||
9728 !partConstr->convalidated ||
9729 partConstr->condeferrable != parentConstr->condeferrable ||
9730 partConstr->condeferred != parentConstr->condeferred ||
9731 partConstr->confupdtype != parentConstr->confupdtype ||
9732 partConstr->confdeltype != parentConstr->confdeltype ||
9733 partConstr->confmatchtype != parentConstr->confmatchtype)
9734 {
9735 ReleaseSysCache(parentConstrTup);
9736 ReleaseSysCache(partcontup);
9737 return false;
9738 }
9739
9740 ReleaseSysCache(partcontup);
9741 ReleaseSysCache(parentConstrTup);
9742
9743 /*
9744 * Looks good! Attach this constraint. The action triggers in the new
9745 * partition become redundant -- the parent table already has equivalent
9746 * ones, and those will be able to reach the partition. Remove the ones
9747 * in the partition. We identify them because they have our constraint
9748 * OID, as well as being on the referenced rel.
9749 */
9750 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
9751 ScanKeyInit(&key,
9752 Anum_pg_trigger_tgconstraint,
9753 BTEqualStrategyNumber, F_OIDEQ,
9754 ObjectIdGetDatum(fk->conoid));
9755
9756 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
9757 NULL, 1, &key);
9758 while ((trigtup = systable_getnext(scan)) != NULL)
9759 {
9760 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
9761 ObjectAddress trigger;
9762
9763 if (trgform->tgconstrrelid != fk->conrelid)
9764 continue;
9765 if (trgform->tgrelid != fk->confrelid)
9766 continue;
9767
9768 /*
9769 * The constraint is originally set up to contain this trigger as an
9770 * implementation object, so there's a dependency record that links
9771 * the two; however, since the trigger is no longer needed, we remove
9772 * the dependency link in order to be able to drop the trigger while
9773 * keeping the constraint intact.
9774 */
9775 deleteDependencyRecordsFor(TriggerRelationId,
9776 trgform->oid,
9777 false);
9778 /* make dependency deletion visible to performDeletion */
9779 CommandCounterIncrement();
9780 ObjectAddressSet(trigger, TriggerRelationId,
9781 trgform->oid);
9782 performDeletion(&trigger, DROP_RESTRICT, 0);
9783 /* make trigger drop visible, in case the loop iterates */
9784 CommandCounterIncrement();
9785 }
9786
9787 systable_endscan(scan);
9788 table_close(trigrel, RowExclusiveLock);
9789
9790 ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
9791 CommandCounterIncrement();
9792 return true;
9793 }
9794
9795
9796 /*
9797 * ALTER TABLE ALTER CONSTRAINT
9798 *
9799 * Update the attributes of a constraint.
9800 *
9801 * Currently only works for Foreign Key constraints.
9802 *
9803 * If the constraint is modified, returns its address; otherwise, return
9804 * InvalidObjectAddress.
9805 */
9806 static ObjectAddress
ATExecAlterConstraint(Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode)9807 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
9808 bool recursing, LOCKMODE lockmode)
9809 {
9810 Constraint *cmdcon;
9811 Relation conrel;
9812 Relation tgrel;
9813 SysScanDesc scan;
9814 ScanKeyData skey[3];
9815 HeapTuple contuple;
9816 Form_pg_constraint currcon;
9817 ObjectAddress address;
9818 List *otherrelids = NIL;
9819 ListCell *lc;
9820
9821 cmdcon = castNode(Constraint, cmd->def);
9822
9823 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
9824 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
9825
9826 /*
9827 * Find and check the target constraint
9828 */
9829 ScanKeyInit(&skey[0],
9830 Anum_pg_constraint_conrelid,
9831 BTEqualStrategyNumber, F_OIDEQ,
9832 ObjectIdGetDatum(RelationGetRelid(rel)));
9833 ScanKeyInit(&skey[1],
9834 Anum_pg_constraint_contypid,
9835 BTEqualStrategyNumber, F_OIDEQ,
9836 ObjectIdGetDatum(InvalidOid));
9837 ScanKeyInit(&skey[2],
9838 Anum_pg_constraint_conname,
9839 BTEqualStrategyNumber, F_NAMEEQ,
9840 CStringGetDatum(cmdcon->conname));
9841 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
9842 true, NULL, 3, skey);
9843
9844 /* There can be at most one matching row */
9845 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
9846 ereport(ERROR,
9847 (errcode(ERRCODE_UNDEFINED_OBJECT),
9848 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
9849 cmdcon->conname, RelationGetRelationName(rel))));
9850
9851 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
9852 if (currcon->contype != CONSTRAINT_FOREIGN)
9853 ereport(ERROR,
9854 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9855 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
9856 cmdcon->conname, RelationGetRelationName(rel))));
9857
9858 /*
9859 * If it's not the topmost constraint, raise an error.
9860 *
9861 * Altering a non-topmost constraint leaves some triggers untouched, since
9862 * they are not directly connected to this constraint; also, pg_dump would
9863 * ignore the deferrability status of the individual constraint, since it
9864 * only dumps topmost constraints. Avoid these problems by refusing this
9865 * operation and telling the user to alter the parent constraint instead.
9866 */
9867 if (OidIsValid(currcon->conparentid))
9868 {
9869 HeapTuple tp;
9870 Oid parent = currcon->conparentid;
9871 char *ancestorname = NULL;
9872 char *ancestortable = NULL;
9873
9874 /* Loop to find the topmost constraint */
9875 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
9876 {
9877 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
9878
9879 /* If no parent, this is the constraint we want */
9880 if (!OidIsValid(contup->conparentid))
9881 {
9882 ancestorname = pstrdup(NameStr(contup->conname));
9883 ancestortable = get_rel_name(contup->conrelid);
9884 ReleaseSysCache(tp);
9885 break;
9886 }
9887
9888 parent = contup->conparentid;
9889 ReleaseSysCache(tp);
9890 }
9891
9892 ereport(ERROR,
9893 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
9894 cmdcon->conname, RelationGetRelationName(rel)),
9895 ancestorname && ancestortable ?
9896 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
9897 cmdcon->conname, ancestorname, ancestortable) : 0,
9898 errhint("You may alter the constraint it derives from, instead.")));
9899 }
9900
9901 /*
9902 * Do the actual catalog work. We can skip changing if already in the
9903 * desired state, but not if a partitioned table: partitions need to be
9904 * processed regardless, in case they had the constraint locally changed.
9905 */
9906 address = InvalidObjectAddress;
9907 if (currcon->condeferrable != cmdcon->deferrable ||
9908 currcon->condeferred != cmdcon->initdeferred ||
9909 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9910 {
9911 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
9912 &otherrelids, lockmode))
9913 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
9914 }
9915
9916 /*
9917 * ATExecConstrRecurse already invalidated relcache for the relations
9918 * having the constraint itself; here we also invalidate for relations
9919 * that have any triggers that are part of the constraint.
9920 */
9921 foreach(lc, otherrelids)
9922 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
9923
9924 systable_endscan(scan);
9925
9926 table_close(tgrel, RowExclusiveLock);
9927 table_close(conrel, RowExclusiveLock);
9928
9929 return address;
9930 }
9931
9932 /*
9933 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
9934 * constraint is altered.
9935 *
9936 * *otherrelids is appended OIDs of relations containing affected triggers.
9937 *
9938 * Note that we must recurse even when the values are correct, in case
9939 * indirect descendants have had their constraints altered locally.
9940 * (This could be avoided if we forbade altering constraints in partitions
9941 * but existing releases don't do that.)
9942 */
9943 static bool
ATExecAlterConstrRecurse(Constraint * cmdcon,Relation conrel,Relation tgrel,Relation rel,HeapTuple contuple,List ** otherrelids,LOCKMODE lockmode)9944 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
9945 Relation rel, HeapTuple contuple, List **otherrelids,
9946 LOCKMODE lockmode)
9947 {
9948 Form_pg_constraint currcon;
9949 Oid conoid;
9950 Oid refrelid;
9951 bool changed = false;
9952
9953 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
9954 conoid = currcon->oid;
9955 refrelid = currcon->confrelid;
9956
9957 /*
9958 * Update pg_constraint with the flags from cmdcon.
9959 *
9960 * If called to modify a constraint that's already in the desired state,
9961 * silently do nothing.
9962 */
9963 if (currcon->condeferrable != cmdcon->deferrable ||
9964 currcon->condeferred != cmdcon->initdeferred)
9965 {
9966 HeapTuple copyTuple;
9967 Form_pg_constraint copy_con;
9968 HeapTuple tgtuple;
9969 ScanKeyData tgkey;
9970 SysScanDesc tgscan;
9971
9972 copyTuple = heap_copytuple(contuple);
9973 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
9974 copy_con->condeferrable = cmdcon->deferrable;
9975 copy_con->condeferred = cmdcon->initdeferred;
9976 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
9977
9978 InvokeObjectPostAlterHook(ConstraintRelationId,
9979 conoid, 0);
9980
9981 heap_freetuple(copyTuple);
9982 changed = true;
9983
9984 /* Make new constraint flags visible to others */
9985 CacheInvalidateRelcache(rel);
9986
9987 /*
9988 * Now we need to update the multiple entries in pg_trigger that
9989 * implement the constraint.
9990 */
9991 ScanKeyInit(&tgkey,
9992 Anum_pg_trigger_tgconstraint,
9993 BTEqualStrategyNumber, F_OIDEQ,
9994 ObjectIdGetDatum(conoid));
9995 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
9996 NULL, 1, &tgkey);
9997 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
9998 {
9999 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
10000 Form_pg_trigger copy_tg;
10001 HeapTuple copyTuple;
10002
10003 /*
10004 * Remember OIDs of other relation(s) involved in FK constraint.
10005 * (Note: it's likely that we could skip forcing a relcache inval
10006 * for other rels that don't have a trigger whose properties
10007 * change, but let's be conservative.)
10008 */
10009 if (tgform->tgrelid != RelationGetRelid(rel))
10010 *otherrelids = list_append_unique_oid(*otherrelids,
10011 tgform->tgrelid);
10012
10013 /*
10014 * Update deferrability of RI_FKey_noaction_del,
10015 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
10016 * triggers, but not others; see createForeignKeyActionTriggers
10017 * and CreateFKCheckTrigger.
10018 */
10019 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
10020 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
10021 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
10022 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
10023 continue;
10024
10025 copyTuple = heap_copytuple(tgtuple);
10026 copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
10027
10028 copy_tg->tgdeferrable = cmdcon->deferrable;
10029 copy_tg->tginitdeferred = cmdcon->initdeferred;
10030 CatalogTupleUpdate(tgrel, ©Tuple->t_self, copyTuple);
10031
10032 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
10033
10034 heap_freetuple(copyTuple);
10035 }
10036
10037 systable_endscan(tgscan);
10038 }
10039
10040 /*
10041 * If the table at either end of the constraint is partitioned, we need to
10042 * recurse and handle every constraint that is a child of this one.
10043 *
10044 * (This assumes that the recurse flag is forcibly set for partitioned
10045 * tables, and not set for legacy inheritance, though we don't check for
10046 * that here.)
10047 */
10048 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
10049 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
10050 {
10051 ScanKeyData pkey;
10052 SysScanDesc pscan;
10053 HeapTuple childtup;
10054
10055 ScanKeyInit(&pkey,
10056 Anum_pg_constraint_conparentid,
10057 BTEqualStrategyNumber, F_OIDEQ,
10058 ObjectIdGetDatum(conoid));
10059
10060 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
10061 true, NULL, 1, &pkey);
10062
10063 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
10064 {
10065 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
10066 Relation childrel;
10067
10068 childrel = table_open(childcon->conrelid, lockmode);
10069 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
10070 otherrelids, lockmode);
10071 table_close(childrel, NoLock);
10072 }
10073
10074 systable_endscan(pscan);
10075 }
10076
10077 return changed;
10078 }
10079
10080 /*
10081 * ALTER TABLE VALIDATE CONSTRAINT
10082 *
10083 * XXX The reason we handle recursion here rather than at Phase 1 is because
10084 * there's no good way to skip recursing when handling foreign keys: there is
10085 * no need to lock children in that case, yet we wouldn't be able to avoid
10086 * doing so at that level.
10087 *
10088 * Return value is the address of the validated constraint. If the constraint
10089 * was already validated, InvalidObjectAddress is returned.
10090 */
10091 static ObjectAddress
ATExecValidateConstraint(List ** wqueue,Relation rel,char * constrName,bool recurse,bool recursing,LOCKMODE lockmode)10092 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
10093 bool recurse, bool recursing, LOCKMODE lockmode)
10094 {
10095 Relation conrel;
10096 SysScanDesc scan;
10097 ScanKeyData skey[3];
10098 HeapTuple tuple;
10099 Form_pg_constraint con;
10100 ObjectAddress address;
10101
10102 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
10103
10104 /*
10105 * Find and check the target constraint
10106 */
10107 ScanKeyInit(&skey[0],
10108 Anum_pg_constraint_conrelid,
10109 BTEqualStrategyNumber, F_OIDEQ,
10110 ObjectIdGetDatum(RelationGetRelid(rel)));
10111 ScanKeyInit(&skey[1],
10112 Anum_pg_constraint_contypid,
10113 BTEqualStrategyNumber, F_OIDEQ,
10114 ObjectIdGetDatum(InvalidOid));
10115 ScanKeyInit(&skey[2],
10116 Anum_pg_constraint_conname,
10117 BTEqualStrategyNumber, F_NAMEEQ,
10118 CStringGetDatum(constrName));
10119 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
10120 true, NULL, 3, skey);
10121
10122 /* There can be at most one matching row */
10123 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
10124 ereport(ERROR,
10125 (errcode(ERRCODE_UNDEFINED_OBJECT),
10126 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
10127 constrName, RelationGetRelationName(rel))));
10128
10129 con = (Form_pg_constraint) GETSTRUCT(tuple);
10130 if (con->contype != CONSTRAINT_FOREIGN &&
10131 con->contype != CONSTRAINT_CHECK)
10132 ereport(ERROR,
10133 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10134 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
10135 constrName, RelationGetRelationName(rel))));
10136
10137 if (!con->convalidated)
10138 {
10139 AlteredTableInfo *tab;
10140 HeapTuple copyTuple;
10141 Form_pg_constraint copy_con;
10142
10143 if (con->contype == CONSTRAINT_FOREIGN)
10144 {
10145 NewConstraint *newcon;
10146 Constraint *fkconstraint;
10147
10148 /* Queue validation for phase 3 */
10149 fkconstraint = makeNode(Constraint);
10150 /* for now this is all we need */
10151 fkconstraint->conname = constrName;
10152
10153 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10154 newcon->name = constrName;
10155 newcon->contype = CONSTR_FOREIGN;
10156 newcon->refrelid = con->confrelid;
10157 newcon->refindid = con->conindid;
10158 newcon->conid = con->oid;
10159 newcon->qual = (Node *) fkconstraint;
10160
10161 /* Find or create work queue entry for this table */
10162 tab = ATGetQueueEntry(wqueue, rel);
10163 tab->constraints = lappend(tab->constraints, newcon);
10164
10165 /*
10166 * We disallow creating invalid foreign keys to or from
10167 * partitioned tables, so ignoring the recursion bit is okay.
10168 */
10169 }
10170 else if (con->contype == CONSTRAINT_CHECK)
10171 {
10172 List *children = NIL;
10173 ListCell *child;
10174 NewConstraint *newcon;
10175 bool isnull;
10176 Datum val;
10177 char *conbin;
10178
10179 /*
10180 * If we're recursing, the parent has already done this, so skip
10181 * it. Also, if the constraint is a NO INHERIT constraint, we
10182 * shouldn't try to look for it in the children.
10183 */
10184 if (!recursing && !con->connoinherit)
10185 children = find_all_inheritors(RelationGetRelid(rel),
10186 lockmode, NULL);
10187
10188 /*
10189 * For CHECK constraints, we must ensure that we only mark the
10190 * constraint as validated on the parent if it's already validated
10191 * on the children.
10192 *
10193 * We recurse before validating on the parent, to reduce risk of
10194 * deadlocks.
10195 */
10196 foreach(child, children)
10197 {
10198 Oid childoid = lfirst_oid(child);
10199 Relation childrel;
10200
10201 if (childoid == RelationGetRelid(rel))
10202 continue;
10203
10204 /*
10205 * If we are told not to recurse, there had better not be any
10206 * child tables, because we can't mark the constraint on the
10207 * parent valid unless it is valid for all child tables.
10208 */
10209 if (!recurse)
10210 ereport(ERROR,
10211 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10212 errmsg("constraint must be validated on child tables too")));
10213
10214 /* find_all_inheritors already got lock */
10215 childrel = table_open(childoid, NoLock);
10216
10217 ATExecValidateConstraint(wqueue, childrel, constrName, false,
10218 true, lockmode);
10219 table_close(childrel, NoLock);
10220 }
10221
10222 /* Queue validation for phase 3 */
10223 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10224 newcon->name = constrName;
10225 newcon->contype = CONSTR_CHECK;
10226 newcon->refrelid = InvalidOid;
10227 newcon->refindid = InvalidOid;
10228 newcon->conid = con->oid;
10229
10230 val = SysCacheGetAttr(CONSTROID, tuple,
10231 Anum_pg_constraint_conbin, &isnull);
10232 if (isnull)
10233 elog(ERROR, "null conbin for constraint %u", con->oid);
10234
10235 conbin = TextDatumGetCString(val);
10236 newcon->qual = (Node *) stringToNode(conbin);
10237
10238 /* Find or create work queue entry for this table */
10239 tab = ATGetQueueEntry(wqueue, rel);
10240 tab->constraints = lappend(tab->constraints, newcon);
10241
10242 /*
10243 * Invalidate relcache so that others see the new validated
10244 * constraint.
10245 */
10246 CacheInvalidateRelcache(rel);
10247 }
10248
10249 /*
10250 * Now update the catalog, while we have the door open.
10251 */
10252 copyTuple = heap_copytuple(tuple);
10253 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
10254 copy_con->convalidated = true;
10255 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
10256
10257 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
10258
10259 heap_freetuple(copyTuple);
10260
10261 ObjectAddressSet(address, ConstraintRelationId, con->oid);
10262 }
10263 else
10264 address = InvalidObjectAddress; /* already validated */
10265
10266 systable_endscan(scan);
10267
10268 table_close(conrel, RowExclusiveLock);
10269
10270 return address;
10271 }
10272
10273
10274 /*
10275 * transformColumnNameList - transform list of column names
10276 *
10277 * Lookup each name and return its attnum and type OID
10278 */
10279 static int
transformColumnNameList(Oid relId,List * colList,int16 * attnums,Oid * atttypids)10280 transformColumnNameList(Oid relId, List *colList,
10281 int16 *attnums, Oid *atttypids)
10282 {
10283 ListCell *l;
10284 int attnum;
10285
10286 attnum = 0;
10287 foreach(l, colList)
10288 {
10289 char *attname = strVal(lfirst(l));
10290 HeapTuple atttuple;
10291
10292 atttuple = SearchSysCacheAttName(relId, attname);
10293 if (!HeapTupleIsValid(atttuple))
10294 ereport(ERROR,
10295 (errcode(ERRCODE_UNDEFINED_COLUMN),
10296 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
10297 attname)));
10298 if (attnum >= INDEX_MAX_KEYS)
10299 ereport(ERROR,
10300 (errcode(ERRCODE_TOO_MANY_COLUMNS),
10301 errmsg("cannot have more than %d keys in a foreign key",
10302 INDEX_MAX_KEYS)));
10303 attnums[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->attnum;
10304 atttypids[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
10305 ReleaseSysCache(atttuple);
10306 attnum++;
10307 }
10308
10309 return attnum;
10310 }
10311
10312 /*
10313 * transformFkeyGetPrimaryKey -
10314 *
10315 * Look up the names, attnums, and types of the primary key attributes
10316 * for the pkrel. Also return the index OID and index opclasses of the
10317 * index supporting the primary key.
10318 *
10319 * All parameters except pkrel are output parameters. Also, the function
10320 * return value is the number of attributes in the primary key.
10321 *
10322 * Used when the column list in the REFERENCES specification is omitted.
10323 */
10324 static int
transformFkeyGetPrimaryKey(Relation pkrel,Oid * indexOid,List ** attnamelist,int16 * attnums,Oid * atttypids,Oid * opclasses)10325 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
10326 List **attnamelist,
10327 int16 *attnums, Oid *atttypids,
10328 Oid *opclasses)
10329 {
10330 List *indexoidlist;
10331 ListCell *indexoidscan;
10332 HeapTuple indexTuple = NULL;
10333 Form_pg_index indexStruct = NULL;
10334 Datum indclassDatum;
10335 bool isnull;
10336 oidvector *indclass;
10337 int i;
10338
10339 /*
10340 * Get the list of index OIDs for the table from the relcache, and look up
10341 * each one in the pg_index syscache until we find one marked primary key
10342 * (hopefully there isn't more than one such). Insist it's valid, too.
10343 */
10344 *indexOid = InvalidOid;
10345
10346 indexoidlist = RelationGetIndexList(pkrel);
10347
10348 foreach(indexoidscan, indexoidlist)
10349 {
10350 Oid indexoid = lfirst_oid(indexoidscan);
10351
10352 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
10353 if (!HeapTupleIsValid(indexTuple))
10354 elog(ERROR, "cache lookup failed for index %u", indexoid);
10355 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
10356 if (indexStruct->indisprimary && indexStruct->indisvalid)
10357 {
10358 /*
10359 * Refuse to use a deferrable primary key. This is per SQL spec,
10360 * and there would be a lot of interesting semantic problems if we
10361 * tried to allow it.
10362 */
10363 if (!indexStruct->indimmediate)
10364 ereport(ERROR,
10365 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
10366 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
10367 RelationGetRelationName(pkrel))));
10368
10369 *indexOid = indexoid;
10370 break;
10371 }
10372 ReleaseSysCache(indexTuple);
10373 }
10374
10375 list_free(indexoidlist);
10376
10377 /*
10378 * Check that we found it
10379 */
10380 if (!OidIsValid(*indexOid))
10381 ereport(ERROR,
10382 (errcode(ERRCODE_UNDEFINED_OBJECT),
10383 errmsg("there is no primary key for referenced table \"%s\"",
10384 RelationGetRelationName(pkrel))));
10385
10386 /* Must get indclass the hard way */
10387 indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
10388 Anum_pg_index_indclass, &isnull);
10389 Assert(!isnull);
10390 indclass = (oidvector *) DatumGetPointer(indclassDatum);
10391
10392 /*
10393 * Now build the list of PK attributes from the indkey definition (we
10394 * assume a primary key cannot have expressional elements)
10395 */
10396 *attnamelist = NIL;
10397 for (i = 0; i < indexStruct->indnkeyatts; i++)
10398 {
10399 int pkattno = indexStruct->indkey.values[i];
10400
10401 attnums[i] = pkattno;
10402 atttypids[i] = attnumTypeId(pkrel, pkattno);
10403 opclasses[i] = indclass->values[i];
10404 *attnamelist = lappend(*attnamelist,
10405 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
10406 }
10407
10408 ReleaseSysCache(indexTuple);
10409
10410 return i;
10411 }
10412
10413 /*
10414 * transformFkeyCheckAttrs -
10415 *
10416 * Make sure that the attributes of a referenced table belong to a unique
10417 * (or primary key) constraint. Return the OID of the index supporting
10418 * the constraint, as well as the opclasses associated with the index
10419 * columns.
10420 */
10421 static Oid
transformFkeyCheckAttrs(Relation pkrel,int numattrs,int16 * attnums,Oid * opclasses)10422 transformFkeyCheckAttrs(Relation pkrel,
10423 int numattrs, int16 *attnums,
10424 Oid *opclasses) /* output parameter */
10425 {
10426 Oid indexoid = InvalidOid;
10427 bool found = false;
10428 bool found_deferrable = false;
10429 List *indexoidlist;
10430 ListCell *indexoidscan;
10431 int i,
10432 j;
10433
10434 /*
10435 * Reject duplicate appearances of columns in the referenced-columns list.
10436 * Such a case is forbidden by the SQL standard, and even if we thought it
10437 * useful to allow it, there would be ambiguity about how to match the
10438 * list to unique indexes (in particular, it'd be unclear which index
10439 * opclass goes with which FK column).
10440 */
10441 for (i = 0; i < numattrs; i++)
10442 {
10443 for (j = i + 1; j < numattrs; j++)
10444 {
10445 if (attnums[i] == attnums[j])
10446 ereport(ERROR,
10447 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10448 errmsg("foreign key referenced-columns list must not contain duplicates")));
10449 }
10450 }
10451
10452 /*
10453 * Get the list of index OIDs for the table from the relcache, and look up
10454 * each one in the pg_index syscache, and match unique indexes to the list
10455 * of attnums we are given.
10456 */
10457 indexoidlist = RelationGetIndexList(pkrel);
10458
10459 foreach(indexoidscan, indexoidlist)
10460 {
10461 HeapTuple indexTuple;
10462 Form_pg_index indexStruct;
10463
10464 indexoid = lfirst_oid(indexoidscan);
10465 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
10466 if (!HeapTupleIsValid(indexTuple))
10467 elog(ERROR, "cache lookup failed for index %u", indexoid);
10468 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
10469
10470 /*
10471 * Must have the right number of columns; must be unique and not a
10472 * partial index; forget it if there are any expressions, too. Invalid
10473 * indexes are out as well.
10474 */
10475 if (indexStruct->indnkeyatts == numattrs &&
10476 indexStruct->indisunique &&
10477 indexStruct->indisvalid &&
10478 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
10479 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
10480 {
10481 Datum indclassDatum;
10482 bool isnull;
10483 oidvector *indclass;
10484
10485 /* Must get indclass the hard way */
10486 indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
10487 Anum_pg_index_indclass, &isnull);
10488 Assert(!isnull);
10489 indclass = (oidvector *) DatumGetPointer(indclassDatum);
10490
10491 /*
10492 * The given attnum list may match the index columns in any order.
10493 * Check for a match, and extract the appropriate opclasses while
10494 * we're at it.
10495 *
10496 * We know that attnums[] is duplicate-free per the test at the
10497 * start of this function, and we checked above that the number of
10498 * index columns agrees, so if we find a match for each attnums[]
10499 * entry then we must have a one-to-one match in some order.
10500 */
10501 for (i = 0; i < numattrs; i++)
10502 {
10503 found = false;
10504 for (j = 0; j < numattrs; j++)
10505 {
10506 if (attnums[i] == indexStruct->indkey.values[j])
10507 {
10508 opclasses[i] = indclass->values[j];
10509 found = true;
10510 break;
10511 }
10512 }
10513 if (!found)
10514 break;
10515 }
10516
10517 /*
10518 * Refuse to use a deferrable unique/primary key. This is per SQL
10519 * spec, and there would be a lot of interesting semantic problems
10520 * if we tried to allow it.
10521 */
10522 if (found && !indexStruct->indimmediate)
10523 {
10524 /*
10525 * Remember that we found an otherwise matching index, so that
10526 * we can generate a more appropriate error message.
10527 */
10528 found_deferrable = true;
10529 found = false;
10530 }
10531 }
10532 ReleaseSysCache(indexTuple);
10533 if (found)
10534 break;
10535 }
10536
10537 if (!found)
10538 {
10539 if (found_deferrable)
10540 ereport(ERROR,
10541 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
10542 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
10543 RelationGetRelationName(pkrel))));
10544 else
10545 ereport(ERROR,
10546 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10547 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
10548 RelationGetRelationName(pkrel))));
10549 }
10550
10551 list_free(indexoidlist);
10552
10553 return indexoid;
10554 }
10555
10556 /*
10557 * findFkeyCast -
10558 *
10559 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
10560 * Caller has equal regard for binary coercibility and for an exact match.
10561 */
10562 static CoercionPathType
findFkeyCast(Oid targetTypeId,Oid sourceTypeId,Oid * funcid)10563 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
10564 {
10565 CoercionPathType ret;
10566
10567 if (targetTypeId == sourceTypeId)
10568 {
10569 ret = COERCION_PATH_RELABELTYPE;
10570 *funcid = InvalidOid;
10571 }
10572 else
10573 {
10574 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
10575 COERCION_IMPLICIT, funcid);
10576 if (ret == COERCION_PATH_NONE)
10577 /* A previously-relied-upon cast is now gone. */
10578 elog(ERROR, "could not find cast from %u to %u",
10579 sourceTypeId, targetTypeId);
10580 }
10581
10582 return ret;
10583 }
10584
10585 /*
10586 * Permissions checks on the referenced table for ADD FOREIGN KEY
10587 *
10588 * Note: we have already checked that the user owns the referencing table,
10589 * else we'd have failed much earlier; no additional checks are needed for it.
10590 */
10591 static void
checkFkeyPermissions(Relation rel,int16 * attnums,int natts)10592 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
10593 {
10594 Oid roleid = GetUserId();
10595 AclResult aclresult;
10596 int i;
10597
10598 /* Okay if we have relation-level REFERENCES permission */
10599 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
10600 ACL_REFERENCES);
10601 if (aclresult == ACLCHECK_OK)
10602 return;
10603 /* Else we must have REFERENCES on each column */
10604 for (i = 0; i < natts; i++)
10605 {
10606 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
10607 roleid, ACL_REFERENCES);
10608 if (aclresult != ACLCHECK_OK)
10609 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
10610 RelationGetRelationName(rel));
10611 }
10612 }
10613
10614 /*
10615 * Scan the existing rows in a table to verify they meet a proposed FK
10616 * constraint.
10617 *
10618 * Caller must have opened and locked both relations appropriately.
10619 */
10620 static void
validateForeignKeyConstraint(char * conname,Relation rel,Relation pkrel,Oid pkindOid,Oid constraintOid)10621 validateForeignKeyConstraint(char *conname,
10622 Relation rel,
10623 Relation pkrel,
10624 Oid pkindOid,
10625 Oid constraintOid)
10626 {
10627 TupleTableSlot *slot;
10628 TableScanDesc scan;
10629 Trigger trig;
10630 Snapshot snapshot;
10631 MemoryContext oldcxt;
10632 MemoryContext perTupCxt;
10633
10634 ereport(DEBUG1,
10635 (errmsg("validating foreign key constraint \"%s\"", conname)));
10636
10637 /*
10638 * Build a trigger call structure; we'll need it either way.
10639 */
10640 MemSet(&trig, 0, sizeof(trig));
10641 trig.tgoid = InvalidOid;
10642 trig.tgname = conname;
10643 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
10644 trig.tgisinternal = true;
10645 trig.tgconstrrelid = RelationGetRelid(pkrel);
10646 trig.tgconstrindid = pkindOid;
10647 trig.tgconstraint = constraintOid;
10648 trig.tgdeferrable = false;
10649 trig.tginitdeferred = false;
10650 /* we needn't fill in remaining fields */
10651
10652 /*
10653 * See if we can do it with a single LEFT JOIN query. A false result
10654 * indicates we must proceed with the fire-the-trigger method.
10655 */
10656 if (RI_Initial_Check(&trig, rel, pkrel))
10657 return;
10658
10659 /*
10660 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
10661 * if that tuple had just been inserted. If any of those fail, it should
10662 * ereport(ERROR) and that's that.
10663 */
10664 snapshot = RegisterSnapshot(GetLatestSnapshot());
10665 slot = table_slot_create(rel, NULL);
10666 scan = table_beginscan(rel, snapshot, 0, NULL);
10667
10668 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
10669 "validateForeignKeyConstraint",
10670 ALLOCSET_SMALL_SIZES);
10671 oldcxt = MemoryContextSwitchTo(perTupCxt);
10672
10673 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
10674 {
10675 LOCAL_FCINFO(fcinfo, 0);
10676 TriggerData trigdata = {0};
10677
10678 CHECK_FOR_INTERRUPTS();
10679
10680 /*
10681 * Make a call to the trigger function
10682 *
10683 * No parameters are passed, but we do set a context
10684 */
10685 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
10686
10687 /*
10688 * We assume RI_FKey_check_ins won't look at flinfo...
10689 */
10690 trigdata.type = T_TriggerData;
10691 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
10692 trigdata.tg_relation = rel;
10693 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
10694 trigdata.tg_trigslot = slot;
10695 trigdata.tg_trigger = &trig;
10696
10697 fcinfo->context = (Node *) &trigdata;
10698
10699 RI_FKey_check_ins(fcinfo);
10700
10701 MemoryContextReset(perTupCxt);
10702 }
10703
10704 MemoryContextSwitchTo(oldcxt);
10705 MemoryContextDelete(perTupCxt);
10706 table_endscan(scan);
10707 UnregisterSnapshot(snapshot);
10708 ExecDropSingleTupleTableSlot(slot);
10709 }
10710
10711 static void
CreateFKCheckTrigger(Oid myRelOid,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid,bool on_insert)10712 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
10713 Oid constraintOid, Oid indexOid, bool on_insert)
10714 {
10715 CreateTrigStmt *fk_trigger;
10716
10717 /*
10718 * Note: for a self-referential FK (referencing and referenced tables are
10719 * the same), it is important that the ON UPDATE action fires before the
10720 * CHECK action, since both triggers will fire on the same row during an
10721 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
10722 * state of the row. Triggers fire in name order, so we ensure this by
10723 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
10724 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
10725 */
10726 fk_trigger = makeNode(CreateTrigStmt);
10727 fk_trigger->trigname = "RI_ConstraintTrigger_c";
10728 fk_trigger->relation = NULL;
10729 fk_trigger->row = true;
10730 fk_trigger->timing = TRIGGER_TYPE_AFTER;
10731
10732 /* Either ON INSERT or ON UPDATE */
10733 if (on_insert)
10734 {
10735 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
10736 fk_trigger->events = TRIGGER_TYPE_INSERT;
10737 }
10738 else
10739 {
10740 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
10741 fk_trigger->events = TRIGGER_TYPE_UPDATE;
10742 }
10743
10744 fk_trigger->columns = NIL;
10745 fk_trigger->transitionRels = NIL;
10746 fk_trigger->whenClause = NULL;
10747 fk_trigger->isconstraint = true;
10748 fk_trigger->deferrable = fkconstraint->deferrable;
10749 fk_trigger->initdeferred = fkconstraint->initdeferred;
10750 fk_trigger->constrrel = NULL;
10751 fk_trigger->args = NIL;
10752
10753 (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid,
10754 indexOid, InvalidOid, InvalidOid, NULL, true, false);
10755
10756 /* Make changes-so-far visible */
10757 CommandCounterIncrement();
10758 }
10759
10760 /*
10761 * createForeignKeyActionTriggers
10762 * Create the referenced-side "action" triggers that implement a foreign
10763 * key.
10764 */
10765 static void
createForeignKeyActionTriggers(Relation rel,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid)10766 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
10767 Oid constraintOid, Oid indexOid)
10768 {
10769 CreateTrigStmt *fk_trigger;
10770
10771 /*
10772 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
10773 * DELETE action on the referenced table.
10774 */
10775 fk_trigger = makeNode(CreateTrigStmt);
10776 fk_trigger->trigname = "RI_ConstraintTrigger_a";
10777 fk_trigger->relation = NULL;
10778 fk_trigger->row = true;
10779 fk_trigger->timing = TRIGGER_TYPE_AFTER;
10780 fk_trigger->events = TRIGGER_TYPE_DELETE;
10781 fk_trigger->columns = NIL;
10782 fk_trigger->transitionRels = NIL;
10783 fk_trigger->whenClause = NULL;
10784 fk_trigger->isconstraint = true;
10785 fk_trigger->constrrel = NULL;
10786 switch (fkconstraint->fk_del_action)
10787 {
10788 case FKCONSTR_ACTION_NOACTION:
10789 fk_trigger->deferrable = fkconstraint->deferrable;
10790 fk_trigger->initdeferred = fkconstraint->initdeferred;
10791 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
10792 break;
10793 case FKCONSTR_ACTION_RESTRICT:
10794 fk_trigger->deferrable = false;
10795 fk_trigger->initdeferred = false;
10796 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
10797 break;
10798 case FKCONSTR_ACTION_CASCADE:
10799 fk_trigger->deferrable = false;
10800 fk_trigger->initdeferred = false;
10801 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
10802 break;
10803 case FKCONSTR_ACTION_SETNULL:
10804 fk_trigger->deferrable = false;
10805 fk_trigger->initdeferred = false;
10806 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
10807 break;
10808 case FKCONSTR_ACTION_SETDEFAULT:
10809 fk_trigger->deferrable = false;
10810 fk_trigger->initdeferred = false;
10811 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
10812 break;
10813 default:
10814 elog(ERROR, "unrecognized FK action type: %d",
10815 (int) fkconstraint->fk_del_action);
10816 break;
10817 }
10818 fk_trigger->args = NIL;
10819
10820 (void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel),
10821 constraintOid,
10822 indexOid, InvalidOid, InvalidOid, NULL, true, false);
10823
10824 /* Make changes-so-far visible */
10825 CommandCounterIncrement();
10826
10827 /*
10828 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
10829 * UPDATE action on the referenced table.
10830 */
10831 fk_trigger = makeNode(CreateTrigStmt);
10832 fk_trigger->trigname = "RI_ConstraintTrigger_a";
10833 fk_trigger->relation = NULL;
10834 fk_trigger->row = true;
10835 fk_trigger->timing = TRIGGER_TYPE_AFTER;
10836 fk_trigger->events = TRIGGER_TYPE_UPDATE;
10837 fk_trigger->columns = NIL;
10838 fk_trigger->transitionRels = NIL;
10839 fk_trigger->whenClause = NULL;
10840 fk_trigger->isconstraint = true;
10841 fk_trigger->constrrel = NULL;
10842 switch (fkconstraint->fk_upd_action)
10843 {
10844 case FKCONSTR_ACTION_NOACTION:
10845 fk_trigger->deferrable = fkconstraint->deferrable;
10846 fk_trigger->initdeferred = fkconstraint->initdeferred;
10847 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
10848 break;
10849 case FKCONSTR_ACTION_RESTRICT:
10850 fk_trigger->deferrable = false;
10851 fk_trigger->initdeferred = false;
10852 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
10853 break;
10854 case FKCONSTR_ACTION_CASCADE:
10855 fk_trigger->deferrable = false;
10856 fk_trigger->initdeferred = false;
10857 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
10858 break;
10859 case FKCONSTR_ACTION_SETNULL:
10860 fk_trigger->deferrable = false;
10861 fk_trigger->initdeferred = false;
10862 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
10863 break;
10864 case FKCONSTR_ACTION_SETDEFAULT:
10865 fk_trigger->deferrable = false;
10866 fk_trigger->initdeferred = false;
10867 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
10868 break;
10869 default:
10870 elog(ERROR, "unrecognized FK action type: %d",
10871 (int) fkconstraint->fk_upd_action);
10872 break;
10873 }
10874 fk_trigger->args = NIL;
10875
10876 (void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel),
10877 constraintOid,
10878 indexOid, InvalidOid, InvalidOid, NULL, true, false);
10879 }
10880
10881 /*
10882 * createForeignKeyCheckTriggers
10883 * Create the referencing-side "check" triggers that implement a foreign
10884 * key.
10885 */
10886 static void
createForeignKeyCheckTriggers(Oid myRelOid,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid)10887 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
10888 Constraint *fkconstraint, Oid constraintOid,
10889 Oid indexOid)
10890 {
10891 CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
10892 indexOid, true);
10893 CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
10894 indexOid, false);
10895 }
10896
10897 /*
10898 * ALTER TABLE DROP CONSTRAINT
10899 *
10900 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
10901 */
10902 static void
ATExecDropConstraint(Relation rel,const char * constrName,DropBehavior behavior,bool recurse,bool recursing,bool missing_ok,LOCKMODE lockmode)10903 ATExecDropConstraint(Relation rel, const char *constrName,
10904 DropBehavior behavior,
10905 bool recurse, bool recursing,
10906 bool missing_ok, LOCKMODE lockmode)
10907 {
10908 List *children;
10909 ListCell *child;
10910 Relation conrel;
10911 Form_pg_constraint con;
10912 SysScanDesc scan;
10913 ScanKeyData skey[3];
10914 HeapTuple tuple;
10915 bool found = false;
10916 bool is_no_inherit_constraint = false;
10917 char contype;
10918
10919 /* At top level, permission check was done in ATPrepCmd, else do it */
10920 if (recursing)
10921 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
10922
10923 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
10924
10925 /*
10926 * Find and drop the target constraint
10927 */
10928 ScanKeyInit(&skey[0],
10929 Anum_pg_constraint_conrelid,
10930 BTEqualStrategyNumber, F_OIDEQ,
10931 ObjectIdGetDatum(RelationGetRelid(rel)));
10932 ScanKeyInit(&skey[1],
10933 Anum_pg_constraint_contypid,
10934 BTEqualStrategyNumber, F_OIDEQ,
10935 ObjectIdGetDatum(InvalidOid));
10936 ScanKeyInit(&skey[2],
10937 Anum_pg_constraint_conname,
10938 BTEqualStrategyNumber, F_NAMEEQ,
10939 CStringGetDatum(constrName));
10940 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
10941 true, NULL, 3, skey);
10942
10943 /* There can be at most one matching row */
10944 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
10945 {
10946 ObjectAddress conobj;
10947
10948 con = (Form_pg_constraint) GETSTRUCT(tuple);
10949
10950 /* Don't drop inherited constraints */
10951 if (con->coninhcount > 0 && !recursing)
10952 ereport(ERROR,
10953 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10954 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
10955 constrName, RelationGetRelationName(rel))));
10956
10957 is_no_inherit_constraint = con->connoinherit;
10958 contype = con->contype;
10959
10960 /*
10961 * If it's a foreign-key constraint, we'd better lock the referenced
10962 * table and check that that's not in use, just as we've already done
10963 * for the constrained table (else we might, eg, be dropping a trigger
10964 * that has unfired events). But we can/must skip that in the
10965 * self-referential case.
10966 */
10967 if (contype == CONSTRAINT_FOREIGN &&
10968 con->confrelid != RelationGetRelid(rel))
10969 {
10970 Relation frel;
10971
10972 /* Must match lock taken by RemoveTriggerById: */
10973 frel = table_open(con->confrelid, AccessExclusiveLock);
10974 CheckTableNotInUse(frel, "ALTER TABLE");
10975 table_close(frel, NoLock);
10976 }
10977
10978 /*
10979 * Perform the actual constraint deletion
10980 */
10981 conobj.classId = ConstraintRelationId;
10982 conobj.objectId = con->oid;
10983 conobj.objectSubId = 0;
10984
10985 performDeletion(&conobj, behavior, 0);
10986
10987 found = true;
10988 }
10989
10990 systable_endscan(scan);
10991
10992 if (!found)
10993 {
10994 if (!missing_ok)
10995 {
10996 ereport(ERROR,
10997 (errcode(ERRCODE_UNDEFINED_OBJECT),
10998 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
10999 constrName, RelationGetRelationName(rel))));
11000 }
11001 else
11002 {
11003 ereport(NOTICE,
11004 (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
11005 constrName, RelationGetRelationName(rel))));
11006 table_close(conrel, RowExclusiveLock);
11007 return;
11008 }
11009 }
11010
11011 /*
11012 * For partitioned tables, non-CHECK inherited constraints are dropped via
11013 * the dependency mechanism, so we're done here.
11014 */
11015 if (contype != CONSTRAINT_CHECK &&
11016 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11017 {
11018 table_close(conrel, RowExclusiveLock);
11019 return;
11020 }
11021
11022 /*
11023 * Propagate to children as appropriate. Unlike most other ALTER
11024 * routines, we have to do this one level of recursion at a time; we can't
11025 * use find_all_inheritors to do it in one pass.
11026 */
11027 if (!is_no_inherit_constraint)
11028 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
11029 else
11030 children = NIL;
11031
11032 /*
11033 * For a partitioned table, if partitions exist and we are told not to
11034 * recurse, it's a user error. It doesn't make sense to have a constraint
11035 * be defined only on the parent, especially if it's a partitioned table.
11036 */
11037 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
11038 children != NIL && !recurse)
11039 ereport(ERROR,
11040 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11041 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
11042 errhint("Do not specify the ONLY keyword.")));
11043
11044 foreach(child, children)
11045 {
11046 Oid childrelid = lfirst_oid(child);
11047 Relation childrel;
11048 HeapTuple copy_tuple;
11049
11050 /* find_inheritance_children already got lock */
11051 childrel = table_open(childrelid, NoLock);
11052 CheckTableNotInUse(childrel, "ALTER TABLE");
11053
11054 ScanKeyInit(&skey[0],
11055 Anum_pg_constraint_conrelid,
11056 BTEqualStrategyNumber, F_OIDEQ,
11057 ObjectIdGetDatum(childrelid));
11058 ScanKeyInit(&skey[1],
11059 Anum_pg_constraint_contypid,
11060 BTEqualStrategyNumber, F_OIDEQ,
11061 ObjectIdGetDatum(InvalidOid));
11062 ScanKeyInit(&skey[2],
11063 Anum_pg_constraint_conname,
11064 BTEqualStrategyNumber, F_NAMEEQ,
11065 CStringGetDatum(constrName));
11066 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11067 true, NULL, 3, skey);
11068
11069 /* There can be at most one matching row */
11070 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11071 ereport(ERROR,
11072 (errcode(ERRCODE_UNDEFINED_OBJECT),
11073 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11074 constrName,
11075 RelationGetRelationName(childrel))));
11076
11077 copy_tuple = heap_copytuple(tuple);
11078
11079 systable_endscan(scan);
11080
11081 con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
11082
11083 /* Right now only CHECK constraints can be inherited */
11084 if (con->contype != CONSTRAINT_CHECK)
11085 elog(ERROR, "inherited constraint is not a CHECK constraint");
11086
11087 if (con->coninhcount <= 0) /* shouldn't happen */
11088 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
11089 childrelid, constrName);
11090
11091 if (recurse)
11092 {
11093 /*
11094 * If the child constraint has other definition sources, just
11095 * decrement its inheritance count; if not, recurse to delete it.
11096 */
11097 if (con->coninhcount == 1 && !con->conislocal)
11098 {
11099 /* Time to delete this child constraint, too */
11100 ATExecDropConstraint(childrel, constrName, behavior,
11101 true, true,
11102 false, lockmode);
11103 }
11104 else
11105 {
11106 /* Child constraint must survive my deletion */
11107 con->coninhcount--;
11108 CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
11109
11110 /* Make update visible */
11111 CommandCounterIncrement();
11112 }
11113 }
11114 else
11115 {
11116 /*
11117 * If we were told to drop ONLY in this table (no recursion), we
11118 * need to mark the inheritors' constraints as locally defined
11119 * rather than inherited.
11120 */
11121 con->coninhcount--;
11122 con->conislocal = true;
11123
11124 CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
11125
11126 /* Make update visible */
11127 CommandCounterIncrement();
11128 }
11129
11130 heap_freetuple(copy_tuple);
11131
11132 table_close(childrel, NoLock);
11133 }
11134
11135 table_close(conrel, RowExclusiveLock);
11136 }
11137
11138 /*
11139 * ALTER COLUMN TYPE
11140 *
11141 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
11142 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
11143 * transformed (and must be, because we rely on some transformed fields).
11144 *
11145 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
11146 * table will be done "in parallel" during phase 3, so all the USING
11147 * expressions should be parsed assuming the original column types. Also,
11148 * this allows a USING expression to refer to a field that will be dropped.
11149 *
11150 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
11151 * the first two execution steps in phase 2; they must not see the effects
11152 * of any other subcommand types, since the USING expressions are parsed
11153 * against the unmodified table's state.
11154 */
11155 static void
ATPrepAlterColumnType(List ** wqueue,AlteredTableInfo * tab,Relation rel,bool recurse,bool recursing,AlterTableCmd * cmd,LOCKMODE lockmode,AlterTableUtilityContext * context)11156 ATPrepAlterColumnType(List **wqueue,
11157 AlteredTableInfo *tab, Relation rel,
11158 bool recurse, bool recursing,
11159 AlterTableCmd *cmd, LOCKMODE lockmode,
11160 AlterTableUtilityContext *context)
11161 {
11162 char *colName = cmd->name;
11163 ColumnDef *def = (ColumnDef *) cmd->def;
11164 TypeName *typeName = def->typeName;
11165 Node *transform = def->cooked_default;
11166 HeapTuple tuple;
11167 Form_pg_attribute attTup;
11168 AttrNumber attnum;
11169 Oid targettype;
11170 int32 targettypmod;
11171 Oid targetcollid;
11172 NewColumnValue *newval;
11173 ParseState *pstate = make_parsestate(NULL);
11174 AclResult aclresult;
11175 bool is_expr;
11176
11177 if (rel->rd_rel->reloftype && !recursing)
11178 ereport(ERROR,
11179 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11180 errmsg("cannot alter column type of typed table")));
11181
11182 /* lookup the attribute so we can check inheritance status */
11183 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
11184 if (!HeapTupleIsValid(tuple))
11185 ereport(ERROR,
11186 (errcode(ERRCODE_UNDEFINED_COLUMN),
11187 errmsg("column \"%s\" of relation \"%s\" does not exist",
11188 colName, RelationGetRelationName(rel))));
11189 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
11190 attnum = attTup->attnum;
11191
11192 /* Can't alter a system attribute */
11193 if (attnum <= 0)
11194 ereport(ERROR,
11195 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11196 errmsg("cannot alter system column \"%s\"",
11197 colName)));
11198
11199 /*
11200 * Don't alter inherited columns. At outer level, there had better not be
11201 * any inherited definition; when recursing, we assume this was checked at
11202 * the parent level (see below).
11203 */
11204 if (attTup->attinhcount > 0 && !recursing)
11205 ereport(ERROR,
11206 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11207 errmsg("cannot alter inherited column \"%s\"",
11208 colName)));
11209
11210 /* Don't alter columns used in the partition key */
11211 if (has_partition_attrs(rel,
11212 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
11213 &is_expr))
11214 ereport(ERROR,
11215 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11216 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
11217 colName, RelationGetRelationName(rel))));
11218
11219 /* Look up the target type */
11220 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
11221
11222 aclresult = pg_type_aclcheck(targettype, GetUserId(), ACL_USAGE);
11223 if (aclresult != ACLCHECK_OK)
11224 aclcheck_error_type(aclresult, targettype);
11225
11226 /* And the collation */
11227 targetcollid = GetColumnDefCollation(NULL, def, targettype);
11228
11229 /* make sure datatype is legal for a column */
11230 CheckAttributeType(colName, targettype, targetcollid,
11231 list_make1_oid(rel->rd_rel->reltype),
11232 0);
11233
11234 if (tab->relkind == RELKIND_RELATION ||
11235 tab->relkind == RELKIND_PARTITIONED_TABLE)
11236 {
11237 /*
11238 * Set up an expression to transform the old data value to the new
11239 * type. If a USING option was given, use the expression as
11240 * transformed by transformAlterTableStmt, else just take the old
11241 * value and try to coerce it. We do this first so that type
11242 * incompatibility can be detected before we waste effort, and because
11243 * we need the expression to be parsed against the original table row
11244 * type.
11245 */
11246 if (!transform)
11247 {
11248 transform = (Node *) makeVar(1, attnum,
11249 attTup->atttypid, attTup->atttypmod,
11250 attTup->attcollation,
11251 0);
11252 }
11253
11254 transform = coerce_to_target_type(pstate,
11255 transform, exprType(transform),
11256 targettype, targettypmod,
11257 COERCION_ASSIGNMENT,
11258 COERCE_IMPLICIT_CAST,
11259 -1);
11260 if (transform == NULL)
11261 {
11262 /* error text depends on whether USING was specified or not */
11263 if (def->cooked_default != NULL)
11264 ereport(ERROR,
11265 (errcode(ERRCODE_DATATYPE_MISMATCH),
11266 errmsg("result of USING clause for column \"%s\""
11267 " cannot be cast automatically to type %s",
11268 colName, format_type_be(targettype)),
11269 errhint("You might need to add an explicit cast.")));
11270 else
11271 ereport(ERROR,
11272 (errcode(ERRCODE_DATATYPE_MISMATCH),
11273 errmsg("column \"%s\" cannot be cast automatically to type %s",
11274 colName, format_type_be(targettype)),
11275 /* translator: USING is SQL, don't translate it */
11276 errhint("You might need to specify \"USING %s::%s\".",
11277 quote_identifier(colName),
11278 format_type_with_typemod(targettype,
11279 targettypmod))));
11280 }
11281
11282 /* Fix collations after all else */
11283 assign_expr_collations(pstate, transform);
11284
11285 /* Plan the expr now so we can accurately assess the need to rewrite. */
11286 transform = (Node *) expression_planner((Expr *) transform);
11287
11288 /*
11289 * Add a work queue item to make ATRewriteTable update the column
11290 * contents.
11291 */
11292 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
11293 newval->attnum = attnum;
11294 newval->expr = (Expr *) transform;
11295 newval->is_generated = false;
11296
11297 tab->newvals = lappend(tab->newvals, newval);
11298 if (ATColumnChangeRequiresRewrite(transform, attnum))
11299 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
11300 }
11301 else if (transform)
11302 ereport(ERROR,
11303 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11304 errmsg("\"%s\" is not a table",
11305 RelationGetRelationName(rel))));
11306
11307 if (tab->relkind == RELKIND_COMPOSITE_TYPE ||
11308 tab->relkind == RELKIND_FOREIGN_TABLE)
11309 {
11310 /*
11311 * For composite types, do this check now. Tables will check it later
11312 * when the table is being rewritten.
11313 */
11314 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
11315 }
11316
11317 ReleaseSysCache(tuple);
11318
11319 /*
11320 * Recurse manually by queueing a new command for each child, if
11321 * necessary. We cannot apply ATSimpleRecursion here because we need to
11322 * remap attribute numbers in the USING expression, if any.
11323 *
11324 * If we are told not to recurse, there had better not be any child
11325 * tables; else the alter would put them out of step.
11326 */
11327 if (recurse)
11328 {
11329 Oid relid = RelationGetRelid(rel);
11330 List *child_oids,
11331 *child_numparents;
11332 ListCell *lo,
11333 *li;
11334
11335 child_oids = find_all_inheritors(relid, lockmode,
11336 &child_numparents);
11337
11338 /*
11339 * find_all_inheritors does the recursive search of the inheritance
11340 * hierarchy, so all we have to do is process all of the relids in the
11341 * list that it returns.
11342 */
11343 forboth(lo, child_oids, li, child_numparents)
11344 {
11345 Oid childrelid = lfirst_oid(lo);
11346 int numparents = lfirst_int(li);
11347 Relation childrel;
11348 HeapTuple childtuple;
11349 Form_pg_attribute childattTup;
11350
11351 if (childrelid == relid)
11352 continue;
11353
11354 /* find_all_inheritors already got lock */
11355 childrel = relation_open(childrelid, NoLock);
11356 CheckTableNotInUse(childrel, "ALTER TABLE");
11357
11358 /*
11359 * Verify that the child doesn't have any inherited definitions of
11360 * this column that came from outside this inheritance hierarchy.
11361 * (renameatt makes a similar test, though in a different way
11362 * because of its different recursion mechanism.)
11363 */
11364 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
11365 colName);
11366 if (!HeapTupleIsValid(childtuple))
11367 ereport(ERROR,
11368 (errcode(ERRCODE_UNDEFINED_COLUMN),
11369 errmsg("column \"%s\" of relation \"%s\" does not exist",
11370 colName, RelationGetRelationName(childrel))));
11371 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
11372
11373 if (childattTup->attinhcount > numparents)
11374 ereport(ERROR,
11375 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11376 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
11377 colName, RelationGetRelationName(childrel))));
11378
11379 ReleaseSysCache(childtuple);
11380
11381 /*
11382 * Remap the attribute numbers. If no USING expression was
11383 * specified, there is no need for this step.
11384 */
11385 if (def->cooked_default)
11386 {
11387 AttrMap *attmap;
11388 bool found_whole_row;
11389
11390 /* create a copy to scribble on */
11391 cmd = copyObject(cmd);
11392
11393 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
11394 RelationGetDescr(rel));
11395 ((ColumnDef *) cmd->def)->cooked_default =
11396 map_variable_attnos(def->cooked_default,
11397 1, 0,
11398 attmap,
11399 InvalidOid, &found_whole_row);
11400 if (found_whole_row)
11401 ereport(ERROR,
11402 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11403 errmsg("cannot convert whole-row table reference"),
11404 errdetail("USING expression contains a whole-row table reference.")));
11405 pfree(attmap);
11406 }
11407 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
11408 relation_close(childrel, NoLock);
11409 }
11410 }
11411 else if (!recursing &&
11412 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
11413 ereport(ERROR,
11414 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11415 errmsg("type of inherited column \"%s\" must be changed in child tables too",
11416 colName)));
11417
11418 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
11419 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
11420 }
11421
11422 /*
11423 * When the data type of a column is changed, a rewrite might not be required
11424 * if the new type is sufficiently identical to the old one, and the USING
11425 * clause isn't trying to insert some other value. It's safe to skip the
11426 * rewrite in these cases:
11427 *
11428 * - the old type is binary coercible to the new type
11429 * - the new type is an unconstrained domain over the old type
11430 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
11431 *
11432 * In the case of a constrained domain, we could get by with scanning the
11433 * table and checking the constraint rather than actually rewriting it, but we
11434 * don't currently try to do that.
11435 */
11436 static bool
ATColumnChangeRequiresRewrite(Node * expr,AttrNumber varattno)11437 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
11438 {
11439 Assert(expr != NULL);
11440
11441 for (;;)
11442 {
11443 /* only one varno, so no need to check that */
11444 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
11445 return false;
11446 else if (IsA(expr, RelabelType))
11447 expr = (Node *) ((RelabelType *) expr)->arg;
11448 else if (IsA(expr, CoerceToDomain))
11449 {
11450 CoerceToDomain *d = (CoerceToDomain *) expr;
11451
11452 if (DomainHasConstraints(d->resulttype))
11453 return true;
11454 expr = (Node *) d->arg;
11455 }
11456 else if (IsA(expr, FuncExpr))
11457 {
11458 FuncExpr *f = (FuncExpr *) expr;
11459
11460 switch (f->funcid)
11461 {
11462 case F_TIMESTAMPTZ_TIMESTAMP:
11463 case F_TIMESTAMP_TIMESTAMPTZ:
11464 if (TimestampTimestampTzRequiresRewrite())
11465 return true;
11466 else
11467 expr = linitial(f->args);
11468 break;
11469 default:
11470 return true;
11471 }
11472 }
11473 else
11474 return true;
11475 }
11476 }
11477
11478 /*
11479 * ALTER COLUMN .. SET DATA TYPE
11480 *
11481 * Return the address of the modified column.
11482 */
11483 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode)11484 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
11485 AlterTableCmd *cmd, LOCKMODE lockmode)
11486 {
11487 char *colName = cmd->name;
11488 ColumnDef *def = (ColumnDef *) cmd->def;
11489 TypeName *typeName = def->typeName;
11490 HeapTuple heapTup;
11491 Form_pg_attribute attTup,
11492 attOldTup;
11493 AttrNumber attnum;
11494 HeapTuple typeTuple;
11495 Form_pg_type tform;
11496 Oid targettype;
11497 int32 targettypmod;
11498 Oid targetcollid;
11499 Node *defaultexpr;
11500 Relation attrelation;
11501 Relation depRel;
11502 ScanKeyData key[3];
11503 SysScanDesc scan;
11504 HeapTuple depTup;
11505 ObjectAddress address;
11506
11507 /*
11508 * Clear all the missing values if we're rewriting the table, since this
11509 * renders them pointless.
11510 */
11511 if (tab->rewrite)
11512 {
11513 Relation newrel;
11514
11515 newrel = table_open(RelationGetRelid(rel), NoLock);
11516 RelationClearMissing(newrel);
11517 relation_close(newrel, NoLock);
11518 /* make sure we don't conflict with later attribute modifications */
11519 CommandCounterIncrement();
11520 }
11521
11522 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
11523
11524 /* Look up the target column */
11525 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
11526 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
11527 ereport(ERROR,
11528 (errcode(ERRCODE_UNDEFINED_COLUMN),
11529 errmsg("column \"%s\" of relation \"%s\" does not exist",
11530 colName, RelationGetRelationName(rel))));
11531 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
11532 attnum = attTup->attnum;
11533 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
11534
11535 /* Check for multiple ALTER TYPE on same column --- can't cope */
11536 if (attTup->atttypid != attOldTup->atttypid ||
11537 attTup->atttypmod != attOldTup->atttypmod)
11538 ereport(ERROR,
11539 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11540 errmsg("cannot alter type of column \"%s\" twice",
11541 colName)));
11542
11543 /* Look up the target type (should not fail, since prep found it) */
11544 typeTuple = typenameType(NULL, typeName, &targettypmod);
11545 tform = (Form_pg_type) GETSTRUCT(typeTuple);
11546 targettype = tform->oid;
11547 /* And the collation */
11548 targetcollid = GetColumnDefCollation(NULL, def, targettype);
11549
11550 /*
11551 * If there is a default expression for the column, get it and ensure we
11552 * can coerce it to the new datatype. (We must do this before changing
11553 * the column type, because build_column_default itself will try to
11554 * coerce, and will not issue the error message we want if it fails.)
11555 *
11556 * We remove any implicit coercion steps at the top level of the old
11557 * default expression; this has been agreed to satisfy the principle of
11558 * least surprise. (The conversion to the new column type should act like
11559 * it started from what the user sees as the stored expression, and the
11560 * implicit coercions aren't going to be shown.)
11561 */
11562 if (attTup->atthasdef)
11563 {
11564 defaultexpr = build_column_default(rel, attnum);
11565 Assert(defaultexpr);
11566 defaultexpr = strip_implicit_coercions(defaultexpr);
11567 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
11568 defaultexpr, exprType(defaultexpr),
11569 targettype, targettypmod,
11570 COERCION_ASSIGNMENT,
11571 COERCE_IMPLICIT_CAST,
11572 -1);
11573 if (defaultexpr == NULL)
11574 {
11575 if (attTup->attgenerated)
11576 ereport(ERROR,
11577 (errcode(ERRCODE_DATATYPE_MISMATCH),
11578 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
11579 colName, format_type_be(targettype))));
11580 else
11581 ereport(ERROR,
11582 (errcode(ERRCODE_DATATYPE_MISMATCH),
11583 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
11584 colName, format_type_be(targettype))));
11585 }
11586 }
11587 else
11588 defaultexpr = NULL;
11589
11590 /*
11591 * Find everything that depends on the column (constraints, indexes, etc),
11592 * and record enough information to let us recreate the objects.
11593 *
11594 * The actual recreation does not happen here, but only after we have
11595 * performed all the individual ALTER TYPE operations. We have to save
11596 * the info before executing ALTER TYPE, though, else the deparser will
11597 * get confused.
11598 */
11599 depRel = table_open(DependRelationId, RowExclusiveLock);
11600
11601 ScanKeyInit(&key[0],
11602 Anum_pg_depend_refclassid,
11603 BTEqualStrategyNumber, F_OIDEQ,
11604 ObjectIdGetDatum(RelationRelationId));
11605 ScanKeyInit(&key[1],
11606 Anum_pg_depend_refobjid,
11607 BTEqualStrategyNumber, F_OIDEQ,
11608 ObjectIdGetDatum(RelationGetRelid(rel)));
11609 ScanKeyInit(&key[2],
11610 Anum_pg_depend_refobjsubid,
11611 BTEqualStrategyNumber, F_INT4EQ,
11612 Int32GetDatum((int32) attnum));
11613
11614 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
11615 NULL, 3, key);
11616
11617 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
11618 {
11619 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
11620 ObjectAddress foundObject;
11621
11622 /* We don't expect any PIN dependencies on columns */
11623 if (foundDep->deptype == DEPENDENCY_PIN)
11624 elog(ERROR, "cannot alter type of a pinned column");
11625
11626 foundObject.classId = foundDep->classid;
11627 foundObject.objectId = foundDep->objid;
11628 foundObject.objectSubId = foundDep->objsubid;
11629
11630 switch (getObjectClass(&foundObject))
11631 {
11632 case OCLASS_CLASS:
11633 {
11634 char relKind = get_rel_relkind(foundObject.objectId);
11635
11636 if (relKind == RELKIND_INDEX ||
11637 relKind == RELKIND_PARTITIONED_INDEX)
11638 {
11639 Assert(foundObject.objectSubId == 0);
11640 RememberIndexForRebuilding(foundObject.objectId, tab);
11641 }
11642 else if (relKind == RELKIND_SEQUENCE)
11643 {
11644 /*
11645 * This must be a SERIAL column's sequence. We need
11646 * not do anything to it.
11647 */
11648 Assert(foundObject.objectSubId == 0);
11649 }
11650 else if (relKind == RELKIND_RELATION &&
11651 foundObject.objectSubId != 0 &&
11652 get_attgenerated(foundObject.objectId, foundObject.objectSubId))
11653 {
11654 /*
11655 * Changing the type of a column that is used by a
11656 * generated column is not allowed by SQL standard. It
11657 * might be doable with some thinking and effort.
11658 */
11659 ereport(ERROR,
11660 (errcode(ERRCODE_SYNTAX_ERROR),
11661 errmsg("cannot alter type of a column used by a generated column"),
11662 errdetail("Column \"%s\" is used by generated column \"%s\".",
11663 colName, get_attname(foundObject.objectId, foundObject.objectSubId, false))));
11664 }
11665 else
11666 {
11667 /* Not expecting any other direct dependencies... */
11668 elog(ERROR, "unexpected object depending on column: %s",
11669 getObjectDescription(&foundObject));
11670 }
11671 break;
11672 }
11673
11674 case OCLASS_CONSTRAINT:
11675 Assert(foundObject.objectSubId == 0);
11676 RememberConstraintForRebuilding(foundObject.objectId, tab);
11677 break;
11678
11679 case OCLASS_REWRITE:
11680 /* XXX someday see if we can cope with revising views */
11681 ereport(ERROR,
11682 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11683 errmsg("cannot alter type of a column used by a view or rule"),
11684 errdetail("%s depends on column \"%s\"",
11685 getObjectDescription(&foundObject),
11686 colName)));
11687 break;
11688
11689 case OCLASS_TRIGGER:
11690
11691 /*
11692 * A trigger can depend on a column because the column is
11693 * specified as an update target, or because the column is
11694 * used in the trigger's WHEN condition. The first case would
11695 * not require any extra work, but the second case would
11696 * require updating the WHEN expression, which will take a
11697 * significant amount of new code. Since we can't easily tell
11698 * which case applies, we punt for both. FIXME someday.
11699 */
11700 ereport(ERROR,
11701 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11702 errmsg("cannot alter type of a column used in a trigger definition"),
11703 errdetail("%s depends on column \"%s\"",
11704 getObjectDescription(&foundObject),
11705 colName)));
11706 break;
11707
11708 case OCLASS_POLICY:
11709
11710 /*
11711 * A policy can depend on a column because the column is
11712 * specified in the policy's USING or WITH CHECK qual
11713 * expressions. It might be possible to rewrite and recheck
11714 * the policy expression, but punt for now. It's certainly
11715 * easy enough to remove and recreate the policy; still, FIXME
11716 * someday.
11717 */
11718 ereport(ERROR,
11719 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11720 errmsg("cannot alter type of a column used in a policy definition"),
11721 errdetail("%s depends on column \"%s\"",
11722 getObjectDescription(&foundObject),
11723 colName)));
11724 break;
11725
11726 case OCLASS_DEFAULT:
11727
11728 /*
11729 * Ignore the column's default expression, since we will fix
11730 * it below.
11731 */
11732 Assert(defaultexpr);
11733 break;
11734
11735 case OCLASS_STATISTIC_EXT:
11736
11737 /*
11738 * Give the extended-stats machinery a chance to fix anything
11739 * that this column type change would break.
11740 */
11741 UpdateStatisticsForTypeChange(foundObject.objectId,
11742 RelationGetRelid(rel), attnum,
11743 attTup->atttypid, targettype);
11744 break;
11745
11746 case OCLASS_PROC:
11747 case OCLASS_TYPE:
11748 case OCLASS_CAST:
11749 case OCLASS_COLLATION:
11750 case OCLASS_CONVERSION:
11751 case OCLASS_LANGUAGE:
11752 case OCLASS_LARGEOBJECT:
11753 case OCLASS_OPERATOR:
11754 case OCLASS_OPCLASS:
11755 case OCLASS_OPFAMILY:
11756 case OCLASS_AM:
11757 case OCLASS_AMOP:
11758 case OCLASS_AMPROC:
11759 case OCLASS_SCHEMA:
11760 case OCLASS_TSPARSER:
11761 case OCLASS_TSDICT:
11762 case OCLASS_TSTEMPLATE:
11763 case OCLASS_TSCONFIG:
11764 case OCLASS_ROLE:
11765 case OCLASS_DATABASE:
11766 case OCLASS_TBLSPACE:
11767 case OCLASS_FDW:
11768 case OCLASS_FOREIGN_SERVER:
11769 case OCLASS_USER_MAPPING:
11770 case OCLASS_DEFACL:
11771 case OCLASS_EXTENSION:
11772 case OCLASS_EVENT_TRIGGER:
11773 case OCLASS_PUBLICATION:
11774 case OCLASS_PUBLICATION_REL:
11775 case OCLASS_SUBSCRIPTION:
11776 case OCLASS_TRANSFORM:
11777
11778 /*
11779 * We don't expect any of these sorts of objects to depend on
11780 * a column.
11781 */
11782 elog(ERROR, "unexpected object depending on column: %s",
11783 getObjectDescription(&foundObject));
11784 break;
11785
11786 /*
11787 * There's intentionally no default: case here; we want the
11788 * compiler to warn if a new OCLASS hasn't been handled above.
11789 */
11790 }
11791 }
11792
11793 systable_endscan(scan);
11794
11795 /*
11796 * Now scan for dependencies of this column on other things. The only
11797 * thing we should find is the dependency on the column datatype, which we
11798 * want to remove, possibly a collation dependency, and dependencies on
11799 * other columns if it is a generated column.
11800 */
11801 ScanKeyInit(&key[0],
11802 Anum_pg_depend_classid,
11803 BTEqualStrategyNumber, F_OIDEQ,
11804 ObjectIdGetDatum(RelationRelationId));
11805 ScanKeyInit(&key[1],
11806 Anum_pg_depend_objid,
11807 BTEqualStrategyNumber, F_OIDEQ,
11808 ObjectIdGetDatum(RelationGetRelid(rel)));
11809 ScanKeyInit(&key[2],
11810 Anum_pg_depend_objsubid,
11811 BTEqualStrategyNumber, F_INT4EQ,
11812 Int32GetDatum((int32) attnum));
11813
11814 scan = systable_beginscan(depRel, DependDependerIndexId, true,
11815 NULL, 3, key);
11816
11817 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
11818 {
11819 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
11820 ObjectAddress foundObject;
11821
11822 foundObject.classId = foundDep->refclassid;
11823 foundObject.objectId = foundDep->refobjid;
11824 foundObject.objectSubId = foundDep->refobjsubid;
11825
11826 if (foundDep->deptype != DEPENDENCY_NORMAL &&
11827 foundDep->deptype != DEPENDENCY_AUTO)
11828 elog(ERROR, "found unexpected dependency type '%c'",
11829 foundDep->deptype);
11830 if (!(foundDep->refclassid == TypeRelationId &&
11831 foundDep->refobjid == attTup->atttypid) &&
11832 !(foundDep->refclassid == CollationRelationId &&
11833 foundDep->refobjid == attTup->attcollation) &&
11834 !(foundDep->refclassid == RelationRelationId &&
11835 foundDep->refobjid == RelationGetRelid(rel) &&
11836 foundDep->refobjsubid != 0)
11837 )
11838 elog(ERROR, "found unexpected dependency for column: %s",
11839 getObjectDescription(&foundObject));
11840
11841 CatalogTupleDelete(depRel, &depTup->t_self);
11842 }
11843
11844 systable_endscan(scan);
11845
11846 table_close(depRel, RowExclusiveLock);
11847
11848 /*
11849 * Here we go --- change the recorded column type and collation. (Note
11850 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
11851 * fix up the missing value if any. There shouldn't be any missing values
11852 * for anything except plain tables, but if there are, ignore them.
11853 */
11854 if (rel->rd_rel->relkind == RELKIND_RELATION && attTup->atthasmissing)
11855 {
11856 Datum missingval;
11857 bool missingNull;
11858
11859 /* if rewrite is true the missing value should already be cleared */
11860 Assert(tab->rewrite == 0);
11861
11862 /* Get the missing value datum */
11863 missingval = heap_getattr(heapTup,
11864 Anum_pg_attribute_attmissingval,
11865 attrelation->rd_att,
11866 &missingNull);
11867
11868 /* if it's a null array there is nothing to do */
11869
11870 if (!missingNull)
11871 {
11872 /*
11873 * Get the datum out of the array and repack it in a new array
11874 * built with the new type data. We assume that since the table
11875 * doesn't need rewriting, the actual Datum doesn't need to be
11876 * changed, only the array metadata.
11877 */
11878
11879 int one = 1;
11880 bool isNull;
11881 Datum valuesAtt[Natts_pg_attribute];
11882 bool nullsAtt[Natts_pg_attribute];
11883 bool replacesAtt[Natts_pg_attribute];
11884 HeapTuple newTup;
11885
11886 MemSet(valuesAtt, 0, sizeof(valuesAtt));
11887 MemSet(nullsAtt, false, sizeof(nullsAtt));
11888 MemSet(replacesAtt, false, sizeof(replacesAtt));
11889
11890 missingval = array_get_element(missingval,
11891 1,
11892 &one,
11893 0,
11894 attTup->attlen,
11895 attTup->attbyval,
11896 attTup->attalign,
11897 &isNull);
11898 missingval = PointerGetDatum(construct_array(&missingval,
11899 1,
11900 targettype,
11901 tform->typlen,
11902 tform->typbyval,
11903 tform->typalign));
11904
11905 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
11906 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
11907 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
11908
11909 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
11910 valuesAtt, nullsAtt, replacesAtt);
11911 heap_freetuple(heapTup);
11912 heapTup = newTup;
11913 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
11914 }
11915 }
11916
11917 attTup->atttypid = targettype;
11918 attTup->atttypmod = targettypmod;
11919 attTup->attcollation = targetcollid;
11920 attTup->attndims = list_length(typeName->arrayBounds);
11921 attTup->attlen = tform->typlen;
11922 attTup->attbyval = tform->typbyval;
11923 attTup->attalign = tform->typalign;
11924 attTup->attstorage = tform->typstorage;
11925
11926 ReleaseSysCache(typeTuple);
11927
11928 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
11929
11930 table_close(attrelation, RowExclusiveLock);
11931
11932 /* Install dependencies on new datatype and collation */
11933 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
11934 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
11935
11936 /*
11937 * Drop any pg_statistic entry for the column, since it's now wrong type
11938 */
11939 RemoveStatistics(RelationGetRelid(rel), attnum);
11940
11941 InvokeObjectPostAlterHook(RelationRelationId,
11942 RelationGetRelid(rel), attnum);
11943
11944 /*
11945 * Update the default, if present, by brute force --- remove and re-add
11946 * the default. Probably unsafe to take shortcuts, since the new version
11947 * may well have additional dependencies. (It's okay to do this now,
11948 * rather than after other ALTER TYPE commands, since the default won't
11949 * depend on other column types.)
11950 */
11951 if (defaultexpr)
11952 {
11953 /* Must make new row visible since it will be updated again */
11954 CommandCounterIncrement();
11955
11956 /*
11957 * We use RESTRICT here for safety, but at present we do not expect
11958 * anything to depend on the default.
11959 */
11960 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
11961 true);
11962
11963 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
11964 }
11965
11966 ObjectAddressSubSet(address, RelationRelationId,
11967 RelationGetRelid(rel), attnum);
11968
11969 /* Cleanup */
11970 heap_freetuple(heapTup);
11971
11972 return address;
11973 }
11974
11975 /*
11976 * Subroutine for ATExecAlterColumnType: remember that a replica identity
11977 * needs to be reset.
11978 */
11979 static void
RememberReplicaIdentityForRebuilding(Oid indoid,AlteredTableInfo * tab)11980 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
11981 {
11982 if (!get_index_isreplident(indoid))
11983 return;
11984
11985 if (tab->replicaIdentityIndex)
11986 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
11987
11988 tab->replicaIdentityIndex = get_rel_name(indoid);
11989 }
11990
11991 /*
11992 * Subroutine for ATExecAlterColumnType: remember any clustered index.
11993 */
11994 static void
RememberClusterOnForRebuilding(Oid indoid,AlteredTableInfo * tab)11995 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
11996 {
11997 if (!get_index_isclustered(indoid))
11998 return;
11999
12000 if (tab->clusterOnIndex)
12001 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
12002
12003 tab->clusterOnIndex = get_rel_name(indoid);
12004 }
12005
12006 /*
12007 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
12008 * to be rebuilt (which we might already know).
12009 */
12010 static void
RememberConstraintForRebuilding(Oid conoid,AlteredTableInfo * tab)12011 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
12012 {
12013 /*
12014 * This de-duplication check is critical for two independent reasons: we
12015 * mustn't try to recreate the same constraint twice, and if a constraint
12016 * depends on more than one column whose type is to be altered, we must
12017 * capture its definition string before applying any of the column type
12018 * changes. ruleutils.c will get confused if we ask again later.
12019 */
12020 if (!list_member_oid(tab->changedConstraintOids, conoid))
12021 {
12022 /* OK, capture the constraint's existing definition string */
12023 char *defstring = pg_get_constraintdef_command(conoid);
12024 Oid indoid;
12025
12026 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
12027 conoid);
12028 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
12029 defstring);
12030
12031 /*
12032 * For the index of a constraint, if any, remember if it is used for
12033 * the table's replica identity or if it is a clustered index, so that
12034 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
12035 * those properties.
12036 */
12037 indoid = get_constraint_index(conoid);
12038 if (OidIsValid(indoid))
12039 {
12040 RememberReplicaIdentityForRebuilding(indoid, tab);
12041 RememberClusterOnForRebuilding(indoid, tab);
12042 }
12043 }
12044 }
12045
12046 /*
12047 * Subroutine for ATExecAlterColumnType: remember that an index needs
12048 * to be rebuilt (which we might already know).
12049 */
12050 static void
RememberIndexForRebuilding(Oid indoid,AlteredTableInfo * tab)12051 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
12052 {
12053 /*
12054 * This de-duplication check is critical for two independent reasons: we
12055 * mustn't try to recreate the same index twice, and if an index depends
12056 * on more than one column whose type is to be altered, we must capture
12057 * its definition string before applying any of the column type changes.
12058 * ruleutils.c will get confused if we ask again later.
12059 */
12060 if (!list_member_oid(tab->changedIndexOids, indoid))
12061 {
12062 /*
12063 * Before adding it as an index-to-rebuild, we'd better see if it
12064 * belongs to a constraint, and if so rebuild the constraint instead.
12065 * Typically this check fails, because constraint indexes normally
12066 * have only dependencies on their constraint. But it's possible for
12067 * such an index to also have direct dependencies on table columns,
12068 * for example with a partial exclusion constraint.
12069 */
12070 Oid conoid = get_index_constraint(indoid);
12071
12072 if (OidIsValid(conoid))
12073 {
12074 RememberConstraintForRebuilding(conoid, tab);
12075 }
12076 else
12077 {
12078 /* OK, capture the index's existing definition string */
12079 char *defstring = pg_get_indexdef_string(indoid);
12080
12081 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
12082 indoid);
12083 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
12084 defstring);
12085
12086 /*
12087 * Remember if this index is used for the table's replica identity
12088 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
12089 * can queue up commands necessary to restore those properties.
12090 */
12091 RememberReplicaIdentityForRebuilding(indoid, tab);
12092 RememberClusterOnForRebuilding(indoid, tab);
12093 }
12094 }
12095 }
12096
12097 /*
12098 * Cleanup after we've finished all the ALTER TYPE operations for a
12099 * particular relation. We have to drop and recreate all the indexes
12100 * and constraints that depend on the altered columns. We do the
12101 * actual dropping here, but re-creation is managed by adding work
12102 * queue entries to do those steps later.
12103 */
12104 static void
ATPostAlterTypeCleanup(List ** wqueue,AlteredTableInfo * tab,LOCKMODE lockmode)12105 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
12106 {
12107 ObjectAddress obj;
12108 ObjectAddresses *objects;
12109 ListCell *def_item;
12110 ListCell *oid_item;
12111
12112 /*
12113 * Collect all the constraints and indexes to drop so we can process them
12114 * in a single call. That way we don't have to worry about dependencies
12115 * among them.
12116 */
12117 objects = new_object_addresses();
12118
12119 /*
12120 * Re-parse the index and constraint definitions, and attach them to the
12121 * appropriate work queue entries. We do this before dropping because in
12122 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
12123 * lock on the table the constraint is attached to, and we need to get
12124 * that before reparsing/dropping.
12125 *
12126 * We can't rely on the output of deparsing to tell us which relation to
12127 * operate on, because concurrent activity might have made the name
12128 * resolve differently. Instead, we've got to use the OID of the
12129 * constraint or index we're processing to figure out which relation to
12130 * operate on.
12131 */
12132 forboth(oid_item, tab->changedConstraintOids,
12133 def_item, tab->changedConstraintDefs)
12134 {
12135 Oid oldId = lfirst_oid(oid_item);
12136 HeapTuple tup;
12137 Form_pg_constraint con;
12138 Oid relid;
12139 Oid confrelid;
12140 char contype;
12141 bool conislocal;
12142
12143 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
12144 if (!HeapTupleIsValid(tup)) /* should not happen */
12145 elog(ERROR, "cache lookup failed for constraint %u", oldId);
12146 con = (Form_pg_constraint) GETSTRUCT(tup);
12147 if (OidIsValid(con->conrelid))
12148 relid = con->conrelid;
12149 else
12150 {
12151 /* must be a domain constraint */
12152 relid = get_typ_typrelid(getBaseType(con->contypid));
12153 if (!OidIsValid(relid))
12154 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
12155 }
12156 confrelid = con->confrelid;
12157 contype = con->contype;
12158 conislocal = con->conislocal;
12159 ReleaseSysCache(tup);
12160
12161 ObjectAddressSet(obj, ConstraintRelationId, oldId);
12162 add_exact_object_address(&obj, objects);
12163
12164 /*
12165 * If the constraint is inherited (only), we don't want to inject a
12166 * new definition here; it'll get recreated when ATAddCheckConstraint
12167 * recurses from adding the parent table's constraint. But we had to
12168 * carry the info this far so that we can drop the constraint below.
12169 */
12170 if (!conislocal)
12171 continue;
12172
12173 /*
12174 * When rebuilding an FK constraint that references the table we're
12175 * modifying, we might not yet have any lock on the FK's table, so get
12176 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
12177 * step, so there's no value in asking for anything weaker.
12178 */
12179 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
12180 LockRelationOid(relid, AccessExclusiveLock);
12181
12182 ATPostAlterTypeParse(oldId, relid, confrelid,
12183 (char *) lfirst(def_item),
12184 wqueue, lockmode, tab->rewrite);
12185 }
12186 forboth(oid_item, tab->changedIndexOids,
12187 def_item, tab->changedIndexDefs)
12188 {
12189 Oid oldId = lfirst_oid(oid_item);
12190 Oid relid;
12191
12192 relid = IndexGetRelation(oldId, false);
12193 ATPostAlterTypeParse(oldId, relid, InvalidOid,
12194 (char *) lfirst(def_item),
12195 wqueue, lockmode, tab->rewrite);
12196
12197 ObjectAddressSet(obj, RelationRelationId, oldId);
12198 add_exact_object_address(&obj, objects);
12199 }
12200
12201 /*
12202 * Queue up command to restore replica identity index marking
12203 */
12204 if (tab->replicaIdentityIndex)
12205 {
12206 AlterTableCmd *cmd = makeNode(AlterTableCmd);
12207 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
12208
12209 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
12210 subcmd->name = tab->replicaIdentityIndex;
12211 cmd->subtype = AT_ReplicaIdentity;
12212 cmd->def = (Node *) subcmd;
12213
12214 /* do it after indexes and constraints */
12215 tab->subcmds[AT_PASS_OLD_CONSTR] =
12216 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
12217 }
12218
12219 /*
12220 * Queue up command to restore marking of index used for cluster.
12221 */
12222 if (tab->clusterOnIndex)
12223 {
12224 AlterTableCmd *cmd = makeNode(AlterTableCmd);
12225
12226 cmd->subtype = AT_ClusterOn;
12227 cmd->name = tab->clusterOnIndex;
12228
12229 /* do it after indexes and constraints */
12230 tab->subcmds[AT_PASS_OLD_CONSTR] =
12231 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
12232 }
12233
12234 /*
12235 * It should be okay to use DROP_RESTRICT here, since nothing else should
12236 * be depending on these objects.
12237 */
12238 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
12239
12240 free_object_addresses(objects);
12241
12242 /*
12243 * The objects will get recreated during subsequent passes over the work
12244 * queue.
12245 */
12246 }
12247
12248 /*
12249 * Parse the previously-saved definition string for a constraint or index
12250 * against the newly-established column data type(s), and queue up the
12251 * resulting command parsetrees for execution.
12252 *
12253 * This might fail if, for example, you have a WHERE clause that uses an
12254 * operator that's not available for the new column type.
12255 */
12256 static void
ATPostAlterTypeParse(Oid oldId,Oid oldRelId,Oid refRelId,char * cmd,List ** wqueue,LOCKMODE lockmode,bool rewrite)12257 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
12258 List **wqueue, LOCKMODE lockmode, bool rewrite)
12259 {
12260 List *raw_parsetree_list;
12261 List *querytree_list;
12262 ListCell *list_item;
12263 Relation rel;
12264
12265 /*
12266 * We expect that we will get only ALTER TABLE and CREATE INDEX
12267 * statements. Hence, there is no need to pass them through
12268 * parse_analyze() or the rewriter, but instead we need to pass them
12269 * through parse_utilcmd.c to make them ready for execution.
12270 */
12271 raw_parsetree_list = raw_parser(cmd);
12272 querytree_list = NIL;
12273 foreach(list_item, raw_parsetree_list)
12274 {
12275 RawStmt *rs = lfirst_node(RawStmt, list_item);
12276 Node *stmt = rs->stmt;
12277
12278 if (IsA(stmt, IndexStmt))
12279 querytree_list = lappend(querytree_list,
12280 transformIndexStmt(oldRelId,
12281 (IndexStmt *) stmt,
12282 cmd));
12283 else if (IsA(stmt, AlterTableStmt))
12284 {
12285 List *beforeStmts;
12286 List *afterStmts;
12287
12288 stmt = (Node *) transformAlterTableStmt(oldRelId,
12289 (AlterTableStmt *) stmt,
12290 cmd,
12291 &beforeStmts,
12292 &afterStmts);
12293 querytree_list = list_concat(querytree_list, beforeStmts);
12294 querytree_list = lappend(querytree_list, stmt);
12295 querytree_list = list_concat(querytree_list, afterStmts);
12296 }
12297 else
12298 querytree_list = lappend(querytree_list, stmt);
12299 }
12300
12301 /* Caller should already have acquired whatever lock we need. */
12302 rel = relation_open(oldRelId, NoLock);
12303
12304 /*
12305 * Attach each generated command to the proper place in the work queue.
12306 * Note this could result in creation of entirely new work-queue entries.
12307 *
12308 * Also note that we have to tweak the command subtypes, because it turns
12309 * out that re-creation of indexes and constraints has to act a bit
12310 * differently from initial creation.
12311 */
12312 foreach(list_item, querytree_list)
12313 {
12314 Node *stm = (Node *) lfirst(list_item);
12315 AlteredTableInfo *tab;
12316
12317 tab = ATGetQueueEntry(wqueue, rel);
12318
12319 if (IsA(stm, IndexStmt))
12320 {
12321 IndexStmt *stmt = (IndexStmt *) stm;
12322 AlterTableCmd *newcmd;
12323
12324 if (!rewrite)
12325 TryReuseIndex(oldId, stmt);
12326 stmt->reset_default_tblspc = true;
12327 /* keep the index's comment */
12328 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
12329
12330 newcmd = makeNode(AlterTableCmd);
12331 newcmd->subtype = AT_ReAddIndex;
12332 newcmd->def = (Node *) stmt;
12333 tab->subcmds[AT_PASS_OLD_INDEX] =
12334 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
12335 }
12336 else if (IsA(stm, AlterTableStmt))
12337 {
12338 AlterTableStmt *stmt = (AlterTableStmt *) stm;
12339 ListCell *lcmd;
12340
12341 foreach(lcmd, stmt->cmds)
12342 {
12343 AlterTableCmd *cmd = castNode(AlterTableCmd, lfirst(lcmd));
12344
12345 if (cmd->subtype == AT_AddIndex)
12346 {
12347 IndexStmt *indstmt;
12348 Oid indoid;
12349
12350 indstmt = castNode(IndexStmt, cmd->def);
12351 indoid = get_constraint_index(oldId);
12352
12353 if (!rewrite)
12354 TryReuseIndex(indoid, indstmt);
12355 /* keep any comment on the index */
12356 indstmt->idxcomment = GetComment(indoid,
12357 RelationRelationId, 0);
12358 indstmt->reset_default_tblspc = true;
12359
12360 cmd->subtype = AT_ReAddIndex;
12361 tab->subcmds[AT_PASS_OLD_INDEX] =
12362 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
12363
12364 /* recreate any comment on the constraint */
12365 RebuildConstraintComment(tab,
12366 AT_PASS_OLD_INDEX,
12367 oldId,
12368 rel,
12369 NIL,
12370 indstmt->idxname);
12371 }
12372 else if (cmd->subtype == AT_AddConstraint)
12373 {
12374 Constraint *con = castNode(Constraint, cmd->def);
12375
12376 con->old_pktable_oid = refRelId;
12377 /* rewriting neither side of a FK */
12378 if (con->contype == CONSTR_FOREIGN &&
12379 !rewrite && tab->rewrite == 0)
12380 TryReuseForeignKey(oldId, con);
12381 con->reset_default_tblspc = true;
12382 cmd->subtype = AT_ReAddConstraint;
12383 tab->subcmds[AT_PASS_OLD_CONSTR] =
12384 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
12385
12386 /* recreate any comment on the constraint */
12387 RebuildConstraintComment(tab,
12388 AT_PASS_OLD_CONSTR,
12389 oldId,
12390 rel,
12391 NIL,
12392 con->conname);
12393 }
12394 else if (cmd->subtype == AT_SetNotNull)
12395 {
12396 /*
12397 * The parser will create AT_SetNotNull subcommands for
12398 * columns of PRIMARY KEY indexes/constraints, but we need
12399 * not do anything with them here, because the columns'
12400 * NOT NULL marks will already have been propagated into
12401 * the new table definition.
12402 */
12403 }
12404 else
12405 elog(ERROR, "unexpected statement subtype: %d",
12406 (int) cmd->subtype);
12407 }
12408 }
12409 else if (IsA(stm, AlterDomainStmt))
12410 {
12411 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
12412
12413 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
12414 {
12415 Constraint *con = castNode(Constraint, stmt->def);
12416 AlterTableCmd *cmd = makeNode(AlterTableCmd);
12417
12418 cmd->subtype = AT_ReAddDomainConstraint;
12419 cmd->def = (Node *) stmt;
12420 tab->subcmds[AT_PASS_OLD_CONSTR] =
12421 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
12422
12423 /* recreate any comment on the constraint */
12424 RebuildConstraintComment(tab,
12425 AT_PASS_OLD_CONSTR,
12426 oldId,
12427 NULL,
12428 stmt->typeName,
12429 con->conname);
12430 }
12431 else
12432 elog(ERROR, "unexpected statement subtype: %d",
12433 (int) stmt->subtype);
12434 }
12435 else
12436 elog(ERROR, "unexpected statement type: %d",
12437 (int) nodeTag(stm));
12438 }
12439
12440 relation_close(rel, NoLock);
12441 }
12442
12443 /*
12444 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
12445 * for a table or domain constraint that is being rebuilt.
12446 *
12447 * objid is the OID of the constraint.
12448 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
12449 * as a string list) for a domain constraint.
12450 * (We could dig that info, as well as the conname, out of the pg_constraint
12451 * entry; but callers already have them so might as well pass them.)
12452 */
12453 static void
RebuildConstraintComment(AlteredTableInfo * tab,int pass,Oid objid,Relation rel,List * domname,const char * conname)12454 RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
12455 Relation rel, List *domname,
12456 const char *conname)
12457 {
12458 CommentStmt *cmd;
12459 char *comment_str;
12460 AlterTableCmd *newcmd;
12461
12462 /* Look for comment for object wanted, and leave if none */
12463 comment_str = GetComment(objid, ConstraintRelationId, 0);
12464 if (comment_str == NULL)
12465 return;
12466
12467 /* Build CommentStmt node, copying all input data for safety */
12468 cmd = makeNode(CommentStmt);
12469 if (rel)
12470 {
12471 cmd->objtype = OBJECT_TABCONSTRAINT;
12472 cmd->object = (Node *)
12473 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
12474 makeString(pstrdup(RelationGetRelationName(rel))),
12475 makeString(pstrdup(conname)));
12476 }
12477 else
12478 {
12479 cmd->objtype = OBJECT_DOMCONSTRAINT;
12480 cmd->object = (Node *)
12481 list_make2(makeTypeNameFromNameList(copyObject(domname)),
12482 makeString(pstrdup(conname)));
12483 }
12484 cmd->comment = comment_str;
12485
12486 /* Append it to list of commands */
12487 newcmd = makeNode(AlterTableCmd);
12488 newcmd->subtype = AT_ReAddComment;
12489 newcmd->def = (Node *) cmd;
12490 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
12491 }
12492
12493 /*
12494 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
12495 * for the real analysis, then mutates the IndexStmt based on that verdict.
12496 */
12497 static void
TryReuseIndex(Oid oldId,IndexStmt * stmt)12498 TryReuseIndex(Oid oldId, IndexStmt *stmt)
12499 {
12500 if (CheckIndexCompatible(oldId,
12501 stmt->accessMethod,
12502 stmt->indexParams,
12503 stmt->excludeOpNames))
12504 {
12505 Relation irel = index_open(oldId, NoLock);
12506
12507 /* If it's a partitioned index, there is no storage to share. */
12508 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
12509 {
12510 stmt->oldNode = irel->rd_node.relNode;
12511 stmt->oldCreateSubid = irel->rd_createSubid;
12512 stmt->oldFirstRelfilenodeSubid = irel->rd_firstRelfilenodeSubid;
12513 }
12514 index_close(irel, NoLock);
12515 }
12516 }
12517
12518 /*
12519 * Subroutine for ATPostAlterTypeParse().
12520 *
12521 * Stash the old P-F equality operator into the Constraint node, for possible
12522 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
12523 * this constraint can be skipped.
12524 */
12525 static void
TryReuseForeignKey(Oid oldId,Constraint * con)12526 TryReuseForeignKey(Oid oldId, Constraint *con)
12527 {
12528 HeapTuple tup;
12529 Datum adatum;
12530 bool isNull;
12531 ArrayType *arr;
12532 Oid *rawarr;
12533 int numkeys;
12534 int i;
12535
12536 Assert(con->contype == CONSTR_FOREIGN);
12537 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
12538
12539 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
12540 if (!HeapTupleIsValid(tup)) /* should not happen */
12541 elog(ERROR, "cache lookup failed for constraint %u", oldId);
12542
12543 adatum = SysCacheGetAttr(CONSTROID, tup,
12544 Anum_pg_constraint_conpfeqop, &isNull);
12545 if (isNull)
12546 elog(ERROR, "null conpfeqop for constraint %u", oldId);
12547 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
12548 numkeys = ARR_DIMS(arr)[0];
12549 /* test follows the one in ri_FetchConstraintInfo() */
12550 if (ARR_NDIM(arr) != 1 ||
12551 ARR_HASNULL(arr) ||
12552 ARR_ELEMTYPE(arr) != OIDOID)
12553 elog(ERROR, "conpfeqop is not a 1-D Oid array");
12554 rawarr = (Oid *) ARR_DATA_PTR(arr);
12555
12556 /* stash a List of the operator Oids in our Constraint node */
12557 for (i = 0; i < numkeys; i++)
12558 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
12559
12560 ReleaseSysCache(tup);
12561 }
12562
12563 /*
12564 * ALTER COLUMN .. OPTIONS ( ... )
12565 *
12566 * Returns the address of the modified column
12567 */
12568 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel,const char * colName,List * options,LOCKMODE lockmode)12569 ATExecAlterColumnGenericOptions(Relation rel,
12570 const char *colName,
12571 List *options,
12572 LOCKMODE lockmode)
12573 {
12574 Relation ftrel;
12575 Relation attrel;
12576 ForeignServer *server;
12577 ForeignDataWrapper *fdw;
12578 HeapTuple tuple;
12579 HeapTuple newtuple;
12580 bool isnull;
12581 Datum repl_val[Natts_pg_attribute];
12582 bool repl_null[Natts_pg_attribute];
12583 bool repl_repl[Natts_pg_attribute];
12584 Datum datum;
12585 Form_pg_foreign_table fttableform;
12586 Form_pg_attribute atttableform;
12587 AttrNumber attnum;
12588 ObjectAddress address;
12589
12590 if (options == NIL)
12591 return InvalidObjectAddress;
12592
12593 /* First, determine FDW validator associated to the foreign table. */
12594 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
12595 tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
12596 if (!HeapTupleIsValid(tuple))
12597 ereport(ERROR,
12598 (errcode(ERRCODE_UNDEFINED_OBJECT),
12599 errmsg("foreign table \"%s\" does not exist",
12600 RelationGetRelationName(rel))));
12601 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
12602 server = GetForeignServer(fttableform->ftserver);
12603 fdw = GetForeignDataWrapper(server->fdwid);
12604
12605 table_close(ftrel, AccessShareLock);
12606 ReleaseSysCache(tuple);
12607
12608 attrel = table_open(AttributeRelationId, RowExclusiveLock);
12609 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
12610 if (!HeapTupleIsValid(tuple))
12611 ereport(ERROR,
12612 (errcode(ERRCODE_UNDEFINED_COLUMN),
12613 errmsg("column \"%s\" of relation \"%s\" does not exist",
12614 colName, RelationGetRelationName(rel))));
12615
12616 /* Prevent them from altering a system attribute */
12617 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
12618 attnum = atttableform->attnum;
12619 if (attnum <= 0)
12620 ereport(ERROR,
12621 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12622 errmsg("cannot alter system column \"%s\"", colName)));
12623
12624
12625 /* Initialize buffers for new tuple values */
12626 memset(repl_val, 0, sizeof(repl_val));
12627 memset(repl_null, false, sizeof(repl_null));
12628 memset(repl_repl, false, sizeof(repl_repl));
12629
12630 /* Extract the current options */
12631 datum = SysCacheGetAttr(ATTNAME,
12632 tuple,
12633 Anum_pg_attribute_attfdwoptions,
12634 &isnull);
12635 if (isnull)
12636 datum = PointerGetDatum(NULL);
12637
12638 /* Transform the options */
12639 datum = transformGenericOptions(AttributeRelationId,
12640 datum,
12641 options,
12642 fdw->fdwvalidator);
12643
12644 if (PointerIsValid(DatumGetPointer(datum)))
12645 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
12646 else
12647 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
12648
12649 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
12650
12651 /* Everything looks good - update the tuple */
12652
12653 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
12654 repl_val, repl_null, repl_repl);
12655
12656 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
12657
12658 InvokeObjectPostAlterHook(RelationRelationId,
12659 RelationGetRelid(rel),
12660 atttableform->attnum);
12661 ObjectAddressSubSet(address, RelationRelationId,
12662 RelationGetRelid(rel), attnum);
12663
12664 ReleaseSysCache(tuple);
12665
12666 table_close(attrel, RowExclusiveLock);
12667
12668 heap_freetuple(newtuple);
12669
12670 return address;
12671 }
12672
12673 /*
12674 * ALTER TABLE OWNER
12675 *
12676 * recursing is true if we are recursing from a table to its indexes,
12677 * sequences, or toast table. We don't allow the ownership of those things to
12678 * be changed separately from the parent table. Also, we can skip permission
12679 * checks (this is necessary not just an optimization, else we'd fail to
12680 * handle toast tables properly).
12681 *
12682 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
12683 * free-standing composite type.
12684 */
12685 void
ATExecChangeOwner(Oid relationOid,Oid newOwnerId,bool recursing,LOCKMODE lockmode)12686 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
12687 {
12688 Relation target_rel;
12689 Relation class_rel;
12690 HeapTuple tuple;
12691 Form_pg_class tuple_class;
12692
12693 /*
12694 * Get exclusive lock till end of transaction on the target table. Use
12695 * relation_open so that we can work on indexes and sequences.
12696 */
12697 target_rel = relation_open(relationOid, lockmode);
12698
12699 /* Get its pg_class tuple, too */
12700 class_rel = table_open(RelationRelationId, RowExclusiveLock);
12701
12702 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
12703 if (!HeapTupleIsValid(tuple))
12704 elog(ERROR, "cache lookup failed for relation %u", relationOid);
12705 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
12706
12707 /* Can we change the ownership of this tuple? */
12708 switch (tuple_class->relkind)
12709 {
12710 case RELKIND_RELATION:
12711 case RELKIND_VIEW:
12712 case RELKIND_MATVIEW:
12713 case RELKIND_FOREIGN_TABLE:
12714 case RELKIND_PARTITIONED_TABLE:
12715 /* ok to change owner */
12716 break;
12717 case RELKIND_INDEX:
12718 if (!recursing)
12719 {
12720 /*
12721 * Because ALTER INDEX OWNER used to be allowed, and in fact
12722 * is generated by old versions of pg_dump, we give a warning
12723 * and do nothing rather than erroring out. Also, to avoid
12724 * unnecessary chatter while restoring those old dumps, say
12725 * nothing at all if the command would be a no-op anyway.
12726 */
12727 if (tuple_class->relowner != newOwnerId)
12728 ereport(WARNING,
12729 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12730 errmsg("cannot change owner of index \"%s\"",
12731 NameStr(tuple_class->relname)),
12732 errhint("Change the ownership of the index's table, instead.")));
12733 /* quick hack to exit via the no-op path */
12734 newOwnerId = tuple_class->relowner;
12735 }
12736 break;
12737 case RELKIND_PARTITIONED_INDEX:
12738 if (recursing)
12739 break;
12740 ereport(ERROR,
12741 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12742 errmsg("cannot change owner of index \"%s\"",
12743 NameStr(tuple_class->relname)),
12744 errhint("Change the ownership of the index's table, instead.")));
12745 break;
12746 case RELKIND_SEQUENCE:
12747 if (!recursing &&
12748 tuple_class->relowner != newOwnerId)
12749 {
12750 /* if it's an owned sequence, disallow changing it by itself */
12751 Oid tableId;
12752 int32 colId;
12753
12754 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
12755 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
12756 ereport(ERROR,
12757 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12758 errmsg("cannot change owner of sequence \"%s\"",
12759 NameStr(tuple_class->relname)),
12760 errdetail("Sequence \"%s\" is linked to table \"%s\".",
12761 NameStr(tuple_class->relname),
12762 get_rel_name(tableId))));
12763 }
12764 break;
12765 case RELKIND_COMPOSITE_TYPE:
12766 if (recursing)
12767 break;
12768 ereport(ERROR,
12769 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12770 errmsg("\"%s\" is a composite type",
12771 NameStr(tuple_class->relname)),
12772 errhint("Use ALTER TYPE instead.")));
12773 break;
12774 case RELKIND_TOASTVALUE:
12775 if (recursing)
12776 break;
12777 /* FALL THRU */
12778 default:
12779 ereport(ERROR,
12780 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12781 errmsg("\"%s\" is not a table, view, sequence, or foreign table",
12782 NameStr(tuple_class->relname))));
12783 }
12784
12785 /*
12786 * If the new owner is the same as the existing owner, consider the
12787 * command to have succeeded. This is for dump restoration purposes.
12788 */
12789 if (tuple_class->relowner != newOwnerId)
12790 {
12791 Datum repl_val[Natts_pg_class];
12792 bool repl_null[Natts_pg_class];
12793 bool repl_repl[Natts_pg_class];
12794 Acl *newAcl;
12795 Datum aclDatum;
12796 bool isNull;
12797 HeapTuple newtuple;
12798
12799 /* skip permission checks when recursing to index or toast table */
12800 if (!recursing)
12801 {
12802 /* Superusers can always do it */
12803 if (!superuser())
12804 {
12805 Oid namespaceOid = tuple_class->relnamespace;
12806 AclResult aclresult;
12807
12808 /* Otherwise, must be owner of the existing object */
12809 if (!pg_class_ownercheck(relationOid, GetUserId()))
12810 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
12811 RelationGetRelationName(target_rel));
12812
12813 /* Must be able to become new owner */
12814 check_is_member_of_role(GetUserId(), newOwnerId);
12815
12816 /* New owner must have CREATE privilege on namespace */
12817 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
12818 ACL_CREATE);
12819 if (aclresult != ACLCHECK_OK)
12820 aclcheck_error(aclresult, OBJECT_SCHEMA,
12821 get_namespace_name(namespaceOid));
12822 }
12823 }
12824
12825 memset(repl_null, false, sizeof(repl_null));
12826 memset(repl_repl, false, sizeof(repl_repl));
12827
12828 repl_repl[Anum_pg_class_relowner - 1] = true;
12829 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
12830
12831 /*
12832 * Determine the modified ACL for the new owner. This is only
12833 * necessary when the ACL is non-null.
12834 */
12835 aclDatum = SysCacheGetAttr(RELOID, tuple,
12836 Anum_pg_class_relacl,
12837 &isNull);
12838 if (!isNull)
12839 {
12840 newAcl = aclnewowner(DatumGetAclP(aclDatum),
12841 tuple_class->relowner, newOwnerId);
12842 repl_repl[Anum_pg_class_relacl - 1] = true;
12843 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
12844 }
12845
12846 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
12847
12848 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
12849
12850 heap_freetuple(newtuple);
12851
12852 /*
12853 * We must similarly update any per-column ACLs to reflect the new
12854 * owner; for neatness reasons that's split out as a subroutine.
12855 */
12856 change_owner_fix_column_acls(relationOid,
12857 tuple_class->relowner,
12858 newOwnerId);
12859
12860 /*
12861 * Update owner dependency reference, if any. A composite type has
12862 * none, because it's tracked for the pg_type entry instead of here;
12863 * indexes and TOAST tables don't have their own entries either.
12864 */
12865 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
12866 tuple_class->relkind != RELKIND_INDEX &&
12867 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
12868 tuple_class->relkind != RELKIND_TOASTVALUE)
12869 changeDependencyOnOwner(RelationRelationId, relationOid,
12870 newOwnerId);
12871
12872 /*
12873 * Also change the ownership of the table's row type, if it has one
12874 */
12875 if (tuple_class->relkind != RELKIND_INDEX &&
12876 tuple_class->relkind != RELKIND_PARTITIONED_INDEX)
12877 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
12878
12879 /*
12880 * If we are operating on a table or materialized view, also change
12881 * the ownership of any indexes and sequences that belong to the
12882 * relation, as well as its toast table (if it has one).
12883 */
12884 if (tuple_class->relkind == RELKIND_RELATION ||
12885 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
12886 tuple_class->relkind == RELKIND_MATVIEW ||
12887 tuple_class->relkind == RELKIND_TOASTVALUE)
12888 {
12889 List *index_oid_list;
12890 ListCell *i;
12891
12892 /* Find all the indexes belonging to this relation */
12893 index_oid_list = RelationGetIndexList(target_rel);
12894
12895 /* For each index, recursively change its ownership */
12896 foreach(i, index_oid_list)
12897 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
12898
12899 list_free(index_oid_list);
12900 }
12901
12902 /* If it has a toast table, recurse to change its ownership */
12903 if (tuple_class->reltoastrelid != InvalidOid)
12904 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
12905 true, lockmode);
12906
12907 /* If it has dependent sequences, recurse to change them too */
12908 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
12909 }
12910
12911 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
12912
12913 ReleaseSysCache(tuple);
12914 table_close(class_rel, RowExclusiveLock);
12915 relation_close(target_rel, NoLock);
12916 }
12917
12918 /*
12919 * change_owner_fix_column_acls
12920 *
12921 * Helper function for ATExecChangeOwner. Scan the columns of the table
12922 * and fix any non-null column ACLs to reflect the new owner.
12923 */
12924 static void
change_owner_fix_column_acls(Oid relationOid,Oid oldOwnerId,Oid newOwnerId)12925 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
12926 {
12927 Relation attRelation;
12928 SysScanDesc scan;
12929 ScanKeyData key[1];
12930 HeapTuple attributeTuple;
12931
12932 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
12933 ScanKeyInit(&key[0],
12934 Anum_pg_attribute_attrelid,
12935 BTEqualStrategyNumber, F_OIDEQ,
12936 ObjectIdGetDatum(relationOid));
12937 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
12938 true, NULL, 1, key);
12939 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
12940 {
12941 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
12942 Datum repl_val[Natts_pg_attribute];
12943 bool repl_null[Natts_pg_attribute];
12944 bool repl_repl[Natts_pg_attribute];
12945 Acl *newAcl;
12946 Datum aclDatum;
12947 bool isNull;
12948 HeapTuple newtuple;
12949
12950 /* Ignore dropped columns */
12951 if (att->attisdropped)
12952 continue;
12953
12954 aclDatum = heap_getattr(attributeTuple,
12955 Anum_pg_attribute_attacl,
12956 RelationGetDescr(attRelation),
12957 &isNull);
12958 /* Null ACLs do not require changes */
12959 if (isNull)
12960 continue;
12961
12962 memset(repl_null, false, sizeof(repl_null));
12963 memset(repl_repl, false, sizeof(repl_repl));
12964
12965 newAcl = aclnewowner(DatumGetAclP(aclDatum),
12966 oldOwnerId, newOwnerId);
12967 repl_repl[Anum_pg_attribute_attacl - 1] = true;
12968 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
12969
12970 newtuple = heap_modify_tuple(attributeTuple,
12971 RelationGetDescr(attRelation),
12972 repl_val, repl_null, repl_repl);
12973
12974 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
12975
12976 heap_freetuple(newtuple);
12977 }
12978 systable_endscan(scan);
12979 table_close(attRelation, RowExclusiveLock);
12980 }
12981
12982 /*
12983 * change_owner_recurse_to_sequences
12984 *
12985 * Helper function for ATExecChangeOwner. Examines pg_depend searching
12986 * for sequences that are dependent on serial columns, and changes their
12987 * ownership.
12988 */
12989 static void
change_owner_recurse_to_sequences(Oid relationOid,Oid newOwnerId,LOCKMODE lockmode)12990 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
12991 {
12992 Relation depRel;
12993 SysScanDesc scan;
12994 ScanKeyData key[2];
12995 HeapTuple tup;
12996
12997 /*
12998 * SERIAL sequences are those having an auto dependency on one of the
12999 * table's columns (we don't care *which* column, exactly).
13000 */
13001 depRel = table_open(DependRelationId, AccessShareLock);
13002
13003 ScanKeyInit(&key[0],
13004 Anum_pg_depend_refclassid,
13005 BTEqualStrategyNumber, F_OIDEQ,
13006 ObjectIdGetDatum(RelationRelationId));
13007 ScanKeyInit(&key[1],
13008 Anum_pg_depend_refobjid,
13009 BTEqualStrategyNumber, F_OIDEQ,
13010 ObjectIdGetDatum(relationOid));
13011 /* we leave refobjsubid unspecified */
13012
13013 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13014 NULL, 2, key);
13015
13016 while (HeapTupleIsValid(tup = systable_getnext(scan)))
13017 {
13018 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
13019 Relation seqRel;
13020
13021 /* skip dependencies other than auto dependencies on columns */
13022 if (depForm->refobjsubid == 0 ||
13023 depForm->classid != RelationRelationId ||
13024 depForm->objsubid != 0 ||
13025 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
13026 continue;
13027
13028 /* Use relation_open just in case it's an index */
13029 seqRel = relation_open(depForm->objid, lockmode);
13030
13031 /* skip non-sequence relations */
13032 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
13033 {
13034 /* No need to keep the lock */
13035 relation_close(seqRel, lockmode);
13036 continue;
13037 }
13038
13039 /* We don't need to close the sequence while we alter it. */
13040 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
13041
13042 /* Now we can close it. Keep the lock till end of transaction. */
13043 relation_close(seqRel, NoLock);
13044 }
13045
13046 systable_endscan(scan);
13047
13048 relation_close(depRel, AccessShareLock);
13049 }
13050
13051 /*
13052 * ALTER TABLE CLUSTER ON
13053 *
13054 * The only thing we have to do is to change the indisclustered bits.
13055 *
13056 * Return the address of the new clustering index.
13057 */
13058 static ObjectAddress
ATExecClusterOn(Relation rel,const char * indexName,LOCKMODE lockmode)13059 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
13060 {
13061 Oid indexOid;
13062 ObjectAddress address;
13063
13064 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
13065
13066 if (!OidIsValid(indexOid))
13067 ereport(ERROR,
13068 (errcode(ERRCODE_UNDEFINED_OBJECT),
13069 errmsg("index \"%s\" for table \"%s\" does not exist",
13070 indexName, RelationGetRelationName(rel))));
13071
13072 /* Check index is valid to cluster on */
13073 check_index_is_clusterable(rel, indexOid, false, lockmode);
13074
13075 /* And do the work */
13076 mark_index_clustered(rel, indexOid, false);
13077
13078 ObjectAddressSet(address,
13079 RelationRelationId, indexOid);
13080
13081 return address;
13082 }
13083
13084 /*
13085 * ALTER TABLE SET WITHOUT CLUSTER
13086 *
13087 * We have to find any indexes on the table that have indisclustered bit
13088 * set and turn it off.
13089 */
13090 static void
ATExecDropCluster(Relation rel,LOCKMODE lockmode)13091 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
13092 {
13093 mark_index_clustered(rel, InvalidOid, false);
13094 }
13095
13096 /*
13097 * ALTER TABLE SET TABLESPACE
13098 */
13099 static void
ATPrepSetTableSpace(AlteredTableInfo * tab,Relation rel,const char * tablespacename,LOCKMODE lockmode)13100 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
13101 {
13102 Oid tablespaceId;
13103
13104 /* Check that the tablespace exists */
13105 tablespaceId = get_tablespace_oid(tablespacename, false);
13106
13107 /* Check permissions except when moving to database's default */
13108 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
13109 {
13110 AclResult aclresult;
13111
13112 aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
13113 if (aclresult != ACLCHECK_OK)
13114 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
13115 }
13116
13117 /* Save info for Phase 3 to do the real work */
13118 if (OidIsValid(tab->newTableSpace))
13119 ereport(ERROR,
13120 (errcode(ERRCODE_SYNTAX_ERROR),
13121 errmsg("cannot have multiple SET TABLESPACE subcommands")));
13122
13123 tab->newTableSpace = tablespaceId;
13124 }
13125
13126 /*
13127 * Set, reset, or replace reloptions.
13128 */
13129 static void
ATExecSetRelOptions(Relation rel,List * defList,AlterTableType operation,LOCKMODE lockmode)13130 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
13131 LOCKMODE lockmode)
13132 {
13133 Oid relid;
13134 Relation pgclass;
13135 HeapTuple tuple;
13136 HeapTuple newtuple;
13137 Datum datum;
13138 bool isnull;
13139 Datum newOptions;
13140 Datum repl_val[Natts_pg_class];
13141 bool repl_null[Natts_pg_class];
13142 bool repl_repl[Natts_pg_class];
13143 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
13144
13145 if (defList == NIL && operation != AT_ReplaceRelOptions)
13146 return; /* nothing to do */
13147
13148 pgclass = table_open(RelationRelationId, RowExclusiveLock);
13149
13150 /* Fetch heap tuple */
13151 relid = RelationGetRelid(rel);
13152 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13153 if (!HeapTupleIsValid(tuple))
13154 elog(ERROR, "cache lookup failed for relation %u", relid);
13155
13156 if (operation == AT_ReplaceRelOptions)
13157 {
13158 /*
13159 * If we're supposed to replace the reloptions list, we just pretend
13160 * there were none before.
13161 */
13162 datum = (Datum) 0;
13163 isnull = true;
13164 }
13165 else
13166 {
13167 /* Get the old reloptions */
13168 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
13169 &isnull);
13170 }
13171
13172 /* Generate new proposed reloptions (text array) */
13173 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
13174 defList, NULL, validnsps, false,
13175 operation == AT_ResetRelOptions);
13176
13177 /* Validate */
13178 switch (rel->rd_rel->relkind)
13179 {
13180 case RELKIND_RELATION:
13181 case RELKIND_TOASTVALUE:
13182 case RELKIND_MATVIEW:
13183 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
13184 break;
13185 case RELKIND_PARTITIONED_TABLE:
13186 (void) partitioned_table_reloptions(newOptions, true);
13187 break;
13188 case RELKIND_VIEW:
13189 (void) view_reloptions(newOptions, true);
13190 break;
13191 case RELKIND_INDEX:
13192 case RELKIND_PARTITIONED_INDEX:
13193 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
13194 break;
13195 default:
13196 ereport(ERROR,
13197 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13198 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
13199 RelationGetRelationName(rel))));
13200 break;
13201 }
13202
13203 /* Special-case validation of view options */
13204 if (rel->rd_rel->relkind == RELKIND_VIEW)
13205 {
13206 Query *view_query = get_view_query(rel);
13207 List *view_options = untransformRelOptions(newOptions);
13208 ListCell *cell;
13209 bool check_option = false;
13210
13211 foreach(cell, view_options)
13212 {
13213 DefElem *defel = (DefElem *) lfirst(cell);
13214
13215 if (strcmp(defel->defname, "check_option") == 0)
13216 check_option = true;
13217 }
13218
13219 /*
13220 * If the check option is specified, look to see if the view is
13221 * actually auto-updatable or not.
13222 */
13223 if (check_option)
13224 {
13225 const char *view_updatable_error =
13226 view_query_is_auto_updatable(view_query, true);
13227
13228 if (view_updatable_error)
13229 ereport(ERROR,
13230 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13231 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
13232 errhint("%s", _(view_updatable_error))));
13233 }
13234 }
13235
13236 /*
13237 * All we need do here is update the pg_class row; the new options will be
13238 * propagated into relcaches during post-commit cache inval.
13239 */
13240 memset(repl_val, 0, sizeof(repl_val));
13241 memset(repl_null, false, sizeof(repl_null));
13242 memset(repl_repl, false, sizeof(repl_repl));
13243
13244 if (newOptions != (Datum) 0)
13245 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
13246 else
13247 repl_null[Anum_pg_class_reloptions - 1] = true;
13248
13249 repl_repl[Anum_pg_class_reloptions - 1] = true;
13250
13251 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
13252 repl_val, repl_null, repl_repl);
13253
13254 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
13255
13256 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13257
13258 heap_freetuple(newtuple);
13259
13260 ReleaseSysCache(tuple);
13261
13262 /* repeat the whole exercise for the toast table, if there's one */
13263 if (OidIsValid(rel->rd_rel->reltoastrelid))
13264 {
13265 Relation toastrel;
13266 Oid toastid = rel->rd_rel->reltoastrelid;
13267
13268 toastrel = table_open(toastid, lockmode);
13269
13270 /* Fetch heap tuple */
13271 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
13272 if (!HeapTupleIsValid(tuple))
13273 elog(ERROR, "cache lookup failed for relation %u", toastid);
13274
13275 if (operation == AT_ReplaceRelOptions)
13276 {
13277 /*
13278 * If we're supposed to replace the reloptions list, we just
13279 * pretend there were none before.
13280 */
13281 datum = (Datum) 0;
13282 isnull = true;
13283 }
13284 else
13285 {
13286 /* Get the old reloptions */
13287 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
13288 &isnull);
13289 }
13290
13291 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
13292 defList, "toast", validnsps, false,
13293 operation == AT_ResetRelOptions);
13294
13295 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
13296
13297 memset(repl_val, 0, sizeof(repl_val));
13298 memset(repl_null, false, sizeof(repl_null));
13299 memset(repl_repl, false, sizeof(repl_repl));
13300
13301 if (newOptions != (Datum) 0)
13302 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
13303 else
13304 repl_null[Anum_pg_class_reloptions - 1] = true;
13305
13306 repl_repl[Anum_pg_class_reloptions - 1] = true;
13307
13308 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
13309 repl_val, repl_null, repl_repl);
13310
13311 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
13312
13313 InvokeObjectPostAlterHookArg(RelationRelationId,
13314 RelationGetRelid(toastrel), 0,
13315 InvalidOid, true);
13316
13317 heap_freetuple(newtuple);
13318
13319 ReleaseSysCache(tuple);
13320
13321 table_close(toastrel, NoLock);
13322 }
13323
13324 table_close(pgclass, RowExclusiveLock);
13325 }
13326
13327 /*
13328 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
13329 * rewriting to be done, so we just want to copy the data as fast as possible.
13330 */
13331 static void
ATExecSetTableSpace(Oid tableOid,Oid newTableSpace,LOCKMODE lockmode)13332 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13333 {
13334 Relation rel;
13335 Oid oldTableSpace;
13336 Oid reltoastrelid;
13337 Oid newrelfilenode;
13338 RelFileNode newrnode;
13339 Relation pg_class;
13340 HeapTuple tuple;
13341 Form_pg_class rd_rel;
13342 List *reltoastidxids = NIL;
13343 ListCell *lc;
13344
13345 /*
13346 * Need lock here in case we are recursing to toast table or index
13347 */
13348 rel = relation_open(tableOid, lockmode);
13349
13350 /*
13351 * No work if no change in tablespace.
13352 */
13353 oldTableSpace = rel->rd_rel->reltablespace;
13354 if (newTableSpace == oldTableSpace ||
13355 (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
13356 {
13357 InvokeObjectPostAlterHook(RelationRelationId,
13358 RelationGetRelid(rel), 0);
13359
13360 relation_close(rel, NoLock);
13361 return;
13362 }
13363
13364 /*
13365 * We cannot support moving mapped relations into different tablespaces.
13366 * (In particular this eliminates all shared catalogs.)
13367 */
13368 if (RelationIsMapped(rel))
13369 ereport(ERROR,
13370 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13371 errmsg("cannot move system relation \"%s\"",
13372 RelationGetRelationName(rel))));
13373
13374 /* Can't move a non-shared relation into pg_global */
13375 if (newTableSpace == GLOBALTABLESPACE_OID)
13376 ereport(ERROR,
13377 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13378 errmsg("only shared relations can be placed in pg_global tablespace")));
13379
13380 /*
13381 * Don't allow moving temp tables of other backends ... their local buffer
13382 * manager is not going to cope.
13383 */
13384 if (RELATION_IS_OTHER_TEMP(rel))
13385 ereport(ERROR,
13386 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13387 errmsg("cannot move temporary tables of other sessions")));
13388
13389 reltoastrelid = rel->rd_rel->reltoastrelid;
13390 /* Fetch the list of indexes on toast relation if necessary */
13391 if (OidIsValid(reltoastrelid))
13392 {
13393 Relation toastRel = relation_open(reltoastrelid, lockmode);
13394
13395 reltoastidxids = RelationGetIndexList(toastRel);
13396 relation_close(toastRel, lockmode);
13397 }
13398
13399 /* Get a modifiable copy of the relation's pg_class row */
13400 pg_class = table_open(RelationRelationId, RowExclusiveLock);
13401
13402 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tableOid));
13403 if (!HeapTupleIsValid(tuple))
13404 elog(ERROR, "cache lookup failed for relation %u", tableOid);
13405 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
13406
13407 /*
13408 * Relfilenodes are not unique in databases across tablespaces, so we need
13409 * to allocate a new one in the new tablespace.
13410 */
13411 newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
13412 rel->rd_rel->relpersistence);
13413
13414 /* Open old and new relation */
13415 newrnode = rel->rd_node;
13416 newrnode.relNode = newrelfilenode;
13417 newrnode.spcNode = newTableSpace;
13418
13419 /* hand off to AM to actually create the new filenode and copy the data */
13420 if (rel->rd_rel->relkind == RELKIND_INDEX)
13421 {
13422 index_copy_data(rel, newrnode);
13423 }
13424 else
13425 {
13426 Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
13427 rel->rd_rel->relkind == RELKIND_MATVIEW ||
13428 rel->rd_rel->relkind == RELKIND_TOASTVALUE);
13429 table_relation_copy_data(rel, &newrnode);
13430 }
13431
13432 /*
13433 * Update the pg_class row.
13434 *
13435 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
13436 * executed on pg_class or its indexes (the above copy wouldn't contain
13437 * the updated pg_class entry), but that's forbidden above.
13438 */
13439 rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
13440 rd_rel->relfilenode = newrelfilenode;
13441 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13442
13443 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13444
13445 heap_freetuple(tuple);
13446
13447 table_close(pg_class, RowExclusiveLock);
13448
13449 RelationAssumeNewRelfilenode(rel);
13450
13451 relation_close(rel, NoLock);
13452
13453 /* Make sure the reltablespace change is visible */
13454 CommandCounterIncrement();
13455
13456 /* Move associated toast relation and/or indexes, too */
13457 if (OidIsValid(reltoastrelid))
13458 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
13459 foreach(lc, reltoastidxids)
13460 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
13461
13462 /* Clean up */
13463 list_free(reltoastidxids);
13464 }
13465
13466 /*
13467 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
13468 * storage that have an interest in preserving tablespace.
13469 *
13470 * Since these have no storage the tablespace can be updated with a simple
13471 * metadata only operation to update the tablespace.
13472 */
13473 static void
ATExecSetTableSpaceNoStorage(Relation rel,Oid newTableSpace)13474 ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
13475 {
13476 HeapTuple tuple;
13477 Oid oldTableSpace;
13478 Relation pg_class;
13479 Form_pg_class rd_rel;
13480 Oid reloid = RelationGetRelid(rel);
13481
13482 /*
13483 * Shouldn't be called on relations having storage; these are processed in
13484 * phase 3.
13485 */
13486 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
13487
13488 /* Can't allow a non-shared relation in pg_global */
13489 if (newTableSpace == GLOBALTABLESPACE_OID)
13490 ereport(ERROR,
13491 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13492 errmsg("only shared relations can be placed in pg_global tablespace")));
13493
13494 /*
13495 * No work if no change in tablespace.
13496 */
13497 oldTableSpace = rel->rd_rel->reltablespace;
13498 if (newTableSpace == oldTableSpace ||
13499 (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
13500 {
13501 InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
13502 return;
13503 }
13504
13505 /* Get a modifiable copy of the relation's pg_class row */
13506 pg_class = table_open(RelationRelationId, RowExclusiveLock);
13507
13508 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
13509 if (!HeapTupleIsValid(tuple))
13510 elog(ERROR, "cache lookup failed for relation %u", reloid);
13511 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
13512
13513 /* update the pg_class row */
13514 rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
13515 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13516
13517 /* Record dependency on tablespace */
13518 changeDependencyOnTablespace(RelationRelationId,
13519 reloid, rd_rel->reltablespace);
13520
13521 InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
13522
13523 heap_freetuple(tuple);
13524
13525 table_close(pg_class, RowExclusiveLock);
13526
13527 /* Make sure the reltablespace change is visible */
13528 CommandCounterIncrement();
13529 }
13530
13531 /*
13532 * Alter Table ALL ... SET TABLESPACE
13533 *
13534 * Allows a user to move all objects of some type in a given tablespace in the
13535 * current database to another tablespace. Objects can be chosen based on the
13536 * owner of the object also, to allow users to move only their objects.
13537 * The user must have CREATE rights on the new tablespace, as usual. The main
13538 * permissions handling is done by the lower-level table move function.
13539 *
13540 * All to-be-moved objects are locked first. If NOWAIT is specified and the
13541 * lock can't be acquired then we ereport(ERROR).
13542 */
13543 Oid
AlterTableMoveAll(AlterTableMoveAllStmt * stmt)13544 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
13545 {
13546 List *relations = NIL;
13547 ListCell *l;
13548 ScanKeyData key[1];
13549 Relation rel;
13550 TableScanDesc scan;
13551 HeapTuple tuple;
13552 Oid orig_tablespaceoid;
13553 Oid new_tablespaceoid;
13554 List *role_oids = roleSpecsToIds(stmt->roles);
13555
13556 /* Ensure we were not asked to move something we can't */
13557 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
13558 stmt->objtype != OBJECT_MATVIEW)
13559 ereport(ERROR,
13560 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13561 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
13562
13563 /* Get the orig and new tablespace OIDs */
13564 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
13565 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
13566
13567 /* Can't move shared relations in to or out of pg_global */
13568 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
13569 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
13570 new_tablespaceoid == GLOBALTABLESPACE_OID)
13571 ereport(ERROR,
13572 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13573 errmsg("cannot move relations in to or out of pg_global tablespace")));
13574
13575 /*
13576 * Must have CREATE rights on the new tablespace, unless it is the
13577 * database default tablespace (which all users implicitly have CREATE
13578 * rights on).
13579 */
13580 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
13581 {
13582 AclResult aclresult;
13583
13584 aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(),
13585 ACL_CREATE);
13586 if (aclresult != ACLCHECK_OK)
13587 aclcheck_error(aclresult, OBJECT_TABLESPACE,
13588 get_tablespace_name(new_tablespaceoid));
13589 }
13590
13591 /*
13592 * Now that the checks are done, check if we should set either to
13593 * InvalidOid because it is our database's default tablespace.
13594 */
13595 if (orig_tablespaceoid == MyDatabaseTableSpace)
13596 orig_tablespaceoid = InvalidOid;
13597
13598 if (new_tablespaceoid == MyDatabaseTableSpace)
13599 new_tablespaceoid = InvalidOid;
13600
13601 /* no-op */
13602 if (orig_tablespaceoid == new_tablespaceoid)
13603 return new_tablespaceoid;
13604
13605 /*
13606 * Walk the list of objects in the tablespace and move them. This will
13607 * only find objects in our database, of course.
13608 */
13609 ScanKeyInit(&key[0],
13610 Anum_pg_class_reltablespace,
13611 BTEqualStrategyNumber, F_OIDEQ,
13612 ObjectIdGetDatum(orig_tablespaceoid));
13613
13614 rel = table_open(RelationRelationId, AccessShareLock);
13615 scan = table_beginscan_catalog(rel, 1, key);
13616 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
13617 {
13618 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
13619 Oid relOid = relForm->oid;
13620
13621 /*
13622 * Do not move objects in pg_catalog as part of this, if an admin
13623 * really wishes to do so, they can issue the individual ALTER
13624 * commands directly.
13625 *
13626 * Also, explicitly avoid any shared tables, temp tables, or TOAST
13627 * (TOAST will be moved with the main table).
13628 */
13629 if (IsCatalogNamespace(relForm->relnamespace) ||
13630 relForm->relisshared ||
13631 isAnyTempNamespace(relForm->relnamespace) ||
13632 IsToastNamespace(relForm->relnamespace))
13633 continue;
13634
13635 /* Only move the object type requested */
13636 if ((stmt->objtype == OBJECT_TABLE &&
13637 relForm->relkind != RELKIND_RELATION &&
13638 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
13639 (stmt->objtype == OBJECT_INDEX &&
13640 relForm->relkind != RELKIND_INDEX &&
13641 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
13642 (stmt->objtype == OBJECT_MATVIEW &&
13643 relForm->relkind != RELKIND_MATVIEW))
13644 continue;
13645
13646 /* Check if we are only moving objects owned by certain roles */
13647 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
13648 continue;
13649
13650 /*
13651 * Handle permissions-checking here since we are locking the tables
13652 * and also to avoid doing a bunch of work only to fail part-way. Note
13653 * that permissions will also be checked by AlterTableInternal().
13654 *
13655 * Caller must be considered an owner on the table to move it.
13656 */
13657 if (!pg_class_ownercheck(relOid, GetUserId()))
13658 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
13659 NameStr(relForm->relname));
13660
13661 if (stmt->nowait &&
13662 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
13663 ereport(ERROR,
13664 (errcode(ERRCODE_OBJECT_IN_USE),
13665 errmsg("aborting because lock on relation \"%s.%s\" is not available",
13666 get_namespace_name(relForm->relnamespace),
13667 NameStr(relForm->relname))));
13668 else
13669 LockRelationOid(relOid, AccessExclusiveLock);
13670
13671 /* Add to our list of objects to move */
13672 relations = lappend_oid(relations, relOid);
13673 }
13674
13675 table_endscan(scan);
13676 table_close(rel, AccessShareLock);
13677
13678 if (relations == NIL)
13679 ereport(NOTICE,
13680 (errcode(ERRCODE_NO_DATA_FOUND),
13681 errmsg("no matching relations in tablespace \"%s\" found",
13682 orig_tablespaceoid == InvalidOid ? "(database default)" :
13683 get_tablespace_name(orig_tablespaceoid))));
13684
13685 /* Everything is locked, loop through and move all of the relations. */
13686 foreach(l, relations)
13687 {
13688 List *cmds = NIL;
13689 AlterTableCmd *cmd = makeNode(AlterTableCmd);
13690
13691 cmd->subtype = AT_SetTableSpace;
13692 cmd->name = stmt->new_tablespacename;
13693
13694 cmds = lappend(cmds, cmd);
13695
13696 EventTriggerAlterTableStart((Node *) stmt);
13697 /* OID is set by AlterTableInternal */
13698 AlterTableInternal(lfirst_oid(l), cmds, false);
13699 EventTriggerAlterTableEnd();
13700 }
13701
13702 return new_tablespaceoid;
13703 }
13704
13705 static void
index_copy_data(Relation rel,RelFileNode newrnode)13706 index_copy_data(Relation rel, RelFileNode newrnode)
13707 {
13708 SMgrRelation dstrel;
13709
13710 dstrel = smgropen(newrnode, rel->rd_backend);
13711 RelationOpenSmgr(rel);
13712
13713 /*
13714 * Since we copy the file directly without looking at the shared buffers,
13715 * we'd better first flush out any pages of the source relation that are
13716 * in shared buffers. We assume no new changes will be made while we are
13717 * holding exclusive lock on the rel.
13718 */
13719 FlushRelationBuffers(rel);
13720
13721 /*
13722 * Create and copy all forks of the relation, and schedule unlinking of
13723 * old physical files.
13724 *
13725 * NOTE: any conflict in relfilenode value will be caught in
13726 * RelationCreateStorage().
13727 */
13728 RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
13729
13730 /* copy main fork */
13731 RelationCopyStorage(rel->rd_smgr, dstrel, MAIN_FORKNUM,
13732 rel->rd_rel->relpersistence);
13733
13734 /* copy those extra forks that exist */
13735 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
13736 forkNum <= MAX_FORKNUM; forkNum++)
13737 {
13738 if (smgrexists(rel->rd_smgr, forkNum))
13739 {
13740 smgrcreate(dstrel, forkNum, false);
13741
13742 /*
13743 * WAL log creation if the relation is persistent, or this is the
13744 * init fork of an unlogged relation.
13745 */
13746 if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
13747 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
13748 forkNum == INIT_FORKNUM))
13749 log_smgrcreate(&newrnode, forkNum);
13750 RelationCopyStorage(rel->rd_smgr, dstrel, forkNum,
13751 rel->rd_rel->relpersistence);
13752 }
13753 }
13754
13755 /* drop old relation, and close new one */
13756 RelationDropStorage(rel);
13757 smgrclose(dstrel);
13758 }
13759
13760 /*
13761 * ALTER TABLE ENABLE/DISABLE TRIGGER
13762 *
13763 * We just pass this off to trigger.c.
13764 */
13765 static void
ATExecEnableDisableTrigger(Relation rel,const char * trigname,char fires_when,bool skip_system,LOCKMODE lockmode)13766 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
13767 char fires_when, bool skip_system, LOCKMODE lockmode)
13768 {
13769 EnableDisableTrigger(rel, trigname, fires_when, skip_system, lockmode);
13770 }
13771
13772 /*
13773 * ALTER TABLE ENABLE/DISABLE RULE
13774 *
13775 * We just pass this off to rewriteDefine.c.
13776 */
13777 static void
ATExecEnableDisableRule(Relation rel,const char * rulename,char fires_when,LOCKMODE lockmode)13778 ATExecEnableDisableRule(Relation rel, const char *rulename,
13779 char fires_when, LOCKMODE lockmode)
13780 {
13781 EnableDisableRule(rel, rulename, fires_when);
13782 }
13783
13784 /*
13785 * ALTER TABLE INHERIT
13786 *
13787 * Add a parent to the child's parents. This verifies that all the columns and
13788 * check constraints of the parent appear in the child and that they have the
13789 * same data types and expressions.
13790 */
13791 static void
ATPrepAddInherit(Relation child_rel)13792 ATPrepAddInherit(Relation child_rel)
13793 {
13794 if (child_rel->rd_rel->reloftype)
13795 ereport(ERROR,
13796 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13797 errmsg("cannot change inheritance of typed table")));
13798
13799 if (child_rel->rd_rel->relispartition)
13800 ereport(ERROR,
13801 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13802 errmsg("cannot change inheritance of a partition")));
13803
13804 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13805 ereport(ERROR,
13806 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13807 errmsg("cannot change inheritance of partitioned table")));
13808 }
13809
13810 /*
13811 * Return the address of the new parent relation.
13812 */
13813 static ObjectAddress
ATExecAddInherit(Relation child_rel,RangeVar * parent,LOCKMODE lockmode)13814 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
13815 {
13816 Relation parent_rel;
13817 List *children;
13818 ObjectAddress address;
13819 const char *trigger_name;
13820
13821 /*
13822 * A self-exclusive lock is needed here. See the similar case in
13823 * MergeAttributes() for a full explanation.
13824 */
13825 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
13826
13827 /*
13828 * Must be owner of both parent and child -- child was checked by
13829 * ATSimplePermissions call in ATPrepCmd
13830 */
13831 ATSimplePermissions(parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
13832
13833 /* Permanent rels cannot inherit from temporary ones */
13834 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13835 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
13836 ereport(ERROR,
13837 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13838 errmsg("cannot inherit from temporary relation \"%s\"",
13839 RelationGetRelationName(parent_rel))));
13840
13841 /* If parent rel is temp, it must belong to this session */
13842 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13843 !parent_rel->rd_islocaltemp)
13844 ereport(ERROR,
13845 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13846 errmsg("cannot inherit from temporary relation of another session")));
13847
13848 /* Ditto for the child */
13849 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13850 !child_rel->rd_islocaltemp)
13851 ereport(ERROR,
13852 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13853 errmsg("cannot inherit to temporary relation of another session")));
13854
13855 /* Prevent partitioned tables from becoming inheritance parents */
13856 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13857 ereport(ERROR,
13858 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13859 errmsg("cannot inherit from partitioned table \"%s\"",
13860 parent->relname)));
13861
13862 /* Likewise for partitions */
13863 if (parent_rel->rd_rel->relispartition)
13864 ereport(ERROR,
13865 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13866 errmsg("cannot inherit from a partition")));
13867
13868 /*
13869 * Prevent circularity by seeing if proposed parent inherits from child.
13870 * (In particular, this disallows making a rel inherit from itself.)
13871 *
13872 * This is not completely bulletproof because of race conditions: in
13873 * multi-level inheritance trees, someone else could concurrently be
13874 * making another inheritance link that closes the loop but does not join
13875 * either of the rels we have locked. Preventing that seems to require
13876 * exclusive locks on the entire inheritance tree, which is a cure worse
13877 * than the disease. find_all_inheritors() will cope with circularity
13878 * anyway, so don't sweat it too much.
13879 *
13880 * We use weakest lock we can on child's children, namely AccessShareLock.
13881 */
13882 children = find_all_inheritors(RelationGetRelid(child_rel),
13883 AccessShareLock, NULL);
13884
13885 if (list_member_oid(children, RelationGetRelid(parent_rel)))
13886 ereport(ERROR,
13887 (errcode(ERRCODE_DUPLICATE_TABLE),
13888 errmsg("circular inheritance not allowed"),
13889 errdetail("\"%s\" is already a child of \"%s\".",
13890 parent->relname,
13891 RelationGetRelationName(child_rel))));
13892
13893 /*
13894 * If child_rel has row-level triggers with transition tables, we
13895 * currently don't allow it to become an inheritance child. See also
13896 * prohibitions in ATExecAttachPartition() and CreateTrigger().
13897 */
13898 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
13899 if (trigger_name != NULL)
13900 ereport(ERROR,
13901 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13902 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
13903 trigger_name, RelationGetRelationName(child_rel)),
13904 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
13905
13906 /* OK to create inheritance */
13907 CreateInheritance(child_rel, parent_rel);
13908
13909 ObjectAddressSet(address, RelationRelationId,
13910 RelationGetRelid(parent_rel));
13911
13912 /* keep our lock on the parent relation until commit */
13913 table_close(parent_rel, NoLock);
13914
13915 return address;
13916 }
13917
13918 /*
13919 * CreateInheritance
13920 * Catalog manipulation portion of creating inheritance between a child
13921 * table and a parent table.
13922 *
13923 * Common to ATExecAddInherit() and ATExecAttachPartition().
13924 */
13925 static void
CreateInheritance(Relation child_rel,Relation parent_rel)13926 CreateInheritance(Relation child_rel, Relation parent_rel)
13927 {
13928 Relation catalogRelation;
13929 SysScanDesc scan;
13930 ScanKeyData key;
13931 HeapTuple inheritsTuple;
13932 int32 inhseqno;
13933
13934 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
13935 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
13936
13937 /*
13938 * Check for duplicates in the list of parents, and determine the highest
13939 * inhseqno already present; we'll use the next one for the new parent.
13940 * Also, if proposed child is a partition, it cannot already be
13941 * inheriting.
13942 *
13943 * Note: we do not reject the case where the child already inherits from
13944 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
13945 */
13946 ScanKeyInit(&key,
13947 Anum_pg_inherits_inhrelid,
13948 BTEqualStrategyNumber, F_OIDEQ,
13949 ObjectIdGetDatum(RelationGetRelid(child_rel)));
13950 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
13951 true, NULL, 1, &key);
13952
13953 /* inhseqno sequences start at 1 */
13954 inhseqno = 0;
13955 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
13956 {
13957 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
13958
13959 if (inh->inhparent == RelationGetRelid(parent_rel))
13960 ereport(ERROR,
13961 (errcode(ERRCODE_DUPLICATE_TABLE),
13962 errmsg("relation \"%s\" would be inherited from more than once",
13963 RelationGetRelationName(parent_rel))));
13964
13965 if (inh->inhseqno > inhseqno)
13966 inhseqno = inh->inhseqno;
13967 }
13968 systable_endscan(scan);
13969
13970 /* Match up the columns and bump attinhcount as needed */
13971 MergeAttributesIntoExisting(child_rel, parent_rel);
13972
13973 /* Match up the constraints and bump coninhcount as needed */
13974 MergeConstraintsIntoExisting(child_rel, parent_rel);
13975
13976 /*
13977 * OK, it looks valid. Make the catalog entries that show inheritance.
13978 */
13979 StoreCatalogInheritance1(RelationGetRelid(child_rel),
13980 RelationGetRelid(parent_rel),
13981 inhseqno + 1,
13982 catalogRelation,
13983 parent_rel->rd_rel->relkind ==
13984 RELKIND_PARTITIONED_TABLE);
13985
13986 /* Now we're done with pg_inherits */
13987 table_close(catalogRelation, RowExclusiveLock);
13988 }
13989
13990 /*
13991 * Obtain the source-text form of the constraint expression for a check
13992 * constraint, given its pg_constraint tuple
13993 */
13994 static char *
decompile_conbin(HeapTuple contup,TupleDesc tupdesc)13995 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
13996 {
13997 Form_pg_constraint con;
13998 bool isnull;
13999 Datum attr;
14000 Datum expr;
14001
14002 con = (Form_pg_constraint) GETSTRUCT(contup);
14003 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
14004 if (isnull)
14005 elog(ERROR, "null conbin for constraint %u", con->oid);
14006
14007 expr = DirectFunctionCall2(pg_get_expr, attr,
14008 ObjectIdGetDatum(con->conrelid));
14009 return TextDatumGetCString(expr);
14010 }
14011
14012 /*
14013 * Determine whether two check constraints are functionally equivalent
14014 *
14015 * The test we apply is to see whether they reverse-compile to the same
14016 * source string. This insulates us from issues like whether attributes
14017 * have the same physical column numbers in parent and child relations.
14018 */
14019 static bool
constraints_equivalent(HeapTuple a,HeapTuple b,TupleDesc tupleDesc)14020 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
14021 {
14022 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
14023 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
14024
14025 if (acon->condeferrable != bcon->condeferrable ||
14026 acon->condeferred != bcon->condeferred ||
14027 strcmp(decompile_conbin(a, tupleDesc),
14028 decompile_conbin(b, tupleDesc)) != 0)
14029 return false;
14030 else
14031 return true;
14032 }
14033
14034 /*
14035 * Check columns in child table match up with columns in parent, and increment
14036 * their attinhcount.
14037 *
14038 * Called by CreateInheritance
14039 *
14040 * Currently all parent columns must be found in child. Missing columns are an
14041 * error. One day we might consider creating new columns like CREATE TABLE
14042 * does. However, that is widely unpopular --- in the common use case of
14043 * partitioned tables it's a foot-gun.
14044 *
14045 * The data type must match exactly. If the parent column is NOT NULL then
14046 * the child must be as well. Defaults are not compared, however.
14047 */
14048 static void
MergeAttributesIntoExisting(Relation child_rel,Relation parent_rel)14049 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
14050 {
14051 Relation attrrel;
14052 AttrNumber parent_attno;
14053 int parent_natts;
14054 TupleDesc tupleDesc;
14055 HeapTuple tuple;
14056 bool child_is_partition = false;
14057
14058 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
14059
14060 tupleDesc = RelationGetDescr(parent_rel);
14061 parent_natts = tupleDesc->natts;
14062
14063 /* If parent_rel is a partitioned table, child_rel must be a partition */
14064 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14065 child_is_partition = true;
14066
14067 for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
14068 {
14069 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
14070 parent_attno - 1);
14071 char *attributeName = NameStr(attribute->attname);
14072
14073 /* Ignore dropped columns in the parent. */
14074 if (attribute->attisdropped)
14075 continue;
14076
14077 /* Find same column in child (matching on column name). */
14078 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
14079 attributeName);
14080 if (HeapTupleIsValid(tuple))
14081 {
14082 /* Check they are same type, typmod, and collation */
14083 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
14084
14085 if (attribute->atttypid != childatt->atttypid ||
14086 attribute->atttypmod != childatt->atttypmod)
14087 ereport(ERROR,
14088 (errcode(ERRCODE_DATATYPE_MISMATCH),
14089 errmsg("child table \"%s\" has different type for column \"%s\"",
14090 RelationGetRelationName(child_rel),
14091 attributeName)));
14092
14093 if (attribute->attcollation != childatt->attcollation)
14094 ereport(ERROR,
14095 (errcode(ERRCODE_COLLATION_MISMATCH),
14096 errmsg("child table \"%s\" has different collation for column \"%s\"",
14097 RelationGetRelationName(child_rel),
14098 attributeName)));
14099
14100 /*
14101 * Check child doesn't discard NOT NULL property. (Other
14102 * constraints are checked elsewhere.)
14103 */
14104 if (attribute->attnotnull && !childatt->attnotnull)
14105 ereport(ERROR,
14106 (errcode(ERRCODE_DATATYPE_MISMATCH),
14107 errmsg("column \"%s\" in child table must be marked NOT NULL",
14108 attributeName)));
14109
14110 /*
14111 * If parent column is generated, child column must be, too.
14112 */
14113 if (attribute->attgenerated && !childatt->attgenerated)
14114 ereport(ERROR,
14115 (errcode(ERRCODE_DATATYPE_MISMATCH),
14116 errmsg("column \"%s\" in child table must be a generated column",
14117 attributeName)));
14118
14119 /*
14120 * Check that both generation expressions match.
14121 *
14122 * The test we apply is to see whether they reverse-compile to the
14123 * same source string. This insulates us from issues like whether
14124 * attributes have the same physical column numbers in parent and
14125 * child relations. (See also constraints_equivalent().)
14126 */
14127 if (attribute->attgenerated && childatt->attgenerated)
14128 {
14129 TupleConstr *child_constr = child_rel->rd_att->constr;
14130 TupleConstr *parent_constr = parent_rel->rd_att->constr;
14131 char *child_expr = NULL;
14132 char *parent_expr = NULL;
14133
14134 Assert(child_constr != NULL);
14135 Assert(parent_constr != NULL);
14136
14137 for (int i = 0; i < child_constr->num_defval; i++)
14138 {
14139 if (child_constr->defval[i].adnum == childatt->attnum)
14140 {
14141 child_expr =
14142 TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
14143 CStringGetTextDatum(child_constr->defval[i].adbin),
14144 ObjectIdGetDatum(child_rel->rd_id)));
14145 break;
14146 }
14147 }
14148 Assert(child_expr != NULL);
14149
14150 for (int i = 0; i < parent_constr->num_defval; i++)
14151 {
14152 if (parent_constr->defval[i].adnum == attribute->attnum)
14153 {
14154 parent_expr =
14155 TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
14156 CStringGetTextDatum(parent_constr->defval[i].adbin),
14157 ObjectIdGetDatum(parent_rel->rd_id)));
14158 break;
14159 }
14160 }
14161 Assert(parent_expr != NULL);
14162
14163 if (strcmp(child_expr, parent_expr) != 0)
14164 ereport(ERROR,
14165 (errcode(ERRCODE_DATATYPE_MISMATCH),
14166 errmsg("column \"%s\" in child table has a conflicting generation expression",
14167 attributeName)));
14168 }
14169
14170 /*
14171 * OK, bump the child column's inheritance count. (If we fail
14172 * later on, this change will just roll back.)
14173 */
14174 childatt->attinhcount++;
14175
14176 /*
14177 * In case of partitions, we must enforce that value of attislocal
14178 * is same in all partitions. (Note: there are only inherited
14179 * attributes in partitions)
14180 */
14181 if (child_is_partition)
14182 {
14183 Assert(childatt->attinhcount == 1);
14184 childatt->attislocal = false;
14185 }
14186
14187 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
14188 heap_freetuple(tuple);
14189 }
14190 else
14191 {
14192 ereport(ERROR,
14193 (errcode(ERRCODE_DATATYPE_MISMATCH),
14194 errmsg("child table is missing column \"%s\"",
14195 attributeName)));
14196 }
14197 }
14198
14199 table_close(attrrel, RowExclusiveLock);
14200 }
14201
14202 /*
14203 * Check constraints in child table match up with constraints in parent,
14204 * and increment their coninhcount.
14205 *
14206 * Constraints that are marked ONLY in the parent are ignored.
14207 *
14208 * Called by CreateInheritance
14209 *
14210 * Currently all constraints in parent must be present in the child. One day we
14211 * may consider adding new constraints like CREATE TABLE does.
14212 *
14213 * XXX This is O(N^2) which may be an issue with tables with hundreds of
14214 * constraints. As long as tables have more like 10 constraints it shouldn't be
14215 * a problem though. Even 100 constraints ought not be the end of the world.
14216 *
14217 * XXX See MergeWithExistingConstraint too if you change this code.
14218 */
14219 static void
MergeConstraintsIntoExisting(Relation child_rel,Relation parent_rel)14220 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
14221 {
14222 Relation catalog_relation;
14223 TupleDesc tuple_desc;
14224 SysScanDesc parent_scan;
14225 ScanKeyData parent_key;
14226 HeapTuple parent_tuple;
14227 bool child_is_partition = false;
14228
14229 catalog_relation = table_open(ConstraintRelationId, RowExclusiveLock);
14230 tuple_desc = RelationGetDescr(catalog_relation);
14231
14232 /* If parent_rel is a partitioned table, child_rel must be a partition */
14233 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14234 child_is_partition = true;
14235
14236 /* Outer loop scans through the parent's constraint definitions */
14237 ScanKeyInit(&parent_key,
14238 Anum_pg_constraint_conrelid,
14239 BTEqualStrategyNumber, F_OIDEQ,
14240 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
14241 parent_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
14242 true, NULL, 1, &parent_key);
14243
14244 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
14245 {
14246 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
14247 SysScanDesc child_scan;
14248 ScanKeyData child_key;
14249 HeapTuple child_tuple;
14250 bool found = false;
14251
14252 if (parent_con->contype != CONSTRAINT_CHECK)
14253 continue;
14254
14255 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
14256 if (parent_con->connoinherit)
14257 continue;
14258
14259 /* Search for a child constraint matching this one */
14260 ScanKeyInit(&child_key,
14261 Anum_pg_constraint_conrelid,
14262 BTEqualStrategyNumber, F_OIDEQ,
14263 ObjectIdGetDatum(RelationGetRelid(child_rel)));
14264 child_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
14265 true, NULL, 1, &child_key);
14266
14267 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
14268 {
14269 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
14270 HeapTuple child_copy;
14271
14272 if (child_con->contype != CONSTRAINT_CHECK)
14273 continue;
14274
14275 if (strcmp(NameStr(parent_con->conname),
14276 NameStr(child_con->conname)) != 0)
14277 continue;
14278
14279 if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
14280 ereport(ERROR,
14281 (errcode(ERRCODE_DATATYPE_MISMATCH),
14282 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
14283 RelationGetRelationName(child_rel),
14284 NameStr(parent_con->conname))));
14285
14286 /* If the child constraint is "no inherit" then cannot merge */
14287 if (child_con->connoinherit)
14288 ereport(ERROR,
14289 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14290 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
14291 NameStr(child_con->conname),
14292 RelationGetRelationName(child_rel))));
14293
14294 /*
14295 * If the child constraint is "not valid" then cannot merge with a
14296 * valid parent constraint
14297 */
14298 if (parent_con->convalidated && !child_con->convalidated)
14299 ereport(ERROR,
14300 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14301 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
14302 NameStr(child_con->conname),
14303 RelationGetRelationName(child_rel))));
14304
14305 /*
14306 * OK, bump the child constraint's inheritance count. (If we fail
14307 * later on, this change will just roll back.)
14308 */
14309 child_copy = heap_copytuple(child_tuple);
14310 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
14311 child_con->coninhcount++;
14312
14313 /*
14314 * In case of partitions, an inherited constraint must be
14315 * inherited only once since it cannot have multiple parents and
14316 * it is never considered local.
14317 */
14318 if (child_is_partition)
14319 {
14320 Assert(child_con->coninhcount == 1);
14321 child_con->conislocal = false;
14322 }
14323
14324 CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
14325 heap_freetuple(child_copy);
14326
14327 found = true;
14328 break;
14329 }
14330
14331 systable_endscan(child_scan);
14332
14333 if (!found)
14334 ereport(ERROR,
14335 (errcode(ERRCODE_DATATYPE_MISMATCH),
14336 errmsg("child table is missing constraint \"%s\"",
14337 NameStr(parent_con->conname))));
14338 }
14339
14340 systable_endscan(parent_scan);
14341 table_close(catalog_relation, RowExclusiveLock);
14342 }
14343
14344 /*
14345 * ALTER TABLE NO INHERIT
14346 *
14347 * Return value is the address of the relation that is no longer parent.
14348 */
14349 static ObjectAddress
ATExecDropInherit(Relation rel,RangeVar * parent,LOCKMODE lockmode)14350 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
14351 {
14352 ObjectAddress address;
14353 Relation parent_rel;
14354
14355 if (rel->rd_rel->relispartition)
14356 ereport(ERROR,
14357 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14358 errmsg("cannot change inheritance of a partition")));
14359
14360 /*
14361 * AccessShareLock on the parent is probably enough, seeing that DROP
14362 * TABLE doesn't lock parent tables at all. We need some lock since we'll
14363 * be inspecting the parent's schema.
14364 */
14365 parent_rel = table_openrv(parent, AccessShareLock);
14366
14367 /*
14368 * We don't bother to check ownership of the parent table --- ownership of
14369 * the child is presumed enough rights.
14370 */
14371
14372 /* Off to RemoveInheritance() where most of the work happens */
14373 RemoveInheritance(rel, parent_rel);
14374
14375 ObjectAddressSet(address, RelationRelationId,
14376 RelationGetRelid(parent_rel));
14377
14378 /* keep our lock on the parent relation until commit */
14379 table_close(parent_rel, NoLock);
14380
14381 return address;
14382 }
14383
14384 /*
14385 * RemoveInheritance
14386 *
14387 * Drop a parent from the child's parents. This just adjusts the attinhcount
14388 * and attislocal of the columns and removes the pg_inherit and pg_depend
14389 * entries.
14390 *
14391 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
14392 * up attislocal stays true, which means if a child is ever removed from a
14393 * parent then its columns will never be automatically dropped which may
14394 * surprise. But at least we'll never surprise by dropping columns someone
14395 * isn't expecting to be dropped which would actually mean data loss.
14396 *
14397 * coninhcount and conislocal for inherited constraints are adjusted in
14398 * exactly the same way.
14399 *
14400 * Common to ATExecDropInherit() and ATExecDetachPartition().
14401 */
14402 static void
RemoveInheritance(Relation child_rel,Relation parent_rel)14403 RemoveInheritance(Relation child_rel, Relation parent_rel)
14404 {
14405 Relation catalogRelation;
14406 SysScanDesc scan;
14407 ScanKeyData key[3];
14408 HeapTuple attributeTuple,
14409 constraintTuple;
14410 List *connames;
14411 bool found;
14412 bool child_is_partition = false;
14413
14414 /* If parent_rel is a partitioned table, child_rel must be a partition */
14415 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14416 child_is_partition = true;
14417
14418 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
14419 RelationGetRelid(parent_rel));
14420 if (!found)
14421 {
14422 if (child_is_partition)
14423 ereport(ERROR,
14424 (errcode(ERRCODE_UNDEFINED_TABLE),
14425 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
14426 RelationGetRelationName(child_rel),
14427 RelationGetRelationName(parent_rel))));
14428 else
14429 ereport(ERROR,
14430 (errcode(ERRCODE_UNDEFINED_TABLE),
14431 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
14432 RelationGetRelationName(parent_rel),
14433 RelationGetRelationName(child_rel))));
14434 }
14435
14436 /*
14437 * Search through child columns looking for ones matching parent rel
14438 */
14439 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
14440 ScanKeyInit(&key[0],
14441 Anum_pg_attribute_attrelid,
14442 BTEqualStrategyNumber, F_OIDEQ,
14443 ObjectIdGetDatum(RelationGetRelid(child_rel)));
14444 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
14445 true, NULL, 1, key);
14446 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
14447 {
14448 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
14449
14450 /* Ignore if dropped or not inherited */
14451 if (att->attisdropped)
14452 continue;
14453 if (att->attinhcount <= 0)
14454 continue;
14455
14456 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
14457 NameStr(att->attname)))
14458 {
14459 /* Decrement inhcount and possibly set islocal to true */
14460 HeapTuple copyTuple = heap_copytuple(attributeTuple);
14461 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
14462
14463 copy_att->attinhcount--;
14464 if (copy_att->attinhcount == 0)
14465 copy_att->attislocal = true;
14466
14467 CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
14468 heap_freetuple(copyTuple);
14469 }
14470 }
14471 systable_endscan(scan);
14472 table_close(catalogRelation, RowExclusiveLock);
14473
14474 /*
14475 * Likewise, find inherited check constraints and disinherit them. To do
14476 * this, we first need a list of the names of the parent's check
14477 * constraints. (We cheat a bit by only checking for name matches,
14478 * assuming that the expressions will match.)
14479 */
14480 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
14481 ScanKeyInit(&key[0],
14482 Anum_pg_constraint_conrelid,
14483 BTEqualStrategyNumber, F_OIDEQ,
14484 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
14485 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
14486 true, NULL, 1, key);
14487
14488 connames = NIL;
14489
14490 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
14491 {
14492 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
14493
14494 if (con->contype == CONSTRAINT_CHECK)
14495 connames = lappend(connames, pstrdup(NameStr(con->conname)));
14496 }
14497
14498 systable_endscan(scan);
14499
14500 /* Now scan the child's constraints */
14501 ScanKeyInit(&key[0],
14502 Anum_pg_constraint_conrelid,
14503 BTEqualStrategyNumber, F_OIDEQ,
14504 ObjectIdGetDatum(RelationGetRelid(child_rel)));
14505 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
14506 true, NULL, 1, key);
14507
14508 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
14509 {
14510 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
14511 bool match;
14512 ListCell *lc;
14513
14514 if (con->contype != CONSTRAINT_CHECK)
14515 continue;
14516
14517 match = false;
14518 foreach(lc, connames)
14519 {
14520 if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
14521 {
14522 match = true;
14523 break;
14524 }
14525 }
14526
14527 if (match)
14528 {
14529 /* Decrement inhcount and possibly set islocal to true */
14530 HeapTuple copyTuple = heap_copytuple(constraintTuple);
14531 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
14532
14533 if (copy_con->coninhcount <= 0) /* shouldn't happen */
14534 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14535 RelationGetRelid(child_rel), NameStr(copy_con->conname));
14536
14537 copy_con->coninhcount--;
14538 if (copy_con->coninhcount == 0)
14539 copy_con->conislocal = true;
14540
14541 CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
14542 heap_freetuple(copyTuple);
14543 }
14544 }
14545
14546 systable_endscan(scan);
14547 table_close(catalogRelation, RowExclusiveLock);
14548
14549 drop_parent_dependency(RelationGetRelid(child_rel),
14550 RelationRelationId,
14551 RelationGetRelid(parent_rel),
14552 child_dependency_type(child_is_partition));
14553
14554 /*
14555 * Post alter hook of this inherits. Since object_access_hook doesn't take
14556 * multiple object identifiers, we relay oid of parent relation using
14557 * auxiliary_id argument.
14558 */
14559 InvokeObjectPostAlterHookArg(InheritsRelationId,
14560 RelationGetRelid(child_rel), 0,
14561 RelationGetRelid(parent_rel), false);
14562 }
14563
14564 /*
14565 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
14566 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
14567 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
14568 * be TypeRelationId). There's no convenient way to do this, so go trawling
14569 * through pg_depend.
14570 */
14571 static void
drop_parent_dependency(Oid relid,Oid refclassid,Oid refobjid,DependencyType deptype)14572 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
14573 DependencyType deptype)
14574 {
14575 Relation catalogRelation;
14576 SysScanDesc scan;
14577 ScanKeyData key[3];
14578 HeapTuple depTuple;
14579
14580 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
14581
14582 ScanKeyInit(&key[0],
14583 Anum_pg_depend_classid,
14584 BTEqualStrategyNumber, F_OIDEQ,
14585 ObjectIdGetDatum(RelationRelationId));
14586 ScanKeyInit(&key[1],
14587 Anum_pg_depend_objid,
14588 BTEqualStrategyNumber, F_OIDEQ,
14589 ObjectIdGetDatum(relid));
14590 ScanKeyInit(&key[2],
14591 Anum_pg_depend_objsubid,
14592 BTEqualStrategyNumber, F_INT4EQ,
14593 Int32GetDatum(0));
14594
14595 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
14596 NULL, 3, key);
14597
14598 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
14599 {
14600 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
14601
14602 if (dep->refclassid == refclassid &&
14603 dep->refobjid == refobjid &&
14604 dep->refobjsubid == 0 &&
14605 dep->deptype == deptype)
14606 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
14607 }
14608
14609 systable_endscan(scan);
14610 table_close(catalogRelation, RowExclusiveLock);
14611 }
14612
14613 /*
14614 * ALTER TABLE OF
14615 *
14616 * Attach a table to a composite type, as though it had been created with CREATE
14617 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
14618 * subject table must not have inheritance parents. These restrictions ensure
14619 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
14620 *
14621 * The address of the type is returned.
14622 */
14623 static ObjectAddress
ATExecAddOf(Relation rel,const TypeName * ofTypename,LOCKMODE lockmode)14624 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
14625 {
14626 Oid relid = RelationGetRelid(rel);
14627 Type typetuple;
14628 Form_pg_type typeform;
14629 Oid typeid;
14630 Relation inheritsRelation,
14631 relationRelation;
14632 SysScanDesc scan;
14633 ScanKeyData key;
14634 AttrNumber table_attno,
14635 type_attno;
14636 TupleDesc typeTupleDesc,
14637 tableTupleDesc;
14638 ObjectAddress tableobj,
14639 typeobj;
14640 HeapTuple classtuple;
14641
14642 /* Validate the type. */
14643 typetuple = typenameType(NULL, ofTypename, NULL);
14644 check_of_type(typetuple);
14645 typeform = (Form_pg_type) GETSTRUCT(typetuple);
14646 typeid = typeform->oid;
14647
14648 /* Fail if the table has any inheritance parents. */
14649 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
14650 ScanKeyInit(&key,
14651 Anum_pg_inherits_inhrelid,
14652 BTEqualStrategyNumber, F_OIDEQ,
14653 ObjectIdGetDatum(relid));
14654 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
14655 true, NULL, 1, &key);
14656 if (HeapTupleIsValid(systable_getnext(scan)))
14657 ereport(ERROR,
14658 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14659 errmsg("typed tables cannot inherit")));
14660 systable_endscan(scan);
14661 table_close(inheritsRelation, AccessShareLock);
14662
14663 /*
14664 * Check the tuple descriptors for compatibility. Unlike inheritance, we
14665 * require that the order also match. However, attnotnull need not match.
14666 */
14667 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
14668 tableTupleDesc = RelationGetDescr(rel);
14669 table_attno = 1;
14670 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
14671 {
14672 Form_pg_attribute type_attr,
14673 table_attr;
14674 const char *type_attname,
14675 *table_attname;
14676
14677 /* Get the next non-dropped type attribute. */
14678 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
14679 if (type_attr->attisdropped)
14680 continue;
14681 type_attname = NameStr(type_attr->attname);
14682
14683 /* Get the next non-dropped table attribute. */
14684 do
14685 {
14686 if (table_attno > tableTupleDesc->natts)
14687 ereport(ERROR,
14688 (errcode(ERRCODE_DATATYPE_MISMATCH),
14689 errmsg("table is missing column \"%s\"",
14690 type_attname)));
14691 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
14692 table_attno++;
14693 } while (table_attr->attisdropped);
14694 table_attname = NameStr(table_attr->attname);
14695
14696 /* Compare name. */
14697 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
14698 ereport(ERROR,
14699 (errcode(ERRCODE_DATATYPE_MISMATCH),
14700 errmsg("table has column \"%s\" where type requires \"%s\"",
14701 table_attname, type_attname)));
14702
14703 /* Compare type. */
14704 if (table_attr->atttypid != type_attr->atttypid ||
14705 table_attr->atttypmod != type_attr->atttypmod ||
14706 table_attr->attcollation != type_attr->attcollation)
14707 ereport(ERROR,
14708 (errcode(ERRCODE_DATATYPE_MISMATCH),
14709 errmsg("table \"%s\" has different type for column \"%s\"",
14710 RelationGetRelationName(rel), type_attname)));
14711 }
14712 DecrTupleDescRefCount(typeTupleDesc);
14713
14714 /* Any remaining columns at the end of the table had better be dropped. */
14715 for (; table_attno <= tableTupleDesc->natts; table_attno++)
14716 {
14717 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
14718 table_attno - 1);
14719
14720 if (!table_attr->attisdropped)
14721 ereport(ERROR,
14722 (errcode(ERRCODE_DATATYPE_MISMATCH),
14723 errmsg("table has extra column \"%s\"",
14724 NameStr(table_attr->attname))));
14725 }
14726
14727 /* If the table was already typed, drop the existing dependency. */
14728 if (rel->rd_rel->reloftype)
14729 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
14730 DEPENDENCY_NORMAL);
14731
14732 /* Record a dependency on the new type. */
14733 tableobj.classId = RelationRelationId;
14734 tableobj.objectId = relid;
14735 tableobj.objectSubId = 0;
14736 typeobj.classId = TypeRelationId;
14737 typeobj.objectId = typeid;
14738 typeobj.objectSubId = 0;
14739 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
14740
14741 /* Update pg_class.reloftype */
14742 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
14743 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
14744 if (!HeapTupleIsValid(classtuple))
14745 elog(ERROR, "cache lookup failed for relation %u", relid);
14746 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
14747 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
14748
14749 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
14750
14751 heap_freetuple(classtuple);
14752 table_close(relationRelation, RowExclusiveLock);
14753
14754 ReleaseSysCache(typetuple);
14755
14756 return typeobj;
14757 }
14758
14759 /*
14760 * ALTER TABLE NOT OF
14761 *
14762 * Detach a typed table from its originating type. Just clear reloftype and
14763 * remove the dependency.
14764 */
14765 static void
ATExecDropOf(Relation rel,LOCKMODE lockmode)14766 ATExecDropOf(Relation rel, LOCKMODE lockmode)
14767 {
14768 Oid relid = RelationGetRelid(rel);
14769 Relation relationRelation;
14770 HeapTuple tuple;
14771
14772 if (!OidIsValid(rel->rd_rel->reloftype))
14773 ereport(ERROR,
14774 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14775 errmsg("\"%s\" is not a typed table",
14776 RelationGetRelationName(rel))));
14777
14778 /*
14779 * We don't bother to check ownership of the type --- ownership of the
14780 * table is presumed enough rights. No lock required on the type, either.
14781 */
14782
14783 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
14784 DEPENDENCY_NORMAL);
14785
14786 /* Clear pg_class.reloftype */
14787 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
14788 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
14789 if (!HeapTupleIsValid(tuple))
14790 elog(ERROR, "cache lookup failed for relation %u", relid);
14791 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
14792 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
14793
14794 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
14795
14796 heap_freetuple(tuple);
14797 table_close(relationRelation, RowExclusiveLock);
14798 }
14799
14800 /*
14801 * relation_mark_replica_identity: Update a table's replica identity
14802 *
14803 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
14804 * index. Otherwise, it should be InvalidOid.
14805 */
14806 static void
relation_mark_replica_identity(Relation rel,char ri_type,Oid indexOid,bool is_internal)14807 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
14808 bool is_internal)
14809 {
14810 Relation pg_index;
14811 Relation pg_class;
14812 HeapTuple pg_class_tuple;
14813 HeapTuple pg_index_tuple;
14814 Form_pg_class pg_class_form;
14815 Form_pg_index pg_index_form;
14816
14817 ListCell *index;
14818
14819 /*
14820 * Check whether relreplident has changed, and update it if so.
14821 */
14822 pg_class = table_open(RelationRelationId, RowExclusiveLock);
14823 pg_class_tuple = SearchSysCacheCopy1(RELOID,
14824 ObjectIdGetDatum(RelationGetRelid(rel)));
14825 if (!HeapTupleIsValid(pg_class_tuple))
14826 elog(ERROR, "cache lookup failed for relation \"%s\"",
14827 RelationGetRelationName(rel));
14828 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
14829 if (pg_class_form->relreplident != ri_type)
14830 {
14831 pg_class_form->relreplident = ri_type;
14832 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
14833 }
14834 table_close(pg_class, RowExclusiveLock);
14835 heap_freetuple(pg_class_tuple);
14836
14837 /*
14838 * Check whether the correct index is marked indisreplident; if so, we're
14839 * done.
14840 */
14841 if (OidIsValid(indexOid))
14842 {
14843 Assert(ri_type == REPLICA_IDENTITY_INDEX);
14844
14845 pg_index_tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
14846 if (!HeapTupleIsValid(pg_index_tuple))
14847 elog(ERROR, "cache lookup failed for index %u", indexOid);
14848 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
14849
14850 if (pg_index_form->indisreplident)
14851 {
14852 ReleaseSysCache(pg_index_tuple);
14853 return;
14854 }
14855 ReleaseSysCache(pg_index_tuple);
14856 }
14857
14858 /*
14859 * Clear the indisreplident flag from any index that had it previously,
14860 * and set it for any index that should have it now.
14861 */
14862 pg_index = table_open(IndexRelationId, RowExclusiveLock);
14863 foreach(index, RelationGetIndexList(rel))
14864 {
14865 Oid thisIndexOid = lfirst_oid(index);
14866 bool dirty = false;
14867
14868 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
14869 ObjectIdGetDatum(thisIndexOid));
14870 if (!HeapTupleIsValid(pg_index_tuple))
14871 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
14872 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
14873
14874 /*
14875 * Unset the bit if set. We know it's wrong because we checked this
14876 * earlier.
14877 */
14878 if (pg_index_form->indisreplident)
14879 {
14880 dirty = true;
14881 pg_index_form->indisreplident = false;
14882 }
14883 else if (thisIndexOid == indexOid)
14884 {
14885 dirty = true;
14886 pg_index_form->indisreplident = true;
14887 }
14888
14889 if (dirty)
14890 {
14891 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
14892 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
14893 InvalidOid, is_internal);
14894 }
14895 heap_freetuple(pg_index_tuple);
14896 }
14897
14898 table_close(pg_index, RowExclusiveLock);
14899 }
14900
14901 /*
14902 * ALTER TABLE <name> REPLICA IDENTITY ...
14903 */
14904 static void
ATExecReplicaIdentity(Relation rel,ReplicaIdentityStmt * stmt,LOCKMODE lockmode)14905 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
14906 {
14907 Oid indexOid;
14908 Relation indexRel;
14909 int key;
14910
14911 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
14912 {
14913 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
14914 return;
14915 }
14916 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
14917 {
14918 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
14919 return;
14920 }
14921 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
14922 {
14923 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
14924 return;
14925 }
14926 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
14927 {
14928 /* fallthrough */ ;
14929 }
14930 else
14931 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
14932
14933
14934 /* Check that the index exists */
14935 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
14936 if (!OidIsValid(indexOid))
14937 ereport(ERROR,
14938 (errcode(ERRCODE_UNDEFINED_OBJECT),
14939 errmsg("index \"%s\" for table \"%s\" does not exist",
14940 stmt->name, RelationGetRelationName(rel))));
14941
14942 indexRel = index_open(indexOid, ShareLock);
14943
14944 /* Check that the index is on the relation we're altering. */
14945 if (indexRel->rd_index == NULL ||
14946 indexRel->rd_index->indrelid != RelationGetRelid(rel))
14947 ereport(ERROR,
14948 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14949 errmsg("\"%s\" is not an index for table \"%s\"",
14950 RelationGetRelationName(indexRel),
14951 RelationGetRelationName(rel))));
14952 /* The AM must support uniqueness, and the index must in fact be unique. */
14953 if (!indexRel->rd_indam->amcanunique ||
14954 !indexRel->rd_index->indisunique)
14955 ereport(ERROR,
14956 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14957 errmsg("cannot use non-unique index \"%s\" as replica identity",
14958 RelationGetRelationName(indexRel))));
14959 /* Deferred indexes are not guaranteed to be always unique. */
14960 if (!indexRel->rd_index->indimmediate)
14961 ereport(ERROR,
14962 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14963 errmsg("cannot use non-immediate index \"%s\" as replica identity",
14964 RelationGetRelationName(indexRel))));
14965 /* Expression indexes aren't supported. */
14966 if (RelationGetIndexExpressions(indexRel) != NIL)
14967 ereport(ERROR,
14968 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14969 errmsg("cannot use expression index \"%s\" as replica identity",
14970 RelationGetRelationName(indexRel))));
14971 /* Predicate indexes aren't supported. */
14972 if (RelationGetIndexPredicate(indexRel) != NIL)
14973 ereport(ERROR,
14974 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14975 errmsg("cannot use partial index \"%s\" as replica identity",
14976 RelationGetRelationName(indexRel))));
14977 /* And neither are invalid indexes. */
14978 if (!indexRel->rd_index->indisvalid)
14979 ereport(ERROR,
14980 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14981 errmsg("cannot use invalid index \"%s\" as replica identity",
14982 RelationGetRelationName(indexRel))));
14983
14984 /* Check index for nullable columns. */
14985 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
14986 {
14987 int16 attno = indexRel->rd_index->indkey.values[key];
14988 Form_pg_attribute attr;
14989
14990 /*
14991 * Reject any other system columns. (Going forward, we'll disallow
14992 * indexes containing such columns in the first place, but they might
14993 * exist in older branches.)
14994 */
14995 if (attno <= 0)
14996 ereport(ERROR,
14997 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
14998 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
14999 RelationGetRelationName(indexRel), attno)));
15000
15001 attr = TupleDescAttr(rel->rd_att, attno - 1);
15002 if (!attr->attnotnull)
15003 ereport(ERROR,
15004 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15005 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
15006 RelationGetRelationName(indexRel),
15007 NameStr(attr->attname))));
15008 }
15009
15010 /* This index is suitable for use as a replica identity. Mark it. */
15011 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
15012
15013 index_close(indexRel, NoLock);
15014 }
15015
15016 /*
15017 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
15018 */
15019 static void
ATExecEnableRowSecurity(Relation rel)15020 ATExecEnableRowSecurity(Relation rel)
15021 {
15022 Relation pg_class;
15023 Oid relid;
15024 HeapTuple tuple;
15025
15026 relid = RelationGetRelid(rel);
15027
15028 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15029
15030 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15031
15032 if (!HeapTupleIsValid(tuple))
15033 elog(ERROR, "cache lookup failed for relation %u", relid);
15034
15035 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = true;
15036 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15037
15038 table_close(pg_class, RowExclusiveLock);
15039 heap_freetuple(tuple);
15040 }
15041
15042 static void
ATExecDisableRowSecurity(Relation rel)15043 ATExecDisableRowSecurity(Relation rel)
15044 {
15045 Relation pg_class;
15046 Oid relid;
15047 HeapTuple tuple;
15048
15049 relid = RelationGetRelid(rel);
15050
15051 /* Pull the record for this relation and update it */
15052 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15053
15054 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15055
15056 if (!HeapTupleIsValid(tuple))
15057 elog(ERROR, "cache lookup failed for relation %u", relid);
15058
15059 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = false;
15060 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15061
15062 table_close(pg_class, RowExclusiveLock);
15063 heap_freetuple(tuple);
15064 }
15065
15066 /*
15067 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
15068 */
15069 static void
ATExecForceNoForceRowSecurity(Relation rel,bool force_rls)15070 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
15071 {
15072 Relation pg_class;
15073 Oid relid;
15074 HeapTuple tuple;
15075
15076 relid = RelationGetRelid(rel);
15077
15078 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15079
15080 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15081
15082 if (!HeapTupleIsValid(tuple))
15083 elog(ERROR, "cache lookup failed for relation %u", relid);
15084
15085 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
15086 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15087
15088 table_close(pg_class, RowExclusiveLock);
15089 heap_freetuple(tuple);
15090 }
15091
15092 /*
15093 * ALTER FOREIGN TABLE <name> OPTIONS (...)
15094 */
15095 static void
ATExecGenericOptions(Relation rel,List * options)15096 ATExecGenericOptions(Relation rel, List *options)
15097 {
15098 Relation ftrel;
15099 ForeignServer *server;
15100 ForeignDataWrapper *fdw;
15101 HeapTuple tuple;
15102 bool isnull;
15103 Datum repl_val[Natts_pg_foreign_table];
15104 bool repl_null[Natts_pg_foreign_table];
15105 bool repl_repl[Natts_pg_foreign_table];
15106 Datum datum;
15107 Form_pg_foreign_table tableform;
15108
15109 if (options == NIL)
15110 return;
15111
15112 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
15113
15114 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
15115 if (!HeapTupleIsValid(tuple))
15116 ereport(ERROR,
15117 (errcode(ERRCODE_UNDEFINED_OBJECT),
15118 errmsg("foreign table \"%s\" does not exist",
15119 RelationGetRelationName(rel))));
15120 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15121 server = GetForeignServer(tableform->ftserver);
15122 fdw = GetForeignDataWrapper(server->fdwid);
15123
15124 memset(repl_val, 0, sizeof(repl_val));
15125 memset(repl_null, false, sizeof(repl_null));
15126 memset(repl_repl, false, sizeof(repl_repl));
15127
15128 /* Extract the current options */
15129 datum = SysCacheGetAttr(FOREIGNTABLEREL,
15130 tuple,
15131 Anum_pg_foreign_table_ftoptions,
15132 &isnull);
15133 if (isnull)
15134 datum = PointerGetDatum(NULL);
15135
15136 /* Transform the options */
15137 datum = transformGenericOptions(ForeignTableRelationId,
15138 datum,
15139 options,
15140 fdw->fdwvalidator);
15141
15142 if (PointerIsValid(DatumGetPointer(datum)))
15143 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
15144 else
15145 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
15146
15147 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
15148
15149 /* Everything looks good - update the tuple */
15150
15151 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
15152 repl_val, repl_null, repl_repl);
15153
15154 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
15155
15156 /*
15157 * Invalidate relcache so that all sessions will refresh any cached plans
15158 * that might depend on the old options.
15159 */
15160 CacheInvalidateRelcache(rel);
15161
15162 InvokeObjectPostAlterHook(ForeignTableRelationId,
15163 RelationGetRelid(rel), 0);
15164
15165 table_close(ftrel, RowExclusiveLock);
15166
15167 heap_freetuple(tuple);
15168 }
15169
15170 /*
15171 * Preparation phase for SET LOGGED/UNLOGGED
15172 *
15173 * This verifies that we're not trying to change a temp table. Also,
15174 * existing foreign key constraints are checked to avoid ending up with
15175 * permanent tables referencing unlogged tables.
15176 *
15177 * Return value is false if the operation is a no-op (in which case the
15178 * checks are skipped), otherwise true.
15179 */
15180 static bool
ATPrepChangePersistence(Relation rel,bool toLogged)15181 ATPrepChangePersistence(Relation rel, bool toLogged)
15182 {
15183 Relation pg_constraint;
15184 HeapTuple tuple;
15185 SysScanDesc scan;
15186 ScanKeyData skey[1];
15187
15188 /*
15189 * Disallow changing status for a temp table. Also verify whether we can
15190 * get away with doing nothing; in such cases we don't need to run the
15191 * checks below, either.
15192 */
15193 switch (rel->rd_rel->relpersistence)
15194 {
15195 case RELPERSISTENCE_TEMP:
15196 ereport(ERROR,
15197 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
15198 errmsg("cannot change logged status of table \"%s\" because it is temporary",
15199 RelationGetRelationName(rel)),
15200 errtable(rel)));
15201 break;
15202 case RELPERSISTENCE_PERMANENT:
15203 if (toLogged)
15204 /* nothing to do */
15205 return false;
15206 break;
15207 case RELPERSISTENCE_UNLOGGED:
15208 if (!toLogged)
15209 /* nothing to do */
15210 return false;
15211 break;
15212 }
15213
15214 /*
15215 * Check that the table is not part any publication when changing to
15216 * UNLOGGED as UNLOGGED tables can't be published.
15217 */
15218 if (!toLogged &&
15219 list_length(GetRelationPublications(RelationGetRelid(rel))) > 0)
15220 ereport(ERROR,
15221 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
15222 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
15223 RelationGetRelationName(rel)),
15224 errdetail("Unlogged relations cannot be replicated.")));
15225
15226 /*
15227 * Check existing foreign key constraints to preserve the invariant that
15228 * permanent tables cannot reference unlogged ones. Self-referencing
15229 * foreign keys can safely be ignored.
15230 */
15231 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
15232
15233 /*
15234 * Scan conrelid if changing to permanent, else confrelid. This also
15235 * determines whether a useful index exists.
15236 */
15237 ScanKeyInit(&skey[0],
15238 toLogged ? Anum_pg_constraint_conrelid :
15239 Anum_pg_constraint_confrelid,
15240 BTEqualStrategyNumber, F_OIDEQ,
15241 ObjectIdGetDatum(RelationGetRelid(rel)));
15242 scan = systable_beginscan(pg_constraint,
15243 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
15244 true, NULL, 1, skey);
15245
15246 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
15247 {
15248 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
15249
15250 if (con->contype == CONSTRAINT_FOREIGN)
15251 {
15252 Oid foreignrelid;
15253 Relation foreignrel;
15254
15255 /* the opposite end of what we used as scankey */
15256 foreignrelid = toLogged ? con->confrelid : con->conrelid;
15257
15258 /* ignore if self-referencing */
15259 if (RelationGetRelid(rel) == foreignrelid)
15260 continue;
15261
15262 foreignrel = relation_open(foreignrelid, AccessShareLock);
15263
15264 if (toLogged)
15265 {
15266 if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
15267 ereport(ERROR,
15268 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
15269 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
15270 RelationGetRelationName(rel),
15271 RelationGetRelationName(foreignrel)),
15272 errtableconstraint(rel, NameStr(con->conname))));
15273 }
15274 else
15275 {
15276 if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
15277 ereport(ERROR,
15278 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
15279 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
15280 RelationGetRelationName(rel),
15281 RelationGetRelationName(foreignrel)),
15282 errtableconstraint(rel, NameStr(con->conname))));
15283 }
15284
15285 relation_close(foreignrel, AccessShareLock);
15286 }
15287 }
15288
15289 systable_endscan(scan);
15290
15291 table_close(pg_constraint, AccessShareLock);
15292
15293 return true;
15294 }
15295
15296 /*
15297 * Execute ALTER TABLE SET SCHEMA
15298 */
15299 ObjectAddress
AlterTableNamespace(AlterObjectSchemaStmt * stmt,Oid * oldschema)15300 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
15301 {
15302 Relation rel;
15303 Oid relid;
15304 Oid oldNspOid;
15305 Oid nspOid;
15306 RangeVar *newrv;
15307 ObjectAddresses *objsMoved;
15308 ObjectAddress myself;
15309
15310 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
15311 stmt->missing_ok ? RVR_MISSING_OK : 0,
15312 RangeVarCallbackForAlterRelation,
15313 (void *) stmt);
15314
15315 if (!OidIsValid(relid))
15316 {
15317 ereport(NOTICE,
15318 (errmsg("relation \"%s\" does not exist, skipping",
15319 stmt->relation->relname)));
15320 return InvalidObjectAddress;
15321 }
15322
15323 rel = relation_open(relid, NoLock);
15324
15325 oldNspOid = RelationGetNamespace(rel);
15326
15327 /* If it's an owned sequence, disallow moving it by itself. */
15328 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
15329 {
15330 Oid tableId;
15331 int32 colId;
15332
15333 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
15334 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
15335 ereport(ERROR,
15336 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15337 errmsg("cannot move an owned sequence into another schema"),
15338 errdetail("Sequence \"%s\" is linked to table \"%s\".",
15339 RelationGetRelationName(rel),
15340 get_rel_name(tableId))));
15341 }
15342
15343 /* Get and lock schema OID and check its permissions. */
15344 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
15345 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
15346
15347 /* common checks on switching namespaces */
15348 CheckSetNamespace(oldNspOid, nspOid);
15349
15350 objsMoved = new_object_addresses();
15351 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
15352 free_object_addresses(objsMoved);
15353
15354 ObjectAddressSet(myself, RelationRelationId, relid);
15355
15356 if (oldschema)
15357 *oldschema = oldNspOid;
15358
15359 /* close rel, but keep lock until commit */
15360 relation_close(rel, NoLock);
15361
15362 return myself;
15363 }
15364
15365 /*
15366 * The guts of relocating a table or materialized view to another namespace:
15367 * besides moving the relation itself, its dependent objects are relocated to
15368 * the new schema.
15369 */
15370 void
AlterTableNamespaceInternal(Relation rel,Oid oldNspOid,Oid nspOid,ObjectAddresses * objsMoved)15371 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
15372 ObjectAddresses *objsMoved)
15373 {
15374 Relation classRel;
15375
15376 Assert(objsMoved != NULL);
15377
15378 /* OK, modify the pg_class row and pg_depend entry */
15379 classRel = table_open(RelationRelationId, RowExclusiveLock);
15380
15381 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
15382 nspOid, true, objsMoved);
15383
15384 /* Fix the table's row type too */
15385 AlterTypeNamespaceInternal(rel->rd_rel->reltype,
15386 nspOid, false, false, objsMoved);
15387
15388 /* Fix other dependent stuff */
15389 if (rel->rd_rel->relkind == RELKIND_RELATION ||
15390 rel->rd_rel->relkind == RELKIND_MATVIEW ||
15391 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15392 {
15393 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
15394 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
15395 objsMoved, AccessExclusiveLock);
15396 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
15397 false, objsMoved);
15398 }
15399
15400 table_close(classRel, RowExclusiveLock);
15401 }
15402
15403 /*
15404 * The guts of relocating a relation to another namespace: fix the pg_class
15405 * entry, and the pg_depend entry if any. Caller must already have
15406 * opened and write-locked pg_class.
15407 */
15408 void
AlterRelationNamespaceInternal(Relation classRel,Oid relOid,Oid oldNspOid,Oid newNspOid,bool hasDependEntry,ObjectAddresses * objsMoved)15409 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
15410 Oid oldNspOid, Oid newNspOid,
15411 bool hasDependEntry,
15412 ObjectAddresses *objsMoved)
15413 {
15414 HeapTuple classTup;
15415 Form_pg_class classForm;
15416 ObjectAddress thisobj;
15417 bool already_done = false;
15418
15419 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
15420 if (!HeapTupleIsValid(classTup))
15421 elog(ERROR, "cache lookup failed for relation %u", relOid);
15422 classForm = (Form_pg_class) GETSTRUCT(classTup);
15423
15424 Assert(classForm->relnamespace == oldNspOid);
15425
15426 thisobj.classId = RelationRelationId;
15427 thisobj.objectId = relOid;
15428 thisobj.objectSubId = 0;
15429
15430 /*
15431 * If the object has already been moved, don't move it again. If it's
15432 * already in the right place, don't move it, but still fire the object
15433 * access hook.
15434 */
15435 already_done = object_address_present(&thisobj, objsMoved);
15436 if (!already_done && oldNspOid != newNspOid)
15437 {
15438 /* check for duplicate name (more friendly than unique-index failure) */
15439 if (get_relname_relid(NameStr(classForm->relname),
15440 newNspOid) != InvalidOid)
15441 ereport(ERROR,
15442 (errcode(ERRCODE_DUPLICATE_TABLE),
15443 errmsg("relation \"%s\" already exists in schema \"%s\"",
15444 NameStr(classForm->relname),
15445 get_namespace_name(newNspOid))));
15446
15447 /* classTup is a copy, so OK to scribble on */
15448 classForm->relnamespace = newNspOid;
15449
15450 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
15451
15452 /* Update dependency on schema if caller said so */
15453 if (hasDependEntry &&
15454 changeDependencyFor(RelationRelationId,
15455 relOid,
15456 NamespaceRelationId,
15457 oldNspOid,
15458 newNspOid) != 1)
15459 elog(ERROR, "failed to change schema dependency for relation \"%s\"",
15460 NameStr(classForm->relname));
15461 }
15462 if (!already_done)
15463 {
15464 add_exact_object_address(&thisobj, objsMoved);
15465
15466 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
15467 }
15468
15469 heap_freetuple(classTup);
15470 }
15471
15472 /*
15473 * Move all indexes for the specified relation to another namespace.
15474 *
15475 * Note: we assume adequate permission checking was done by the caller,
15476 * and that the caller has a suitable lock on the owning relation.
15477 */
15478 static void
AlterIndexNamespaces(Relation classRel,Relation rel,Oid oldNspOid,Oid newNspOid,ObjectAddresses * objsMoved)15479 AlterIndexNamespaces(Relation classRel, Relation rel,
15480 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
15481 {
15482 List *indexList;
15483 ListCell *l;
15484
15485 indexList = RelationGetIndexList(rel);
15486
15487 foreach(l, indexList)
15488 {
15489 Oid indexOid = lfirst_oid(l);
15490 ObjectAddress thisobj;
15491
15492 thisobj.classId = RelationRelationId;
15493 thisobj.objectId = indexOid;
15494 thisobj.objectSubId = 0;
15495
15496 /*
15497 * Note: currently, the index will not have its own dependency on the
15498 * namespace, so we don't need to do changeDependencyFor(). There's no
15499 * row type in pg_type, either.
15500 *
15501 * XXX this objsMoved test may be pointless -- surely we have a single
15502 * dependency link from a relation to each index?
15503 */
15504 if (!object_address_present(&thisobj, objsMoved))
15505 {
15506 AlterRelationNamespaceInternal(classRel, indexOid,
15507 oldNspOid, newNspOid,
15508 false, objsMoved);
15509 add_exact_object_address(&thisobj, objsMoved);
15510 }
15511 }
15512
15513 list_free(indexList);
15514 }
15515
15516 /*
15517 * Move all identity and SERIAL-column sequences of the specified relation to another
15518 * namespace.
15519 *
15520 * Note: we assume adequate permission checking was done by the caller,
15521 * and that the caller has a suitable lock on the owning relation.
15522 */
15523 static void
AlterSeqNamespaces(Relation classRel,Relation rel,Oid oldNspOid,Oid newNspOid,ObjectAddresses * objsMoved,LOCKMODE lockmode)15524 AlterSeqNamespaces(Relation classRel, Relation rel,
15525 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
15526 LOCKMODE lockmode)
15527 {
15528 Relation depRel;
15529 SysScanDesc scan;
15530 ScanKeyData key[2];
15531 HeapTuple tup;
15532
15533 /*
15534 * SERIAL sequences are those having an auto dependency on one of the
15535 * table's columns (we don't care *which* column, exactly).
15536 */
15537 depRel = table_open(DependRelationId, AccessShareLock);
15538
15539 ScanKeyInit(&key[0],
15540 Anum_pg_depend_refclassid,
15541 BTEqualStrategyNumber, F_OIDEQ,
15542 ObjectIdGetDatum(RelationRelationId));
15543 ScanKeyInit(&key[1],
15544 Anum_pg_depend_refobjid,
15545 BTEqualStrategyNumber, F_OIDEQ,
15546 ObjectIdGetDatum(RelationGetRelid(rel)));
15547 /* we leave refobjsubid unspecified */
15548
15549 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15550 NULL, 2, key);
15551
15552 while (HeapTupleIsValid(tup = systable_getnext(scan)))
15553 {
15554 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15555 Relation seqRel;
15556
15557 /* skip dependencies other than auto dependencies on columns */
15558 if (depForm->refobjsubid == 0 ||
15559 depForm->classid != RelationRelationId ||
15560 depForm->objsubid != 0 ||
15561 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15562 continue;
15563
15564 /* Use relation_open just in case it's an index */
15565 seqRel = relation_open(depForm->objid, lockmode);
15566
15567 /* skip non-sequence relations */
15568 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15569 {
15570 /* No need to keep the lock */
15571 relation_close(seqRel, lockmode);
15572 continue;
15573 }
15574
15575 /* Fix the pg_class and pg_depend entries */
15576 AlterRelationNamespaceInternal(classRel, depForm->objid,
15577 oldNspOid, newNspOid,
15578 true, objsMoved);
15579
15580 /*
15581 * Sequences have entries in pg_type. We need to be careful to move
15582 * them to the new namespace, too.
15583 */
15584 AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype,
15585 newNspOid, false, false, objsMoved);
15586
15587 /* Now we can close it. Keep the lock till end of transaction. */
15588 relation_close(seqRel, NoLock);
15589 }
15590
15591 systable_endscan(scan);
15592
15593 relation_close(depRel, AccessShareLock);
15594 }
15595
15596
15597 /*
15598 * This code supports
15599 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
15600 *
15601 * Because we only support this for TEMP tables, it's sufficient to remember
15602 * the state in a backend-local data structure.
15603 */
15604
15605 /*
15606 * Register a newly-created relation's ON COMMIT action.
15607 */
15608 void
register_on_commit_action(Oid relid,OnCommitAction action)15609 register_on_commit_action(Oid relid, OnCommitAction action)
15610 {
15611 OnCommitItem *oc;
15612 MemoryContext oldcxt;
15613
15614 /*
15615 * We needn't bother registering the relation unless there is an ON COMMIT
15616 * action we need to take.
15617 */
15618 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
15619 return;
15620
15621 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
15622
15623 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
15624 oc->relid = relid;
15625 oc->oncommit = action;
15626 oc->creating_subid = GetCurrentSubTransactionId();
15627 oc->deleting_subid = InvalidSubTransactionId;
15628
15629 /*
15630 * We use lcons() here so that ON COMMIT actions are processed in reverse
15631 * order of registration. That might not be essential but it seems
15632 * reasonable.
15633 */
15634 on_commits = lcons(oc, on_commits);
15635
15636 MemoryContextSwitchTo(oldcxt);
15637 }
15638
15639 /*
15640 * Unregister any ON COMMIT action when a relation is deleted.
15641 *
15642 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
15643 */
15644 void
remove_on_commit_action(Oid relid)15645 remove_on_commit_action(Oid relid)
15646 {
15647 ListCell *l;
15648
15649 foreach(l, on_commits)
15650 {
15651 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
15652
15653 if (oc->relid == relid)
15654 {
15655 oc->deleting_subid = GetCurrentSubTransactionId();
15656 break;
15657 }
15658 }
15659 }
15660
15661 /*
15662 * Perform ON COMMIT actions.
15663 *
15664 * This is invoked just before actually committing, since it's possible
15665 * to encounter errors.
15666 */
15667 void
PreCommit_on_commit_actions(void)15668 PreCommit_on_commit_actions(void)
15669 {
15670 ListCell *l;
15671 List *oids_to_truncate = NIL;
15672 List *oids_to_drop = NIL;
15673
15674 foreach(l, on_commits)
15675 {
15676 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
15677
15678 /* Ignore entry if already dropped in this xact */
15679 if (oc->deleting_subid != InvalidSubTransactionId)
15680 continue;
15681
15682 switch (oc->oncommit)
15683 {
15684 case ONCOMMIT_NOOP:
15685 case ONCOMMIT_PRESERVE_ROWS:
15686 /* Do nothing (there shouldn't be such entries, actually) */
15687 break;
15688 case ONCOMMIT_DELETE_ROWS:
15689
15690 /*
15691 * If this transaction hasn't accessed any temporary
15692 * relations, we can skip truncating ON COMMIT DELETE ROWS
15693 * tables, as they must still be empty.
15694 */
15695 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
15696 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
15697 break;
15698 case ONCOMMIT_DROP:
15699 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
15700 break;
15701 }
15702 }
15703
15704 /*
15705 * Truncate relations before dropping so that all dependencies between
15706 * relations are removed after they are worked on. Doing it like this
15707 * might be a waste as it is possible that a relation being truncated will
15708 * be dropped anyway due to its parent being dropped, but this makes the
15709 * code more robust because of not having to re-check that the relation
15710 * exists at truncation time.
15711 */
15712 if (oids_to_truncate != NIL)
15713 heap_truncate(oids_to_truncate);
15714
15715 if (oids_to_drop != NIL)
15716 {
15717 ObjectAddresses *targetObjects = new_object_addresses();
15718 ListCell *l;
15719
15720 foreach(l, oids_to_drop)
15721 {
15722 ObjectAddress object;
15723
15724 object.classId = RelationRelationId;
15725 object.objectId = lfirst_oid(l);
15726 object.objectSubId = 0;
15727
15728 Assert(!object_address_present(&object, targetObjects));
15729
15730 add_exact_object_address(&object, targetObjects);
15731 }
15732
15733 /*
15734 * Since this is an automatic drop, rather than one directly initiated
15735 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
15736 */
15737 performMultipleDeletions(targetObjects, DROP_CASCADE,
15738 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
15739
15740 #ifdef USE_ASSERT_CHECKING
15741
15742 /*
15743 * Note that table deletion will call remove_on_commit_action, so the
15744 * entry should get marked as deleted.
15745 */
15746 foreach(l, on_commits)
15747 {
15748 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
15749
15750 if (oc->oncommit != ONCOMMIT_DROP)
15751 continue;
15752
15753 Assert(oc->deleting_subid != InvalidSubTransactionId);
15754 }
15755 #endif
15756 }
15757 }
15758
15759 /*
15760 * Post-commit or post-abort cleanup for ON COMMIT management.
15761 *
15762 * All we do here is remove no-longer-needed OnCommitItem entries.
15763 *
15764 * During commit, remove entries that were deleted during this transaction;
15765 * during abort, remove those created during this transaction.
15766 */
15767 void
AtEOXact_on_commit_actions(bool isCommit)15768 AtEOXact_on_commit_actions(bool isCommit)
15769 {
15770 ListCell *cur_item;
15771
15772 foreach(cur_item, on_commits)
15773 {
15774 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
15775
15776 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
15777 oc->creating_subid != InvalidSubTransactionId)
15778 {
15779 /* cur_item must be removed */
15780 on_commits = foreach_delete_current(on_commits, cur_item);
15781 pfree(oc);
15782 }
15783 else
15784 {
15785 /* cur_item must be preserved */
15786 oc->creating_subid = InvalidSubTransactionId;
15787 oc->deleting_subid = InvalidSubTransactionId;
15788 }
15789 }
15790 }
15791
15792 /*
15793 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
15794 *
15795 * During subabort, we can immediately remove entries created during this
15796 * subtransaction. During subcommit, just relabel entries marked during
15797 * this subtransaction as being the parent's responsibility.
15798 */
15799 void
AtEOSubXact_on_commit_actions(bool isCommit,SubTransactionId mySubid,SubTransactionId parentSubid)15800 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
15801 SubTransactionId parentSubid)
15802 {
15803 ListCell *cur_item;
15804
15805 foreach(cur_item, on_commits)
15806 {
15807 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
15808
15809 if (!isCommit && oc->creating_subid == mySubid)
15810 {
15811 /* cur_item must be removed */
15812 on_commits = foreach_delete_current(on_commits, cur_item);
15813 pfree(oc);
15814 }
15815 else
15816 {
15817 /* cur_item must be preserved */
15818 if (oc->creating_subid == mySubid)
15819 oc->creating_subid = parentSubid;
15820 if (oc->deleting_subid == mySubid)
15821 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
15822 }
15823 }
15824 }
15825
15826 /*
15827 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
15828 * the relation to be locked only if (1) it's a plain table, materialized
15829 * view, or TOAST table and (2) the current user is the owner (or the
15830 * superuser). This meets the permission-checking needs of CLUSTER, REINDEX
15831 * TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it can be
15832 * used by all.
15833 */
15834 void
RangeVarCallbackOwnsTable(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)15835 RangeVarCallbackOwnsTable(const RangeVar *relation,
15836 Oid relId, Oid oldRelId, void *arg)
15837 {
15838 char relkind;
15839
15840 /* Nothing to do if the relation was not found. */
15841 if (!OidIsValid(relId))
15842 return;
15843
15844 /*
15845 * If the relation does exist, check whether it's an index. But note that
15846 * the relation might have been dropped between the time we did the name
15847 * lookup and now. In that case, there's nothing to do.
15848 */
15849 relkind = get_rel_relkind(relId);
15850 if (!relkind)
15851 return;
15852 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
15853 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
15854 ereport(ERROR,
15855 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15856 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
15857
15858 /* Check permissions */
15859 if (!pg_class_ownercheck(relId, GetUserId()))
15860 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)), relation->relname);
15861 }
15862
15863 /*
15864 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
15865 */
15866 static void
RangeVarCallbackForTruncate(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)15867 RangeVarCallbackForTruncate(const RangeVar *relation,
15868 Oid relId, Oid oldRelId, void *arg)
15869 {
15870 HeapTuple tuple;
15871
15872 /* Nothing to do if the relation was not found. */
15873 if (!OidIsValid(relId))
15874 return;
15875
15876 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
15877 if (!HeapTupleIsValid(tuple)) /* should not happen */
15878 elog(ERROR, "cache lookup failed for relation %u", relId);
15879
15880 truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
15881 truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
15882
15883 ReleaseSysCache(tuple);
15884 }
15885
15886 /*
15887 * Callback to RangeVarGetRelidExtended(), similar to
15888 * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
15889 */
15890 void
RangeVarCallbackOwnsRelation(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)15891 RangeVarCallbackOwnsRelation(const RangeVar *relation,
15892 Oid relId, Oid oldRelId, void *arg)
15893 {
15894 HeapTuple tuple;
15895
15896 /* Nothing to do if the relation was not found. */
15897 if (!OidIsValid(relId))
15898 return;
15899
15900 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
15901 if (!HeapTupleIsValid(tuple)) /* should not happen */
15902 elog(ERROR, "cache lookup failed for relation %u", relId);
15903
15904 if (!pg_class_ownercheck(relId, GetUserId()))
15905 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
15906 relation->relname);
15907
15908 if (!allowSystemTableMods &&
15909 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
15910 ereport(ERROR,
15911 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
15912 errmsg("permission denied: \"%s\" is a system catalog",
15913 relation->relname)));
15914
15915 ReleaseSysCache(tuple);
15916 }
15917
15918 /*
15919 * Common RangeVarGetRelid callback for rename, set schema, and alter table
15920 * processing.
15921 */
15922 static void
RangeVarCallbackForAlterRelation(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)15923 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
15924 void *arg)
15925 {
15926 Node *stmt = (Node *) arg;
15927 ObjectType reltype;
15928 HeapTuple tuple;
15929 Form_pg_class classform;
15930 AclResult aclresult;
15931 char relkind;
15932
15933 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
15934 if (!HeapTupleIsValid(tuple))
15935 return; /* concurrently dropped */
15936 classform = (Form_pg_class) GETSTRUCT(tuple);
15937 relkind = classform->relkind;
15938
15939 /* Must own relation. */
15940 if (!pg_class_ownercheck(relid, GetUserId()))
15941 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
15942
15943 /* No system table modifications unless explicitly allowed. */
15944 if (!allowSystemTableMods && IsSystemClass(relid, classform))
15945 ereport(ERROR,
15946 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
15947 errmsg("permission denied: \"%s\" is a system catalog",
15948 rv->relname)));
15949
15950 /*
15951 * Extract the specified relation type from the statement parse tree.
15952 *
15953 * Also, for ALTER .. RENAME, check permissions: the user must (still)
15954 * have CREATE rights on the containing namespace.
15955 */
15956 if (IsA(stmt, RenameStmt))
15957 {
15958 aclresult = pg_namespace_aclcheck(classform->relnamespace,
15959 GetUserId(), ACL_CREATE);
15960 if (aclresult != ACLCHECK_OK)
15961 aclcheck_error(aclresult, OBJECT_SCHEMA,
15962 get_namespace_name(classform->relnamespace));
15963 reltype = ((RenameStmt *) stmt)->renameType;
15964 }
15965 else if (IsA(stmt, AlterObjectSchemaStmt))
15966 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
15967
15968 else if (IsA(stmt, AlterTableStmt))
15969 reltype = ((AlterTableStmt *) stmt)->relkind;
15970 else
15971 {
15972 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
15973 reltype = OBJECT_TABLE; /* placate compiler */
15974 }
15975
15976 /*
15977 * For compatibility with prior releases, we allow ALTER TABLE to be used
15978 * with most other types of relations (but not composite types). We allow
15979 * similar flexibility for ALTER INDEX in the case of RENAME, but not
15980 * otherwise. Otherwise, the user must select the correct form of the
15981 * command for the relation at issue.
15982 */
15983 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
15984 ereport(ERROR,
15985 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15986 errmsg("\"%s\" is not a sequence", rv->relname)));
15987
15988 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
15989 ereport(ERROR,
15990 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15991 errmsg("\"%s\" is not a view", rv->relname)));
15992
15993 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
15994 ereport(ERROR,
15995 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15996 errmsg("\"%s\" is not a materialized view", rv->relname)));
15997
15998 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
15999 ereport(ERROR,
16000 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16001 errmsg("\"%s\" is not a foreign table", rv->relname)));
16002
16003 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
16004 ereport(ERROR,
16005 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16006 errmsg("\"%s\" is not a composite type", rv->relname)));
16007
16008 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
16009 relkind != RELKIND_PARTITIONED_INDEX
16010 && !IsA(stmt, RenameStmt))
16011 ereport(ERROR,
16012 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16013 errmsg("\"%s\" is not an index", rv->relname)));
16014
16015 /*
16016 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
16017 * TYPE for that.
16018 */
16019 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
16020 ereport(ERROR,
16021 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16022 errmsg("\"%s\" is a composite type", rv->relname),
16023 errhint("Use ALTER TYPE instead.")));
16024
16025 /*
16026 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
16027 * to a different schema, such as indexes and TOAST tables.
16028 */
16029 if (IsA(stmt, AlterObjectSchemaStmt) &&
16030 relkind != RELKIND_RELATION &&
16031 relkind != RELKIND_VIEW &&
16032 relkind != RELKIND_MATVIEW &&
16033 relkind != RELKIND_SEQUENCE &&
16034 relkind != RELKIND_FOREIGN_TABLE &&
16035 relkind != RELKIND_PARTITIONED_TABLE)
16036 ereport(ERROR,
16037 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16038 errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table",
16039 rv->relname)));
16040
16041 ReleaseSysCache(tuple);
16042 }
16043
16044 /*
16045 * Transform any expressions present in the partition key
16046 *
16047 * Returns a transformed PartitionSpec, as well as the strategy code
16048 */
16049 static PartitionSpec *
transformPartitionSpec(Relation rel,PartitionSpec * partspec,char * strategy)16050 transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
16051 {
16052 PartitionSpec *newspec;
16053 ParseState *pstate;
16054 ParseNamespaceItem *nsitem;
16055 ListCell *l;
16056
16057 newspec = makeNode(PartitionSpec);
16058
16059 newspec->strategy = partspec->strategy;
16060 newspec->partParams = NIL;
16061 newspec->location = partspec->location;
16062
16063 /* Parse partitioning strategy name */
16064 if (pg_strcasecmp(partspec->strategy, "hash") == 0)
16065 *strategy = PARTITION_STRATEGY_HASH;
16066 else if (pg_strcasecmp(partspec->strategy, "list") == 0)
16067 *strategy = PARTITION_STRATEGY_LIST;
16068 else if (pg_strcasecmp(partspec->strategy, "range") == 0)
16069 *strategy = PARTITION_STRATEGY_RANGE;
16070 else
16071 ereport(ERROR,
16072 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16073 errmsg("unrecognized partitioning strategy \"%s\"",
16074 partspec->strategy)));
16075
16076 /* Check valid number of columns for strategy */
16077 if (*strategy == PARTITION_STRATEGY_LIST &&
16078 list_length(partspec->partParams) != 1)
16079 ereport(ERROR,
16080 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16081 errmsg("cannot use \"list\" partition strategy with more than one column")));
16082
16083 /*
16084 * Create a dummy ParseState and insert the target relation as its sole
16085 * rangetable entry. We need a ParseState for transformExpr.
16086 */
16087 pstate = make_parsestate(NULL);
16088 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
16089 NULL, false, true);
16090 addNSItemToQuery(pstate, nsitem, true, true, true);
16091
16092 /* take care of any partition expressions */
16093 foreach(l, partspec->partParams)
16094 {
16095 PartitionElem *pelem = castNode(PartitionElem, lfirst(l));
16096
16097 if (pelem->expr)
16098 {
16099 /* Copy, to avoid scribbling on the input */
16100 pelem = copyObject(pelem);
16101
16102 /* Now do parse transformation of the expression */
16103 pelem->expr = transformExpr(pstate, pelem->expr,
16104 EXPR_KIND_PARTITION_EXPRESSION);
16105
16106 /* we have to fix its collations too */
16107 assign_expr_collations(pstate, pelem->expr);
16108 }
16109
16110 newspec->partParams = lappend(newspec->partParams, pelem);
16111 }
16112
16113 return newspec;
16114 }
16115
16116 /*
16117 * Compute per-partition-column information from a list of PartitionElems.
16118 * Expressions in the PartitionElems must be parse-analyzed already.
16119 */
16120 static void
ComputePartitionAttrs(ParseState * pstate,Relation rel,List * partParams,AttrNumber * partattrs,List ** partexprs,Oid * partopclass,Oid * partcollation,char strategy)16121 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
16122 List **partexprs, Oid *partopclass, Oid *partcollation,
16123 char strategy)
16124 {
16125 int attn;
16126 ListCell *lc;
16127 Oid am_oid;
16128
16129 attn = 0;
16130 foreach(lc, partParams)
16131 {
16132 PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
16133 Oid atttype;
16134 Oid attcollation;
16135
16136 if (pelem->name != NULL)
16137 {
16138 /* Simple attribute reference */
16139 HeapTuple atttuple;
16140 Form_pg_attribute attform;
16141
16142 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
16143 pelem->name);
16144 if (!HeapTupleIsValid(atttuple))
16145 ereport(ERROR,
16146 (errcode(ERRCODE_UNDEFINED_COLUMN),
16147 errmsg("column \"%s\" named in partition key does not exist",
16148 pelem->name),
16149 parser_errposition(pstate, pelem->location)));
16150 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
16151
16152 if (attform->attnum <= 0)
16153 ereport(ERROR,
16154 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16155 errmsg("cannot use system column \"%s\" in partition key",
16156 pelem->name),
16157 parser_errposition(pstate, pelem->location)));
16158
16159 /*
16160 * Generated columns cannot work: They are computed after BEFORE
16161 * triggers, but partition routing is done before all triggers.
16162 */
16163 if (attform->attgenerated)
16164 ereport(ERROR,
16165 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16166 errmsg("cannot use generated column in partition key"),
16167 errdetail("Column \"%s\" is a generated column.",
16168 pelem->name),
16169 parser_errposition(pstate, pelem->location)));
16170
16171 partattrs[attn] = attform->attnum;
16172 atttype = attform->atttypid;
16173 attcollation = attform->attcollation;
16174 ReleaseSysCache(atttuple);
16175 }
16176 else
16177 {
16178 /* Expression */
16179 Node *expr = pelem->expr;
16180 char partattname[16];
16181
16182 Assert(expr != NULL);
16183 atttype = exprType(expr);
16184 attcollation = exprCollation(expr);
16185
16186 /*
16187 * The expression must be of a storable type (e.g., not RECORD).
16188 * The test is the same as for whether a table column is of a safe
16189 * type (which is why we needn't check for the non-expression
16190 * case).
16191 */
16192 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
16193 CheckAttributeType(partattname,
16194 atttype, attcollation,
16195 NIL, CHKATYPE_IS_PARTKEY);
16196
16197 /*
16198 * Strip any top-level COLLATE clause. This ensures that we treat
16199 * "x COLLATE y" and "(x COLLATE y)" alike.
16200 */
16201 while (IsA(expr, CollateExpr))
16202 expr = (Node *) ((CollateExpr *) expr)->arg;
16203
16204 if (IsA(expr, Var) &&
16205 ((Var *) expr)->varattno > 0)
16206 {
16207 /*
16208 * User wrote "(column)" or "(column COLLATE something)".
16209 * Treat it like simple attribute anyway.
16210 */
16211 partattrs[attn] = ((Var *) expr)->varattno;
16212 }
16213 else
16214 {
16215 Bitmapset *expr_attrs = NULL;
16216 int i;
16217
16218 partattrs[attn] = 0; /* marks the column as expression */
16219 *partexprs = lappend(*partexprs, expr);
16220
16221 /*
16222 * Try to simplify the expression before checking for
16223 * mutability. The main practical value of doing it in this
16224 * order is that an inline-able SQL-language function will be
16225 * accepted if its expansion is immutable, whether or not the
16226 * function itself is marked immutable.
16227 *
16228 * Note that expression_planner does not change the passed in
16229 * expression destructively and we have already saved the
16230 * expression to be stored into the catalog above.
16231 */
16232 expr = (Node *) expression_planner((Expr *) expr);
16233
16234 /*
16235 * Partition expression cannot contain mutable functions,
16236 * because a given row must always map to the same partition
16237 * as long as there is no change in the partition boundary
16238 * structure.
16239 */
16240 if (contain_mutable_functions(expr))
16241 ereport(ERROR,
16242 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16243 errmsg("functions in partition key expression must be marked IMMUTABLE")));
16244
16245 /*
16246 * transformPartitionSpec() should have already rejected
16247 * subqueries, aggregates, window functions, and SRFs, based
16248 * on the EXPR_KIND_ for partition expressions.
16249 */
16250
16251 /*
16252 * Cannot allow system column references, since that would
16253 * make partition routing impossible: their values won't be
16254 * known yet when we need to do that.
16255 */
16256 pull_varattnos(expr, 1, &expr_attrs);
16257 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
16258 {
16259 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
16260 expr_attrs))
16261 ereport(ERROR,
16262 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16263 errmsg("partition key expressions cannot contain system column references")));
16264 }
16265
16266 /*
16267 * Generated columns cannot work: They are computed after
16268 * BEFORE triggers, but partition routing is done before all
16269 * triggers.
16270 */
16271 i = -1;
16272 while ((i = bms_next_member(expr_attrs, i)) >= 0)
16273 {
16274 AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
16275
16276 if (attno > 0 &&
16277 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
16278 ereport(ERROR,
16279 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16280 errmsg("cannot use generated column in partition key"),
16281 errdetail("Column \"%s\" is a generated column.",
16282 get_attname(RelationGetRelid(rel), attno, false)),
16283 parser_errposition(pstate, pelem->location)));
16284 }
16285
16286 /*
16287 * While it is not exactly *wrong* for a partition expression
16288 * to be a constant, it seems better to reject such keys.
16289 */
16290 if (IsA(expr, Const))
16291 ereport(ERROR,
16292 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16293 errmsg("cannot use constant expression as partition key")));
16294 }
16295 }
16296
16297 /*
16298 * Apply collation override if any
16299 */
16300 if (pelem->collation)
16301 attcollation = get_collation_oid(pelem->collation, false);
16302
16303 /*
16304 * Check we have a collation iff it's a collatable type. The only
16305 * expected failures here are (1) COLLATE applied to a noncollatable
16306 * type, or (2) partition expression had an unresolved collation. But
16307 * we might as well code this to be a complete consistency check.
16308 */
16309 if (type_is_collatable(atttype))
16310 {
16311 if (!OidIsValid(attcollation))
16312 ereport(ERROR,
16313 (errcode(ERRCODE_INDETERMINATE_COLLATION),
16314 errmsg("could not determine which collation to use for partition expression"),
16315 errhint("Use the COLLATE clause to set the collation explicitly.")));
16316 }
16317 else
16318 {
16319 if (OidIsValid(attcollation))
16320 ereport(ERROR,
16321 (errcode(ERRCODE_DATATYPE_MISMATCH),
16322 errmsg("collations are not supported by type %s",
16323 format_type_be(atttype))));
16324 }
16325
16326 partcollation[attn] = attcollation;
16327
16328 /*
16329 * Identify the appropriate operator class. For list and range
16330 * partitioning, we use a btree operator class; hash partitioning uses
16331 * a hash operator class.
16332 */
16333 if (strategy == PARTITION_STRATEGY_HASH)
16334 am_oid = HASH_AM_OID;
16335 else
16336 am_oid = BTREE_AM_OID;
16337
16338 if (!pelem->opclass)
16339 {
16340 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
16341
16342 if (!OidIsValid(partopclass[attn]))
16343 {
16344 if (strategy == PARTITION_STRATEGY_HASH)
16345 ereport(ERROR,
16346 (errcode(ERRCODE_UNDEFINED_OBJECT),
16347 errmsg("data type %s has no default operator class for access method \"%s\"",
16348 format_type_be(atttype), "hash"),
16349 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
16350 else
16351 ereport(ERROR,
16352 (errcode(ERRCODE_UNDEFINED_OBJECT),
16353 errmsg("data type %s has no default operator class for access method \"%s\"",
16354 format_type_be(atttype), "btree"),
16355 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
16356
16357 }
16358 }
16359 else
16360 partopclass[attn] = ResolveOpClass(pelem->opclass,
16361 atttype,
16362 am_oid == HASH_AM_OID ? "hash" : "btree",
16363 am_oid);
16364
16365 attn++;
16366 }
16367 }
16368
16369 /*
16370 * PartConstraintImpliedByRelConstraint
16371 * Do scanrel's existing constraints imply the partition constraint?
16372 *
16373 * "Existing constraints" include its check constraints and column-level
16374 * NOT NULL constraints. partConstraint describes the partition constraint,
16375 * in implicit-AND form.
16376 */
16377 bool
PartConstraintImpliedByRelConstraint(Relation scanrel,List * partConstraint)16378 PartConstraintImpliedByRelConstraint(Relation scanrel,
16379 List *partConstraint)
16380 {
16381 List *existConstraint = NIL;
16382 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
16383 int i;
16384
16385 if (constr && constr->has_not_null)
16386 {
16387 int natts = scanrel->rd_att->natts;
16388
16389 for (i = 1; i <= natts; i++)
16390 {
16391 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
16392
16393 if (att->attnotnull && !att->attisdropped)
16394 {
16395 NullTest *ntest = makeNode(NullTest);
16396
16397 ntest->arg = (Expr *) makeVar(1,
16398 i,
16399 att->atttypid,
16400 att->atttypmod,
16401 att->attcollation,
16402 0);
16403 ntest->nulltesttype = IS_NOT_NULL;
16404
16405 /*
16406 * argisrow=false is correct even for a composite column,
16407 * because attnotnull does not represent a SQL-spec IS NOT
16408 * NULL test in such a case, just IS DISTINCT FROM NULL.
16409 */
16410 ntest->argisrow = false;
16411 ntest->location = -1;
16412 existConstraint = lappend(existConstraint, ntest);
16413 }
16414 }
16415 }
16416
16417 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
16418 }
16419
16420 /*
16421 * ConstraintImpliedByRelConstraint
16422 * Do scanrel's existing constraints imply the given constraint?
16423 *
16424 * testConstraint is the constraint to validate. provenConstraint is a
16425 * caller-provided list of conditions which this function may assume
16426 * to be true. Both provenConstraint and testConstraint must be in
16427 * implicit-AND form, must only contain immutable clauses, and must
16428 * contain only Vars with varno = 1.
16429 */
16430 bool
ConstraintImpliedByRelConstraint(Relation scanrel,List * testConstraint,List * provenConstraint)16431 ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
16432 {
16433 List *existConstraint = list_copy(provenConstraint);
16434 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
16435 int num_check,
16436 i;
16437
16438 num_check = (constr != NULL) ? constr->num_check : 0;
16439 for (i = 0; i < num_check; i++)
16440 {
16441 Node *cexpr;
16442
16443 /*
16444 * If this constraint hasn't been fully validated yet, we must ignore
16445 * it here.
16446 */
16447 if (!constr->check[i].ccvalid)
16448 continue;
16449
16450 cexpr = stringToNode(constr->check[i].ccbin);
16451
16452 /*
16453 * Run each expression through const-simplification and
16454 * canonicalization. It is necessary, because we will be comparing it
16455 * to similarly-processed partition constraint expressions, and may
16456 * fail to detect valid matches without this.
16457 */
16458 cexpr = eval_const_expressions(NULL, cexpr);
16459 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
16460
16461 existConstraint = list_concat(existConstraint,
16462 make_ands_implicit((Expr *) cexpr));
16463 }
16464
16465 /*
16466 * Try to make the proof. Since we are comparing CHECK constraints, we
16467 * need to use weak implication, i.e., we assume existConstraint is
16468 * not-false and try to prove the same for testConstraint.
16469 *
16470 * Note that predicate_implied_by assumes its first argument is known
16471 * immutable. That should always be true for both NOT NULL and partition
16472 * constraints, so we don't test it here.
16473 */
16474 return predicate_implied_by(testConstraint, existConstraint, true);
16475 }
16476
16477 /*
16478 * QueuePartitionConstraintValidation
16479 *
16480 * Add an entry to wqueue to have the given partition constraint validated by
16481 * Phase 3, for the given relation, and all its children.
16482 *
16483 * We first verify whether the given constraint is implied by pre-existing
16484 * relation constraints; if it is, there's no need to scan the table to
16485 * validate, so don't queue in that case.
16486 */
16487 static void
QueuePartitionConstraintValidation(List ** wqueue,Relation scanrel,List * partConstraint,bool validate_default)16488 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
16489 List *partConstraint,
16490 bool validate_default)
16491 {
16492 /*
16493 * Based on the table's existing constraints, determine whether or not we
16494 * may skip scanning the table.
16495 */
16496 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
16497 {
16498 if (!validate_default)
16499 ereport(DEBUG1,
16500 (errmsg("partition constraint for table \"%s\" is implied by existing constraints",
16501 RelationGetRelationName(scanrel))));
16502 else
16503 ereport(DEBUG1,
16504 (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
16505 RelationGetRelationName(scanrel))));
16506 return;
16507 }
16508
16509 /*
16510 * Constraints proved insufficient. For plain relations, queue a
16511 * validation item now; for partitioned tables, recurse to process each
16512 * partition.
16513 */
16514 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
16515 {
16516 AlteredTableInfo *tab;
16517
16518 /* Grab a work queue entry. */
16519 tab = ATGetQueueEntry(wqueue, scanrel);
16520 Assert(tab->partition_constraint == NULL);
16521 tab->partition_constraint = (Expr *) linitial(partConstraint);
16522 tab->validate_default = validate_default;
16523 }
16524 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16525 {
16526 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel);
16527 int i;
16528
16529 for (i = 0; i < partdesc->nparts; i++)
16530 {
16531 Relation part_rel;
16532 List *thisPartConstraint;
16533
16534 /*
16535 * This is the minimum lock we need to prevent deadlocks.
16536 */
16537 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
16538
16539 /*
16540 * Adjust the constraint for scanrel so that it matches this
16541 * partition's attribute numbers.
16542 */
16543 thisPartConstraint =
16544 map_partition_varattnos(partConstraint, 1,
16545 part_rel, scanrel);
16546
16547 QueuePartitionConstraintValidation(wqueue, part_rel,
16548 thisPartConstraint,
16549 validate_default);
16550 table_close(part_rel, NoLock); /* keep lock till commit */
16551 }
16552 }
16553 }
16554
16555 /*
16556 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
16557 *
16558 * Return the address of the newly attached partition.
16559 */
16560 static ObjectAddress
ATExecAttachPartition(List ** wqueue,Relation rel,PartitionCmd * cmd)16561 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
16562 {
16563 Relation attachrel,
16564 catalog;
16565 List *attachrel_children;
16566 List *partConstraint;
16567 SysScanDesc scan;
16568 ScanKeyData skey;
16569 AttrNumber attno;
16570 int natts;
16571 TupleDesc tupleDesc;
16572 ObjectAddress address;
16573 const char *trigger_name;
16574 Oid defaultPartOid;
16575 List *partBoundConstraint;
16576
16577 /*
16578 * We must lock the default partition if one exists, because attaching a
16579 * new partition will change its partition constraint.
16580 */
16581 defaultPartOid =
16582 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel));
16583 if (OidIsValid(defaultPartOid))
16584 LockRelationOid(defaultPartOid, AccessExclusiveLock);
16585
16586 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
16587
16588 /*
16589 * XXX I think it'd be a good idea to grab locks on all tables referenced
16590 * by FKs at this point also.
16591 */
16592
16593 /*
16594 * Must be owner of both parent and source table -- parent was checked by
16595 * ATSimplePermissions call in ATPrepCmd
16596 */
16597 ATSimplePermissions(attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
16598
16599 /* A partition can only have one parent */
16600 if (attachrel->rd_rel->relispartition)
16601 ereport(ERROR,
16602 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16603 errmsg("\"%s\" is already a partition",
16604 RelationGetRelationName(attachrel))));
16605
16606 if (OidIsValid(attachrel->rd_rel->reloftype))
16607 ereport(ERROR,
16608 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16609 errmsg("cannot attach a typed table as partition")));
16610
16611 /*
16612 * Table being attached should not already be part of inheritance; either
16613 * as a child table...
16614 */
16615 catalog = table_open(InheritsRelationId, AccessShareLock);
16616 ScanKeyInit(&skey,
16617 Anum_pg_inherits_inhrelid,
16618 BTEqualStrategyNumber, F_OIDEQ,
16619 ObjectIdGetDatum(RelationGetRelid(attachrel)));
16620 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
16621 NULL, 1, &skey);
16622 if (HeapTupleIsValid(systable_getnext(scan)))
16623 ereport(ERROR,
16624 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16625 errmsg("cannot attach inheritance child as partition")));
16626 systable_endscan(scan);
16627
16628 /* ...or as a parent table (except the case when it is partitioned) */
16629 ScanKeyInit(&skey,
16630 Anum_pg_inherits_inhparent,
16631 BTEqualStrategyNumber, F_OIDEQ,
16632 ObjectIdGetDatum(RelationGetRelid(attachrel)));
16633 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
16634 1, &skey);
16635 if (HeapTupleIsValid(systable_getnext(scan)) &&
16636 attachrel->rd_rel->relkind == RELKIND_RELATION)
16637 ereport(ERROR,
16638 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16639 errmsg("cannot attach inheritance parent as partition")));
16640 systable_endscan(scan);
16641 table_close(catalog, AccessShareLock);
16642
16643 /*
16644 * Prevent circularity by seeing if rel is a partition of attachrel. (In
16645 * particular, this disallows making a rel a partition of itself.)
16646 *
16647 * We do that by checking if rel is a member of the list of attachrel's
16648 * partitions provided the latter is partitioned at all. We want to avoid
16649 * having to construct this list again, so we request the strongest lock
16650 * on all partitions. We need the strongest lock, because we may decide
16651 * to scan them if we find out that the table being attached (or its leaf
16652 * partitions) may contain rows that violate the partition constraint. If
16653 * the table has a constraint that would prevent such rows, which by
16654 * definition is present in all the partitions, we need not scan the
16655 * table, nor its partitions. But we cannot risk a deadlock by taking a
16656 * weaker lock now and the stronger one only when needed.
16657 */
16658 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
16659 AccessExclusiveLock, NULL);
16660 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
16661 ereport(ERROR,
16662 (errcode(ERRCODE_DUPLICATE_TABLE),
16663 errmsg("circular inheritance not allowed"),
16664 errdetail("\"%s\" is already a child of \"%s\".",
16665 RelationGetRelationName(rel),
16666 RelationGetRelationName(attachrel))));
16667
16668 /* If the parent is permanent, so must be all of its partitions. */
16669 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
16670 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
16671 ereport(ERROR,
16672 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16673 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
16674 RelationGetRelationName(rel))));
16675
16676 /* Temp parent cannot have a partition that is itself not a temp */
16677 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16678 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16679 ereport(ERROR,
16680 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16681 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
16682 RelationGetRelationName(rel))));
16683
16684 /* If the parent is temp, it must belong to this session */
16685 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16686 !rel->rd_islocaltemp)
16687 ereport(ERROR,
16688 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16689 errmsg("cannot attach as partition of temporary relation of another session")));
16690
16691 /* Ditto for the partition */
16692 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16693 !attachrel->rd_islocaltemp)
16694 ereport(ERROR,
16695 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16696 errmsg("cannot attach temporary relation of another session as partition")));
16697
16698 /* Check if there are any columns in attachrel that aren't in the parent */
16699 tupleDesc = RelationGetDescr(attachrel);
16700 natts = tupleDesc->natts;
16701 for (attno = 1; attno <= natts; attno++)
16702 {
16703 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
16704 char *attributeName = NameStr(attribute->attname);
16705
16706 /* Ignore dropped */
16707 if (attribute->attisdropped)
16708 continue;
16709
16710 /* Try to find the column in parent (matching on column name) */
16711 if (!SearchSysCacheExists2(ATTNAME,
16712 ObjectIdGetDatum(RelationGetRelid(rel)),
16713 CStringGetDatum(attributeName)))
16714 ereport(ERROR,
16715 (errcode(ERRCODE_DATATYPE_MISMATCH),
16716 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
16717 RelationGetRelationName(attachrel), attributeName,
16718 RelationGetRelationName(rel)),
16719 errdetail("The new partition may contain only the columns present in parent.")));
16720 }
16721
16722 /*
16723 * If child_rel has row-level triggers with transition tables, we
16724 * currently don't allow it to become a partition. See also prohibitions
16725 * in ATExecAddInherit() and CreateTrigger().
16726 */
16727 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
16728 if (trigger_name != NULL)
16729 ereport(ERROR,
16730 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16731 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
16732 trigger_name, RelationGetRelationName(attachrel)),
16733 errdetail("ROW triggers with transition tables are not supported on partitions")));
16734
16735 /*
16736 * Check that the new partition's bound is valid and does not overlap any
16737 * of existing partitions of the parent - note that it does not return on
16738 * error.
16739 */
16740 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
16741 cmd->bound);
16742
16743 /* OK to create inheritance. Rest of the checks performed there */
16744 CreateInheritance(attachrel, rel);
16745
16746 /* Update the pg_class entry. */
16747 StorePartitionBound(attachrel, rel, cmd->bound);
16748
16749 /* Ensure there exists a correct set of indexes in the partition. */
16750 AttachPartitionEnsureIndexes(rel, attachrel);
16751
16752 /* and triggers */
16753 CloneRowTriggersToPartition(rel, attachrel);
16754
16755 /*
16756 * Clone foreign key constraints. Callee is responsible for setting up
16757 * for phase 3 constraint verification.
16758 */
16759 CloneForeignKeyConstraints(wqueue, rel, attachrel);
16760
16761 /*
16762 * Generate partition constraint from the partition bound specification.
16763 * If the parent itself is a partition, make sure to include its
16764 * constraint as well.
16765 */
16766 partBoundConstraint = get_qual_from_partbound(attachrel, rel, cmd->bound);
16767 partConstraint = list_concat(partBoundConstraint,
16768 RelationGetPartitionQual(rel));
16769
16770 /* Skip validation if there are no constraints to validate. */
16771 if (partConstraint)
16772 {
16773 /*
16774 * Run the partition quals through const-simplification similar to
16775 * check constraints. We skip canonicalize_qual, though, because
16776 * partition quals should be in canonical form already.
16777 */
16778 partConstraint =
16779 (List *) eval_const_expressions(NULL,
16780 (Node *) partConstraint);
16781
16782 /* XXX this sure looks wrong */
16783 partConstraint = list_make1(make_ands_explicit(partConstraint));
16784
16785 /*
16786 * Adjust the generated constraint to match this partition's attribute
16787 * numbers.
16788 */
16789 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
16790 rel);
16791
16792 /* Validate partition constraints against the table being attached. */
16793 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
16794 false);
16795 }
16796
16797 /*
16798 * If we're attaching a partition other than the default partition and a
16799 * default one exists, then that partition's partition constraint changes,
16800 * so add an entry to the work queue to validate it, too. (We must not do
16801 * this when the partition being attached is the default one; we already
16802 * did it above!)
16803 */
16804 if (OidIsValid(defaultPartOid))
16805 {
16806 Relation defaultrel;
16807 List *defPartConstraint;
16808
16809 Assert(!cmd->bound->is_default);
16810
16811 /* we already hold a lock on the default partition */
16812 defaultrel = table_open(defaultPartOid, NoLock);
16813 defPartConstraint =
16814 get_proposed_default_constraint(partBoundConstraint);
16815
16816 /*
16817 * Map the Vars in the constraint expression from rel's attnos to
16818 * defaultrel's.
16819 */
16820 defPartConstraint =
16821 map_partition_varattnos(defPartConstraint,
16822 1, defaultrel, rel);
16823 QueuePartitionConstraintValidation(wqueue, defaultrel,
16824 defPartConstraint, true);
16825
16826 /* keep our lock until commit. */
16827 table_close(defaultrel, NoLock);
16828 }
16829
16830 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
16831
16832 /*
16833 * If the partition we just attached is partitioned itself, invalidate
16834 * relcache for all descendent partitions too to ensure that their
16835 * rd_partcheck expression trees are rebuilt; partitions already locked
16836 * at the beginning of this function.
16837 */
16838 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16839 {
16840 ListCell *l;
16841
16842 foreach(l, attachrel_children)
16843 {
16844 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
16845 }
16846 }
16847
16848 /* keep our lock until commit */
16849 table_close(attachrel, NoLock);
16850
16851 return address;
16852 }
16853
16854 /*
16855 * AttachPartitionEnsureIndexes
16856 * subroutine for ATExecAttachPartition to create/match indexes
16857 *
16858 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
16859 * PARTITION: every partition must have an index attached to each index on the
16860 * partitioned table.
16861 */
16862 static void
AttachPartitionEnsureIndexes(Relation rel,Relation attachrel)16863 AttachPartitionEnsureIndexes(Relation rel, Relation attachrel)
16864 {
16865 List *idxes;
16866 List *attachRelIdxs;
16867 Relation *attachrelIdxRels;
16868 IndexInfo **attachInfos;
16869 int i;
16870 ListCell *cell;
16871 MemoryContext cxt;
16872 MemoryContext oldcxt;
16873
16874 cxt = AllocSetContextCreate(CurrentMemoryContext,
16875 "AttachPartitionEnsureIndexes",
16876 ALLOCSET_DEFAULT_SIZES);
16877 oldcxt = MemoryContextSwitchTo(cxt);
16878
16879 idxes = RelationGetIndexList(rel);
16880 attachRelIdxs = RelationGetIndexList(attachrel);
16881 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
16882 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
16883
16884 /* Build arrays of all existing indexes and their IndexInfos */
16885 i = 0;
16886 foreach(cell, attachRelIdxs)
16887 {
16888 Oid cldIdxId = lfirst_oid(cell);
16889
16890 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
16891 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
16892 i++;
16893 }
16894
16895 /*
16896 * If we're attaching a foreign table, we must fail if any of the indexes
16897 * is a constraint index; otherwise, there's nothing to do here. Do this
16898 * before starting work, to avoid wasting the effort of building a few
16899 * non-unique indexes before coming across a unique one.
16900 */
16901 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
16902 {
16903 foreach(cell, idxes)
16904 {
16905 Oid idx = lfirst_oid(cell);
16906 Relation idxRel = index_open(idx, AccessShareLock);
16907
16908 if (idxRel->rd_index->indisunique ||
16909 idxRel->rd_index->indisprimary)
16910 ereport(ERROR,
16911 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16912 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
16913 RelationGetRelationName(attachrel),
16914 RelationGetRelationName(rel)),
16915 errdetail("Table \"%s\" contains unique indexes.",
16916 RelationGetRelationName(rel))));
16917 index_close(idxRel, AccessShareLock);
16918 }
16919
16920 goto out;
16921 }
16922
16923 /*
16924 * For each index on the partitioned table, find a matching one in the
16925 * partition-to-be; if one is not found, create one.
16926 */
16927 foreach(cell, idxes)
16928 {
16929 Oid idx = lfirst_oid(cell);
16930 Relation idxRel = index_open(idx, AccessShareLock);
16931 IndexInfo *info;
16932 AttrMap *attmap;
16933 bool found = false;
16934 Oid constraintOid;
16935
16936 /*
16937 * Ignore indexes in the partitioned table other than partitioned
16938 * indexes.
16939 */
16940 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
16941 {
16942 index_close(idxRel, AccessShareLock);
16943 continue;
16944 }
16945
16946 /* construct an indexinfo to compare existing indexes against */
16947 info = BuildIndexInfo(idxRel);
16948 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
16949 RelationGetDescr(rel));
16950 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
16951
16952 /*
16953 * Scan the list of existing indexes in the partition-to-be, and mark
16954 * the first matching, unattached one we find, if any, as partition of
16955 * the parent index. If we find one, we're done.
16956 */
16957 for (i = 0; i < list_length(attachRelIdxs); i++)
16958 {
16959 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
16960 Oid cldConstrOid = InvalidOid;
16961
16962 /* does this index have a parent? if so, can't use it */
16963 if (attachrelIdxRels[i]->rd_rel->relispartition)
16964 continue;
16965
16966 if (CompareIndexInfo(attachInfos[i], info,
16967 attachrelIdxRels[i]->rd_indcollation,
16968 idxRel->rd_indcollation,
16969 attachrelIdxRels[i]->rd_opfamily,
16970 idxRel->rd_opfamily,
16971 attmap))
16972 {
16973 /*
16974 * If this index is being created in the parent because of a
16975 * constraint, then the child needs to have a constraint also,
16976 * so look for one. If there is no such constraint, this
16977 * index is no good, so keep looking.
16978 */
16979 if (OidIsValid(constraintOid))
16980 {
16981 cldConstrOid =
16982 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
16983 cldIdxId);
16984 /* no dice */
16985 if (!OidIsValid(cldConstrOid))
16986 continue;
16987 }
16988
16989 /* bingo. */
16990 IndexSetParentIndex(attachrelIdxRels[i], idx);
16991 if (OidIsValid(constraintOid))
16992 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
16993 RelationGetRelid(attachrel));
16994 found = true;
16995
16996 CommandCounterIncrement();
16997 break;
16998 }
16999 }
17000
17001 /*
17002 * If no suitable index was found in the partition-to-be, create one
17003 * now.
17004 */
17005 if (!found)
17006 {
17007 IndexStmt *stmt;
17008 Oid constraintOid;
17009
17010 stmt = generateClonedIndexStmt(NULL,
17011 idxRel, attmap,
17012 &constraintOid);
17013 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
17014 RelationGetRelid(idxRel),
17015 constraintOid,
17016 true, false, false, false, false);
17017 }
17018
17019 index_close(idxRel, AccessShareLock);
17020 }
17021
17022 out:
17023 /* Clean up. */
17024 for (i = 0; i < list_length(attachRelIdxs); i++)
17025 index_close(attachrelIdxRels[i], AccessShareLock);
17026 MemoryContextSwitchTo(oldcxt);
17027 MemoryContextDelete(cxt);
17028 }
17029
17030 /*
17031 * CloneRowTriggersToPartition
17032 * subroutine for ATExecAttachPartition/DefineRelation to create row
17033 * triggers on partitions
17034 */
17035 static void
CloneRowTriggersToPartition(Relation parent,Relation partition)17036 CloneRowTriggersToPartition(Relation parent, Relation partition)
17037 {
17038 Relation pg_trigger;
17039 ScanKeyData key;
17040 SysScanDesc scan;
17041 HeapTuple tuple;
17042 MemoryContext perTupCxt;
17043
17044 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
17045 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
17046 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
17047 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
17048 true, NULL, 1, &key);
17049
17050 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
17051 "clone trig", ALLOCSET_SMALL_SIZES);
17052
17053 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17054 {
17055 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
17056 CreateTrigStmt *trigStmt;
17057 Node *qual = NULL;
17058 Datum value;
17059 bool isnull;
17060 List *cols = NIL;
17061 List *trigargs = NIL;
17062 MemoryContext oldcxt;
17063
17064 /*
17065 * Ignore statement-level triggers; those are not cloned.
17066 */
17067 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
17068 continue;
17069
17070 /*
17071 * Internal triggers require careful examination. Ideally, we don't
17072 * clone them. However, if our parent is itself a partition, there
17073 * might be internal triggers that must not be skipped; for example,
17074 * triggers on our parent that are in turn clones from its parent (our
17075 * grandparent) are marked internal, yet they are to be cloned.
17076 *
17077 * Note we dare not verify that the other trigger belongs to an
17078 * ancestor relation of our parent, because that creates deadlock
17079 * opportunities.
17080 */
17081 if (trigForm->tgisinternal &&
17082 (!parent->rd_rel->relispartition ||
17083 !OidIsValid(trigForm->tgparentid)))
17084 continue;
17085
17086 /*
17087 * Complain if we find an unexpected trigger type.
17088 */
17089 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
17090 !TRIGGER_FOR_AFTER(trigForm->tgtype))
17091 elog(ERROR, "unexpected trigger \"%s\" found",
17092 NameStr(trigForm->tgname));
17093
17094 /* Use short-lived context for CREATE TRIGGER */
17095 oldcxt = MemoryContextSwitchTo(perTupCxt);
17096
17097 /*
17098 * If there is a WHEN clause, generate a 'cooked' version of it that's
17099 * appropriate for the partition.
17100 */
17101 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
17102 RelationGetDescr(pg_trigger), &isnull);
17103 if (!isnull)
17104 {
17105 qual = stringToNode(TextDatumGetCString(value));
17106 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
17107 partition, parent);
17108 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
17109 partition, parent);
17110 }
17111
17112 /*
17113 * If there is a column list, transform it to a list of column names.
17114 * Note we don't need to map this list in any way ...
17115 */
17116 if (trigForm->tgattr.dim1 > 0)
17117 {
17118 int i;
17119
17120 for (i = 0; i < trigForm->tgattr.dim1; i++)
17121 {
17122 Form_pg_attribute col;
17123
17124 col = TupleDescAttr(parent->rd_att,
17125 trigForm->tgattr.values[i] - 1);
17126 cols = lappend(cols,
17127 makeString(pstrdup(NameStr(col->attname))));
17128 }
17129 }
17130
17131 /* Reconstruct trigger arguments list. */
17132 if (trigForm->tgnargs > 0)
17133 {
17134 char *p;
17135
17136 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
17137 RelationGetDescr(pg_trigger), &isnull);
17138 if (isnull)
17139 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
17140 NameStr(trigForm->tgname), RelationGetRelationName(partition));
17141
17142 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
17143
17144 for (int i = 0; i < trigForm->tgnargs; i++)
17145 {
17146 trigargs = lappend(trigargs, makeString(pstrdup(p)));
17147 p += strlen(p) + 1;
17148 }
17149 }
17150
17151 trigStmt = makeNode(CreateTrigStmt);
17152 trigStmt->trigname = NameStr(trigForm->tgname);
17153 trigStmt->relation = NULL;
17154 trigStmt->funcname = NULL; /* passed separately */
17155 trigStmt->args = trigargs;
17156 trigStmt->row = true;
17157 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
17158 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
17159 trigStmt->columns = cols;
17160 trigStmt->whenClause = NULL; /* passed separately */
17161 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
17162 trigStmt->transitionRels = NIL; /* not supported at present */
17163 trigStmt->deferrable = trigForm->tgdeferrable;
17164 trigStmt->initdeferred = trigForm->tginitdeferred;
17165 trigStmt->constrrel = NULL; /* passed separately */
17166
17167 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
17168 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
17169 trigForm->tgfoid, trigForm->oid, qual,
17170 false, true, trigForm->tgenabled);
17171
17172 MemoryContextSwitchTo(oldcxt);
17173 MemoryContextReset(perTupCxt);
17174 }
17175
17176 MemoryContextDelete(perTupCxt);
17177
17178 systable_endscan(scan);
17179 table_close(pg_trigger, RowExclusiveLock);
17180 }
17181
17182 /*
17183 * ALTER TABLE DETACH PARTITION
17184 *
17185 * Return the address of the relation that is no longer a partition of rel.
17186 */
17187 static ObjectAddress
ATExecDetachPartition(Relation rel,RangeVar * name)17188 ATExecDetachPartition(Relation rel, RangeVar *name)
17189 {
17190 Relation partRel,
17191 classRel;
17192 HeapTuple tuple,
17193 newtuple;
17194 Datum new_val[Natts_pg_class];
17195 bool new_null[Natts_pg_class],
17196 new_repl[Natts_pg_class];
17197 ObjectAddress address;
17198 Oid defaultPartOid;
17199 List *indexes;
17200 List *fks;
17201 ListCell *cell;
17202
17203 /*
17204 * We must lock the default partition, because detaching this partition
17205 * will change its partition constraint.
17206 */
17207 defaultPartOid =
17208 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel));
17209 if (OidIsValid(defaultPartOid))
17210 LockRelationOid(defaultPartOid, AccessExclusiveLock);
17211
17212 partRel = table_openrv(name, ShareUpdateExclusiveLock);
17213
17214 /* Ensure that foreign keys still hold after this detach */
17215 ATDetachCheckNoForeignKeyRefs(partRel);
17216
17217 /* All inheritance related checks are performed within the function */
17218 RemoveInheritance(partRel, rel);
17219
17220 /* Update pg_class tuple */
17221 classRel = table_open(RelationRelationId, RowExclusiveLock);
17222 tuple = SearchSysCacheCopy1(RELOID,
17223 ObjectIdGetDatum(RelationGetRelid(partRel)));
17224 if (!HeapTupleIsValid(tuple))
17225 elog(ERROR, "cache lookup failed for relation %u",
17226 RelationGetRelid(partRel));
17227 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
17228
17229 /* Clear relpartbound and reset relispartition */
17230 memset(new_val, 0, sizeof(new_val));
17231 memset(new_null, false, sizeof(new_null));
17232 memset(new_repl, false, sizeof(new_repl));
17233 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
17234 new_null[Anum_pg_class_relpartbound - 1] = true;
17235 new_repl[Anum_pg_class_relpartbound - 1] = true;
17236 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
17237 new_val, new_null, new_repl);
17238
17239 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
17240 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
17241 heap_freetuple(newtuple);
17242
17243 if (OidIsValid(defaultPartOid))
17244 {
17245 /*
17246 * If the relation being detached is the default partition itself,
17247 * remove it from the parent's pg_partitioned_table entry.
17248 *
17249 * If not, we must invalidate default partition's relcache entry, as
17250 * in StorePartitionBound: its partition constraint depends on every
17251 * other partition's partition constraint.
17252 */
17253 if (RelationGetRelid(partRel) == defaultPartOid)
17254 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
17255 else
17256 CacheInvalidateRelcacheByRelid(defaultPartOid);
17257 }
17258
17259 /* detach indexes too */
17260 indexes = RelationGetIndexList(partRel);
17261 foreach(cell, indexes)
17262 {
17263 Oid idxid = lfirst_oid(cell);
17264 Relation idx;
17265 Oid constrOid;
17266
17267 if (!has_superclass(idxid))
17268 continue;
17269
17270 Assert((IndexGetRelation(get_partition_parent(idxid), false) ==
17271 RelationGetRelid(rel)));
17272
17273 idx = index_open(idxid, AccessExclusiveLock);
17274 IndexSetParentIndex(idx, InvalidOid);
17275
17276 /* If there's a constraint associated with the index, detach it too */
17277 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
17278 idxid);
17279 if (OidIsValid(constrOid))
17280 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
17281
17282 index_close(idx, NoLock);
17283 }
17284 table_close(classRel, RowExclusiveLock);
17285
17286 /* Drop any triggers that were cloned on creation/attach. */
17287 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
17288
17289 /*
17290 * Detach any foreign keys that are inherited. This includes creating
17291 * additional action triggers.
17292 */
17293 fks = copyObject(RelationGetFKeyList(partRel));
17294 foreach(cell, fks)
17295 {
17296 ForeignKeyCacheInfo *fk = lfirst(cell);
17297 HeapTuple contup;
17298 Form_pg_constraint conform;
17299 Constraint *fkconstraint;
17300
17301 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
17302 if (!HeapTupleIsValid(contup))
17303 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
17304 conform = (Form_pg_constraint) GETSTRUCT(contup);
17305
17306 /* consider only the inherited foreign keys */
17307 if (conform->contype != CONSTRAINT_FOREIGN ||
17308 !OidIsValid(conform->conparentid))
17309 {
17310 ReleaseSysCache(contup);
17311 continue;
17312 }
17313
17314 /* unset conparentid and adjust conislocal, coninhcount, etc. */
17315 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
17316
17317 /*
17318 * Make the action triggers on the referenced relation. When this was
17319 * a partition the action triggers pointed to the parent rel (they
17320 * still do), but now we need separate ones of our own.
17321 */
17322 fkconstraint = makeNode(Constraint);
17323 fkconstraint->conname = pstrdup(NameStr(conform->conname));
17324 fkconstraint->fk_upd_action = conform->confupdtype;
17325 fkconstraint->fk_del_action = conform->confdeltype;
17326 fkconstraint->deferrable = conform->condeferrable;
17327 fkconstraint->initdeferred = conform->condeferred;
17328
17329 createForeignKeyActionTriggers(partRel, conform->confrelid,
17330 fkconstraint, fk->conoid,
17331 conform->conindid);
17332
17333 ReleaseSysCache(contup);
17334 }
17335 list_free_deep(fks);
17336
17337 /*
17338 * Any sub-constraints that are in the referenced-side of a larger
17339 * constraint have to be removed. This partition is no longer part of the
17340 * key space of the constraint.
17341 */
17342 foreach(cell, GetParentedForeignKeyRefs(partRel))
17343 {
17344 Oid constrOid = lfirst_oid(cell);
17345 ObjectAddress constraint;
17346
17347 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
17348 deleteDependencyRecordsForClass(ConstraintRelationId,
17349 constrOid,
17350 ConstraintRelationId,
17351 DEPENDENCY_INTERNAL);
17352 CommandCounterIncrement();
17353
17354 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
17355 performDeletion(&constraint, DROP_RESTRICT, 0);
17356 }
17357 CommandCounterIncrement();
17358
17359 /*
17360 * Invalidate the parent's relcache so that the partition is no longer
17361 * included in its partition descriptor.
17362 */
17363 CacheInvalidateRelcache(rel);
17364
17365 /*
17366 * If the partition we just detached is partitioned itself, invalidate
17367 * relcache for all descendent partitions too to ensure that their
17368 * rd_partcheck expression trees are rebuilt; must lock partitions
17369 * before doing so, using the same lockmode as what partRel has been
17370 * locked with by the caller.
17371 */
17372 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17373 {
17374 List *children;
17375
17376 children = find_all_inheritors(RelationGetRelid(partRel),
17377 AccessExclusiveLock, NULL);
17378 foreach(cell, children)
17379 {
17380 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
17381 }
17382 }
17383
17384 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
17385
17386 /* keep our lock until commit */
17387 table_close(partRel, NoLock);
17388
17389 return address;
17390 }
17391
17392 /*
17393 * DropClonedTriggersFromPartition
17394 * subroutine for ATExecDetachPartition to remove any triggers that were
17395 * cloned to the partition when it was created-as-partition or attached.
17396 * This undoes what CloneRowTriggersToPartition did.
17397 */
17398 static void
DropClonedTriggersFromPartition(Oid partitionId)17399 DropClonedTriggersFromPartition(Oid partitionId)
17400 {
17401 ScanKeyData skey;
17402 SysScanDesc scan;
17403 HeapTuple trigtup;
17404 Relation tgrel;
17405 ObjectAddresses *objects;
17406
17407 objects = new_object_addresses();
17408
17409 /*
17410 * Scan pg_trigger to search for all triggers on this rel.
17411 */
17412 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
17413 F_OIDEQ, ObjectIdGetDatum(partitionId));
17414 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
17415 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
17416 true, NULL, 1, &skey);
17417 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
17418 {
17419 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
17420 ObjectAddress trig;
17421
17422 /* Ignore triggers that weren't cloned */
17423 if (!OidIsValid(pg_trigger->tgparentid))
17424 continue;
17425
17426 /*
17427 * This is ugly, but necessary: remove the dependency markings on the
17428 * trigger so that it can be removed.
17429 */
17430 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
17431 TriggerRelationId,
17432 DEPENDENCY_PARTITION_PRI);
17433 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
17434 RelationRelationId,
17435 DEPENDENCY_PARTITION_SEC);
17436
17437 /* remember this trigger to remove it below */
17438 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
17439 add_exact_object_address(&trig, objects);
17440 }
17441
17442 /* make the dependency removal visible to the deletion below */
17443 CommandCounterIncrement();
17444 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
17445
17446 /* done */
17447 free_object_addresses(objects);
17448 systable_endscan(scan);
17449 table_close(tgrel, RowExclusiveLock);
17450 }
17451
17452 /*
17453 * Before acquiring lock on an index, acquire the same lock on the owning
17454 * table.
17455 */
17456 struct AttachIndexCallbackState
17457 {
17458 Oid partitionOid;
17459 Oid parentTblOid;
17460 bool lockedParentTbl;
17461 };
17462
17463 static void
RangeVarCallbackForAttachIndex(const RangeVar * rv,Oid relOid,Oid oldRelOid,void * arg)17464 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
17465 void *arg)
17466 {
17467 struct AttachIndexCallbackState *state;
17468 Form_pg_class classform;
17469 HeapTuple tuple;
17470
17471 state = (struct AttachIndexCallbackState *) arg;
17472
17473 if (!state->lockedParentTbl)
17474 {
17475 LockRelationOid(state->parentTblOid, AccessShareLock);
17476 state->lockedParentTbl = true;
17477 }
17478
17479 /*
17480 * If we previously locked some other heap, and the name we're looking up
17481 * no longer refers to an index on that relation, release the now-useless
17482 * lock. XXX maybe we should do *after* we verify whether the index does
17483 * not actually belong to the same relation ...
17484 */
17485 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
17486 {
17487 UnlockRelationOid(state->partitionOid, AccessShareLock);
17488 state->partitionOid = InvalidOid;
17489 }
17490
17491 /* Didn't find a relation, so no need for locking or permission checks. */
17492 if (!OidIsValid(relOid))
17493 return;
17494
17495 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
17496 if (!HeapTupleIsValid(tuple))
17497 return; /* concurrently dropped, so nothing to do */
17498 classform = (Form_pg_class) GETSTRUCT(tuple);
17499 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
17500 classform->relkind != RELKIND_INDEX)
17501 ereport(ERROR,
17502 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17503 errmsg("\"%s\" is not an index", rv->relname)));
17504 ReleaseSysCache(tuple);
17505
17506 /*
17507 * Since we need only examine the heap's tupledesc, an access share lock
17508 * on it (preventing any DDL) is sufficient.
17509 */
17510 state->partitionOid = IndexGetRelation(relOid, false);
17511 LockRelationOid(state->partitionOid, AccessShareLock);
17512 }
17513
17514 /*
17515 * ALTER INDEX i1 ATTACH PARTITION i2
17516 */
17517 static ObjectAddress
ATExecAttachPartitionIdx(List ** wqueue,Relation parentIdx,RangeVar * name)17518 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
17519 {
17520 Relation partIdx;
17521 Relation partTbl;
17522 Relation parentTbl;
17523 ObjectAddress address;
17524 Oid partIdxId;
17525 Oid currParent;
17526 struct AttachIndexCallbackState state;
17527
17528 /*
17529 * We need to obtain lock on the index 'name' to modify it, but we also
17530 * need to read its owning table's tuple descriptor -- so we need to lock
17531 * both. To avoid deadlocks, obtain lock on the table before doing so on
17532 * the index. Furthermore, we need to examine the parent table of the
17533 * partition, so lock that one too.
17534 */
17535 state.partitionOid = InvalidOid;
17536 state.parentTblOid = parentIdx->rd_index->indrelid;
17537 state.lockedParentTbl = false;
17538 partIdxId =
17539 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
17540 RangeVarCallbackForAttachIndex,
17541 (void *) &state);
17542 /* Not there? */
17543 if (!OidIsValid(partIdxId))
17544 ereport(ERROR,
17545 (errcode(ERRCODE_UNDEFINED_OBJECT),
17546 errmsg("index \"%s\" does not exist", name->relname)));
17547
17548 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
17549 partIdx = relation_open(partIdxId, AccessExclusiveLock);
17550
17551 /* we already hold locks on both tables, so this is safe: */
17552 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
17553 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
17554
17555 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
17556
17557 /* Silently do nothing if already in the right state */
17558 currParent = partIdx->rd_rel->relispartition ?
17559 get_partition_parent(partIdxId) : InvalidOid;
17560 if (currParent != RelationGetRelid(parentIdx))
17561 {
17562 IndexInfo *childInfo;
17563 IndexInfo *parentInfo;
17564 AttrMap *attmap;
17565 bool found;
17566 int i;
17567 PartitionDesc partDesc;
17568 Oid constraintOid,
17569 cldConstrId = InvalidOid;
17570
17571 /*
17572 * If this partition already has an index attached, refuse the
17573 * operation.
17574 */
17575 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
17576
17577 if (OidIsValid(currParent))
17578 ereport(ERROR,
17579 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17580 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
17581 RelationGetRelationName(partIdx),
17582 RelationGetRelationName(parentIdx)),
17583 errdetail("Index \"%s\" is already attached to another index.",
17584 RelationGetRelationName(partIdx))));
17585
17586 /* Make sure it indexes a partition of the other index's table */
17587 partDesc = RelationGetPartitionDesc(parentTbl);
17588 found = false;
17589 for (i = 0; i < partDesc->nparts; i++)
17590 {
17591 if (partDesc->oids[i] == state.partitionOid)
17592 {
17593 found = true;
17594 break;
17595 }
17596 }
17597 if (!found)
17598 ereport(ERROR,
17599 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17600 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
17601 RelationGetRelationName(partIdx),
17602 RelationGetRelationName(parentIdx)),
17603 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
17604 RelationGetRelationName(partIdx),
17605 RelationGetRelationName(parentTbl))));
17606
17607 /* Ensure the indexes are compatible */
17608 childInfo = BuildIndexInfo(partIdx);
17609 parentInfo = BuildIndexInfo(parentIdx);
17610 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
17611 RelationGetDescr(parentTbl));
17612 if (!CompareIndexInfo(childInfo, parentInfo,
17613 partIdx->rd_indcollation,
17614 parentIdx->rd_indcollation,
17615 partIdx->rd_opfamily,
17616 parentIdx->rd_opfamily,
17617 attmap))
17618 ereport(ERROR,
17619 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17620 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
17621 RelationGetRelationName(partIdx),
17622 RelationGetRelationName(parentIdx)),
17623 errdetail("The index definitions do not match.")));
17624
17625 /*
17626 * If there is a constraint in the parent, make sure there is one in
17627 * the child too.
17628 */
17629 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
17630 RelationGetRelid(parentIdx));
17631
17632 if (OidIsValid(constraintOid))
17633 {
17634 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
17635 partIdxId);
17636 if (!OidIsValid(cldConstrId))
17637 ereport(ERROR,
17638 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17639 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
17640 RelationGetRelationName(partIdx),
17641 RelationGetRelationName(parentIdx)),
17642 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
17643 RelationGetRelationName(parentIdx),
17644 RelationGetRelationName(parentTbl),
17645 RelationGetRelationName(partIdx))));
17646 }
17647
17648 /* All good -- do it */
17649 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
17650 if (OidIsValid(constraintOid))
17651 ConstraintSetParentConstraint(cldConstrId, constraintOid,
17652 RelationGetRelid(partTbl));
17653
17654 free_attrmap(attmap);
17655
17656 validatePartitionedIndex(parentIdx, parentTbl);
17657 }
17658
17659 relation_close(parentTbl, AccessShareLock);
17660 /* keep these locks till commit */
17661 relation_close(partTbl, NoLock);
17662 relation_close(partIdx, NoLock);
17663
17664 return address;
17665 }
17666
17667 /*
17668 * Verify whether the given partition already contains an index attached
17669 * to the given partitioned index. If so, raise an error.
17670 */
17671 static void
refuseDupeIndexAttach(Relation parentIdx,Relation partIdx,Relation partitionTbl)17672 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
17673 {
17674 Oid existingIdx;
17675
17676 existingIdx = index_get_partition(partitionTbl,
17677 RelationGetRelid(parentIdx));
17678 if (OidIsValid(existingIdx))
17679 ereport(ERROR,
17680 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17681 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
17682 RelationGetRelationName(partIdx),
17683 RelationGetRelationName(parentIdx)),
17684 errdetail("Another index is already attached for partition \"%s\".",
17685 RelationGetRelationName(partitionTbl))));
17686 }
17687
17688 /*
17689 * Verify whether the set of attached partition indexes to a parent index on
17690 * a partitioned table is complete. If it is, mark the parent index valid.
17691 *
17692 * This should be called each time a partition index is attached.
17693 */
17694 static void
validatePartitionedIndex(Relation partedIdx,Relation partedTbl)17695 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
17696 {
17697 Relation inheritsRel;
17698 SysScanDesc scan;
17699 ScanKeyData key;
17700 int tuples = 0;
17701 HeapTuple inhTup;
17702 bool updated = false;
17703
17704 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
17705
17706 /*
17707 * Scan pg_inherits for this parent index. Count each valid index we find
17708 * (verifying the pg_index entry for each), and if we reach the total
17709 * amount we expect, we can mark this parent index as valid.
17710 */
17711 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
17712 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
17713 BTEqualStrategyNumber, F_OIDEQ,
17714 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
17715 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
17716 NULL, 1, &key);
17717 while ((inhTup = systable_getnext(scan)) != NULL)
17718 {
17719 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
17720 HeapTuple indTup;
17721 Form_pg_index indexForm;
17722
17723 indTup = SearchSysCache1(INDEXRELID,
17724 ObjectIdGetDatum(inhForm->inhrelid));
17725 if (!HeapTupleIsValid(indTup))
17726 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
17727 indexForm = (Form_pg_index) GETSTRUCT(indTup);
17728 if (indexForm->indisvalid)
17729 tuples += 1;
17730 ReleaseSysCache(indTup);
17731 }
17732
17733 /* Done with pg_inherits */
17734 systable_endscan(scan);
17735 table_close(inheritsRel, AccessShareLock);
17736
17737 /*
17738 * If we found as many inherited indexes as the partitioned table has
17739 * partitions, we're good; update pg_index to set indisvalid.
17740 */
17741 if (tuples == RelationGetPartitionDesc(partedTbl)->nparts)
17742 {
17743 Relation idxRel;
17744 HeapTuple newtup;
17745
17746 idxRel = table_open(IndexRelationId, RowExclusiveLock);
17747
17748 newtup = heap_copytuple(partedIdx->rd_indextuple);
17749 ((Form_pg_index) GETSTRUCT(newtup))->indisvalid = true;
17750 updated = true;
17751
17752 CatalogTupleUpdate(idxRel, &partedIdx->rd_indextuple->t_self, newtup);
17753
17754 table_close(idxRel, RowExclusiveLock);
17755 }
17756
17757 /*
17758 * If this index is in turn a partition of a larger index, validating it
17759 * might cause the parent to become valid also. Try that.
17760 */
17761 if (updated && partedIdx->rd_rel->relispartition)
17762 {
17763 Oid parentIdxId,
17764 parentTblId;
17765 Relation parentIdx,
17766 parentTbl;
17767
17768 /* make sure we see the validation we just did */
17769 CommandCounterIncrement();
17770
17771 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx));
17772 parentTblId = get_partition_parent(RelationGetRelid(partedTbl));
17773 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
17774 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
17775 Assert(!parentIdx->rd_index->indisvalid);
17776
17777 validatePartitionedIndex(parentIdx, parentTbl);
17778
17779 relation_close(parentIdx, AccessExclusiveLock);
17780 relation_close(parentTbl, AccessExclusiveLock);
17781 }
17782 }
17783
17784 /*
17785 * Return an OID list of constraints that reference the given relation
17786 * that are marked as having a parent constraints.
17787 */
17788 static List *
GetParentedForeignKeyRefs(Relation partition)17789 GetParentedForeignKeyRefs(Relation partition)
17790 {
17791 Relation pg_constraint;
17792 HeapTuple tuple;
17793 SysScanDesc scan;
17794 ScanKeyData key[2];
17795 List *constraints = NIL;
17796
17797 /*
17798 * If no indexes, or no columns are referenceable by FKs, we can avoid the
17799 * scan.
17800 */
17801 if (RelationGetIndexList(partition) == NIL ||
17802 bms_is_empty(RelationGetIndexAttrBitmap(partition,
17803 INDEX_ATTR_BITMAP_KEY)))
17804 return NIL;
17805
17806 /* Search for constraints referencing this table */
17807 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17808 ScanKeyInit(&key[0],
17809 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
17810 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
17811 ScanKeyInit(&key[1],
17812 Anum_pg_constraint_contype, BTEqualStrategyNumber,
17813 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
17814
17815 /* XXX This is a seqscan, as we don't have a usable index */
17816 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
17817 while ((tuple = systable_getnext(scan)) != NULL)
17818 {
17819 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
17820
17821 /*
17822 * We only need to process constraints that are part of larger ones.
17823 */
17824 if (!OidIsValid(constrForm->conparentid))
17825 continue;
17826
17827 constraints = lappend_oid(constraints, constrForm->oid);
17828 }
17829
17830 systable_endscan(scan);
17831 table_close(pg_constraint, AccessShareLock);
17832
17833 return constraints;
17834 }
17835
17836 /*
17837 * During DETACH PARTITION, verify that any foreign keys pointing to the
17838 * partitioned table would not become invalid. An error is raised if any
17839 * referenced values exist.
17840 */
17841 static void
ATDetachCheckNoForeignKeyRefs(Relation partition)17842 ATDetachCheckNoForeignKeyRefs(Relation partition)
17843 {
17844 List *constraints;
17845 ListCell *cell;
17846
17847 constraints = GetParentedForeignKeyRefs(partition);
17848
17849 foreach(cell, constraints)
17850 {
17851 Oid constrOid = lfirst_oid(cell);
17852 HeapTuple tuple;
17853 Form_pg_constraint constrForm;
17854 Relation rel;
17855 Trigger trig;
17856
17857 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
17858 if (!HeapTupleIsValid(tuple))
17859 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
17860 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
17861
17862 Assert(OidIsValid(constrForm->conparentid));
17863 Assert(constrForm->confrelid == RelationGetRelid(partition));
17864
17865 /* prevent data changes into the referencing table until commit */
17866 rel = table_open(constrForm->conrelid, ShareLock);
17867
17868 MemSet(&trig, 0, sizeof(trig));
17869 trig.tgoid = InvalidOid;
17870 trig.tgname = NameStr(constrForm->conname);
17871 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
17872 trig.tgisinternal = true;
17873 trig.tgconstrrelid = RelationGetRelid(partition);
17874 trig.tgconstrindid = constrForm->conindid;
17875 trig.tgconstraint = constrForm->oid;
17876 trig.tgdeferrable = false;
17877 trig.tginitdeferred = false;
17878 /* we needn't fill in remaining fields */
17879
17880 RI_PartitionRemove_Check(&trig, rel, partition);
17881
17882 ReleaseSysCache(tuple);
17883
17884 table_close(rel, NoLock);
17885 }
17886 }
17887