1 /*-------------------------------------------------------------------------
2 *
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
5 *
6 * Portions Copyright (c) 1996-2018, 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/genam.h"
18 #include "access/heapam.h"
19 #include "access/heapam_xlog.h"
20 #include "access/multixact.h"
21 #include "access/reloptions.h"
22 #include "access/relscan.h"
23 #include "access/sysattr.h"
24 #include "access/tupconvert.h"
25 #include "access/xact.h"
26 #include "access/xlog.h"
27 #include "catalog/catalog.h"
28 #include "catalog/dependency.h"
29 #include "catalog/heap.h"
30 #include "catalog/index.h"
31 #include "catalog/indexing.h"
32 #include "catalog/namespace.h"
33 #include "catalog/objectaccess.h"
34 #include "catalog/partition.h"
35 #include "catalog/pg_am.h"
36 #include "catalog/pg_collation.h"
37 #include "catalog/pg_constraint.h"
38 #include "catalog/pg_depend.h"
39 #include "catalog/pg_foreign_table.h"
40 #include "catalog/pg_inherits.h"
41 #include "catalog/pg_namespace.h"
42 #include "catalog/pg_opclass.h"
43 #include "catalog/pg_tablespace.h"
44 #include "catalog/pg_trigger.h"
45 #include "catalog/pg_type.h"
46 #include "catalog/storage.h"
47 #include "catalog/storage_xlog.h"
48 #include "catalog/toasting.h"
49 #include "commands/cluster.h"
50 #include "commands/comment.h"
51 #include "commands/defrem.h"
52 #include "commands/event_trigger.h"
53 #include "commands/policy.h"
54 #include "commands/sequence.h"
55 #include "commands/tablecmds.h"
56 #include "commands/tablespace.h"
57 #include "commands/trigger.h"
58 #include "commands/typecmds.h"
59 #include "commands/user.h"
60 #include "executor/executor.h"
61 #include "foreign/foreign.h"
62 #include "miscadmin.h"
63 #include "nodes/makefuncs.h"
64 #include "nodes/nodeFuncs.h"
65 #include "nodes/parsenodes.h"
66 #include "optimizer/clauses.h"
67 #include "optimizer/planner.h"
68 #include "optimizer/predtest.h"
69 #include "optimizer/prep.h"
70 #include "optimizer/var.h"
71 #include "parser/parse_clause.h"
72 #include "parser/parse_coerce.h"
73 #include "parser/parse_collate.h"
74 #include "parser/parse_expr.h"
75 #include "parser/parse_oper.h"
76 #include "parser/parse_relation.h"
77 #include "parser/parse_type.h"
78 #include "parser/parse_utilcmd.h"
79 #include "parser/parser.h"
80 #include "partitioning/partbounds.h"
81 #include "pgstat.h"
82 #include "rewrite/rewriteDefine.h"
83 #include "rewrite/rewriteHandler.h"
84 #include "rewrite/rewriteManip.h"
85 #include "storage/bufmgr.h"
86 #include "storage/lmgr.h"
87 #include "storage/lock.h"
88 #include "storage/predicate.h"
89 #include "storage/smgr.h"
90 #include "utils/acl.h"
91 #include "utils/builtins.h"
92 #include "utils/fmgroids.h"
93 #include "utils/inval.h"
94 #include "utils/lsyscache.h"
95 #include "utils/memutils.h"
96 #include "utils/partcache.h"
97 #include "utils/relcache.h"
98 #include "utils/ruleutils.h"
99 #include "utils/snapmgr.h"
100 #include "utils/syscache.h"
101 #include "utils/tqual.h"
102 #include "utils/typcache.h"
103
104
105 /*
106 * ON COMMIT action list
107 */
108 typedef struct OnCommitItem
109 {
110 Oid relid; /* relid of relation */
111 OnCommitAction oncommit; /* what to do at end of xact */
112
113 /*
114 * If this entry was created during the current transaction,
115 * creating_subid is the ID of the creating subxact; if created in a prior
116 * transaction, creating_subid is zero. If deleted during the current
117 * transaction, deleting_subid is the ID of the deleting subxact; if no
118 * deletion request is pending, deleting_subid is zero.
119 */
120 SubTransactionId creating_subid;
121 SubTransactionId deleting_subid;
122 } OnCommitItem;
123
124 static List *on_commits = NIL;
125
126
127 /*
128 * State information for ALTER TABLE
129 *
130 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
131 * structs, one for each table modified by the operation (the named table
132 * plus any child tables that are affected). We save lists of subcommands
133 * to apply to this table (possibly modified by parse transformation steps);
134 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
135 * necessary information is stored in the constraints and newvals lists.
136 *
137 * Phase 2 is divided into multiple passes; subcommands are executed in
138 * a pass determined by subcommand type.
139 */
140
141 #define AT_PASS_UNSET -1 /* UNSET will cause ERROR */
142 #define AT_PASS_DROP 0 /* DROP (all flavors) */
143 #define AT_PASS_ALTER_TYPE 1 /* ALTER COLUMN TYPE */
144 #define AT_PASS_OLD_INDEX 2 /* re-add existing indexes */
145 #define AT_PASS_OLD_CONSTR 3 /* re-add existing constraints */
146 #define AT_PASS_COL_ATTRS 4 /* set other column attributes */
147 /* We could support a RENAME COLUMN pass here, but not currently used */
148 #define AT_PASS_ADD_COL 5 /* ADD COLUMN */
149 #define AT_PASS_ADD_INDEX 6 /* ADD indexes */
150 #define AT_PASS_ADD_CONSTR 7 /* ADD constraints, defaults */
151 #define AT_PASS_MISC 8 /* other stuff */
152 #define AT_NUM_PASSES 9
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 bool new_notnull; /* T if we added new NOT NULL constraints */
166 int rewrite; /* Reason for forced rewrite, if any */
167 Oid newTableSpace; /* new tablespace; 0 means no change */
168 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
169 char newrelpersistence; /* if above is true */
170 Expr *partition_constraint; /* for attach partition validation */
171 /* true, if validating default due to some other attach/detach */
172 bool validate_default;
173 /* Objects to rebuild after completing ALTER TYPE operations */
174 List *changedConstraintOids; /* OIDs of constraints to rebuild */
175 List *changedConstraintDefs; /* string definitions of same */
176 List *changedIndexOids; /* OIDs of indexes to rebuild */
177 List *changedIndexDefs; /* string definitions of same */
178 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
179 char *clusterOnIndex; /* index to use for CLUSTER */
180 } AlteredTableInfo;
181
182 /* Struct describing one new constraint to check in Phase 3 scan */
183 /* Note: new NOT NULL constraints are handled elsewhere */
184 typedef struct NewConstraint
185 {
186 char *name; /* Constraint name, or NULL if none */
187 ConstrType contype; /* CHECK or FOREIGN */
188 Oid refrelid; /* PK rel, if FOREIGN */
189 Oid refindid; /* OID of PK's index, if FOREIGN */
190 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
191 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
192 ExprState *qualstate; /* Execution state for CHECK expr */
193 } NewConstraint;
194
195 /*
196 * Struct describing one new column value that needs to be computed during
197 * Phase 3 copy (this could be either a new column with a non-null default, or
198 * a column that we're changing the type of). Columns without such an entry
199 * are just copied from the old table during ATRewriteTable. Note that the
200 * expr is an expression over *old* table values.
201 */
202 typedef struct NewColumnValue
203 {
204 AttrNumber attnum; /* which column */
205 Expr *expr; /* expression to compute */
206 ExprState *exprstate; /* execution state */
207 } NewColumnValue;
208
209 /*
210 * Error-reporting support for RemoveRelations
211 */
212 struct dropmsgstrings
213 {
214 char kind;
215 int nonexistent_code;
216 const char *nonexistent_msg;
217 const char *skipping_msg;
218 const char *nota_msg;
219 const char *drophint_msg;
220 };
221
222 static const struct dropmsgstrings dropmsgstringarray[] = {
223 {RELKIND_RELATION,
224 ERRCODE_UNDEFINED_TABLE,
225 gettext_noop("table \"%s\" does not exist"),
226 gettext_noop("table \"%s\" does not exist, skipping"),
227 gettext_noop("\"%s\" is not a table"),
228 gettext_noop("Use DROP TABLE to remove a table.")},
229 {RELKIND_SEQUENCE,
230 ERRCODE_UNDEFINED_TABLE,
231 gettext_noop("sequence \"%s\" does not exist"),
232 gettext_noop("sequence \"%s\" does not exist, skipping"),
233 gettext_noop("\"%s\" is not a sequence"),
234 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
235 {RELKIND_VIEW,
236 ERRCODE_UNDEFINED_TABLE,
237 gettext_noop("view \"%s\" does not exist"),
238 gettext_noop("view \"%s\" does not exist, skipping"),
239 gettext_noop("\"%s\" is not a view"),
240 gettext_noop("Use DROP VIEW to remove a view.")},
241 {RELKIND_MATVIEW,
242 ERRCODE_UNDEFINED_TABLE,
243 gettext_noop("materialized view \"%s\" does not exist"),
244 gettext_noop("materialized view \"%s\" does not exist, skipping"),
245 gettext_noop("\"%s\" is not a materialized view"),
246 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
247 {RELKIND_INDEX,
248 ERRCODE_UNDEFINED_OBJECT,
249 gettext_noop("index \"%s\" does not exist"),
250 gettext_noop("index \"%s\" does not exist, skipping"),
251 gettext_noop("\"%s\" is not an index"),
252 gettext_noop("Use DROP INDEX to remove an index.")},
253 {RELKIND_COMPOSITE_TYPE,
254 ERRCODE_UNDEFINED_OBJECT,
255 gettext_noop("type \"%s\" does not exist"),
256 gettext_noop("type \"%s\" does not exist, skipping"),
257 gettext_noop("\"%s\" is not a type"),
258 gettext_noop("Use DROP TYPE to remove a type.")},
259 {RELKIND_FOREIGN_TABLE,
260 ERRCODE_UNDEFINED_OBJECT,
261 gettext_noop("foreign table \"%s\" does not exist"),
262 gettext_noop("foreign table \"%s\" does not exist, skipping"),
263 gettext_noop("\"%s\" is not a foreign table"),
264 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
265 {RELKIND_PARTITIONED_TABLE,
266 ERRCODE_UNDEFINED_TABLE,
267 gettext_noop("table \"%s\" does not exist"),
268 gettext_noop("table \"%s\" does not exist, skipping"),
269 gettext_noop("\"%s\" is not a table"),
270 gettext_noop("Use DROP TABLE to remove a table.")},
271 {RELKIND_PARTITIONED_INDEX,
272 ERRCODE_UNDEFINED_OBJECT,
273 gettext_noop("index \"%s\" does not exist"),
274 gettext_noop("index \"%s\" does not exist, skipping"),
275 gettext_noop("\"%s\" is not an index"),
276 gettext_noop("Use DROP INDEX to remove an index.")},
277 {'\0', 0, NULL, NULL, NULL, NULL}
278 };
279
280 struct DropRelationCallbackState
281 {
282 char relkind;
283 Oid heapOid;
284 Oid partParentOid;
285 bool concurrent;
286 };
287
288 /* Alter table target-type flags for ATSimplePermissions */
289 #define ATT_TABLE 0x0001
290 #define ATT_VIEW 0x0002
291 #define ATT_MATVIEW 0x0004
292 #define ATT_INDEX 0x0008
293 #define ATT_COMPOSITE_TYPE 0x0010
294 #define ATT_FOREIGN_TABLE 0x0020
295 #define ATT_PARTITIONED_INDEX 0x0040
296
297 /*
298 * Partition tables are expected to be dropped when the parent partitioned
299 * table gets dropped. Hence for partitioning we use AUTO dependency.
300 * Otherwise, for regular inheritance use NORMAL dependency.
301 */
302 #define child_dependency_type(child_is_partition) \
303 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
304
305 static void truncate_check_rel(Relation rel);
306 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
307 bool is_partition, List **supOids, List **supconstr,
308 int *supOidCount);
309 static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
310 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
311 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
312 static void StoreCatalogInheritance(Oid relationId, List *supers,
313 bool child_is_partition);
314 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
315 int32 seqNumber, Relation inhRelation,
316 bool child_is_partition);
317 static int findAttrByName(const char *attributeName, List *schema);
318 static void AlterIndexNamespaces(Relation classRel, Relation rel,
319 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
320 static void AlterSeqNamespaces(Relation classRel, Relation rel,
321 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
322 LOCKMODE lockmode);
323 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
324 bool recurse, bool recursing, LOCKMODE lockmode);
325 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel,
326 Relation tgrel, Relation rel, HeapTuple contuple,
327 List **otherrelids, LOCKMODE lockmode);
328 static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel,
329 char *constrName, bool recurse, bool recursing,
330 LOCKMODE lockmode);
331 static int transformColumnNameList(Oid relId, List *colList,
332 int16 *attnums, Oid *atttypids);
333 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
334 List **attnamelist,
335 int16 *attnums, Oid *atttypids,
336 Oid *opclasses);
337 static Oid transformFkeyCheckAttrs(Relation pkrel,
338 int numattrs, int16 *attnums,
339 Oid *opclasses);
340 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
341 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
342 Oid *funcid);
343 static void validateForeignKeyConstraint(char *conname,
344 Relation rel, Relation pkrel,
345 Oid pkindOid, Oid constraintOid);
346 static void ATController(AlterTableStmt *parsetree,
347 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
348 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
349 bool recurse, bool recursing, LOCKMODE lockmode);
350 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode);
351 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
352 AlterTableCmd *cmd, LOCKMODE lockmode);
353 static void ATRewriteTables(AlterTableStmt *parsetree,
354 List **wqueue, LOCKMODE lockmode);
355 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
356 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
357 static void ATSimplePermissions(Relation rel, int allowed_targets);
358 static void ATWrongRelkindError(Relation rel, int allowed_targets);
359 static void ATSimpleRecursion(List **wqueue, Relation rel,
360 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
361 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
362 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
363 LOCKMODE lockmode);
364 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
365 DropBehavior behavior);
366 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
367 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode);
368 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
369 Relation rel, ColumnDef *colDef, bool isOid,
370 bool recurse, bool recursing,
371 bool if_not_exists, LOCKMODE lockmode);
372 static bool check_for_column_name_collision(Relation rel, const char *colname,
373 bool if_not_exists);
374 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
375 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
376 static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
377 AlterTableCmd *cmd, LOCKMODE lockmode);
378 static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
379 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
380 static void ATPrepSetNotNull(Relation rel, bool recurse, bool recursing);
381 static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
382 const char *colName, LOCKMODE lockmode);
383 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
384 Node *newDefault, LOCKMODE lockmode);
385 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
386 Node *def, LOCKMODE lockmode);
387 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
388 Node *def, LOCKMODE lockmode);
389 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
390 static void ATPrepSetStatistics(Relation rel, const char *colName, int16 colNum,
391 Node *newValue, LOCKMODE lockmode);
392 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
393 Node *newValue, LOCKMODE lockmode);
394 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
395 Node *options, bool isReset, LOCKMODE lockmode);
396 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
397 Node *newValue, LOCKMODE lockmode);
398 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
399 AlterTableCmd *cmd, LOCKMODE lockmode);
400 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
401 DropBehavior behavior,
402 bool recurse, bool recursing,
403 bool missing_ok, LOCKMODE lockmode);
404 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
405 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
406 static ObjectAddress ATExecAddConstraint(List **wqueue,
407 AlteredTableInfo *tab, Relation rel,
408 Constraint *newConstraint, bool recurse, bool is_readd,
409 LOCKMODE lockmode);
410 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
411 IndexStmt *stmt, LOCKMODE lockmode);
412 static ObjectAddress ATAddCheckConstraint(List **wqueue,
413 AlteredTableInfo *tab, Relation rel,
414 Constraint *constr,
415 bool recurse, bool recursing, bool is_readd,
416 LOCKMODE lockmode);
417 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
418 Relation rel, Constraint *fkconstraint, Oid parentConstr,
419 bool recurse, bool recursing,
420 LOCKMODE lockmode);
421 static void CloneFkReferencing(Relation pg_constraint, Relation parentRel,
422 Relation partRel, List *clone, List **cloned);
423 static void ATExecDropConstraint(Relation rel, const char *constrName,
424 DropBehavior behavior,
425 bool recurse, bool recursing,
426 bool missing_ok, LOCKMODE lockmode);
427 static void ATPrepAlterColumnType(List **wqueue,
428 AlteredTableInfo *tab, Relation rel,
429 bool recurse, bool recursing,
430 AlterTableCmd *cmd, LOCKMODE lockmode);
431 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
432 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
433 AlterTableCmd *cmd, LOCKMODE lockmode);
434 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
435 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
436 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
437 List *options, LOCKMODE lockmode);
438 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
439 LOCKMODE lockmode);
440 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
441 char *cmd, List **wqueue, LOCKMODE lockmode,
442 bool rewrite);
443 static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
444 Oid objid, Relation rel, List *domname,
445 const char *conname);
446 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
447 static void TryReuseForeignKey(Oid oldId, Constraint *con);
448 static void change_owner_fix_column_acls(Oid relationOid,
449 Oid oldOwnerId, Oid newOwnerId);
450 static void change_owner_recurse_to_sequences(Oid relationOid,
451 Oid newOwnerId, LOCKMODE lockmode);
452 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
453 LOCKMODE lockmode);
454 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
455 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
456 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
457 const char *tablespacename, LOCKMODE lockmode);
458 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
459 static void ATExecPartedIdxSetTableSpace(Relation rel, Oid newTableSpace);
460 static void ATExecSetRelOptions(Relation rel, List *defList,
461 AlterTableType operation,
462 LOCKMODE lockmode);
463 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
464 char fires_when, bool skip_system, LOCKMODE lockmode);
465 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
466 char fires_when, LOCKMODE lockmode);
467 static void ATPrepAddInherit(Relation child_rel);
468 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
469 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
470 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
471 DependencyType deptype);
472 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
473 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
474 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
475 static void ATExecGenericOptions(Relation rel, List *options);
476 static void ATExecEnableRowSecurity(Relation rel);
477 static void ATExecDisableRowSecurity(Relation rel);
478 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
479
480 static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
481 ForkNumber forkNum, char relpersistence);
482 static const char *storage_name(char c);
483
484 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
485 Oid oldRelOid, void *arg);
486 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
487 Oid oldrelid, void *arg);
488 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
489 static void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
490 List **partexprs, Oid *partopclass, Oid *partcollation, char strategy);
491 static void CreateInheritance(Relation child_rel, Relation parent_rel);
492 static void RemoveInheritance(Relation child_rel, Relation parent_rel);
493 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
494 PartitionCmd *cmd);
495 static void AttachPartitionEnsureIndexes(Relation rel, Relation attachrel);
496 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
497 List *partConstraint,
498 bool validate_default);
499 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
500 static void DropClonedTriggersFromPartition(Oid partitionId);
501 static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
502 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation rel,
503 RangeVar *name);
504 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
505 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
506 Relation partitionTbl);
507
508
509 /* ----------------------------------------------------------------
510 * DefineRelation
511 * Creates a new relation.
512 *
513 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
514 * The other arguments are used to extend the behavior for other cases:
515 * relkind: relkind to assign to the new relation
516 * ownerId: if not InvalidOid, use this as the new relation's owner.
517 * typaddress: if not null, it's set to the pg_type entry's address.
518 * queryString: for error reporting
519 *
520 * Note that permissions checks are done against current user regardless of
521 * ownerId. A nonzero ownerId is used when someone is creating a relation
522 * "on behalf of" someone else, so we still want to see that the current user
523 * has permissions to do it.
524 *
525 * If successful, returns the address of the new relation.
526 * ----------------------------------------------------------------
527 */
528 ObjectAddress
DefineRelation(CreateStmt * stmt,char relkind,Oid ownerId,ObjectAddress * typaddress,const char * queryString)529 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
530 ObjectAddress *typaddress, const char *queryString)
531 {
532 char relname[NAMEDATALEN];
533 Oid namespaceId;
534 Oid relationId;
535 Oid tablespaceId;
536 Relation rel;
537 TupleDesc descriptor;
538 List *inheritOids;
539 List *old_constraints;
540 bool localHasOids;
541 int parentOidCount;
542 List *rawDefaults;
543 List *cookedDefaults;
544 Datum reloptions;
545 ListCell *listptr;
546 AttrNumber attnum;
547 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
548 Oid ofTypeId;
549 ObjectAddress address;
550
551 /*
552 * Truncate relname to appropriate length (probably a waste of time, as
553 * parser should have done this already).
554 */
555 StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
556
557 /*
558 * Check consistency of arguments
559 */
560 if (stmt->oncommit != ONCOMMIT_NOOP
561 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
562 ereport(ERROR,
563 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
564 errmsg("ON COMMIT can only be used on temporary tables")));
565
566 if (stmt->partspec != NULL)
567 {
568 if (relkind != RELKIND_RELATION)
569 elog(ERROR, "unexpected relkind: %d", (int) relkind);
570
571 relkind = RELKIND_PARTITIONED_TABLE;
572 }
573
574 /*
575 * Look up the namespace in which we are supposed to create the relation,
576 * check we have permission to create there, lock it against concurrent
577 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
578 * namespace is selected.
579 */
580 namespaceId =
581 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
582
583 /*
584 * Security check: disallow creating temp tables from security-restricted
585 * code. This is needed because calling code might not expect untrusted
586 * tables to appear in pg_temp at the front of its search path.
587 */
588 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
589 && InSecurityRestrictedOperation())
590 ereport(ERROR,
591 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
592 errmsg("cannot create temporary table within security-restricted operation")));
593
594 /*
595 * Select tablespace to use. If not specified, use default tablespace
596 * (which may in turn default to database's default).
597 */
598 if (stmt->tablespacename)
599 {
600 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
601 }
602 else
603 {
604 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence);
605 /* note InvalidOid is OK in this case */
606 }
607
608 /* Check permissions except when using database's default */
609 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
610 {
611 AclResult aclresult;
612
613 aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
614 ACL_CREATE);
615 if (aclresult != ACLCHECK_OK)
616 aclcheck_error(aclresult, OBJECT_TABLESPACE,
617 get_tablespace_name(tablespaceId));
618 }
619
620 /* In all cases disallow placing user relations in pg_global */
621 if (tablespaceId == GLOBALTABLESPACE_OID)
622 ereport(ERROR,
623 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
624 errmsg("only shared relations can be placed in pg_global tablespace")));
625
626 /* Identify user ID that will own the table */
627 if (!OidIsValid(ownerId))
628 ownerId = GetUserId();
629
630 /*
631 * Parse and validate reloptions, if any.
632 */
633 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
634 true, false);
635
636 if (relkind == RELKIND_VIEW)
637 (void) view_reloptions(reloptions, true);
638 else
639 (void) heap_reloptions(relkind, reloptions, true);
640
641 if (stmt->ofTypename)
642 {
643 AclResult aclresult;
644
645 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
646
647 aclresult = pg_type_aclcheck(ofTypeId, GetUserId(), ACL_USAGE);
648 if (aclresult != ACLCHECK_OK)
649 aclcheck_error_type(aclresult, ofTypeId);
650 }
651 else
652 ofTypeId = InvalidOid;
653
654 /*
655 * Look up inheritance ancestors and generate relation schema, including
656 * inherited attributes. (Note that stmt->tableElts is destructively
657 * modified by MergeAttributes.)
658 */
659 stmt->tableElts =
660 MergeAttributes(stmt->tableElts, stmt->inhRelations,
661 stmt->relation->relpersistence,
662 stmt->partbound != NULL,
663 &inheritOids, &old_constraints, &parentOidCount);
664
665 /*
666 * Create a tuple descriptor from the relation schema. Note that this
667 * deals with column names, types, and NOT NULL constraints, but not
668 * default values or CHECK constraints; we handle those below.
669 */
670 descriptor = BuildDescForRelation(stmt->tableElts);
671
672 /*
673 * Notice that we allow OIDs here only for plain tables and partitioned
674 * tables, even though some other relkinds can support them. This is
675 * necessary because the default_with_oids GUC must apply only to plain
676 * tables and not any other relkind; doing otherwise would break existing
677 * pg_dump files. We could allow explicit "WITH OIDS" while not allowing
678 * default_with_oids to affect other relkinds, but it would complicate
679 * interpretOidsOption().
680 */
681 localHasOids = interpretOidsOption(stmt->options,
682 (relkind == RELKIND_RELATION ||
683 relkind == RELKIND_PARTITIONED_TABLE));
684 descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
685
686 /*
687 * If a partitioned table doesn't have the system OID column, then none of
688 * its partitions should have it.
689 */
690 if (stmt->partbound && parentOidCount == 0 && localHasOids)
691 ereport(ERROR,
692 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
693 errmsg("cannot create table with OIDs as partition of table without OIDs")));
694
695 /*
696 * Find columns with default values and prepare for insertion of the
697 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
698 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
699 * while raw defaults go into a list of RawColumnDefault structs that will
700 * be processed by AddRelationNewConstraints. (We can't deal with raw
701 * expressions until we can do transformExpr.)
702 *
703 * We can set the atthasdef flags now in the tuple descriptor; this just
704 * saves StoreAttrDefault from having to do an immediate update of the
705 * pg_attribute rows.
706 */
707 rawDefaults = NIL;
708 cookedDefaults = NIL;
709 attnum = 0;
710
711 foreach(listptr, stmt->tableElts)
712 {
713 ColumnDef *colDef = lfirst(listptr);
714 Form_pg_attribute attr;
715
716 attnum++;
717 attr = TupleDescAttr(descriptor, attnum - 1);
718
719 if (colDef->raw_default != NULL)
720 {
721 RawColumnDefault *rawEnt;
722
723 Assert(colDef->cooked_default == NULL);
724
725 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
726 rawEnt->attnum = attnum;
727 rawEnt->raw_default = colDef->raw_default;
728 rawEnt->missingMode = false;
729 rawDefaults = lappend(rawDefaults, rawEnt);
730 attr->atthasdef = true;
731 }
732 else if (colDef->cooked_default != NULL)
733 {
734 CookedConstraint *cooked;
735
736 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
737 cooked->contype = CONSTR_DEFAULT;
738 cooked->conoid = InvalidOid; /* until created */
739 cooked->name = NULL;
740 cooked->attnum = attnum;
741 cooked->expr = colDef->cooked_default;
742 cooked->skip_validation = false;
743 cooked->is_local = true; /* not used for defaults */
744 cooked->inhcount = 0; /* ditto */
745 cooked->is_no_inherit = false;
746 cookedDefaults = lappend(cookedDefaults, cooked);
747 attr->atthasdef = true;
748 }
749
750 if (colDef->identity)
751 attr->attidentity = colDef->identity;
752 }
753
754 /*
755 * Create the relation. Inherited defaults and constraints are passed in
756 * for immediate handling --- since they don't need parsing, they can be
757 * stored immediately.
758 */
759 relationId = heap_create_with_catalog(relname,
760 namespaceId,
761 tablespaceId,
762 InvalidOid,
763 InvalidOid,
764 ofTypeId,
765 ownerId,
766 descriptor,
767 list_concat(cookedDefaults,
768 old_constraints),
769 relkind,
770 stmt->relation->relpersistence,
771 false,
772 false,
773 localHasOids,
774 parentOidCount,
775 stmt->oncommit,
776 reloptions,
777 true,
778 allowSystemTableMods,
779 false,
780 InvalidOid,
781 typaddress);
782
783 /*
784 * We must bump the command counter to make the newly-created relation
785 * tuple visible for opening.
786 */
787 CommandCounterIncrement();
788
789 /*
790 * Open the new relation and acquire exclusive lock on it. This isn't
791 * really necessary for locking out other backends (since they can't see
792 * the new rel anyway until we commit), but it keeps the lock manager from
793 * complaining about deadlock risks.
794 */
795 rel = relation_open(relationId, AccessExclusiveLock);
796
797 /* Process and store partition bound, if any. */
798 if (stmt->partbound)
799 {
800 PartitionBoundSpec *bound;
801 ParseState *pstate;
802 Oid parentId = linitial_oid(inheritOids),
803 defaultPartOid;
804 Relation parent,
805 defaultRel = NULL;
806
807 /* Already have strong enough lock on the parent */
808 parent = heap_open(parentId, NoLock);
809
810 /*
811 * We are going to try to validate the partition bound specification
812 * against the partition key of parentRel, so it better have one.
813 */
814 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
815 ereport(ERROR,
816 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
817 errmsg("\"%s\" is not partitioned",
818 RelationGetRelationName(parent))));
819
820 /*
821 * The partition constraint of the default partition depends on the
822 * partition bounds of every other partition. It is possible that
823 * another backend might be about to execute a query on the default
824 * partition table, and that the query relies on previously cached
825 * default partition constraints. We must therefore take a table lock
826 * strong enough to prevent all queries on the default partition from
827 * proceeding until we commit and send out a shared-cache-inval notice
828 * that will make them update their index lists.
829 *
830 * Order of locking: The relation being added won't be visible to
831 * other backends until it is committed, hence here in
832 * DefineRelation() the order of locking the default partition and the
833 * relation being added does not matter. But at all other places we
834 * need to lock the default relation before we lock the relation being
835 * added or removed i.e. we should take the lock in same order at all
836 * the places such that lock parent, lock default partition and then
837 * lock the partition so as to avoid a deadlock.
838 */
839 defaultPartOid =
840 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent));
841 if (OidIsValid(defaultPartOid))
842 defaultRel = heap_open(defaultPartOid, AccessExclusiveLock);
843
844 /* Tranform the bound values */
845 pstate = make_parsestate(NULL);
846 pstate->p_sourcetext = queryString;
847
848 bound = transformPartitionBound(pstate, parent, stmt->partbound);
849
850 /*
851 * Check first that the new partition's bound is valid and does not
852 * overlap with any of existing partitions of the parent.
853 */
854 check_new_partition_bound(relname, parent, bound);
855
856 /*
857 * If the default partition exists, its partition constraints will
858 * change after the addition of this new partition such that it won't
859 * allow any row that qualifies for this new partition. So, check that
860 * the existing data in the default partition satisfies the constraint
861 * as it will exist after adding this partition.
862 */
863 if (OidIsValid(defaultPartOid))
864 {
865 check_default_partition_contents(parent, defaultRel, bound);
866 /* Keep the lock until commit. */
867 heap_close(defaultRel, NoLock);
868 }
869
870 /* Update the pg_class entry. */
871 StorePartitionBound(rel, parent, bound);
872
873 heap_close(parent, NoLock);
874 }
875
876 /* Store inheritance information for new rel. */
877 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
878
879 /*
880 * Process the partitioning specification (if any) and store the partition
881 * key information into the catalog.
882 */
883 if (stmt->partspec)
884 {
885 char strategy;
886 int partnatts;
887 AttrNumber partattrs[PARTITION_MAX_KEYS];
888 Oid partopclass[PARTITION_MAX_KEYS];
889 Oid partcollation[PARTITION_MAX_KEYS];
890 List *partexprs = NIL;
891
892 partnatts = list_length(stmt->partspec->partParams);
893
894 /* Protect fixed-size arrays here and in executor */
895 if (partnatts > PARTITION_MAX_KEYS)
896 ereport(ERROR,
897 (errcode(ERRCODE_TOO_MANY_COLUMNS),
898 errmsg("cannot partition using more than %d columns",
899 PARTITION_MAX_KEYS)));
900
901 /*
902 * We need to transform the raw parsetrees corresponding to partition
903 * expressions into executable expression trees. Like column defaults
904 * and CHECK constraints, we could not have done the transformation
905 * earlier.
906 */
907 stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
908 &strategy);
909
910 ComputePartitionAttrs(rel, stmt->partspec->partParams,
911 partattrs, &partexprs, partopclass,
912 partcollation, strategy);
913
914 StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
915 partopclass, partcollation);
916
917 /* make it all visible */
918 CommandCounterIncrement();
919 }
920
921 /*
922 * If we're creating a partition, create now all the indexes, triggers,
923 * FKs defined in the parent.
924 *
925 * We can't do it earlier, because DefineIndex wants to know the partition
926 * key which we just stored.
927 */
928 if (stmt->partbound)
929 {
930 Oid parentId = linitial_oid(inheritOids);
931 Relation parent;
932 List *idxlist;
933 ListCell *cell;
934
935 /* Already have strong enough lock on the parent */
936 parent = heap_open(parentId, NoLock);
937 idxlist = RelationGetIndexList(parent);
938
939 /*
940 * For each index in the parent table, create one in the partition
941 */
942 foreach(cell, idxlist)
943 {
944 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
945 AttrNumber *attmap;
946 IndexStmt *idxstmt;
947 Oid constraintOid;
948
949 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
950 {
951 if (idxRel->rd_index->indisunique)
952 ereport(ERROR,
953 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
954 errmsg("cannot create foreign partition of partitioned table \"%s\"",
955 RelationGetRelationName(parent)),
956 errdetail("Table \"%s\" contains indexes that are unique.",
957 RelationGetRelationName(parent))));
958 else
959 {
960 index_close(idxRel, AccessShareLock);
961 continue;
962 }
963 }
964
965 attmap = convert_tuples_by_name_map(RelationGetDescr(rel),
966 RelationGetDescr(parent),
967 gettext_noop("could not convert row type"));
968 idxstmt =
969 generateClonedIndexStmt(NULL, RelationGetRelid(rel), idxRel,
970 attmap, RelationGetDescr(parent)->natts,
971 &constraintOid);
972 DefineIndex(RelationGetRelid(rel),
973 idxstmt,
974 InvalidOid,
975 RelationGetRelid(idxRel),
976 constraintOid,
977 false, false, false, false, false);
978
979 index_close(idxRel, AccessShareLock);
980 }
981
982 list_free(idxlist);
983
984 /*
985 * If there are any row-level triggers, clone them to the new
986 * partition.
987 */
988 if (parent->trigdesc != NULL)
989 CloneRowTriggersToPartition(parent, rel);
990
991 /*
992 * And foreign keys too. Note that because we're freshly creating the
993 * table, there is no need to verify these new constraints.
994 */
995 CloneForeignKeyConstraints(parentId, relationId, NULL);
996
997 heap_close(parent, NoLock);
998 }
999
1000 /*
1001 * Now add any newly specified column default values and CHECK constraints
1002 * to the new relation. These are passed to us in the form of raw
1003 * parsetrees; we need to transform them to executable expression trees
1004 * before they can be added. The most convenient way to do that is to
1005 * apply the parser's transformExpr routine, but transformExpr doesn't
1006 * work unless we have a pre-existing relation. So, the transformation has
1007 * to be postponed to this final step of CREATE TABLE.
1008 */
1009 if (rawDefaults || stmt->constraints)
1010 AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
1011 true, true, false);
1012
1013 ObjectAddressSet(address, RelationRelationId, relationId);
1014
1015 /*
1016 * Clean up. We keep lock on new relation (although it shouldn't be
1017 * visible to anyone else anyway, until commit).
1018 */
1019 relation_close(rel, NoLock);
1020
1021 return address;
1022 }
1023
1024 /*
1025 * Emit the right error or warning message for a "DROP" command issued on a
1026 * non-existent relation
1027 */
1028 static void
DropErrorMsgNonExistent(RangeVar * rel,char rightkind,bool missing_ok)1029 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1030 {
1031 const struct dropmsgstrings *rentry;
1032
1033 if (rel->schemaname != NULL &&
1034 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1035 {
1036 if (!missing_ok)
1037 {
1038 ereport(ERROR,
1039 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1040 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1041 }
1042 else
1043 {
1044 ereport(NOTICE,
1045 (errmsg("schema \"%s\" does not exist, skipping",
1046 rel->schemaname)));
1047 }
1048 return;
1049 }
1050
1051 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1052 {
1053 if (rentry->kind == rightkind)
1054 {
1055 if (!missing_ok)
1056 {
1057 ereport(ERROR,
1058 (errcode(rentry->nonexistent_code),
1059 errmsg(rentry->nonexistent_msg, rel->relname)));
1060 }
1061 else
1062 {
1063 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1064 break;
1065 }
1066 }
1067 }
1068
1069 Assert(rentry->kind != '\0'); /* Should be impossible */
1070 }
1071
1072 /*
1073 * Emit the right error message for a "DROP" command issued on a
1074 * relation of the wrong type
1075 */
1076 static void
DropErrorMsgWrongType(const char * relname,char wrongkind,char rightkind)1077 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1078 {
1079 const struct dropmsgstrings *rentry;
1080 const struct dropmsgstrings *wentry;
1081
1082 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1083 if (rentry->kind == rightkind)
1084 break;
1085 Assert(rentry->kind != '\0');
1086
1087 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1088 if (wentry->kind == wrongkind)
1089 break;
1090 /* wrongkind could be something we don't have in our table... */
1091
1092 ereport(ERROR,
1093 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1094 errmsg(rentry->nota_msg, relname),
1095 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1096 }
1097
1098 /*
1099 * RemoveRelations
1100 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1101 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1102 */
1103 void
RemoveRelations(DropStmt * drop)1104 RemoveRelations(DropStmt *drop)
1105 {
1106 ObjectAddresses *objects;
1107 char relkind;
1108 ListCell *cell;
1109 int flags = 0;
1110 LOCKMODE lockmode = AccessExclusiveLock;
1111
1112 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1113 if (drop->concurrent)
1114 {
1115 /*
1116 * Note that for temporary relations this lock may get upgraded
1117 * later on, but as no other session can access a temporary
1118 * relation, this is actually fine.
1119 */
1120 lockmode = ShareUpdateExclusiveLock;
1121 Assert(drop->removeType == OBJECT_INDEX);
1122 if (list_length(drop->objects) != 1)
1123 ereport(ERROR,
1124 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1125 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1126 if (drop->behavior == DROP_CASCADE)
1127 ereport(ERROR,
1128 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1129 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1130 }
1131
1132 /*
1133 * First we identify all the relations, then we delete them in a single
1134 * performMultipleDeletions() call. This is to avoid unwanted DROP
1135 * RESTRICT errors if one of the relations depends on another.
1136 */
1137
1138 /* Determine required relkind */
1139 switch (drop->removeType)
1140 {
1141 case OBJECT_TABLE:
1142 relkind = RELKIND_RELATION;
1143 break;
1144
1145 case OBJECT_INDEX:
1146 relkind = RELKIND_INDEX;
1147 break;
1148
1149 case OBJECT_SEQUENCE:
1150 relkind = RELKIND_SEQUENCE;
1151 break;
1152
1153 case OBJECT_VIEW:
1154 relkind = RELKIND_VIEW;
1155 break;
1156
1157 case OBJECT_MATVIEW:
1158 relkind = RELKIND_MATVIEW;
1159 break;
1160
1161 case OBJECT_FOREIGN_TABLE:
1162 relkind = RELKIND_FOREIGN_TABLE;
1163 break;
1164
1165 default:
1166 elog(ERROR, "unrecognized drop object type: %d",
1167 (int) drop->removeType);
1168 relkind = 0; /* keep compiler quiet */
1169 break;
1170 }
1171
1172 /* Lock and validate each relation; build a list of object addresses */
1173 objects = new_object_addresses();
1174
1175 foreach(cell, drop->objects)
1176 {
1177 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1178 Oid relOid;
1179 ObjectAddress obj;
1180 struct DropRelationCallbackState state;
1181
1182 /*
1183 * These next few steps are a great deal like relation_openrv, but we
1184 * don't bother building a relcache entry since we don't need it.
1185 *
1186 * Check for shared-cache-inval messages before trying to access the
1187 * relation. This is needed to cover the case where the name
1188 * identifies a rel that has been dropped and recreated since the
1189 * start of our transaction: if we don't flush the old syscache entry,
1190 * then we'll latch onto that entry and suffer an error later.
1191 */
1192 AcceptInvalidationMessages();
1193
1194 /* Look up the appropriate relation using namespace search. */
1195 state.relkind = relkind;
1196 state.heapOid = InvalidOid;
1197 state.partParentOid = InvalidOid;
1198 state.concurrent = drop->concurrent;
1199 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1200 RangeVarCallbackForDropRelation,
1201 (void *) &state);
1202
1203 /* Not there? */
1204 if (!OidIsValid(relOid))
1205 {
1206 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1207 continue;
1208 }
1209
1210 /*
1211 * Decide if concurrent mode needs to be used here or not. The
1212 * relation persistence cannot be known without its OID.
1213 */
1214 if (drop->concurrent &&
1215 get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
1216 {
1217 Assert(list_length(drop->objects) == 1 &&
1218 drop->removeType == OBJECT_INDEX);
1219 flags |= PERFORM_DELETION_CONCURRENTLY;
1220 }
1221
1222 /*
1223 * Concurrent index drop cannot be used with partitioned indexes,
1224 * either.
1225 */
1226 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1227 get_rel_relkind(relOid) == RELKIND_PARTITIONED_INDEX)
1228 ereport(ERROR,
1229 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1230 errmsg("cannot drop partitioned index \"%s\" concurrently",
1231 rel->relname)));
1232
1233 /* OK, we're ready to delete this one */
1234 obj.classId = RelationRelationId;
1235 obj.objectId = relOid;
1236 obj.objectSubId = 0;
1237
1238 add_exact_object_address(&obj, objects);
1239 }
1240
1241 performMultipleDeletions(objects, drop->behavior, flags);
1242
1243 free_object_addresses(objects);
1244 }
1245
1246 /*
1247 * Before acquiring a table lock, check whether we have sufficient rights.
1248 * In the case of DROP INDEX, also try to lock the table before the index.
1249 * Also, if the table to be dropped is a partition, we try to lock the parent
1250 * first.
1251 */
1252 static void
RangeVarCallbackForDropRelation(const RangeVar * rel,Oid relOid,Oid oldRelOid,void * arg)1253 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1254 void *arg)
1255 {
1256 HeapTuple tuple;
1257 struct DropRelationCallbackState *state;
1258 char relkind;
1259 char expected_relkind;
1260 bool is_partition;
1261 Form_pg_class classform;
1262 LOCKMODE heap_lockmode;
1263
1264 state = (struct DropRelationCallbackState *) arg;
1265 relkind = state->relkind;
1266 heap_lockmode = state->concurrent ?
1267 ShareUpdateExclusiveLock : AccessExclusiveLock;
1268
1269 /*
1270 * If we previously locked some other index's heap, and the name we're
1271 * looking up no longer refers to that relation, release the now-useless
1272 * lock.
1273 */
1274 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1275 {
1276 UnlockRelationOid(state->heapOid, heap_lockmode);
1277 state->heapOid = InvalidOid;
1278 }
1279
1280 /*
1281 * Similarly, if we previously locked some other partition's heap, and the
1282 * name we're looking up no longer refers to that relation, release the
1283 * now-useless lock.
1284 */
1285 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1286 {
1287 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1288 state->partParentOid = InvalidOid;
1289 }
1290
1291 /* Didn't find a relation, so no need for locking or permission checks. */
1292 if (!OidIsValid(relOid))
1293 return;
1294
1295 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1296 if (!HeapTupleIsValid(tuple))
1297 return; /* concurrently dropped, so nothing to do */
1298 classform = (Form_pg_class) GETSTRUCT(tuple);
1299 is_partition = classform->relispartition;
1300
1301 /*
1302 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1303 * but RemoveRelations() can only pass one relkind for a given relation.
1304 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1305 * That means we must be careful before giving the wrong type error when
1306 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1307 * exists with indexes.
1308 */
1309 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1310 expected_relkind = RELKIND_RELATION;
1311 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1312 expected_relkind = RELKIND_INDEX;
1313 else
1314 expected_relkind = classform->relkind;
1315
1316 if (relkind != expected_relkind)
1317 DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
1318
1319 /* Allow DROP to either table owner or schema owner */
1320 if (!pg_class_ownercheck(relOid, GetUserId()) &&
1321 !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
1322 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
1323 rel->relname);
1324
1325 if (!allowSystemTableMods && IsSystemClass(relOid, classform))
1326 ereport(ERROR,
1327 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1328 errmsg("permission denied: \"%s\" is a system catalog",
1329 rel->relname)));
1330
1331 ReleaseSysCache(tuple);
1332
1333 /*
1334 * In DROP INDEX, attempt to acquire lock on the parent table before
1335 * locking the index. index_drop() will need this anyway, and since
1336 * regular queries lock tables before their indexes, we risk deadlock if
1337 * we do it the other way around. No error if we don't find a pg_index
1338 * entry, though --- the relation may have been dropped.
1339 */
1340 if ((relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX) &&
1341 relOid != oldRelOid)
1342 {
1343 state->heapOid = IndexGetRelation(relOid, true);
1344 if (OidIsValid(state->heapOid))
1345 LockRelationOid(state->heapOid, heap_lockmode);
1346 }
1347
1348 /*
1349 * Similarly, if the relation is a partition, we must acquire lock on its
1350 * parent before locking the partition. That's because queries lock the
1351 * parent before its partitions, so we risk deadlock it we do it the other
1352 * way around.
1353 */
1354 if (is_partition && relOid != oldRelOid)
1355 {
1356 state->partParentOid = get_partition_parent(relOid);
1357 if (OidIsValid(state->partParentOid))
1358 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1359 }
1360 }
1361
1362 /*
1363 * ExecuteTruncate
1364 * Executes a TRUNCATE command.
1365 *
1366 * This is a multi-relation truncate. We first open and grab exclusive
1367 * lock on all relations involved, checking permissions and otherwise
1368 * verifying that the relation is OK for truncation. In CASCADE mode,
1369 * relations having FK references to the targeted relations are automatically
1370 * added to the group; in RESTRICT mode, we check that all FK references are
1371 * internal to the group that's being truncated. Finally all the relations
1372 * are truncated and reindexed.
1373 */
1374 void
ExecuteTruncate(TruncateStmt * stmt)1375 ExecuteTruncate(TruncateStmt *stmt)
1376 {
1377 List *rels = NIL;
1378 List *relids = NIL;
1379 List *relids_logged = NIL;
1380 ListCell *cell;
1381
1382 /*
1383 * Open, exclusive-lock, and check all the explicitly-specified relations
1384 */
1385 foreach(cell, stmt->relations)
1386 {
1387 RangeVar *rv = lfirst(cell);
1388 Relation rel;
1389 bool recurse = rv->inh;
1390 Oid myrelid;
1391 LOCKMODE lockmode = AccessExclusiveLock;
1392
1393 rel = heap_openrv(rv, lockmode);
1394 myrelid = RelationGetRelid(rel);
1395 /* don't throw error for "TRUNCATE foo, foo" */
1396 if (list_member_oid(relids, myrelid))
1397 {
1398 heap_close(rel, lockmode);
1399 continue;
1400 }
1401 truncate_check_rel(rel);
1402 rels = lappend(rels, rel);
1403 relids = lappend_oid(relids, myrelid);
1404 /* Log this relation only if needed for logical decoding */
1405 if (RelationIsLogicallyLogged(rel))
1406 relids_logged = lappend_oid(relids_logged, myrelid);
1407
1408 if (recurse)
1409 {
1410 ListCell *child;
1411 List *children;
1412
1413 children = find_all_inheritors(myrelid, lockmode, NULL);
1414
1415 foreach(child, children)
1416 {
1417 Oid childrelid = lfirst_oid(child);
1418
1419 if (list_member_oid(relids, childrelid))
1420 continue;
1421
1422 /* find_all_inheritors already got lock */
1423 rel = heap_open(childrelid, NoLock);
1424
1425 /*
1426 * It is possible that the parent table has children that are
1427 * temp tables of other backends. We cannot safely access
1428 * such tables (because of buffering issues), and the best
1429 * thing to do is to silently ignore them. Note that this
1430 * check is the same as one of the checks done in
1431 * truncate_check_rel() called below, still it is kept
1432 * here for simplicity.
1433 */
1434 if (RELATION_IS_OTHER_TEMP(rel))
1435 {
1436 heap_close(rel, lockmode);
1437 continue;
1438 }
1439
1440 truncate_check_rel(rel);
1441 rels = lappend(rels, rel);
1442 relids = lappend_oid(relids, childrelid);
1443 /* Log this relation only if needed for logical decoding */
1444 if (RelationIsLogicallyLogged(rel))
1445 relids_logged = lappend_oid(relids_logged, childrelid);
1446 }
1447 }
1448 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1449 ereport(ERROR,
1450 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1451 errmsg("cannot truncate only a partitioned table"),
1452 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1453 }
1454
1455 ExecuteTruncateGuts(rels, relids, relids_logged,
1456 stmt->behavior, stmt->restart_seqs);
1457
1458 /* And close the rels */
1459 foreach(cell, rels)
1460 {
1461 Relation rel = (Relation) lfirst(cell);
1462
1463 heap_close(rel, NoLock);
1464 }
1465 }
1466
1467 /*
1468 * ExecuteTruncateGuts
1469 *
1470 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1471 * command (see above) as well as replication subscribers that execute a
1472 * replicated TRUNCATE action.
1473 *
1474 * explicit_rels is the list of Relations to truncate that the command
1475 * specified. relids is the list of Oids corresponding to explicit_rels.
1476 * relids_logged is the list of Oids (a subset of relids) that require
1477 * WAL-logging. This is all a bit redundant, but the existing callers have
1478 * this information handy in this form.
1479 */
1480 void
ExecuteTruncateGuts(List * explicit_rels,List * relids,List * relids_logged,DropBehavior behavior,bool restart_seqs)1481 ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
1482 DropBehavior behavior, bool restart_seqs)
1483 {
1484 List *rels;
1485 List *seq_relids = NIL;
1486 EState *estate;
1487 ResultRelInfo *resultRelInfos;
1488 ResultRelInfo *resultRelInfo;
1489 SubTransactionId mySubid;
1490 ListCell *cell;
1491 Oid *logrelids;
1492
1493 /*
1494 * Check the explicitly-specified relations.
1495 *
1496 * In CASCADE mode, suck in all referencing relations as well. This
1497 * requires multiple iterations to find indirectly-dependent relations. At
1498 * each phase, we need to exclusive-lock new rels before looking for their
1499 * dependencies, else we might miss something. Also, we check each rel as
1500 * soon as we open it, to avoid a faux pas such as holding lock for a long
1501 * time on a rel we have no permissions for.
1502 */
1503 rels = list_copy(explicit_rels);
1504 if (behavior == DROP_CASCADE)
1505 {
1506 for (;;)
1507 {
1508 List *newrelids;
1509
1510 newrelids = heap_truncate_find_FKs(relids);
1511 if (newrelids == NIL)
1512 break; /* nothing else to add */
1513
1514 foreach(cell, newrelids)
1515 {
1516 Oid relid = lfirst_oid(cell);
1517 Relation rel;
1518
1519 rel = heap_open(relid, AccessExclusiveLock);
1520 ereport(NOTICE,
1521 (errmsg("truncate cascades to table \"%s\"",
1522 RelationGetRelationName(rel))));
1523 truncate_check_rel(rel);
1524 rels = lappend(rels, rel);
1525 relids = lappend_oid(relids, relid);
1526 /* Log this relation only if needed for logical decoding */
1527 if (RelationIsLogicallyLogged(rel))
1528 relids_logged = lappend_oid(relids_logged, relid);
1529 }
1530 }
1531 }
1532
1533 /*
1534 * Check foreign key references. In CASCADE mode, this should be
1535 * unnecessary since we just pulled in all the references; but as a
1536 * cross-check, do it anyway if in an Assert-enabled build.
1537 */
1538 #ifdef USE_ASSERT_CHECKING
1539 heap_truncate_check_FKs(rels, false);
1540 #else
1541 if (behavior == DROP_RESTRICT)
1542 heap_truncate_check_FKs(rels, false);
1543 #endif
1544
1545 /*
1546 * If we are asked to restart sequences, find all the sequences, lock them
1547 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1548 * We want to do this early since it's pointless to do all the truncation
1549 * work only to fail on sequence permissions.
1550 */
1551 if (restart_seqs)
1552 {
1553 foreach(cell, rels)
1554 {
1555 Relation rel = (Relation) lfirst(cell);
1556 List *seqlist = getOwnedSequences(RelationGetRelid(rel), 0);
1557 ListCell *seqcell;
1558
1559 foreach(seqcell, seqlist)
1560 {
1561 Oid seq_relid = lfirst_oid(seqcell);
1562 Relation seq_rel;
1563
1564 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1565
1566 /* This check must match AlterSequence! */
1567 if (!pg_class_ownercheck(seq_relid, GetUserId()))
1568 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
1569 RelationGetRelationName(seq_rel));
1570
1571 seq_relids = lappend_oid(seq_relids, seq_relid);
1572
1573 relation_close(seq_rel, NoLock);
1574 }
1575 }
1576 }
1577
1578 /* Prepare to catch AFTER triggers. */
1579 AfterTriggerBeginQuery();
1580
1581 /*
1582 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1583 * each relation. We don't need to call ExecOpenIndices, though.
1584 */
1585 estate = CreateExecutorState();
1586 resultRelInfos = (ResultRelInfo *)
1587 palloc(list_length(rels) * sizeof(ResultRelInfo));
1588 resultRelInfo = resultRelInfos;
1589 foreach(cell, rels)
1590 {
1591 Relation rel = (Relation) lfirst(cell);
1592
1593 InitResultRelInfo(resultRelInfo,
1594 rel,
1595 0, /* dummy rangetable index */
1596 NULL,
1597 0);
1598 resultRelInfo++;
1599 }
1600 estate->es_result_relations = resultRelInfos;
1601 estate->es_num_result_relations = list_length(rels);
1602
1603 /*
1604 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
1605 * truncating (this is because one of them might throw an error). Also, if
1606 * we were to allow them to prevent statement execution, that would need
1607 * to be handled here.
1608 */
1609 resultRelInfo = resultRelInfos;
1610 foreach(cell, rels)
1611 {
1612 estate->es_result_relation_info = resultRelInfo;
1613 ExecBSTruncateTriggers(estate, resultRelInfo);
1614 resultRelInfo++;
1615 }
1616
1617 /*
1618 * OK, truncate each table.
1619 */
1620 mySubid = GetCurrentSubTransactionId();
1621
1622 foreach(cell, rels)
1623 {
1624 Relation rel = (Relation) lfirst(cell);
1625
1626 /* Skip partitioned tables as there is nothing to do */
1627 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1628 continue;
1629
1630 /*
1631 * Normally, we need a transaction-safe truncation here. However, if
1632 * the table was either created in the current (sub)transaction or has
1633 * a new relfilenode in the current (sub)transaction, then we can just
1634 * truncate it in-place, because a rollback would cause the whole
1635 * table or the current physical file to be thrown away anyway.
1636 */
1637 if (rel->rd_createSubid == mySubid ||
1638 rel->rd_newRelfilenodeSubid == mySubid)
1639 {
1640 /* Immediate, non-rollbackable truncation is OK */
1641 heap_truncate_one_rel(rel);
1642 }
1643 else
1644 {
1645 Oid heap_relid;
1646 Oid toast_relid;
1647 MultiXactId minmulti;
1648
1649 /*
1650 * This effectively deletes all rows in the table, and may be done
1651 * in a serializable transaction. In that case we must record a
1652 * rw-conflict in to this transaction from each transaction
1653 * holding a predicate lock on the table.
1654 */
1655 CheckTableForSerializableConflictIn(rel);
1656
1657 minmulti = GetOldestMultiXactId();
1658
1659 /*
1660 * Need the full transaction-safe pushups.
1661 *
1662 * Create a new empty storage file for the relation, and assign it
1663 * as the relfilenode value. The old storage file is scheduled for
1664 * deletion at commit.
1665 */
1666 RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence,
1667 RecentXmin, minmulti);
1668 if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
1669 heap_create_init_fork(rel);
1670
1671 heap_relid = RelationGetRelid(rel);
1672
1673 /*
1674 * The same for the toast table, if any.
1675 */
1676 toast_relid = rel->rd_rel->reltoastrelid;
1677 if (OidIsValid(toast_relid))
1678 {
1679 Relation toastrel = relation_open(toast_relid,
1680 AccessExclusiveLock);
1681
1682 RelationSetNewRelfilenode(toastrel,
1683 toastrel->rd_rel->relpersistence,
1684 RecentXmin, minmulti);
1685 if (toastrel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
1686 heap_create_init_fork(toastrel);
1687 heap_close(toastrel, NoLock);
1688 }
1689
1690 /*
1691 * Reconstruct the indexes to match, and we're done.
1692 */
1693 reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, 0);
1694 }
1695
1696 pgstat_count_truncate(rel);
1697 }
1698
1699 /*
1700 * Restart owned sequences if we were asked to.
1701 */
1702 foreach(cell, seq_relids)
1703 {
1704 Oid seq_relid = lfirst_oid(cell);
1705
1706 ResetSequence(seq_relid);
1707 }
1708
1709 /*
1710 * Write a WAL record to allow this set of actions to be logically
1711 * decoded.
1712 *
1713 * Assemble an array of relids so we can write a single WAL record for the
1714 * whole action.
1715 */
1716 if (list_length(relids_logged) > 0)
1717 {
1718 xl_heap_truncate xlrec;
1719 int i = 0;
1720
1721 /* should only get here if wal_level >= logical */
1722 Assert(XLogLogicalInfoActive());
1723
1724 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
1725 foreach(cell, relids_logged)
1726 logrelids[i++] = lfirst_oid(cell);
1727
1728 xlrec.dbId = MyDatabaseId;
1729 xlrec.nrelids = list_length(relids_logged);
1730 xlrec.flags = 0;
1731 if (behavior == DROP_CASCADE)
1732 xlrec.flags |= XLH_TRUNCATE_CASCADE;
1733 if (restart_seqs)
1734 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
1735
1736 XLogBeginInsert();
1737 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
1738 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
1739
1740 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
1741
1742 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
1743 }
1744
1745 /*
1746 * Process all AFTER STATEMENT TRUNCATE triggers.
1747 */
1748 resultRelInfo = resultRelInfos;
1749 foreach(cell, rels)
1750 {
1751 estate->es_result_relation_info = resultRelInfo;
1752 ExecASTruncateTriggers(estate, resultRelInfo);
1753 resultRelInfo++;
1754 }
1755
1756 /* Handle queued AFTER triggers */
1757 AfterTriggerEndQuery(estate);
1758
1759 /* We can clean up the EState now */
1760 FreeExecutorState(estate);
1761
1762 /*
1763 * Close any rels opened by CASCADE (can't do this while EState still
1764 * holds refs)
1765 */
1766 rels = list_difference_ptr(rels, explicit_rels);
1767 foreach(cell, rels)
1768 {
1769 Relation rel = (Relation) lfirst(cell);
1770
1771 heap_close(rel, NoLock);
1772 }
1773 }
1774
1775 /*
1776 * Check that a given rel is safe to truncate. Subroutine for ExecuteTruncate
1777 */
1778 static void
truncate_check_rel(Relation rel)1779 truncate_check_rel(Relation rel)
1780 {
1781 AclResult aclresult;
1782
1783 /*
1784 * Only allow truncate on regular tables and partitioned tables (although,
1785 * the latter are only being included here for the following checks; no
1786 * physical truncation will occur in their case.)
1787 */
1788 if (rel->rd_rel->relkind != RELKIND_RELATION &&
1789 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1790 ereport(ERROR,
1791 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1792 errmsg("\"%s\" is not a table",
1793 RelationGetRelationName(rel))));
1794
1795 /* Permissions checks */
1796 aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
1797 ACL_TRUNCATE);
1798 if (aclresult != ACLCHECK_OK)
1799 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
1800 RelationGetRelationName(rel));
1801
1802 if (!allowSystemTableMods && IsSystemRelation(rel))
1803 ereport(ERROR,
1804 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1805 errmsg("permission denied: \"%s\" is a system catalog",
1806 RelationGetRelationName(rel))));
1807
1808 /*
1809 * Don't allow truncate on temp tables of other backends ... their local
1810 * buffer manager is not going to cope.
1811 */
1812 if (RELATION_IS_OTHER_TEMP(rel))
1813 ereport(ERROR,
1814 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1815 errmsg("cannot truncate temporary tables of other sessions")));
1816
1817 /*
1818 * Also check for active uses of the relation in the current transaction,
1819 * including open scans and pending AFTER trigger events.
1820 */
1821 CheckTableNotInUse(rel, "TRUNCATE");
1822 }
1823
1824 /*
1825 * storage_name
1826 * returns the name corresponding to a typstorage/attstorage enum value
1827 */
1828 static const char *
storage_name(char c)1829 storage_name(char c)
1830 {
1831 switch (c)
1832 {
1833 case 'p':
1834 return "PLAIN";
1835 case 'm':
1836 return "MAIN";
1837 case 'x':
1838 return "EXTENDED";
1839 case 'e':
1840 return "EXTERNAL";
1841 default:
1842 return "???";
1843 }
1844 }
1845
1846 /*----------
1847 * MergeAttributes
1848 * Returns new schema given initial schema and superclasses.
1849 *
1850 * Input arguments:
1851 * 'schema' is the column/attribute definition for the table. (It's a list
1852 * of ColumnDef's.) It is destructively changed.
1853 * 'supers' is a list of names (as RangeVar nodes) of parent relations.
1854 * 'relpersistence' is a persistence type of the table.
1855 * 'is_partition' tells if the table is a partition
1856 *
1857 * Output arguments:
1858 * 'supOids' receives a list of the OIDs of the parent relations.
1859 * 'supconstr' receives a list of constraints belonging to the parents,
1860 * updated as necessary to be valid for the child.
1861 * 'supOidCount' is set to the number of parents that have OID columns.
1862 *
1863 * Return value:
1864 * Completed schema list.
1865 *
1866 * Notes:
1867 * The order in which the attributes are inherited is very important.
1868 * Intuitively, the inherited attributes should come first. If a table
1869 * inherits from multiple parents, the order of those attributes are
1870 * according to the order of the parents specified in CREATE TABLE.
1871 *
1872 * Here's an example:
1873 *
1874 * create table person (name text, age int4, location point);
1875 * create table emp (salary int4, manager text) inherits(person);
1876 * create table student (gpa float8) inherits (person);
1877 * create table stud_emp (percent int4) inherits (emp, student);
1878 *
1879 * The order of the attributes of stud_emp is:
1880 *
1881 * person {1:name, 2:age, 3:location}
1882 * / \
1883 * {6:gpa} student emp {4:salary, 5:manager}
1884 * \ /
1885 * stud_emp {7:percent}
1886 *
1887 * If the same attribute name appears multiple times, then it appears
1888 * in the result table in the proper location for its first appearance.
1889 *
1890 * Constraints (including NOT NULL constraints) for the child table
1891 * are the union of all relevant constraints, from both the child schema
1892 * and parent tables.
1893 *
1894 * The default value for a child column is defined as:
1895 * (1) If the child schema specifies a default, that value is used.
1896 * (2) If neither the child nor any parent specifies a default, then
1897 * the column will not have a default.
1898 * (3) If conflicting defaults are inherited from different parents
1899 * (and not overridden by the child), an error is raised.
1900 * (4) Otherwise the inherited default is used.
1901 * Rule (3) is new in Postgres 7.1; in earlier releases you got a
1902 * rather arbitrary choice of which parent default to use.
1903 *----------
1904 */
1905 static List *
MergeAttributes(List * schema,List * supers,char relpersistence,bool is_partition,List ** supOids,List ** supconstr,int * supOidCount)1906 MergeAttributes(List *schema, List *supers, char relpersistence,
1907 bool is_partition, List **supOids, List **supconstr,
1908 int *supOidCount)
1909 {
1910 ListCell *entry;
1911 List *inhSchema = NIL;
1912 List *parentOids = NIL;
1913 List *constraints = NIL;
1914 int parentsWithOids = 0;
1915 bool have_bogus_defaults = false;
1916 int child_attno;
1917 static Node bogus_marker = {0}; /* marks conflicting defaults */
1918 List *saved_schema = NIL;
1919
1920 /*
1921 * Check for and reject tables with too many columns. We perform this
1922 * check relatively early for two reasons: (a) we don't run the risk of
1923 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
1924 * okay if we're processing <= 1600 columns, but could take minutes to
1925 * execute if the user attempts to create a table with hundreds of
1926 * thousands of columns.
1927 *
1928 * Note that we also need to check that we do not exceed this figure after
1929 * including columns from inherited relations.
1930 */
1931 if (list_length(schema) > MaxHeapAttributeNumber)
1932 ereport(ERROR,
1933 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1934 errmsg("tables can have at most %d columns",
1935 MaxHeapAttributeNumber)));
1936
1937 /*
1938 * Check for duplicate names in the explicit list of attributes.
1939 *
1940 * Although we might consider merging such entries in the same way that we
1941 * handle name conflicts for inherited attributes, it seems to make more
1942 * sense to assume such conflicts are errors.
1943 */
1944 foreach(entry, schema)
1945 {
1946 ColumnDef *coldef = lfirst(entry);
1947 ListCell *rest = lnext(entry);
1948 ListCell *prev = entry;
1949
1950 if (!is_partition && coldef->typeName == NULL)
1951 {
1952 /*
1953 * Typed table column option that does not belong to a column from
1954 * the type. This works because the columns from the type come
1955 * first in the list. (We omit this check for partition column
1956 * lists; those are processed separately below.)
1957 */
1958 ereport(ERROR,
1959 (errcode(ERRCODE_UNDEFINED_COLUMN),
1960 errmsg("column \"%s\" does not exist",
1961 coldef->colname)));
1962 }
1963
1964 while (rest != NULL)
1965 {
1966 ColumnDef *restdef = lfirst(rest);
1967 ListCell *next = lnext(rest); /* need to save it in case we
1968 * delete it */
1969
1970 if (strcmp(coldef->colname, restdef->colname) == 0)
1971 {
1972 if (coldef->is_from_type)
1973 {
1974 /*
1975 * merge the column options into the column from the type
1976 */
1977 coldef->is_not_null = restdef->is_not_null;
1978 coldef->raw_default = restdef->raw_default;
1979 coldef->cooked_default = restdef->cooked_default;
1980 coldef->constraints = restdef->constraints;
1981 coldef->is_from_type = false;
1982 list_delete_cell(schema, rest, prev);
1983 }
1984 else
1985 ereport(ERROR,
1986 (errcode(ERRCODE_DUPLICATE_COLUMN),
1987 errmsg("column \"%s\" specified more than once",
1988 coldef->colname)));
1989 }
1990 prev = rest;
1991 rest = next;
1992 }
1993 }
1994
1995 /*
1996 * In case of a partition, there are no new column definitions, only dummy
1997 * ColumnDefs created for column constraints. Set them aside for now and
1998 * process them at the end.
1999 */
2000 if (is_partition)
2001 {
2002 saved_schema = schema;
2003 schema = NIL;
2004 }
2005
2006 /*
2007 * Scan the parents left-to-right, and merge their attributes to form a
2008 * list of inherited attributes (inhSchema). Also check to see if we need
2009 * to inherit an OID column.
2010 */
2011 child_attno = 0;
2012 foreach(entry, supers)
2013 {
2014 RangeVar *parent = (RangeVar *) lfirst(entry);
2015 Relation relation;
2016 TupleDesc tupleDesc;
2017 TupleConstr *constr;
2018 AttrNumber *newattno;
2019 AttrNumber parent_attno;
2020
2021 /*
2022 * A self-exclusive lock is needed here. If two backends attempt to
2023 * add children to the same parent simultaneously, and that parent has
2024 * no pre-existing children, then both will attempt to update the
2025 * parent's relhassubclass field, leading to a "tuple concurrently
2026 * updated" error. Also, this interlocks against a concurrent ANALYZE
2027 * on the parent table, which might otherwise be attempting to clear
2028 * the parent's relhassubclass field, if its previous children were
2029 * recently dropped.
2030 *
2031 * If the child table is a partition, then we instead grab an
2032 * exclusive lock on the parent because its partition descriptor will
2033 * be changed by addition of the new partition.
2034 */
2035 if (!is_partition)
2036 relation = heap_openrv(parent, ShareUpdateExclusiveLock);
2037 else
2038 relation = heap_openrv(parent, AccessExclusiveLock);
2039
2040 /*
2041 * Check for active uses of the parent partitioned table in the
2042 * current transaction, such as being used in some manner by an
2043 * enclosing command.
2044 */
2045 if (is_partition)
2046 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2047
2048 /*
2049 * We do not allow partitioned tables and partitions to participate in
2050 * regular inheritance.
2051 */
2052 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2053 !is_partition)
2054 ereport(ERROR,
2055 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2056 errmsg("cannot inherit from partitioned table \"%s\"",
2057 parent->relname)));
2058 if (relation->rd_rel->relispartition && !is_partition)
2059 ereport(ERROR,
2060 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2061 errmsg("cannot inherit from partition \"%s\"",
2062 parent->relname)));
2063
2064 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2065 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2066 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2067 ereport(ERROR,
2068 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2069 errmsg("inherited relation \"%s\" is not a table or foreign table",
2070 parent->relname)));
2071
2072 /*
2073 * If the parent is permanent, so must be all of its partitions. Note
2074 * that inheritance allows that case.
2075 */
2076 if (is_partition &&
2077 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2078 relpersistence == RELPERSISTENCE_TEMP)
2079 ereport(ERROR,
2080 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2081 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2082 RelationGetRelationName(relation))));
2083
2084 /* Permanent rels cannot inherit from temporary ones */
2085 if (relpersistence != RELPERSISTENCE_TEMP &&
2086 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2087 ereport(ERROR,
2088 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2089 errmsg(!is_partition
2090 ? "cannot inherit from temporary relation \"%s\""
2091 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2092 parent->relname)));
2093
2094 /* If existing rel is temp, it must belong to this session */
2095 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2096 !relation->rd_islocaltemp)
2097 ereport(ERROR,
2098 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2099 errmsg(!is_partition
2100 ? "cannot inherit from temporary relation of another session"
2101 : "cannot create as partition of temporary relation of another session")));
2102
2103 /*
2104 * We should have an UNDER permission flag for this, but for now,
2105 * demand that creator of a child table own the parent.
2106 */
2107 if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
2108 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2109 RelationGetRelationName(relation));
2110
2111 /*
2112 * Reject duplications in the list of parents.
2113 */
2114 if (list_member_oid(parentOids, RelationGetRelid(relation)))
2115 ereport(ERROR,
2116 (errcode(ERRCODE_DUPLICATE_TABLE),
2117 errmsg("relation \"%s\" would be inherited from more than once",
2118 parent->relname)));
2119
2120 parentOids = lappend_oid(parentOids, RelationGetRelid(relation));
2121
2122 if (relation->rd_rel->relhasoids)
2123 parentsWithOids++;
2124
2125 tupleDesc = RelationGetDescr(relation);
2126 constr = tupleDesc->constr;
2127
2128 /*
2129 * newattno[] will contain the child-table attribute numbers for the
2130 * attributes of this parent table. (They are not the same for
2131 * parents after the first one, nor if we have dropped columns.)
2132 */
2133 newattno = (AttrNumber *)
2134 palloc0(tupleDesc->natts * sizeof(AttrNumber));
2135
2136 for (parent_attno = 1; parent_attno <= tupleDesc->natts;
2137 parent_attno++)
2138 {
2139 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2140 parent_attno - 1);
2141 char *attributeName = NameStr(attribute->attname);
2142 int exist_attno;
2143 ColumnDef *def;
2144
2145 /*
2146 * Ignore dropped columns in the parent.
2147 */
2148 if (attribute->attisdropped)
2149 continue; /* leave newattno entry as zero */
2150
2151 /*
2152 * Does it conflict with some previously inherited column?
2153 */
2154 exist_attno = findAttrByName(attributeName, inhSchema);
2155 if (exist_attno > 0)
2156 {
2157 Oid defTypeId;
2158 int32 deftypmod;
2159 Oid defCollId;
2160
2161 /*
2162 * Yes, try to merge the two column definitions. They must
2163 * have the same type, typmod, and collation.
2164 */
2165 ereport(NOTICE,
2166 (errmsg("merging multiple inherited definitions of column \"%s\"",
2167 attributeName)));
2168 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2169 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2170 if (defTypeId != attribute->atttypid ||
2171 deftypmod != attribute->atttypmod)
2172 ereport(ERROR,
2173 (errcode(ERRCODE_DATATYPE_MISMATCH),
2174 errmsg("inherited column \"%s\" has a type conflict",
2175 attributeName),
2176 errdetail("%s versus %s",
2177 format_type_with_typemod(defTypeId,
2178 deftypmod),
2179 format_type_with_typemod(attribute->atttypid,
2180 attribute->atttypmod))));
2181 defCollId = GetColumnDefCollation(NULL, def, defTypeId);
2182 if (defCollId != attribute->attcollation)
2183 ereport(ERROR,
2184 (errcode(ERRCODE_COLLATION_MISMATCH),
2185 errmsg("inherited column \"%s\" has a collation conflict",
2186 attributeName),
2187 errdetail("\"%s\" versus \"%s\"",
2188 get_collation_name(defCollId),
2189 get_collation_name(attribute->attcollation))));
2190
2191 /* Copy storage parameter */
2192 if (def->storage == 0)
2193 def->storage = attribute->attstorage;
2194 else if (def->storage != attribute->attstorage)
2195 ereport(ERROR,
2196 (errcode(ERRCODE_DATATYPE_MISMATCH),
2197 errmsg("inherited column \"%s\" has a storage parameter conflict",
2198 attributeName),
2199 errdetail("%s versus %s",
2200 storage_name(def->storage),
2201 storage_name(attribute->attstorage))));
2202
2203 def->inhcount++;
2204 /* Merge of NOT NULL constraints = OR 'em together */
2205 def->is_not_null |= attribute->attnotnull;
2206 /* Default and other constraints are handled below */
2207 newattno[parent_attno - 1] = exist_attno;
2208 }
2209 else
2210 {
2211 /*
2212 * No, create a new inherited column
2213 */
2214 def = makeNode(ColumnDef);
2215 def->colname = pstrdup(attributeName);
2216 def->typeName = makeTypeNameFromOid(attribute->atttypid,
2217 attribute->atttypmod);
2218 def->inhcount = 1;
2219 def->is_local = false;
2220 def->is_not_null = attribute->attnotnull;
2221 def->is_from_type = false;
2222 def->storage = attribute->attstorage;
2223 def->raw_default = NULL;
2224 def->cooked_default = NULL;
2225 def->collClause = NULL;
2226 def->collOid = attribute->attcollation;
2227 def->constraints = NIL;
2228 def->location = -1;
2229 inhSchema = lappend(inhSchema, def);
2230 newattno[parent_attno - 1] = ++child_attno;
2231 }
2232
2233 /*
2234 * Copy default if any
2235 */
2236 if (attribute->atthasdef)
2237 {
2238 Node *this_default = NULL;
2239 AttrDefault *attrdef;
2240 int i;
2241
2242 /* Find default in constraint structure */
2243 Assert(constr != NULL);
2244 attrdef = constr->defval;
2245 for (i = 0; i < constr->num_defval; i++)
2246 {
2247 if (attrdef[i].adnum == parent_attno)
2248 {
2249 this_default = stringToNode(attrdef[i].adbin);
2250 break;
2251 }
2252 }
2253 Assert(this_default != NULL);
2254
2255 /*
2256 * If default expr could contain any vars, we'd need to fix
2257 * 'em, but it can't; so default is ready to apply to child.
2258 *
2259 * If we already had a default from some prior parent, check
2260 * to see if they are the same. If so, no problem; if not,
2261 * mark the column as having a bogus default. Below, we will
2262 * complain if the bogus default isn't overridden by the child
2263 * schema.
2264 */
2265 Assert(def->raw_default == NULL);
2266 if (def->cooked_default == NULL)
2267 def->cooked_default = this_default;
2268 else if (!equal(def->cooked_default, this_default))
2269 {
2270 def->cooked_default = &bogus_marker;
2271 have_bogus_defaults = true;
2272 }
2273 }
2274 }
2275
2276 /*
2277 * Now copy the CHECK constraints of this parent, adjusting attnos
2278 * using the completed newattno[] map. Identically named constraints
2279 * are merged if possible, else we throw error.
2280 */
2281 if (constr && constr->num_check > 0)
2282 {
2283 ConstrCheck *check = constr->check;
2284 int i;
2285
2286 for (i = 0; i < constr->num_check; i++)
2287 {
2288 char *name = check[i].ccname;
2289 Node *expr;
2290 bool found_whole_row;
2291
2292 /* ignore if the constraint is non-inheritable */
2293 if (check[i].ccnoinherit)
2294 continue;
2295
2296 /* Adjust Vars to match new table's column numbering */
2297 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2298 1, 0,
2299 newattno, tupleDesc->natts,
2300 InvalidOid, &found_whole_row);
2301
2302 /*
2303 * For the moment we have to reject whole-row variables. We
2304 * could convert them, if we knew the new table's rowtype OID,
2305 * but that hasn't been assigned yet.
2306 */
2307 if (found_whole_row)
2308 ereport(ERROR,
2309 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2310 errmsg("cannot convert whole-row table reference"),
2311 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2312 name,
2313 RelationGetRelationName(relation))));
2314
2315 /* check for duplicate */
2316 if (!MergeCheckConstraint(constraints, name, expr))
2317 {
2318 /* nope, this is a new one */
2319 CookedConstraint *cooked;
2320
2321 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
2322 cooked->contype = CONSTR_CHECK;
2323 cooked->conoid = InvalidOid; /* until created */
2324 cooked->name = pstrdup(name);
2325 cooked->attnum = 0; /* not used for constraints */
2326 cooked->expr = expr;
2327 cooked->skip_validation = false;
2328 cooked->is_local = false;
2329 cooked->inhcount = 1;
2330 cooked->is_no_inherit = false;
2331 constraints = lappend(constraints, cooked);
2332 }
2333 }
2334 }
2335
2336 pfree(newattno);
2337
2338 /*
2339 * Close the parent rel, but keep our lock on it until xact commit.
2340 * That will prevent someone else from deleting or ALTERing the parent
2341 * before the child is committed.
2342 */
2343 heap_close(relation, NoLock);
2344 }
2345
2346 /*
2347 * If we had no inherited attributes, the result schema is just the
2348 * explicitly declared columns. Otherwise, we need to merge the declared
2349 * columns into the inherited schema list. Although, we never have any
2350 * explicitly declared columns if the table is a partition.
2351 */
2352 if (inhSchema != NIL)
2353 {
2354 int schema_attno = 0;
2355
2356 foreach(entry, schema)
2357 {
2358 ColumnDef *newdef = lfirst(entry);
2359 char *attributeName = newdef->colname;
2360 int exist_attno;
2361
2362 schema_attno++;
2363
2364 /*
2365 * Does it conflict with some previously inherited column?
2366 */
2367 exist_attno = findAttrByName(attributeName, inhSchema);
2368 if (exist_attno > 0)
2369 {
2370 ColumnDef *def;
2371 Oid defTypeId,
2372 newTypeId;
2373 int32 deftypmod,
2374 newtypmod;
2375 Oid defcollid,
2376 newcollid;
2377
2378 /*
2379 * Partitions have only one parent and have no column
2380 * definitions of their own, so conflict should never occur.
2381 */
2382 Assert(!is_partition);
2383
2384 /*
2385 * Yes, try to merge the two column definitions. They must
2386 * have the same type, typmod, and collation.
2387 */
2388 if (exist_attno == schema_attno)
2389 ereport(NOTICE,
2390 (errmsg("merging column \"%s\" with inherited definition",
2391 attributeName)));
2392 else
2393 ereport(NOTICE,
2394 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
2395 errdetail("User-specified column moved to the position of the inherited column.")));
2396 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2397 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2398 typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
2399 if (defTypeId != newTypeId || deftypmod != newtypmod)
2400 ereport(ERROR,
2401 (errcode(ERRCODE_DATATYPE_MISMATCH),
2402 errmsg("column \"%s\" has a type conflict",
2403 attributeName),
2404 errdetail("%s versus %s",
2405 format_type_with_typemod(defTypeId,
2406 deftypmod),
2407 format_type_with_typemod(newTypeId,
2408 newtypmod))));
2409 defcollid = GetColumnDefCollation(NULL, def, defTypeId);
2410 newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
2411 if (defcollid != newcollid)
2412 ereport(ERROR,
2413 (errcode(ERRCODE_COLLATION_MISMATCH),
2414 errmsg("column \"%s\" has a collation conflict",
2415 attributeName),
2416 errdetail("\"%s\" versus \"%s\"",
2417 get_collation_name(defcollid),
2418 get_collation_name(newcollid))));
2419
2420 /*
2421 * Identity is never inherited. The new column can have an
2422 * identity definition, so we always just take that one.
2423 */
2424 def->identity = newdef->identity;
2425
2426 /* Copy storage parameter */
2427 if (def->storage == 0)
2428 def->storage = newdef->storage;
2429 else if (newdef->storage != 0 && def->storage != newdef->storage)
2430 ereport(ERROR,
2431 (errcode(ERRCODE_DATATYPE_MISMATCH),
2432 errmsg("column \"%s\" has a storage parameter conflict",
2433 attributeName),
2434 errdetail("%s versus %s",
2435 storage_name(def->storage),
2436 storage_name(newdef->storage))));
2437
2438 /* Mark the column as locally defined */
2439 def->is_local = true;
2440 /* Merge of NOT NULL constraints = OR 'em together */
2441 def->is_not_null |= newdef->is_not_null;
2442 /* If new def has a default, override previous default */
2443 if (newdef->raw_default != NULL)
2444 {
2445 def->raw_default = newdef->raw_default;
2446 def->cooked_default = newdef->cooked_default;
2447 }
2448 }
2449 else
2450 {
2451 /*
2452 * No, attach new column to result schema
2453 */
2454 inhSchema = lappend(inhSchema, newdef);
2455 }
2456 }
2457
2458 schema = inhSchema;
2459
2460 /*
2461 * Check that we haven't exceeded the legal # of columns after merging
2462 * in inherited columns.
2463 */
2464 if (list_length(schema) > MaxHeapAttributeNumber)
2465 ereport(ERROR,
2466 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2467 errmsg("tables can have at most %d columns",
2468 MaxHeapAttributeNumber)));
2469 }
2470
2471 /*
2472 * Now that we have the column definition list for a partition, we can
2473 * check whether the columns referenced in the column constraint specs
2474 * actually exist. Also, we merge NOT NULL and defaults into each
2475 * corresponding column definition.
2476 */
2477 if (is_partition)
2478 {
2479 foreach(entry, saved_schema)
2480 {
2481 ColumnDef *restdef = lfirst(entry);
2482 bool found = false;
2483 ListCell *l;
2484
2485 foreach(l, schema)
2486 {
2487 ColumnDef *coldef = lfirst(l);
2488
2489 if (strcmp(coldef->colname, restdef->colname) == 0)
2490 {
2491 found = true;
2492 coldef->is_not_null |= restdef->is_not_null;
2493
2494 /*
2495 * Override the parent's default value for this column
2496 * (coldef->cooked_default) with the partition's local
2497 * definition (restdef->raw_default), if there's one. It
2498 * should be physically impossible to get a cooked default
2499 * in the local definition or a raw default in the
2500 * inherited definition, but make sure they're nulls, for
2501 * future-proofing.
2502 */
2503 Assert(restdef->cooked_default == NULL);
2504 Assert(coldef->raw_default == NULL);
2505 if (restdef->raw_default)
2506 {
2507 coldef->raw_default = restdef->raw_default;
2508 coldef->cooked_default = NULL;
2509 }
2510 }
2511 }
2512
2513 /* complain for constraints on columns not in parent */
2514 if (!found)
2515 ereport(ERROR,
2516 (errcode(ERRCODE_UNDEFINED_COLUMN),
2517 errmsg("column \"%s\" does not exist",
2518 restdef->colname)));
2519 }
2520 }
2521
2522 /*
2523 * If we found any conflicting parent default values, check to make sure
2524 * they were overridden by the child.
2525 */
2526 if (have_bogus_defaults)
2527 {
2528 foreach(entry, schema)
2529 {
2530 ColumnDef *def = lfirst(entry);
2531
2532 if (def->cooked_default == &bogus_marker)
2533 ereport(ERROR,
2534 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2535 errmsg("column \"%s\" inherits conflicting default values",
2536 def->colname),
2537 errhint("To resolve the conflict, specify a default explicitly.")));
2538 }
2539 }
2540
2541 *supOids = parentOids;
2542 *supconstr = constraints;
2543 *supOidCount = parentsWithOids;
2544 return schema;
2545 }
2546
2547
2548 /*
2549 * MergeCheckConstraint
2550 * Try to merge an inherited CHECK constraint with previous ones
2551 *
2552 * If we inherit identically-named constraints from multiple parents, we must
2553 * merge them, or throw an error if they don't have identical definitions.
2554 *
2555 * constraints is a list of CookedConstraint structs for previous constraints.
2556 *
2557 * Returns true if merged (constraint is a duplicate), or false if it's
2558 * got a so-far-unique name, or throws error if conflict.
2559 */
2560 static bool
MergeCheckConstraint(List * constraints,char * name,Node * expr)2561 MergeCheckConstraint(List *constraints, char *name, Node *expr)
2562 {
2563 ListCell *lc;
2564
2565 foreach(lc, constraints)
2566 {
2567 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
2568
2569 Assert(ccon->contype == CONSTR_CHECK);
2570
2571 /* Non-matching names never conflict */
2572 if (strcmp(ccon->name, name) != 0)
2573 continue;
2574
2575 if (equal(expr, ccon->expr))
2576 {
2577 /* OK to merge */
2578 ccon->inhcount++;
2579 return true;
2580 }
2581
2582 ereport(ERROR,
2583 (errcode(ERRCODE_DUPLICATE_OBJECT),
2584 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
2585 name)));
2586 }
2587
2588 return false;
2589 }
2590
2591
2592 /*
2593 * StoreCatalogInheritance
2594 * Updates the system catalogs with proper inheritance information.
2595 *
2596 * supers is a list of the OIDs of the new relation's direct ancestors.
2597 */
2598 static void
StoreCatalogInheritance(Oid relationId,List * supers,bool child_is_partition)2599 StoreCatalogInheritance(Oid relationId, List *supers,
2600 bool child_is_partition)
2601 {
2602 Relation relation;
2603 int32 seqNumber;
2604 ListCell *entry;
2605
2606 /*
2607 * sanity checks
2608 */
2609 AssertArg(OidIsValid(relationId));
2610
2611 if (supers == NIL)
2612 return;
2613
2614 /*
2615 * Store INHERITS information in pg_inherits using direct ancestors only.
2616 * Also enter dependencies on the direct ancestors, and make sure they are
2617 * marked with relhassubclass = true.
2618 *
2619 * (Once upon a time, both direct and indirect ancestors were found here
2620 * and then entered into pg_ipl. Since that catalog doesn't exist
2621 * anymore, there's no need to look for indirect ancestors.)
2622 */
2623 relation = heap_open(InheritsRelationId, RowExclusiveLock);
2624
2625 seqNumber = 1;
2626 foreach(entry, supers)
2627 {
2628 Oid parentOid = lfirst_oid(entry);
2629
2630 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
2631 child_is_partition);
2632 seqNumber++;
2633 }
2634
2635 heap_close(relation, RowExclusiveLock);
2636 }
2637
2638 /*
2639 * Make catalog entries showing relationId as being an inheritance child
2640 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
2641 */
2642 static void
StoreCatalogInheritance1(Oid relationId,Oid parentOid,int32 seqNumber,Relation inhRelation,bool child_is_partition)2643 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
2644 int32 seqNumber, Relation inhRelation,
2645 bool child_is_partition)
2646 {
2647 ObjectAddress childobject,
2648 parentobject;
2649
2650 /* store the pg_inherits row */
2651 StoreSingleInheritance(relationId, parentOid, seqNumber);
2652
2653 /*
2654 * Store a dependency too
2655 */
2656 parentobject.classId = RelationRelationId;
2657 parentobject.objectId = parentOid;
2658 parentobject.objectSubId = 0;
2659 childobject.classId = RelationRelationId;
2660 childobject.objectId = relationId;
2661 childobject.objectSubId = 0;
2662
2663 recordDependencyOn(&childobject, &parentobject,
2664 child_dependency_type(child_is_partition));
2665
2666 /*
2667 * Post creation hook of this inheritance. Since object_access_hook
2668 * doesn't take multiple object identifiers, we relay oid of parent
2669 * relation using auxiliary_id argument.
2670 */
2671 InvokeObjectPostAlterHookArg(InheritsRelationId,
2672 relationId, 0,
2673 parentOid, false);
2674
2675 /*
2676 * Mark the parent as having subclasses.
2677 */
2678 SetRelationHasSubclass(parentOid, true);
2679 }
2680
2681 /*
2682 * Look for an existing schema entry with the given name.
2683 *
2684 * Returns the index (starting with 1) if attribute already exists in schema,
2685 * 0 if it doesn't.
2686 */
2687 static int
findAttrByName(const char * attributeName,List * schema)2688 findAttrByName(const char *attributeName, List *schema)
2689 {
2690 ListCell *s;
2691 int i = 1;
2692
2693 foreach(s, schema)
2694 {
2695 ColumnDef *def = lfirst(s);
2696
2697 if (strcmp(attributeName, def->colname) == 0)
2698 return i;
2699
2700 i++;
2701 }
2702 return 0;
2703 }
2704
2705
2706 /*
2707 * SetRelationHasSubclass
2708 * Set the value of the relation's relhassubclass field in pg_class.
2709 *
2710 * NOTE: caller must be holding an appropriate lock on the relation.
2711 * ShareUpdateExclusiveLock is sufficient.
2712 *
2713 * NOTE: an important side-effect of this operation is that an SI invalidation
2714 * message is sent out to all backends --- including me --- causing plans
2715 * referencing the relation to be rebuilt with the new list of children.
2716 * This must happen even if we find that no change is needed in the pg_class
2717 * row.
2718 */
2719 void
SetRelationHasSubclass(Oid relationId,bool relhassubclass)2720 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
2721 {
2722 Relation relationRelation;
2723 HeapTuple tuple;
2724 Form_pg_class classtuple;
2725
2726 /*
2727 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
2728 */
2729 relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
2730 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
2731 if (!HeapTupleIsValid(tuple))
2732 elog(ERROR, "cache lookup failed for relation %u", relationId);
2733 classtuple = (Form_pg_class) GETSTRUCT(tuple);
2734
2735 if (classtuple->relhassubclass != relhassubclass)
2736 {
2737 classtuple->relhassubclass = relhassubclass;
2738 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
2739 }
2740 else
2741 {
2742 /* no need to change tuple, but force relcache rebuild anyway */
2743 CacheInvalidateRelcacheByTuple(tuple);
2744 }
2745
2746 heap_freetuple(tuple);
2747 heap_close(relationRelation, RowExclusiveLock);
2748 }
2749
2750 /*
2751 * renameatt_check - basic sanity checks before attribute rename
2752 */
2753 static void
renameatt_check(Oid myrelid,Form_pg_class classform,bool recursing)2754 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
2755 {
2756 char relkind = classform->relkind;
2757
2758 if (classform->reloftype && !recursing)
2759 ereport(ERROR,
2760 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2761 errmsg("cannot rename column of typed table")));
2762
2763 /*
2764 * Renaming the columns of sequences or toast tables doesn't actually
2765 * break anything from the system's point of view, since internal
2766 * references are by attnum. But it doesn't seem right to allow users to
2767 * change names that are hardcoded into the system, hence the following
2768 * restriction.
2769 */
2770 if (relkind != RELKIND_RELATION &&
2771 relkind != RELKIND_VIEW &&
2772 relkind != RELKIND_MATVIEW &&
2773 relkind != RELKIND_COMPOSITE_TYPE &&
2774 relkind != RELKIND_INDEX &&
2775 relkind != RELKIND_PARTITIONED_INDEX &&
2776 relkind != RELKIND_FOREIGN_TABLE &&
2777 relkind != RELKIND_PARTITIONED_TABLE)
2778 ereport(ERROR,
2779 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2780 errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table",
2781 NameStr(classform->relname))));
2782
2783 /*
2784 * permissions checking. only the owner of a class can change its schema.
2785 */
2786 if (!pg_class_ownercheck(myrelid, GetUserId()))
2787 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
2788 NameStr(classform->relname));
2789 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
2790 ereport(ERROR,
2791 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2792 errmsg("permission denied: \"%s\" is a system catalog",
2793 NameStr(classform->relname))));
2794 }
2795
2796 /*
2797 * renameatt_internal - workhorse for renameatt
2798 *
2799 * Return value is the attribute number in the 'myrelid' relation.
2800 */
2801 static AttrNumber
renameatt_internal(Oid myrelid,const char * oldattname,const char * newattname,bool recurse,bool recursing,int expected_parents,DropBehavior behavior)2802 renameatt_internal(Oid myrelid,
2803 const char *oldattname,
2804 const char *newattname,
2805 bool recurse,
2806 bool recursing,
2807 int expected_parents,
2808 DropBehavior behavior)
2809 {
2810 Relation targetrelation;
2811 Relation attrelation;
2812 HeapTuple atttup;
2813 Form_pg_attribute attform;
2814 AttrNumber attnum;
2815
2816 /*
2817 * Grab an exclusive lock on the target table, which we will NOT release
2818 * until end of transaction.
2819 */
2820 targetrelation = relation_open(myrelid, AccessExclusiveLock);
2821 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
2822
2823 /*
2824 * if the 'recurse' flag is set then we are supposed to rename this
2825 * attribute in all classes that inherit from 'relname' (as well as in
2826 * 'relname').
2827 *
2828 * any permissions or problems with duplicate attributes will cause the
2829 * whole transaction to abort, which is what we want -- all or nothing.
2830 */
2831 if (recurse)
2832 {
2833 List *child_oids,
2834 *child_numparents;
2835 ListCell *lo,
2836 *li;
2837
2838 /*
2839 * we need the number of parents for each child so that the recursive
2840 * calls to renameatt() can determine whether there are any parents
2841 * outside the inheritance hierarchy being processed.
2842 */
2843 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
2844 &child_numparents);
2845
2846 /*
2847 * find_all_inheritors does the recursive search of the inheritance
2848 * hierarchy, so all we have to do is process all of the relids in the
2849 * list that it returns.
2850 */
2851 forboth(lo, child_oids, li, child_numparents)
2852 {
2853 Oid childrelid = lfirst_oid(lo);
2854 int numparents = lfirst_int(li);
2855
2856 if (childrelid == myrelid)
2857 continue;
2858 /* note we need not recurse again */
2859 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
2860 }
2861 }
2862 else
2863 {
2864 /*
2865 * If we are told not to recurse, there had better not be any child
2866 * tables; else the rename would put them out of step.
2867 *
2868 * expected_parents will only be 0 if we are not already recursing.
2869 */
2870 if (expected_parents == 0 &&
2871 find_inheritance_children(myrelid, NoLock) != NIL)
2872 ereport(ERROR,
2873 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2874 errmsg("inherited column \"%s\" must be renamed in child tables too",
2875 oldattname)));
2876 }
2877
2878 /* rename attributes in typed tables of composite type */
2879 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
2880 {
2881 List *child_oids;
2882 ListCell *lo;
2883
2884 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
2885 RelationGetRelationName(targetrelation),
2886 behavior);
2887
2888 foreach(lo, child_oids)
2889 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
2890 }
2891
2892 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
2893
2894 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
2895 if (!HeapTupleIsValid(atttup))
2896 ereport(ERROR,
2897 (errcode(ERRCODE_UNDEFINED_COLUMN),
2898 errmsg("column \"%s\" does not exist",
2899 oldattname)));
2900 attform = (Form_pg_attribute) GETSTRUCT(atttup);
2901
2902 attnum = attform->attnum;
2903 if (attnum <= 0)
2904 ereport(ERROR,
2905 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2906 errmsg("cannot rename system column \"%s\"",
2907 oldattname)));
2908
2909 /*
2910 * if the attribute is inherited, forbid the renaming. if this is a
2911 * top-level call to renameatt(), then expected_parents will be 0, so the
2912 * effect of this code will be to prohibit the renaming if the attribute
2913 * is inherited at all. if this is a recursive call to renameatt(),
2914 * expected_parents will be the number of parents the current relation has
2915 * within the inheritance hierarchy being processed, so we'll prohibit the
2916 * renaming only if there are additional parents from elsewhere.
2917 */
2918 if (attform->attinhcount > expected_parents)
2919 ereport(ERROR,
2920 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2921 errmsg("cannot rename inherited column \"%s\"",
2922 oldattname)));
2923
2924 /* new name should not already exist */
2925 (void) check_for_column_name_collision(targetrelation, newattname, false);
2926
2927 /* apply the update */
2928 namestrcpy(&(attform->attname), newattname);
2929
2930 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
2931
2932 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
2933
2934 heap_freetuple(atttup);
2935
2936 heap_close(attrelation, RowExclusiveLock);
2937
2938 relation_close(targetrelation, NoLock); /* close rel but keep lock */
2939
2940 return attnum;
2941 }
2942
2943 /*
2944 * Perform permissions and integrity checks before acquiring a relation lock.
2945 */
2946 static void
RangeVarCallbackForRenameAttribute(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)2947 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
2948 void *arg)
2949 {
2950 HeapTuple tuple;
2951 Form_pg_class form;
2952
2953 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
2954 if (!HeapTupleIsValid(tuple))
2955 return; /* concurrently dropped */
2956 form = (Form_pg_class) GETSTRUCT(tuple);
2957 renameatt_check(relid, form, false);
2958 ReleaseSysCache(tuple);
2959 }
2960
2961 /*
2962 * renameatt - changes the name of an attribute in a relation
2963 *
2964 * The returned ObjectAddress is that of the renamed column.
2965 */
2966 ObjectAddress
renameatt(RenameStmt * stmt)2967 renameatt(RenameStmt *stmt)
2968 {
2969 Oid relid;
2970 AttrNumber attnum;
2971 ObjectAddress address;
2972
2973 /* lock level taken here should match renameatt_internal */
2974 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
2975 stmt->missing_ok ? RVR_MISSING_OK : 0,
2976 RangeVarCallbackForRenameAttribute,
2977 NULL);
2978
2979 if (!OidIsValid(relid))
2980 {
2981 ereport(NOTICE,
2982 (errmsg("relation \"%s\" does not exist, skipping",
2983 stmt->relation->relname)));
2984 return InvalidObjectAddress;
2985 }
2986
2987 attnum =
2988 renameatt_internal(relid,
2989 stmt->subname, /* old att name */
2990 stmt->newname, /* new att name */
2991 stmt->relation->inh, /* recursive? */
2992 false, /* recursing? */
2993 0, /* expected inhcount */
2994 stmt->behavior);
2995
2996 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
2997
2998 return address;
2999 }
3000
3001 /*
3002 * same logic as renameatt_internal
3003 */
3004 static ObjectAddress
rename_constraint_internal(Oid myrelid,Oid mytypid,const char * oldconname,const char * newconname,bool recurse,bool recursing,int expected_parents)3005 rename_constraint_internal(Oid myrelid,
3006 Oid mytypid,
3007 const char *oldconname,
3008 const char *newconname,
3009 bool recurse,
3010 bool recursing,
3011 int expected_parents)
3012 {
3013 Relation targetrelation = NULL;
3014 Oid constraintOid;
3015 HeapTuple tuple;
3016 Form_pg_constraint con;
3017 ObjectAddress address;
3018
3019 AssertArg(!myrelid || !mytypid);
3020
3021 if (mytypid)
3022 {
3023 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3024 }
3025 else
3026 {
3027 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3028
3029 /*
3030 * don't tell it whether we're recursing; we allow changing typed
3031 * tables here
3032 */
3033 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3034
3035 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3036 }
3037
3038 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3039 if (!HeapTupleIsValid(tuple))
3040 elog(ERROR, "cache lookup failed for constraint %u",
3041 constraintOid);
3042 con = (Form_pg_constraint) GETSTRUCT(tuple);
3043
3044 if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
3045 {
3046 if (recurse)
3047 {
3048 List *child_oids,
3049 *child_numparents;
3050 ListCell *lo,
3051 *li;
3052
3053 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3054 &child_numparents);
3055
3056 forboth(lo, child_oids, li, child_numparents)
3057 {
3058 Oid childrelid = lfirst_oid(lo);
3059 int numparents = lfirst_int(li);
3060
3061 if (childrelid == myrelid)
3062 continue;
3063
3064 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3065 }
3066 }
3067 else
3068 {
3069 if (expected_parents == 0 &&
3070 find_inheritance_children(myrelid, NoLock) != NIL)
3071 ereport(ERROR,
3072 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3073 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3074 oldconname)));
3075 }
3076
3077 if (con->coninhcount > expected_parents)
3078 ereport(ERROR,
3079 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3080 errmsg("cannot rename inherited constraint \"%s\"",
3081 oldconname)));
3082 }
3083
3084 if (con->conindid
3085 && (con->contype == CONSTRAINT_PRIMARY
3086 || con->contype == CONSTRAINT_UNIQUE
3087 || con->contype == CONSTRAINT_EXCLUSION))
3088 /* rename the index; this renames the constraint as well */
3089 RenameRelationInternal(con->conindid, newconname, false);
3090 else
3091 RenameConstraintById(constraintOid, newconname);
3092
3093 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
3094
3095 ReleaseSysCache(tuple);
3096
3097 if (targetrelation)
3098 {
3099 /*
3100 * Invalidate relcache so as others can see the new constraint name.
3101 */
3102 CacheInvalidateRelcache(targetrelation);
3103
3104 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3105 }
3106
3107 return address;
3108 }
3109
3110 ObjectAddress
RenameConstraint(RenameStmt * stmt)3111 RenameConstraint(RenameStmt *stmt)
3112 {
3113 Oid relid = InvalidOid;
3114 Oid typid = InvalidOid;
3115
3116 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
3117 {
3118 Relation rel;
3119 HeapTuple tup;
3120
3121 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
3122 rel = heap_open(TypeRelationId, RowExclusiveLock);
3123 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
3124 if (!HeapTupleIsValid(tup))
3125 elog(ERROR, "cache lookup failed for type %u", typid);
3126 checkDomainOwner(tup);
3127 ReleaseSysCache(tup);
3128 heap_close(rel, NoLock);
3129 }
3130 else
3131 {
3132 /* lock level taken here should match rename_constraint_internal */
3133 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3134 stmt->missing_ok ? RVR_MISSING_OK : 0,
3135 RangeVarCallbackForRenameAttribute,
3136 NULL);
3137 if (!OidIsValid(relid))
3138 {
3139 ereport(NOTICE,
3140 (errmsg("relation \"%s\" does not exist, skipping",
3141 stmt->relation->relname)));
3142 return InvalidObjectAddress;
3143 }
3144 }
3145
3146 return
3147 rename_constraint_internal(relid, typid,
3148 stmt->subname,
3149 stmt->newname,
3150 (stmt->relation &&
3151 stmt->relation->inh), /* recursive? */
3152 false, /* recursing? */
3153 0 /* expected inhcount */ );
3154
3155 }
3156
3157 /*
3158 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
3159 * RENAME
3160 */
3161 ObjectAddress
RenameRelation(RenameStmt * stmt)3162 RenameRelation(RenameStmt *stmt)
3163 {
3164 Oid relid;
3165 ObjectAddress address;
3166
3167 /*
3168 * Grab an exclusive lock on the target table, index, sequence, view,
3169 * materialized view, or foreign table, which we will NOT release until
3170 * end of transaction.
3171 *
3172 * Lock level used here should match RenameRelationInternal, to avoid lock
3173 * escalation.
3174 */
3175 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3176 stmt->missing_ok ? RVR_MISSING_OK : 0,
3177 RangeVarCallbackForAlterRelation,
3178 (void *) stmt);
3179
3180 if (!OidIsValid(relid))
3181 {
3182 ereport(NOTICE,
3183 (errmsg("relation \"%s\" does not exist, skipping",
3184 stmt->relation->relname)));
3185 return InvalidObjectAddress;
3186 }
3187
3188 /* Do the work */
3189 RenameRelationInternal(relid, stmt->newname, false);
3190
3191 ObjectAddressSet(address, RelationRelationId, relid);
3192
3193 return address;
3194 }
3195
3196 /*
3197 * RenameRelationInternal - change the name of a relation
3198 */
3199 void
RenameRelationInternal(Oid myrelid,const char * newrelname,bool is_internal)3200 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
3201 {
3202 Relation targetrelation;
3203 Relation relrelation; /* for RELATION relation */
3204 HeapTuple reltup;
3205 Form_pg_class relform;
3206 Oid namespaceId;
3207
3208 /*
3209 * Grab an exclusive lock on the target table, index, sequence, view,
3210 * materialized view, or foreign table, which we will NOT release until
3211 * end of transaction.
3212 */
3213 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3214 namespaceId = RelationGetNamespace(targetrelation);
3215
3216 /*
3217 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
3218 */
3219 relrelation = heap_open(RelationRelationId, RowExclusiveLock);
3220
3221 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
3222 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
3223 elog(ERROR, "cache lookup failed for relation %u", myrelid);
3224 relform = (Form_pg_class) GETSTRUCT(reltup);
3225
3226 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
3227 ereport(ERROR,
3228 (errcode(ERRCODE_DUPLICATE_TABLE),
3229 errmsg("relation \"%s\" already exists",
3230 newrelname)));
3231
3232 /*
3233 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
3234 * because it's a copy...)
3235 */
3236 namestrcpy(&(relform->relname), newrelname);
3237
3238 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
3239
3240 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
3241 InvalidOid, is_internal);
3242
3243 heap_freetuple(reltup);
3244 heap_close(relrelation, RowExclusiveLock);
3245
3246 /*
3247 * Also rename the associated type, if any.
3248 */
3249 if (OidIsValid(targetrelation->rd_rel->reltype))
3250 RenameTypeInternal(targetrelation->rd_rel->reltype,
3251 newrelname, namespaceId);
3252
3253 /*
3254 * Also rename the associated constraint, if any.
3255 */
3256 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
3257 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
3258 {
3259 Oid constraintId = get_index_constraint(myrelid);
3260
3261 if (OidIsValid(constraintId))
3262 RenameConstraintById(constraintId, newrelname);
3263 }
3264
3265 /*
3266 * Close rel, but keep exclusive lock!
3267 */
3268 relation_close(targetrelation, NoLock);
3269 }
3270
3271 /*
3272 * ResetRelRewrite - reset relrewrite
3273 */
3274 void
ResetRelRewrite(Oid myrelid)3275 ResetRelRewrite(Oid myrelid)
3276 {
3277 Relation relrelation; /* for RELATION relation */
3278 HeapTuple reltup;
3279 Form_pg_class relform;
3280
3281 /*
3282 * Find relation's pg_class tuple.
3283 */
3284 relrelation = heap_open(RelationRelationId, RowExclusiveLock);
3285
3286 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
3287 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
3288 elog(ERROR, "cache lookup failed for relation %u", myrelid);
3289 relform = (Form_pg_class) GETSTRUCT(reltup);
3290
3291 /*
3292 * Update pg_class tuple.
3293 */
3294 relform->relrewrite = InvalidOid;
3295
3296 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
3297
3298 heap_freetuple(reltup);
3299 heap_close(relrelation, RowExclusiveLock);
3300 }
3301
3302 /*
3303 * Disallow ALTER TABLE (and similar commands) when the current backend has
3304 * any open reference to the target table besides the one just acquired by
3305 * the calling command; this implies there's an open cursor or active plan.
3306 * We need this check because our lock doesn't protect us against stomping
3307 * on our own foot, only other people's feet!
3308 *
3309 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
3310 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
3311 * possibly be relaxed to only error out for certain types of alterations.
3312 * But the use-case for allowing any of these things is not obvious, so we
3313 * won't work hard at it for now.
3314 *
3315 * We also reject these commands if there are any pending AFTER trigger events
3316 * for the rel. This is certainly necessary for the rewriting variants of
3317 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
3318 * events would try to fetch the wrong tuples. It might be overly cautious
3319 * in other cases, but again it seems better to err on the side of paranoia.
3320 *
3321 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
3322 * we are worried about active indexscans on the index. The trigger-event
3323 * check can be skipped, since we are doing no damage to the parent table.
3324 *
3325 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
3326 */
3327 void
CheckTableNotInUse(Relation rel,const char * stmt)3328 CheckTableNotInUse(Relation rel, const char *stmt)
3329 {
3330 int expected_refcnt;
3331
3332 expected_refcnt = rel->rd_isnailed ? 2 : 1;
3333 if (rel->rd_refcnt != expected_refcnt)
3334 ereport(ERROR,
3335 (errcode(ERRCODE_OBJECT_IN_USE),
3336 /* translator: first %s is a SQL command, eg ALTER TABLE */
3337 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
3338 stmt, RelationGetRelationName(rel))));
3339
3340 if (rel->rd_rel->relkind != RELKIND_INDEX &&
3341 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
3342 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
3343 ereport(ERROR,
3344 (errcode(ERRCODE_OBJECT_IN_USE),
3345 /* translator: first %s is a SQL command, eg ALTER TABLE */
3346 errmsg("cannot %s \"%s\" because it has pending trigger events",
3347 stmt, RelationGetRelationName(rel))));
3348 }
3349
3350 /*
3351 * AlterTableLookupRelation
3352 * Look up, and lock, the OID for the relation named by an alter table
3353 * statement.
3354 */
3355 Oid
AlterTableLookupRelation(AlterTableStmt * stmt,LOCKMODE lockmode)3356 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
3357 {
3358 return RangeVarGetRelidExtended(stmt->relation, lockmode,
3359 stmt->missing_ok ? RVR_MISSING_OK : 0,
3360 RangeVarCallbackForAlterRelation,
3361 (void *) stmt);
3362 }
3363
3364 /*
3365 * AlterTable
3366 * Execute ALTER TABLE, which can be a list of subcommands
3367 *
3368 * ALTER TABLE is performed in three phases:
3369 * 1. Examine subcommands and perform pre-transformation checking.
3370 * 2. Update system catalogs.
3371 * 3. Scan table(s) to check new constraints, and optionally recopy
3372 * the data into new table(s).
3373 * Phase 3 is not performed unless one or more of the subcommands requires
3374 * it. The intention of this design is to allow multiple independent
3375 * updates of the table schema to be performed with only one pass over the
3376 * data.
3377 *
3378 * ATPrepCmd performs phase 1. A "work queue" entry is created for
3379 * each table to be affected (there may be multiple affected tables if the
3380 * commands traverse a table inheritance hierarchy). Also we do preliminary
3381 * validation of the subcommands, including parse transformation of those
3382 * expressions that need to be evaluated with respect to the old table
3383 * schema.
3384 *
3385 * ATRewriteCatalogs performs phase 2 for each affected table. (Note that
3386 * phases 2 and 3 normally do no explicit recursion, since phase 1 already
3387 * did it --- although some subcommands have to recurse in phase 2 instead.)
3388 * Certain subcommands need to be performed before others to avoid
3389 * unnecessary conflicts; for example, DROP COLUMN should come before
3390 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
3391 * lists, one for each logical "pass" of phase 2.
3392 *
3393 * ATRewriteTables performs phase 3 for those tables that need it.
3394 *
3395 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
3396 * the whole operation; we don't have to do anything special to clean up.
3397 *
3398 * The caller must lock the relation, with an appropriate lock level
3399 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
3400 * or higher. We pass the lock level down
3401 * so that we can apply it recursively to inherited tables. Note that the
3402 * lock level we want as we recurse might well be higher than required for
3403 * that specific subcommand. So we pass down the overall lock requirement,
3404 * rather than reassess it at lower levels.
3405 */
3406 void
AlterTable(Oid relid,LOCKMODE lockmode,AlterTableStmt * stmt)3407 AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt)
3408 {
3409 Relation rel;
3410
3411 /* Caller is required to provide an adequate lock. */
3412 rel = relation_open(relid, NoLock);
3413
3414 CheckTableNotInUse(rel, "ALTER TABLE");
3415
3416 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode);
3417 }
3418
3419 /*
3420 * AlterTableInternal
3421 *
3422 * ALTER TABLE with target specified by OID
3423 *
3424 * We do not reject if the relation is already open, because it's quite
3425 * likely that one or more layers of caller have it open. That means it
3426 * is unsafe to use this entry point for alterations that could break
3427 * existing query plans. On the assumption it's not used for such, we
3428 * don't have to reject pending AFTER triggers, either.
3429 */
3430 void
AlterTableInternal(Oid relid,List * cmds,bool recurse)3431 AlterTableInternal(Oid relid, List *cmds, bool recurse)
3432 {
3433 Relation rel;
3434 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
3435
3436 rel = relation_open(relid, lockmode);
3437
3438 EventTriggerAlterTableRelid(relid);
3439
3440 ATController(NULL, rel, cmds, recurse, lockmode);
3441 }
3442
3443 /*
3444 * AlterTableGetLockLevel
3445 *
3446 * Sets the overall lock level required for the supplied list of subcommands.
3447 * Policy for doing this set according to needs of AlterTable(), see
3448 * comments there for overall explanation.
3449 *
3450 * Function is called before and after parsing, so it must give same
3451 * answer each time it is called. Some subcommands are transformed
3452 * into other subcommand types, so the transform must never be made to a
3453 * lower lock level than previously assigned. All transforms are noted below.
3454 *
3455 * Since this is called before we lock the table we cannot use table metadata
3456 * to influence the type of lock we acquire.
3457 *
3458 * There should be no lockmodes hardcoded into the subcommand functions. All
3459 * lockmode decisions for ALTER TABLE are made here only. The one exception is
3460 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
3461 * and does not travel through this section of code and cannot be combined with
3462 * any of the subcommands given here.
3463 *
3464 * Note that Hot Standby only knows about AccessExclusiveLocks on the master
3465 * so any changes that might affect SELECTs running on standbys need to use
3466 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
3467 * have a solution for that also.
3468 *
3469 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
3470 * that takes a lock less than AccessExclusiveLock can change object definitions
3471 * while pg_dump is running. Be careful to check that the appropriate data is
3472 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
3473 * otherwise we might end up with an inconsistent dump that can't restore.
3474 */
3475 LOCKMODE
AlterTableGetLockLevel(List * cmds)3476 AlterTableGetLockLevel(List *cmds)
3477 {
3478 /*
3479 * This only works if we read catalog tables using MVCC snapshots.
3480 */
3481 ListCell *lcmd;
3482 LOCKMODE lockmode = ShareUpdateExclusiveLock;
3483
3484 foreach(lcmd, cmds)
3485 {
3486 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3487 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
3488
3489 switch (cmd->subtype)
3490 {
3491 /*
3492 * These subcommands rewrite the heap, so require full locks.
3493 */
3494 case AT_AddColumn: /* may rewrite heap, in some cases and visible
3495 * to SELECT */
3496 case AT_SetTableSpace: /* must rewrite heap */
3497 case AT_AlterColumnType: /* must rewrite heap */
3498 case AT_AddOids: /* must rewrite heap */
3499 cmd_lockmode = AccessExclusiveLock;
3500 break;
3501
3502 /*
3503 * These subcommands may require addition of toast tables. If
3504 * we add a toast table to a table currently being scanned, we
3505 * might miss data added to the new toast table by concurrent
3506 * insert transactions.
3507 */
3508 case AT_SetStorage: /* may add toast tables, see
3509 * ATRewriteCatalogs() */
3510 cmd_lockmode = AccessExclusiveLock;
3511 break;
3512
3513 /*
3514 * Removing constraints can affect SELECTs that have been
3515 * optimised assuming the constraint holds true.
3516 */
3517 case AT_DropConstraint: /* as DROP INDEX */
3518 case AT_DropNotNull: /* may change some SQL plans */
3519 cmd_lockmode = AccessExclusiveLock;
3520 break;
3521
3522 /*
3523 * Subcommands that may be visible to concurrent SELECTs
3524 */
3525 case AT_DropColumn: /* change visible to SELECT */
3526 case AT_AddColumnToView: /* CREATE VIEW */
3527 case AT_DropOids: /* calls AT_DropColumn */
3528 case AT_EnableAlwaysRule: /* may change SELECT rules */
3529 case AT_EnableReplicaRule: /* may change SELECT rules */
3530 case AT_EnableRule: /* may change SELECT rules */
3531 case AT_DisableRule: /* may change SELECT rules */
3532 cmd_lockmode = AccessExclusiveLock;
3533 break;
3534
3535 /*
3536 * Changing owner may remove implicit SELECT privileges
3537 */
3538 case AT_ChangeOwner: /* change visible to SELECT */
3539 cmd_lockmode = AccessExclusiveLock;
3540 break;
3541
3542 /*
3543 * Changing foreign table options may affect optimization.
3544 */
3545 case AT_GenericOptions:
3546 case AT_AlterColumnGenericOptions:
3547 cmd_lockmode = AccessExclusiveLock;
3548 break;
3549
3550 /*
3551 * These subcommands affect write operations only.
3552 */
3553 case AT_EnableTrig:
3554 case AT_EnableAlwaysTrig:
3555 case AT_EnableReplicaTrig:
3556 case AT_EnableTrigAll:
3557 case AT_EnableTrigUser:
3558 case AT_DisableTrig:
3559 case AT_DisableTrigAll:
3560 case AT_DisableTrigUser:
3561 cmd_lockmode = ShareRowExclusiveLock;
3562 break;
3563
3564 /*
3565 * These subcommands affect write operations only. XXX
3566 * Theoretically, these could be ShareRowExclusiveLock.
3567 */
3568 case AT_ColumnDefault:
3569 case AT_AlterConstraint:
3570 case AT_AddIndex: /* from ADD CONSTRAINT */
3571 case AT_AddIndexConstraint:
3572 case AT_ReplicaIdentity:
3573 case AT_SetNotNull:
3574 case AT_EnableRowSecurity:
3575 case AT_DisableRowSecurity:
3576 case AT_ForceRowSecurity:
3577 case AT_NoForceRowSecurity:
3578 case AT_AddIdentity:
3579 case AT_DropIdentity:
3580 case AT_SetIdentity:
3581 cmd_lockmode = AccessExclusiveLock;
3582 break;
3583
3584 case AT_AddConstraint:
3585 case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
3586 case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
3587 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
3588 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
3589 if (IsA(cmd->def, Constraint))
3590 {
3591 Constraint *con = (Constraint *) cmd->def;
3592
3593 switch (con->contype)
3594 {
3595 case CONSTR_EXCLUSION:
3596 case CONSTR_PRIMARY:
3597 case CONSTR_UNIQUE:
3598
3599 /*
3600 * Cases essentially the same as CREATE INDEX. We
3601 * could reduce the lock strength to ShareLock if
3602 * we can work out how to allow concurrent catalog
3603 * updates. XXX Might be set down to
3604 * ShareRowExclusiveLock but requires further
3605 * analysis.
3606 */
3607 cmd_lockmode = AccessExclusiveLock;
3608 break;
3609 case CONSTR_FOREIGN:
3610
3611 /*
3612 * We add triggers to both tables when we add a
3613 * Foreign Key, so the lock level must be at least
3614 * as strong as CREATE TRIGGER.
3615 */
3616 cmd_lockmode = ShareRowExclusiveLock;
3617 break;
3618
3619 default:
3620 cmd_lockmode = AccessExclusiveLock;
3621 }
3622 }
3623 break;
3624
3625 /*
3626 * These subcommands affect inheritance behaviour. Queries
3627 * started before us will continue to see the old inheritance
3628 * behaviour, while queries started after we commit will see
3629 * new behaviour. No need to prevent reads or writes to the
3630 * subtable while we hook it up though. Changing the TupDesc
3631 * may be a problem, so keep highest lock.
3632 */
3633 case AT_AddInherit:
3634 case AT_DropInherit:
3635 cmd_lockmode = AccessExclusiveLock;
3636 break;
3637
3638 /*
3639 * These subcommands affect implicit row type conversion. They
3640 * have affects similar to CREATE/DROP CAST on queries. don't
3641 * provide for invalidating parse trees as a result of such
3642 * changes, so we keep these at AccessExclusiveLock.
3643 */
3644 case AT_AddOf:
3645 case AT_DropOf:
3646 cmd_lockmode = AccessExclusiveLock;
3647 break;
3648
3649 /*
3650 * Only used by CREATE OR REPLACE VIEW which must conflict
3651 * with an SELECTs currently using the view.
3652 */
3653 case AT_ReplaceRelOptions:
3654 cmd_lockmode = AccessExclusiveLock;
3655 break;
3656
3657 /*
3658 * These subcommands affect general strategies for performance
3659 * and maintenance, though don't change the semantic results
3660 * from normal data reads and writes. Delaying an ALTER TABLE
3661 * behind currently active writes only delays the point where
3662 * the new strategy begins to take effect, so there is no
3663 * benefit in waiting. In this case the minimum restriction
3664 * applies: we don't currently allow concurrent catalog
3665 * updates.
3666 */
3667 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
3668 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
3669 case AT_DropCluster: /* Uses MVCC in getIndexes() */
3670 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
3671 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
3672 cmd_lockmode = ShareUpdateExclusiveLock;
3673 break;
3674
3675 case AT_SetLogged:
3676 case AT_SetUnLogged:
3677 cmd_lockmode = AccessExclusiveLock;
3678 break;
3679
3680 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
3681 cmd_lockmode = ShareUpdateExclusiveLock;
3682 break;
3683
3684 /*
3685 * Rel options are more complex than first appears. Options
3686 * are set here for tables, views and indexes; for historical
3687 * reasons these can all be used with ALTER TABLE, so we can't
3688 * decide between them using the basic grammar.
3689 */
3690 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
3691 * getTables() */
3692 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
3693 * getTables() */
3694 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
3695 break;
3696
3697 case AT_AttachPartition:
3698 case AT_DetachPartition:
3699 cmd_lockmode = AccessExclusiveLock;
3700 break;
3701
3702 default: /* oops */
3703 elog(ERROR, "unrecognized alter table type: %d",
3704 (int) cmd->subtype);
3705 break;
3706 }
3707
3708 /*
3709 * Take the greatest lockmode from any subcommand
3710 */
3711 if (cmd_lockmode > lockmode)
3712 lockmode = cmd_lockmode;
3713 }
3714
3715 return lockmode;
3716 }
3717
3718 /*
3719 * ATController provides top level control over the phases.
3720 *
3721 * parsetree is passed in to allow it to be passed to event triggers
3722 * when requested.
3723 */
3724 static void
ATController(AlterTableStmt * parsetree,Relation rel,List * cmds,bool recurse,LOCKMODE lockmode)3725 ATController(AlterTableStmt *parsetree,
3726 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
3727 {
3728 List *wqueue = NIL;
3729 ListCell *lcmd;
3730
3731 /* Phase 1: preliminary examination of commands, create work queue */
3732 foreach(lcmd, cmds)
3733 {
3734 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3735
3736 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode);
3737 }
3738
3739 /* Close the relation, but keep lock until commit */
3740 relation_close(rel, NoLock);
3741
3742 /* Phase 2: update system catalogs */
3743 ATRewriteCatalogs(&wqueue, lockmode);
3744
3745 /* Phase 3: scan/rewrite tables as needed */
3746 ATRewriteTables(parsetree, &wqueue, lockmode);
3747 }
3748
3749 /*
3750 * ATPrepCmd
3751 *
3752 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
3753 * recursion and permission checks.
3754 *
3755 * Caller must have acquired appropriate lock type on relation already.
3756 * This lock should be held until commit.
3757 */
3758 static void
ATPrepCmd(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode)3759 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
3760 bool recurse, bool recursing, LOCKMODE lockmode)
3761 {
3762 AlteredTableInfo *tab;
3763 int pass = AT_PASS_UNSET;
3764
3765 /* Find or create work queue entry for this table */
3766 tab = ATGetQueueEntry(wqueue, rel);
3767
3768 /*
3769 * Copy the original subcommand for each table. This avoids conflicts
3770 * when different child tables need to make different parse
3771 * transformations (for example, the same column may have different column
3772 * numbers in different children).
3773 */
3774 cmd = copyObject(cmd);
3775
3776 /*
3777 * Do permissions checking, recursion to child tables if needed, and any
3778 * additional phase-1 processing needed.
3779 */
3780 switch (cmd->subtype)
3781 {
3782 case AT_AddColumn: /* ADD COLUMN */
3783 ATSimplePermissions(rel,
3784 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3785 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
3786 lockmode);
3787 /* Recursion occurs during execution phase */
3788 pass = AT_PASS_ADD_COL;
3789 break;
3790 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
3791 ATSimplePermissions(rel, ATT_VIEW);
3792 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
3793 lockmode);
3794 /* Recursion occurs during execution phase */
3795 pass = AT_PASS_ADD_COL;
3796 break;
3797 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
3798
3799 /*
3800 * We allow defaults on views so that INSERT into a view can have
3801 * default-ish behavior. This works because the rewriter
3802 * substitutes default values into INSERTs before it expands
3803 * rules.
3804 */
3805 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3806 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3807 /* No command-specific prep needed */
3808 pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
3809 break;
3810 case AT_AddIdentity:
3811 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3812 /* This command never recurses */
3813 pass = AT_PASS_ADD_CONSTR;
3814 break;
3815 case AT_SetIdentity:
3816 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3817 /* This command never recurses */
3818 pass = AT_PASS_COL_ATTRS;
3819 break;
3820 case AT_DropIdentity:
3821 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3822 /* This command never recurses */
3823 pass = AT_PASS_DROP;
3824 break;
3825 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
3826 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3827 ATPrepDropNotNull(rel, recurse, recursing);
3828 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3829 /* No command-specific prep needed */
3830 pass = AT_PASS_DROP;
3831 break;
3832 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
3833 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3834 ATPrepSetNotNull(rel, recurse, recursing);
3835 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3836 /* No command-specific prep needed */
3837 pass = AT_PASS_ADD_CONSTR;
3838 break;
3839 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
3840 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3841 /* Performs own permission checks */
3842 ATPrepSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
3843 pass = AT_PASS_MISC;
3844 break;
3845 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
3846 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
3847 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE);
3848 /* This command never recurses */
3849 pass = AT_PASS_MISC;
3850 break;
3851 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
3852 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
3853 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3854 /* No command-specific prep needed */
3855 pass = AT_PASS_MISC;
3856 break;
3857 case AT_DropColumn: /* DROP COLUMN */
3858 ATSimplePermissions(rel,
3859 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3860 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
3861 /* Recursion occurs during execution phase */
3862 pass = AT_PASS_DROP;
3863 break;
3864 case AT_AddIndex: /* ADD INDEX */
3865 ATSimplePermissions(rel, ATT_TABLE);
3866 /* This command never recurses */
3867 /* No command-specific prep needed */
3868 pass = AT_PASS_ADD_INDEX;
3869 break;
3870 case AT_AddConstraint: /* ADD CONSTRAINT */
3871 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3872 /* Recursion occurs during execution phase */
3873 /* No command-specific prep needed except saving recurse flag */
3874 if (recurse)
3875 cmd->subtype = AT_AddConstraintRecurse;
3876 pass = AT_PASS_ADD_CONSTR;
3877 break;
3878 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
3879 ATSimplePermissions(rel, ATT_TABLE);
3880 /* This command never recurses */
3881 /* No command-specific prep needed */
3882 pass = AT_PASS_ADD_CONSTR;
3883 break;
3884 case AT_DropConstraint: /* DROP CONSTRAINT */
3885 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3886 ATCheckPartitionsNotInUse(rel, lockmode);
3887 /* Other recursion occurs during execution phase */
3888 /* No command-specific prep needed except saving recurse flag */
3889 if (recurse)
3890 cmd->subtype = AT_DropConstraintRecurse;
3891 pass = AT_PASS_DROP;
3892 break;
3893 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
3894 ATSimplePermissions(rel,
3895 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3896 /* Performs own recursion */
3897 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
3898 pass = AT_PASS_ALTER_TYPE;
3899 break;
3900 case AT_AlterColumnGenericOptions:
3901 ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
3902 /* This command never recurses */
3903 /* No command-specific prep needed */
3904 pass = AT_PASS_MISC;
3905 break;
3906 case AT_ChangeOwner: /* ALTER OWNER */
3907 /* This command never recurses */
3908 /* No command-specific prep needed */
3909 pass = AT_PASS_MISC;
3910 break;
3911 case AT_ClusterOn: /* CLUSTER ON */
3912 case AT_DropCluster: /* SET WITHOUT CLUSTER */
3913 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
3914 /* These commands never recurse */
3915 /* No command-specific prep needed */
3916 pass = AT_PASS_MISC;
3917 break;
3918 case AT_SetLogged: /* SET LOGGED */
3919 ATSimplePermissions(rel, ATT_TABLE);
3920 tab->chgPersistence = ATPrepChangePersistence(rel, true);
3921 /* force rewrite if necessary; see comment in ATRewriteTables */
3922 if (tab->chgPersistence)
3923 {
3924 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3925 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
3926 }
3927 pass = AT_PASS_MISC;
3928 break;
3929 case AT_SetUnLogged: /* SET UNLOGGED */
3930 ATSimplePermissions(rel, ATT_TABLE);
3931 tab->chgPersistence = ATPrepChangePersistence(rel, false);
3932 /* force rewrite if necessary; see comment in ATRewriteTables */
3933 if (tab->chgPersistence)
3934 {
3935 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3936 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
3937 }
3938 pass = AT_PASS_MISC;
3939 break;
3940 case AT_AddOids: /* SET WITH OIDS */
3941 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3942 if (!rel->rd_rel->relhasoids || recursing)
3943 ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
3944 /* Recursion occurs during execution phase */
3945 pass = AT_PASS_ADD_COL;
3946 break;
3947 case AT_DropOids: /* SET WITHOUT OIDS */
3948 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3949 /* Performs own recursion */
3950 if (rel->rd_rel->relhasoids)
3951 {
3952 AlterTableCmd *dropCmd = makeNode(AlterTableCmd);
3953
3954 dropCmd->subtype = AT_DropColumn;
3955 dropCmd->name = pstrdup("oid");
3956 dropCmd->behavior = cmd->behavior;
3957 ATPrepCmd(wqueue, rel, dropCmd, recurse, false, lockmode);
3958 }
3959 pass = AT_PASS_DROP;
3960 break;
3961 case AT_SetTableSpace: /* SET TABLESPACE */
3962 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
3963 ATT_PARTITIONED_INDEX);
3964 /* This command never recurses */
3965 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
3966 pass = AT_PASS_MISC; /* doesn't actually matter */
3967 break;
3968 case AT_SetRelOptions: /* SET (...) */
3969 case AT_ResetRelOptions: /* RESET (...) */
3970 case AT_ReplaceRelOptions: /* reset them all, then set just these */
3971 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
3972 /* This command never recurses */
3973 /* No command-specific prep needed */
3974 pass = AT_PASS_MISC;
3975 break;
3976 case AT_AddInherit: /* INHERIT */
3977 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3978 /* This command never recurses */
3979 ATPrepAddInherit(rel);
3980 pass = AT_PASS_MISC;
3981 break;
3982 case AT_DropInherit: /* NO INHERIT */
3983 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3984 /* This command never recurses */
3985 /* No command-specific prep needed */
3986 pass = AT_PASS_MISC;
3987 break;
3988 case AT_AlterConstraint: /* ALTER CONSTRAINT */
3989 ATSimplePermissions(rel, ATT_TABLE);
3990 /* Recursion occurs during execution phase */
3991 pass = AT_PASS_MISC;
3992 break;
3993 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
3994 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3995 /* Recursion occurs during execution phase */
3996 /* No command-specific prep needed except saving recurse flag */
3997 if (recurse)
3998 cmd->subtype = AT_ValidateConstraintRecurse;
3999 pass = AT_PASS_MISC;
4000 break;
4001 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
4002 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
4003 pass = AT_PASS_MISC;
4004 /* This command never recurses */
4005 /* No command-specific prep needed */
4006 break;
4007 case AT_EnableTrig: /* ENABLE TRIGGER variants */
4008 case AT_EnableAlwaysTrig:
4009 case AT_EnableReplicaTrig:
4010 case AT_EnableTrigAll:
4011 case AT_EnableTrigUser:
4012 case AT_DisableTrig: /* DISABLE TRIGGER variants */
4013 case AT_DisableTrigAll:
4014 case AT_DisableTrigUser:
4015 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4016 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4017 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
4018 pass = AT_PASS_MISC;
4019 break;
4020 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
4021 case AT_EnableAlwaysRule:
4022 case AT_EnableReplicaRule:
4023 case AT_DisableRule:
4024 case AT_AddOf: /* OF */
4025 case AT_DropOf: /* NOT OF */
4026 case AT_EnableRowSecurity:
4027 case AT_DisableRowSecurity:
4028 case AT_ForceRowSecurity:
4029 case AT_NoForceRowSecurity:
4030 ATSimplePermissions(rel, ATT_TABLE);
4031 /* These commands never recurse */
4032 /* No command-specific prep needed */
4033 pass = AT_PASS_MISC;
4034 break;
4035 case AT_GenericOptions:
4036 ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
4037 /* No command-specific prep needed */
4038 pass = AT_PASS_MISC;
4039 break;
4040 case AT_AttachPartition:
4041 ATSimplePermissions(rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
4042 /* No command-specific prep needed */
4043 pass = AT_PASS_MISC;
4044 break;
4045 case AT_DetachPartition:
4046 ATSimplePermissions(rel, ATT_TABLE);
4047 /* No command-specific prep needed */
4048 pass = AT_PASS_MISC;
4049 break;
4050 default: /* oops */
4051 elog(ERROR, "unrecognized alter table type: %d",
4052 (int) cmd->subtype);
4053 pass = AT_PASS_UNSET; /* keep compiler quiet */
4054 break;
4055 }
4056 Assert(pass > AT_PASS_UNSET);
4057
4058 /* Add the subcommand to the appropriate list for phase 2 */
4059 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
4060 }
4061
4062 /*
4063 * ATRewriteCatalogs
4064 *
4065 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
4066 * dispatched in a "safe" execution order (designed to avoid unnecessary
4067 * conflicts).
4068 */
4069 static void
ATRewriteCatalogs(List ** wqueue,LOCKMODE lockmode)4070 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
4071 {
4072 int pass;
4073 ListCell *ltab;
4074
4075 /*
4076 * We process all the tables "in parallel", one pass at a time. This is
4077 * needed because we may have to propagate work from one table to another
4078 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
4079 * re-adding of the foreign key constraint to the other table). Work can
4080 * only be propagated into later passes, however.
4081 */
4082 for (pass = 0; pass < AT_NUM_PASSES; pass++)
4083 {
4084 /* Go through each table that needs to be processed */
4085 foreach(ltab, *wqueue)
4086 {
4087 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4088 List *subcmds = tab->subcmds[pass];
4089 Relation rel;
4090 ListCell *lcmd;
4091
4092 if (subcmds == NIL)
4093 continue;
4094
4095 /*
4096 * Appropriate lock was obtained by phase 1, needn't get it again
4097 */
4098 rel = relation_open(tab->relid, NoLock);
4099
4100 foreach(lcmd, subcmds)
4101 ATExecCmd(wqueue, tab, rel,
4102 castNode(AlterTableCmd, lfirst(lcmd)),
4103 lockmode);
4104
4105 /*
4106 * After the ALTER TYPE pass, do cleanup work (this is not done in
4107 * ATExecAlterColumnType since it should be done only once if
4108 * multiple columns of a table are altered).
4109 */
4110 if (pass == AT_PASS_ALTER_TYPE)
4111 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
4112
4113 relation_close(rel, NoLock);
4114 }
4115 }
4116
4117 /* Check to see if a toast table must be added. */
4118 foreach(ltab, *wqueue)
4119 {
4120 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4121
4122 /*
4123 * If the table is source table of ATTACH PARTITION command, we did
4124 * not modify anything about it that will change its toasting
4125 * requirement, so no need to check.
4126 */
4127 if (((tab->relkind == RELKIND_RELATION ||
4128 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
4129 tab->partition_constraint == NULL) ||
4130 tab->relkind == RELKIND_MATVIEW)
4131 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
4132 }
4133 }
4134
4135 /*
4136 * ATExecCmd: dispatch a subcommand to appropriate execution routine
4137 */
4138 static void
ATExecCmd(List ** wqueue,AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode)4139 ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
4140 AlterTableCmd *cmd, LOCKMODE lockmode)
4141 {
4142 ObjectAddress address = InvalidObjectAddress;
4143
4144 switch (cmd->subtype)
4145 {
4146 case AT_AddColumn: /* ADD COLUMN */
4147 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4148 address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
4149 false, false, false,
4150 cmd->missing_ok, lockmode);
4151 break;
4152 case AT_AddColumnRecurse:
4153 address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
4154 false, true, false,
4155 cmd->missing_ok, lockmode);
4156 break;
4157 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4158 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
4159 break;
4160 case AT_AddIdentity:
4161 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
4162 break;
4163 case AT_SetIdentity:
4164 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
4165 break;
4166 case AT_DropIdentity:
4167 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
4168 break;
4169 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4170 address = ATExecDropNotNull(rel, cmd->name, lockmode);
4171 break;
4172 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4173 address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
4174 break;
4175 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4176 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
4177 break;
4178 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4179 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
4180 break;
4181 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4182 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
4183 break;
4184 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4185 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
4186 break;
4187 case AT_DropColumn: /* DROP COLUMN */
4188 address = ATExecDropColumn(wqueue, rel, cmd->name,
4189 cmd->behavior, false, false,
4190 cmd->missing_ok, lockmode);
4191 break;
4192 case AT_DropColumnRecurse: /* DROP COLUMN with recursion */
4193 address = ATExecDropColumn(wqueue, rel, cmd->name,
4194 cmd->behavior, true, false,
4195 cmd->missing_ok, lockmode);
4196 break;
4197 case AT_AddIndex: /* ADD INDEX */
4198 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
4199 lockmode);
4200 break;
4201 case AT_ReAddIndex: /* ADD INDEX */
4202 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
4203 lockmode);
4204 break;
4205 case AT_AddConstraint: /* ADD CONSTRAINT */
4206 address =
4207 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
4208 false, false, lockmode);
4209 break;
4210 case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
4211 address =
4212 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
4213 true, false, lockmode);
4214 break;
4215 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
4216 address =
4217 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
4218 true, true, lockmode);
4219 break;
4220 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
4221 * constraint */
4222 address =
4223 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
4224 ((AlterDomainStmt *) cmd->def)->def,
4225 NULL);
4226 break;
4227 case AT_ReAddComment: /* Re-add existing comment */
4228 address = CommentObject((CommentStmt *) cmd->def);
4229 break;
4230 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4231 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
4232 lockmode);
4233 break;
4234 case AT_AlterConstraint: /* ALTER CONSTRAINT */
4235 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
4236 break;
4237 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
4238 address = ATExecValidateConstraint(wqueue, rel, cmd->name, false,
4239 false, lockmode);
4240 break;
4241 case AT_ValidateConstraintRecurse: /* VALIDATE CONSTRAINT with
4242 * recursion */
4243 address = ATExecValidateConstraint(wqueue, rel, cmd->name, true,
4244 false, lockmode);
4245 break;
4246 case AT_DropConstraint: /* DROP CONSTRAINT */
4247 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
4248 false, false,
4249 cmd->missing_ok, lockmode);
4250 break;
4251 case AT_DropConstraintRecurse: /* DROP CONSTRAINT with recursion */
4252 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
4253 true, false,
4254 cmd->missing_ok, lockmode);
4255 break;
4256 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4257 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
4258 break;
4259 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
4260 address =
4261 ATExecAlterColumnGenericOptions(rel, cmd->name,
4262 (List *) cmd->def, lockmode);
4263 break;
4264 case AT_ChangeOwner: /* ALTER OWNER */
4265 ATExecChangeOwner(RelationGetRelid(rel),
4266 get_rolespec_oid(cmd->newowner, false),
4267 false, lockmode);
4268 break;
4269 case AT_ClusterOn: /* CLUSTER ON */
4270 address = ATExecClusterOn(rel, cmd->name, lockmode);
4271 break;
4272 case AT_DropCluster: /* SET WITHOUT CLUSTER */
4273 ATExecDropCluster(rel, lockmode);
4274 break;
4275 case AT_SetLogged: /* SET LOGGED */
4276 case AT_SetUnLogged: /* SET UNLOGGED */
4277 break;
4278 case AT_AddOids: /* SET WITH OIDS */
4279 /* Use the ADD COLUMN code, unless prep decided to do nothing */
4280 if (cmd->def != NULL)
4281 address =
4282 ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
4283 true, false, false,
4284 cmd->missing_ok, lockmode);
4285 break;
4286 case AT_AddOidsRecurse: /* SET WITH OIDS */
4287 /* Use the ADD COLUMN code, unless prep decided to do nothing */
4288 if (cmd->def != NULL)
4289 address =
4290 ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
4291 true, true, false,
4292 cmd->missing_ok, lockmode);
4293 break;
4294 case AT_DropOids: /* SET WITHOUT OIDS */
4295
4296 /*
4297 * Nothing to do here; we'll have generated a DropColumn
4298 * subcommand to do the real work
4299 */
4300 break;
4301 case AT_SetTableSpace: /* SET TABLESPACE */
4302 /*
4303 * Only do this for partitioned indexes, for which this is just
4304 * a catalog change. Other relation types are handled by Phase 3.
4305 */
4306 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4307 ATExecPartedIdxSetTableSpace(rel, tab->newTableSpace);
4308
4309 break;
4310 case AT_SetRelOptions: /* SET (...) */
4311 case AT_ResetRelOptions: /* RESET (...) */
4312 case AT_ReplaceRelOptions: /* replace entire option list */
4313 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
4314 break;
4315 case AT_EnableTrig: /* ENABLE TRIGGER name */
4316 ATExecEnableDisableTrigger(rel, cmd->name,
4317 TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
4318 break;
4319 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
4320 ATExecEnableDisableTrigger(rel, cmd->name,
4321 TRIGGER_FIRES_ALWAYS, false, lockmode);
4322 break;
4323 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
4324 ATExecEnableDisableTrigger(rel, cmd->name,
4325 TRIGGER_FIRES_ON_REPLICA, false, lockmode);
4326 break;
4327 case AT_DisableTrig: /* DISABLE TRIGGER name */
4328 ATExecEnableDisableTrigger(rel, cmd->name,
4329 TRIGGER_DISABLED, false, lockmode);
4330 break;
4331 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
4332 ATExecEnableDisableTrigger(rel, NULL,
4333 TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
4334 break;
4335 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
4336 ATExecEnableDisableTrigger(rel, NULL,
4337 TRIGGER_DISABLED, false, lockmode);
4338 break;
4339 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
4340 ATExecEnableDisableTrigger(rel, NULL,
4341 TRIGGER_FIRES_ON_ORIGIN, true, lockmode);
4342 break;
4343 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
4344 ATExecEnableDisableTrigger(rel, NULL,
4345 TRIGGER_DISABLED, true, lockmode);
4346 break;
4347
4348 case AT_EnableRule: /* ENABLE RULE name */
4349 ATExecEnableDisableRule(rel, cmd->name,
4350 RULE_FIRES_ON_ORIGIN, lockmode);
4351 break;
4352 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
4353 ATExecEnableDisableRule(rel, cmd->name,
4354 RULE_FIRES_ALWAYS, lockmode);
4355 break;
4356 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
4357 ATExecEnableDisableRule(rel, cmd->name,
4358 RULE_FIRES_ON_REPLICA, lockmode);
4359 break;
4360 case AT_DisableRule: /* DISABLE RULE name */
4361 ATExecEnableDisableRule(rel, cmd->name,
4362 RULE_DISABLED, lockmode);
4363 break;
4364
4365 case AT_AddInherit:
4366 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
4367 break;
4368 case AT_DropInherit:
4369 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
4370 break;
4371 case AT_AddOf:
4372 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
4373 break;
4374 case AT_DropOf:
4375 ATExecDropOf(rel, lockmode);
4376 break;
4377 case AT_ReplicaIdentity:
4378 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
4379 break;
4380 case AT_EnableRowSecurity:
4381 ATExecEnableRowSecurity(rel);
4382 break;
4383 case AT_DisableRowSecurity:
4384 ATExecDisableRowSecurity(rel);
4385 break;
4386 case AT_ForceRowSecurity:
4387 ATExecForceNoForceRowSecurity(rel, true);
4388 break;
4389 case AT_NoForceRowSecurity:
4390 ATExecForceNoForceRowSecurity(rel, false);
4391 break;
4392 case AT_GenericOptions:
4393 ATExecGenericOptions(rel, (List *) cmd->def);
4394 break;
4395 case AT_AttachPartition:
4396 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4397 ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
4398 else
4399 ATExecAttachPartitionIdx(wqueue, rel,
4400 ((PartitionCmd *) cmd->def)->name);
4401 break;
4402 case AT_DetachPartition:
4403 /* ATPrepCmd ensures it must be a table */
4404 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
4405 ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
4406 break;
4407 default: /* oops */
4408 elog(ERROR, "unrecognized alter table type: %d",
4409 (int) cmd->subtype);
4410 break;
4411 }
4412
4413 /*
4414 * Report the subcommand to interested event triggers.
4415 */
4416 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
4417
4418 /*
4419 * Bump the command counter to ensure the next subcommand in the sequence
4420 * can see the changes so far
4421 */
4422 CommandCounterIncrement();
4423 }
4424
4425 /*
4426 * ATRewriteTables: ALTER TABLE phase 3
4427 */
4428 static void
ATRewriteTables(AlterTableStmt * parsetree,List ** wqueue,LOCKMODE lockmode)4429 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
4430 {
4431 ListCell *ltab;
4432
4433 /* Go through each table that needs to be checked or rewritten */
4434 foreach(ltab, *wqueue)
4435 {
4436 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4437
4438 /*
4439 * Foreign tables have no storage, nor do partitioned tables and
4440 * indexes.
4441 */
4442 if (tab->relkind == RELKIND_FOREIGN_TABLE ||
4443 tab->relkind == RELKIND_PARTITIONED_TABLE ||
4444 tab->relkind == RELKIND_PARTITIONED_INDEX)
4445 continue;
4446
4447 /*
4448 * If we change column data types or add/remove OIDs, the operation
4449 * has to be propagated to tables that use this table's rowtype as a
4450 * column type. tab->newvals will also be non-NULL in the case where
4451 * we're adding a column with a default. We choose to forbid that
4452 * case as well, since composite types might eventually support
4453 * defaults.
4454 *
4455 * (Eventually we'll probably need to check for composite type
4456 * dependencies even when we're just scanning the table without a
4457 * rewrite, but at the moment a composite type does not enforce any
4458 * constraints, so it's not necessary/appropriate to enforce them just
4459 * during ALTER.)
4460 */
4461 if (tab->newvals != NIL || tab->rewrite > 0)
4462 {
4463 Relation rel;
4464
4465 rel = heap_open(tab->relid, NoLock);
4466 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
4467 heap_close(rel, NoLock);
4468 }
4469
4470 /*
4471 * We only need to rewrite the table if at least one column needs to
4472 * be recomputed, we are adding/removing the OID column, or we are
4473 * changing its persistence.
4474 *
4475 * There are two reasons for requiring a rewrite when changing
4476 * persistence: on one hand, we need to ensure that the buffers
4477 * belonging to each of the two relations are marked with or without
4478 * BM_PERMANENT properly. On the other hand, since rewriting creates
4479 * and assigns a new relfilenode, we automatically create or drop an
4480 * init fork for the relation as appropriate.
4481 */
4482 if (tab->rewrite > 0)
4483 {
4484 /* Build a temporary relation and copy data */
4485 Relation OldHeap;
4486 Oid OIDNewHeap;
4487 Oid NewTableSpace;
4488 char persistence;
4489
4490 OldHeap = heap_open(tab->relid, NoLock);
4491
4492 /*
4493 * We don't support rewriting of system catalogs; there are too
4494 * many corner cases and too little benefit. In particular this
4495 * is certainly not going to work for mapped catalogs.
4496 */
4497 if (IsSystemRelation(OldHeap))
4498 ereport(ERROR,
4499 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4500 errmsg("cannot rewrite system relation \"%s\"",
4501 RelationGetRelationName(OldHeap))));
4502
4503 if (RelationIsUsedAsCatalogTable(OldHeap))
4504 ereport(ERROR,
4505 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4506 errmsg("cannot rewrite table \"%s\" used as a catalog table",
4507 RelationGetRelationName(OldHeap))));
4508
4509 /*
4510 * Don't allow rewrite on temp tables of other backends ... their
4511 * local buffer manager is not going to cope.
4512 */
4513 if (RELATION_IS_OTHER_TEMP(OldHeap))
4514 ereport(ERROR,
4515 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4516 errmsg("cannot rewrite temporary tables of other sessions")));
4517
4518 /*
4519 * Select destination tablespace (same as original unless user
4520 * requested a change)
4521 */
4522 if (tab->newTableSpace)
4523 NewTableSpace = tab->newTableSpace;
4524 else
4525 NewTableSpace = OldHeap->rd_rel->reltablespace;
4526
4527 /*
4528 * Select persistence of transient table (same as original unless
4529 * user requested a change)
4530 */
4531 persistence = tab->chgPersistence ?
4532 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
4533
4534 heap_close(OldHeap, NoLock);
4535
4536 /*
4537 * Fire off an Event Trigger now, before actually rewriting the
4538 * table.
4539 *
4540 * We don't support Event Trigger for nested commands anywhere,
4541 * here included, and parsetree is given NULL when coming from
4542 * AlterTableInternal.
4543 *
4544 * And fire it only once.
4545 */
4546 if (parsetree)
4547 EventTriggerTableRewrite((Node *) parsetree,
4548 tab->relid,
4549 tab->rewrite);
4550
4551 /*
4552 * Create transient table that will receive the modified data.
4553 *
4554 * Ensure it is marked correctly as logged or unlogged. We have
4555 * to do this here so that buffers for the new relfilenode will
4556 * have the right persistence set, and at the same time ensure
4557 * that the original filenode's buffers will get read in with the
4558 * correct setting (i.e. the original one). Otherwise a rollback
4559 * after the rewrite would possibly result with buffers for the
4560 * original filenode having the wrong persistence setting.
4561 *
4562 * NB: This relies on swap_relation_files() also swapping the
4563 * persistence. That wouldn't work for pg_class, but that can't be
4564 * unlogged anyway.
4565 */
4566 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
4567 lockmode);
4568
4569 /*
4570 * Copy the heap data into the new table with the desired
4571 * modifications, and test the current data within the table
4572 * against new constraints generated by ALTER TABLE commands.
4573 */
4574 ATRewriteTable(tab, OIDNewHeap, lockmode);
4575
4576 /*
4577 * Swap the physical files of the old and new heaps, then rebuild
4578 * indexes and discard the old heap. We can use RecentXmin for
4579 * the table's new relfrozenxid because we rewrote all the tuples
4580 * in ATRewriteTable, so no older Xid remains in the table. Also,
4581 * we never try to swap toast tables by content, since we have no
4582 * interest in letting this code work on system catalogs.
4583 */
4584 finish_heap_swap(tab->relid, OIDNewHeap,
4585 false, false, true,
4586 !OidIsValid(tab->newTableSpace),
4587 RecentXmin,
4588 ReadNextMultiXactId(),
4589 persistence);
4590 }
4591 else
4592 {
4593 /*
4594 * Test the current data within the table against new constraints
4595 * generated by ALTER TABLE commands, but don't rebuild data.
4596 */
4597 if (tab->constraints != NIL || tab->new_notnull ||
4598 tab->partition_constraint != NULL)
4599 ATRewriteTable(tab, InvalidOid, lockmode);
4600
4601 /*
4602 * If we had SET TABLESPACE but no reason to reconstruct tuples,
4603 * just do a block-by-block copy.
4604 */
4605 if (tab->newTableSpace)
4606 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
4607 }
4608 }
4609
4610 /*
4611 * Foreign key constraints are checked in a final pass, since (a) it's
4612 * generally best to examine each one separately, and (b) it's at least
4613 * theoretically possible that we have changed both relations of the
4614 * foreign key, and we'd better have finished both rewrites before we try
4615 * to read the tables.
4616 */
4617 foreach(ltab, *wqueue)
4618 {
4619 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4620 Relation rel = NULL;
4621 ListCell *lcon;
4622
4623 /*
4624 * Foreign tables have no storage, nor do partitioned tables and
4625 * indexes.
4626 */
4627 if (tab->relkind == RELKIND_FOREIGN_TABLE ||
4628 tab->relkind == RELKIND_PARTITIONED_TABLE ||
4629 tab->relkind == RELKIND_PARTITIONED_INDEX)
4630 continue;
4631
4632 foreach(lcon, tab->constraints)
4633 {
4634 NewConstraint *con = lfirst(lcon);
4635
4636 if (con->contype == CONSTR_FOREIGN)
4637 {
4638 Constraint *fkconstraint = (Constraint *) con->qual;
4639 Relation refrel;
4640
4641 if (rel == NULL)
4642 {
4643 /* Long since locked, no need for another */
4644 rel = heap_open(tab->relid, NoLock);
4645 }
4646
4647 refrel = heap_open(con->refrelid, RowShareLock);
4648
4649 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
4650 con->refindid,
4651 con->conid);
4652
4653 /*
4654 * No need to mark the constraint row as validated, we did
4655 * that when we inserted the row earlier.
4656 */
4657
4658 heap_close(refrel, NoLock);
4659 }
4660 }
4661
4662 if (rel)
4663 heap_close(rel, NoLock);
4664 }
4665 }
4666
4667 /*
4668 * ATRewriteTable: scan or rewrite one table
4669 *
4670 * OIDNewHeap is InvalidOid if we don't need to rewrite
4671 */
4672 static void
ATRewriteTable(AlteredTableInfo * tab,Oid OIDNewHeap,LOCKMODE lockmode)4673 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
4674 {
4675 Relation oldrel;
4676 Relation newrel;
4677 TupleDesc oldTupDesc;
4678 TupleDesc newTupDesc;
4679 bool needscan = false;
4680 List *notnull_attrs;
4681 int i;
4682 ListCell *l;
4683 EState *estate;
4684 CommandId mycid;
4685 BulkInsertState bistate;
4686 int hi_options;
4687 ExprState *partqualstate = NULL;
4688
4689 /*
4690 * Open the relation(s). We have surely already locked the existing
4691 * table.
4692 */
4693 oldrel = heap_open(tab->relid, NoLock);
4694 oldTupDesc = tab->oldDesc;
4695 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
4696
4697 if (OidIsValid(OIDNewHeap))
4698 newrel = heap_open(OIDNewHeap, lockmode);
4699 else
4700 newrel = NULL;
4701
4702 /*
4703 * Prepare a BulkInsertState and options for heap_insert. Because we're
4704 * building a new heap, we can skip WAL-logging and fsync it to disk at
4705 * the end instead (unless WAL-logging is required for archiving or
4706 * streaming replication). The FSM is empty too, so don't bother using it.
4707 */
4708 if (newrel)
4709 {
4710 mycid = GetCurrentCommandId(true);
4711 bistate = GetBulkInsertState();
4712
4713 hi_options = HEAP_INSERT_SKIP_FSM;
4714 if (!XLogIsNeeded())
4715 hi_options |= HEAP_INSERT_SKIP_WAL;
4716 }
4717 else
4718 {
4719 /* keep compiler quiet about using these uninitialized */
4720 mycid = 0;
4721 bistate = NULL;
4722 hi_options = 0;
4723 }
4724
4725 /*
4726 * Generate the constraint and default execution states
4727 */
4728
4729 estate = CreateExecutorState();
4730
4731 /* Build the needed expression execution states */
4732 foreach(l, tab->constraints)
4733 {
4734 NewConstraint *con = lfirst(l);
4735
4736 switch (con->contype)
4737 {
4738 case CONSTR_CHECK:
4739 needscan = true;
4740 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
4741 break;
4742 case CONSTR_FOREIGN:
4743 /* Nothing to do here */
4744 break;
4745 default:
4746 elog(ERROR, "unrecognized constraint type: %d",
4747 (int) con->contype);
4748 }
4749 }
4750
4751 /* Build expression execution states for partition check quals */
4752 if (tab->partition_constraint)
4753 {
4754 needscan = true;
4755 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
4756 }
4757
4758 foreach(l, tab->newvals)
4759 {
4760 NewColumnValue *ex = lfirst(l);
4761
4762 /* expr already planned */
4763 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
4764 }
4765
4766 notnull_attrs = NIL;
4767 if (newrel || tab->new_notnull)
4768 {
4769 /*
4770 * If we are rebuilding the tuples OR if we added any new NOT NULL
4771 * constraints, check all not-null constraints. This is a bit of
4772 * overkill but it minimizes risk of bugs, and heap_attisnull is a
4773 * pretty cheap test anyway.
4774 */
4775 for (i = 0; i < newTupDesc->natts; i++)
4776 {
4777 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
4778
4779 if (attr->attnotnull && !attr->attisdropped)
4780 notnull_attrs = lappend_int(notnull_attrs, i);
4781 }
4782 if (notnull_attrs)
4783 needscan = true;
4784 }
4785
4786 if (newrel || needscan)
4787 {
4788 ExprContext *econtext;
4789 Datum *values;
4790 bool *isnull;
4791 TupleTableSlot *oldslot;
4792 TupleTableSlot *newslot;
4793 HeapScanDesc scan;
4794 HeapTuple tuple;
4795 MemoryContext oldCxt;
4796 List *dropped_attrs = NIL;
4797 ListCell *lc;
4798 Snapshot snapshot;
4799
4800 if (newrel)
4801 ereport(DEBUG1,
4802 (errmsg("rewriting table \"%s\"",
4803 RelationGetRelationName(oldrel))));
4804 else
4805 ereport(DEBUG1,
4806 (errmsg("verifying table \"%s\"",
4807 RelationGetRelationName(oldrel))));
4808
4809 if (newrel)
4810 {
4811 /*
4812 * All predicate locks on the tuples or pages are about to be made
4813 * invalid, because we move tuples around. Promote them to
4814 * relation locks.
4815 */
4816 TransferPredicateLocksToHeapRelation(oldrel);
4817 }
4818
4819 econtext = GetPerTupleExprContext(estate);
4820
4821 /*
4822 * Make tuple slots for old and new tuples. Note that even when the
4823 * tuples are the same, the tupDescs might not be (consider ADD COLUMN
4824 * without a default).
4825 */
4826 oldslot = MakeSingleTupleTableSlot(oldTupDesc);
4827 newslot = MakeSingleTupleTableSlot(newTupDesc);
4828
4829 /* Preallocate values/isnull arrays */
4830 i = Max(newTupDesc->natts, oldTupDesc->natts);
4831 values = (Datum *) palloc(i * sizeof(Datum));
4832 isnull = (bool *) palloc(i * sizeof(bool));
4833 memset(values, 0, i * sizeof(Datum));
4834 memset(isnull, true, i * sizeof(bool));
4835
4836 /*
4837 * Any attributes that are dropped according to the new tuple
4838 * descriptor can be set to NULL. We precompute the list of dropped
4839 * attributes to avoid needing to do so in the per-tuple loop.
4840 */
4841 for (i = 0; i < newTupDesc->natts; i++)
4842 {
4843 if (TupleDescAttr(newTupDesc, i)->attisdropped)
4844 dropped_attrs = lappend_int(dropped_attrs, i);
4845 }
4846
4847 /*
4848 * Scan through the rows, generating a new row if needed and then
4849 * checking all the constraints.
4850 */
4851 snapshot = RegisterSnapshot(GetLatestSnapshot());
4852 scan = heap_beginscan(oldrel, snapshot, 0, NULL);
4853
4854 /*
4855 * Switch to per-tuple memory context and reset it for each tuple
4856 * produced, so we don't leak memory.
4857 */
4858 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
4859
4860 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
4861 {
4862 if (tab->rewrite > 0)
4863 {
4864 Oid tupOid = InvalidOid;
4865
4866 /* Extract data from old tuple */
4867 heap_deform_tuple(tuple, oldTupDesc, values, isnull);
4868 if (oldTupDesc->tdhasoid)
4869 tupOid = HeapTupleGetOid(tuple);
4870
4871 /* Set dropped attributes to null in new tuple */
4872 foreach(lc, dropped_attrs)
4873 isnull[lfirst_int(lc)] = true;
4874
4875 /*
4876 * Process supplied expressions to replace selected columns.
4877 * Expression inputs come from the old tuple.
4878 */
4879 ExecStoreTuple(tuple, oldslot, InvalidBuffer, false);
4880 econtext->ecxt_scantuple = oldslot;
4881
4882 foreach(l, tab->newvals)
4883 {
4884 NewColumnValue *ex = lfirst(l);
4885
4886 values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
4887 econtext,
4888 &isnull[ex->attnum - 1]);
4889 }
4890
4891 /*
4892 * Form the new tuple. Note that we don't explicitly pfree it,
4893 * since the per-tuple memory context will be reset shortly.
4894 */
4895 tuple = heap_form_tuple(newTupDesc, values, isnull);
4896
4897 /* Preserve OID, if any */
4898 if (newTupDesc->tdhasoid)
4899 HeapTupleSetOid(tuple, tupOid);
4900
4901 /*
4902 * Constraints might reference the tableoid column, so
4903 * initialize t_tableOid before evaluating them.
4904 */
4905 tuple->t_tableOid = RelationGetRelid(oldrel);
4906 }
4907
4908 /* Now check any constraints on the possibly-changed tuple */
4909 ExecStoreTuple(tuple, newslot, InvalidBuffer, false);
4910 econtext->ecxt_scantuple = newslot;
4911
4912 foreach(l, notnull_attrs)
4913 {
4914 int attn = lfirst_int(l);
4915
4916 if (heap_attisnull(tuple, attn + 1, newTupDesc))
4917 {
4918 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
4919
4920 ereport(ERROR,
4921 (errcode(ERRCODE_NOT_NULL_VIOLATION),
4922 errmsg("column \"%s\" contains null values",
4923 NameStr(attr->attname)),
4924 errtablecol(oldrel, attn + 1)));
4925 }
4926 }
4927
4928 foreach(l, tab->constraints)
4929 {
4930 NewConstraint *con = lfirst(l);
4931
4932 switch (con->contype)
4933 {
4934 case CONSTR_CHECK:
4935 if (!ExecCheck(con->qualstate, econtext))
4936 ereport(ERROR,
4937 (errcode(ERRCODE_CHECK_VIOLATION),
4938 errmsg("check constraint \"%s\" is violated by some row",
4939 con->name),
4940 errtableconstraint(oldrel, con->name)));
4941 break;
4942 case CONSTR_FOREIGN:
4943 /* Nothing to do here */
4944 break;
4945 default:
4946 elog(ERROR, "unrecognized constraint type: %d",
4947 (int) con->contype);
4948 }
4949 }
4950
4951 if (partqualstate && !ExecCheck(partqualstate, econtext))
4952 {
4953 if (tab->validate_default)
4954 ereport(ERROR,
4955 (errcode(ERRCODE_CHECK_VIOLATION),
4956 errmsg("updated partition constraint for default partition would be violated by some row")));
4957 else
4958 ereport(ERROR,
4959 (errcode(ERRCODE_CHECK_VIOLATION),
4960 errmsg("partition constraint is violated by some row")));
4961 }
4962
4963 /* Write the tuple out to the new relation */
4964 if (newrel)
4965 heap_insert(newrel, tuple, mycid, hi_options, bistate);
4966
4967 ResetExprContext(econtext);
4968
4969 CHECK_FOR_INTERRUPTS();
4970 }
4971
4972 MemoryContextSwitchTo(oldCxt);
4973 heap_endscan(scan);
4974 UnregisterSnapshot(snapshot);
4975
4976 ExecDropSingleTupleTableSlot(oldslot);
4977 ExecDropSingleTupleTableSlot(newslot);
4978 }
4979
4980 FreeExecutorState(estate);
4981
4982 heap_close(oldrel, NoLock);
4983 if (newrel)
4984 {
4985 FreeBulkInsertState(bistate);
4986
4987 /* If we skipped writing WAL, then we need to sync the heap. */
4988 if (hi_options & HEAP_INSERT_SKIP_WAL)
4989 heap_sync(newrel);
4990
4991 heap_close(newrel, NoLock);
4992 }
4993 }
4994
4995 /*
4996 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
4997 */
4998 static AlteredTableInfo *
ATGetQueueEntry(List ** wqueue,Relation rel)4999 ATGetQueueEntry(List **wqueue, Relation rel)
5000 {
5001 Oid relid = RelationGetRelid(rel);
5002 AlteredTableInfo *tab;
5003 ListCell *ltab;
5004
5005 foreach(ltab, *wqueue)
5006 {
5007 tab = (AlteredTableInfo *) lfirst(ltab);
5008 if (tab->relid == relid)
5009 return tab;
5010 }
5011
5012 /*
5013 * Not there, so add it. Note that we make a copy of the relation's
5014 * existing descriptor before anything interesting can happen to it.
5015 */
5016 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
5017 tab->relid = relid;
5018 tab->relkind = rel->rd_rel->relkind;
5019 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
5020 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5021 tab->chgPersistence = false;
5022
5023 *wqueue = lappend(*wqueue, tab);
5024
5025 return tab;
5026 }
5027
5028 /*
5029 * ATSimplePermissions
5030 *
5031 * - Ensure that it is a relation (or possibly a view)
5032 * - Ensure this user is the owner
5033 * - Ensure that it is not a system table
5034 */
5035 static void
ATSimplePermissions(Relation rel,int allowed_targets)5036 ATSimplePermissions(Relation rel, int allowed_targets)
5037 {
5038 int actual_target;
5039
5040 switch (rel->rd_rel->relkind)
5041 {
5042 case RELKIND_RELATION:
5043 case RELKIND_PARTITIONED_TABLE:
5044 actual_target = ATT_TABLE;
5045 break;
5046 case RELKIND_VIEW:
5047 actual_target = ATT_VIEW;
5048 break;
5049 case RELKIND_MATVIEW:
5050 actual_target = ATT_MATVIEW;
5051 break;
5052 case RELKIND_INDEX:
5053 actual_target = ATT_INDEX;
5054 break;
5055 case RELKIND_PARTITIONED_INDEX:
5056 actual_target = ATT_PARTITIONED_INDEX;
5057 break;
5058 case RELKIND_COMPOSITE_TYPE:
5059 actual_target = ATT_COMPOSITE_TYPE;
5060 break;
5061 case RELKIND_FOREIGN_TABLE:
5062 actual_target = ATT_FOREIGN_TABLE;
5063 break;
5064 default:
5065 actual_target = 0;
5066 break;
5067 }
5068
5069 /* Wrong target type? */
5070 if ((actual_target & allowed_targets) == 0)
5071 ATWrongRelkindError(rel, allowed_targets);
5072
5073 /* Permissions checks */
5074 if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
5075 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
5076 RelationGetRelationName(rel));
5077
5078 if (!allowSystemTableMods && IsSystemRelation(rel))
5079 ereport(ERROR,
5080 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
5081 errmsg("permission denied: \"%s\" is a system catalog",
5082 RelationGetRelationName(rel))));
5083 }
5084
5085 /*
5086 * ATWrongRelkindError
5087 *
5088 * Throw an error when a relation has been determined to be of the wrong
5089 * type.
5090 */
5091 static void
ATWrongRelkindError(Relation rel,int allowed_targets)5092 ATWrongRelkindError(Relation rel, int allowed_targets)
5093 {
5094 char *msg;
5095
5096 switch (allowed_targets)
5097 {
5098 case ATT_TABLE:
5099 msg = _("\"%s\" is not a table");
5100 break;
5101 case ATT_TABLE | ATT_VIEW:
5102 msg = _("\"%s\" is not a table or view");
5103 break;
5104 case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
5105 msg = _("\"%s\" is not a table, view, or foreign table");
5106 break;
5107 case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
5108 msg = _("\"%s\" is not a table, view, materialized view, or index");
5109 break;
5110 case ATT_TABLE | ATT_MATVIEW:
5111 msg = _("\"%s\" is not a table or materialized view");
5112 break;
5113 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX:
5114 msg = _("\"%s\" is not a table, materialized view, or index");
5115 break;
5116 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX:
5117 msg = _("\"%s\" is not a table, materialized view, index, or partitioned index");
5118 break;
5119 case ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE:
5120 msg = _("\"%s\" is not a table, materialized view, or foreign table");
5121 break;
5122 case ATT_TABLE | ATT_FOREIGN_TABLE:
5123 msg = _("\"%s\" is not a table or foreign table");
5124 break;
5125 case ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE:
5126 msg = _("\"%s\" is not a table, composite type, or foreign table");
5127 break;
5128 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE:
5129 msg = _("\"%s\" is not a table, materialized view, index, or foreign table");
5130 break;
5131 case ATT_TABLE | ATT_PARTITIONED_INDEX:
5132 msg = _("\"%s\" is not a table or partitioned index");
5133 break;
5134 case ATT_VIEW:
5135 msg = _("\"%s\" is not a view");
5136 break;
5137 case ATT_FOREIGN_TABLE:
5138 msg = _("\"%s\" is not a foreign table");
5139 break;
5140 default:
5141 /* shouldn't get here, add all necessary cases above */
5142 msg = _("\"%s\" is of the wrong type");
5143 break;
5144 }
5145
5146 ereport(ERROR,
5147 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5148 errmsg(msg, RelationGetRelationName(rel))));
5149 }
5150
5151 /*
5152 * ATSimpleRecursion
5153 *
5154 * Simple table recursion sufficient for most ALTER TABLE operations.
5155 * All direct and indirect children are processed in an unspecified order.
5156 * Note that if a child inherits from the original table via multiple
5157 * inheritance paths, it will be visited just once.
5158 */
5159 static void
ATSimpleRecursion(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,LOCKMODE lockmode)5160 ATSimpleRecursion(List **wqueue, Relation rel,
5161 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
5162 {
5163 /*
5164 * Propagate to children if desired. Only plain tables, foreign tables
5165 * and partitioned tables have children, so no need to search for other
5166 * relkinds.
5167 */
5168 if (recurse &&
5169 (rel->rd_rel->relkind == RELKIND_RELATION ||
5170 rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
5171 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
5172 {
5173 Oid relid = RelationGetRelid(rel);
5174 ListCell *child;
5175 List *children;
5176
5177 children = find_all_inheritors(relid, lockmode, NULL);
5178
5179 /*
5180 * find_all_inheritors does the recursive search of the inheritance
5181 * hierarchy, so all we have to do is process all of the relids in the
5182 * list that it returns.
5183 */
5184 foreach(child, children)
5185 {
5186 Oid childrelid = lfirst_oid(child);
5187 Relation childrel;
5188
5189 if (childrelid == relid)
5190 continue;
5191 /* find_all_inheritors already got lock */
5192 childrel = relation_open(childrelid, NoLock);
5193 CheckTableNotInUse(childrel, "ALTER TABLE");
5194 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
5195 relation_close(childrel, NoLock);
5196 }
5197 }
5198 }
5199
5200 /*
5201 * Obtain list of partitions of the given table, locking them all at the given
5202 * lockmode and ensuring that they all pass CheckTableNotInUse.
5203 *
5204 * This function is a no-op if the given relation is not a partitioned table;
5205 * in particular, nothing is done if it's a legacy inheritance parent.
5206 */
5207 static void
ATCheckPartitionsNotInUse(Relation rel,LOCKMODE lockmode)5208 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
5209 {
5210 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5211 {
5212 List *inh;
5213 ListCell *cell;
5214
5215 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5216 /* first element is the parent rel; must ignore it */
5217 for_each_cell(cell, lnext(list_head(inh)))
5218 {
5219 Relation childrel;
5220
5221 /* find_all_inheritors already got lock */
5222 childrel = heap_open(lfirst_oid(cell), NoLock);
5223 CheckTableNotInUse(childrel, "ALTER TABLE");
5224 heap_close(childrel, NoLock);
5225 }
5226 list_free(inh);
5227 }
5228 }
5229
5230 /*
5231 * ATTypedTableRecursion
5232 *
5233 * Propagate ALTER TYPE operations to the typed tables of that type.
5234 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
5235 * recursion to inheritance children of the typed tables.
5236 */
5237 static void
ATTypedTableRecursion(List ** wqueue,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode)5238 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
5239 LOCKMODE lockmode)
5240 {
5241 ListCell *child;
5242 List *children;
5243
5244 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
5245
5246 children = find_typed_table_dependencies(rel->rd_rel->reltype,
5247 RelationGetRelationName(rel),
5248 cmd->behavior);
5249
5250 foreach(child, children)
5251 {
5252 Oid childrelid = lfirst_oid(child);
5253 Relation childrel;
5254
5255 childrel = relation_open(childrelid, lockmode);
5256 CheckTableNotInUse(childrel, "ALTER TABLE");
5257 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode);
5258 relation_close(childrel, NoLock);
5259 }
5260 }
5261
5262
5263 /*
5264 * find_composite_type_dependencies
5265 *
5266 * Check to see if the type "typeOid" is being used as a column in some table
5267 * (possibly nested several levels deep in composite types, arrays, etc!).
5268 * Eventually, we'd like to propagate the check or rewrite operation
5269 * into such tables, but for now, just error out if we find any.
5270 *
5271 * Caller should provide either the associated relation of a rowtype,
5272 * or a type name (not both) for use in the error message, if any.
5273 *
5274 * Note that "typeOid" is not necessarily a composite type; it could also be
5275 * another container type such as an array or range, or a domain over one of
5276 * these things. The name of this function is therefore somewhat historical,
5277 * but it's not worth changing.
5278 *
5279 * We assume that functions and views depending on the type are not reasons
5280 * to reject the ALTER. (How safe is this really?)
5281 */
5282 void
find_composite_type_dependencies(Oid typeOid,Relation origRelation,const char * origTypeName)5283 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
5284 const char *origTypeName)
5285 {
5286 Relation depRel;
5287 ScanKeyData key[2];
5288 SysScanDesc depScan;
5289 HeapTuple depTup;
5290
5291 /* since this function recurses, it could be driven to stack overflow */
5292 check_stack_depth();
5293
5294 /*
5295 * We scan pg_depend to find those things that depend on the given type.
5296 * (We assume we can ignore refobjsubid for a type.)
5297 */
5298 depRel = heap_open(DependRelationId, AccessShareLock);
5299
5300 ScanKeyInit(&key[0],
5301 Anum_pg_depend_refclassid,
5302 BTEqualStrategyNumber, F_OIDEQ,
5303 ObjectIdGetDatum(TypeRelationId));
5304 ScanKeyInit(&key[1],
5305 Anum_pg_depend_refobjid,
5306 BTEqualStrategyNumber, F_OIDEQ,
5307 ObjectIdGetDatum(typeOid));
5308
5309 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
5310 NULL, 2, key);
5311
5312 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
5313 {
5314 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
5315 Relation rel;
5316 Form_pg_attribute att;
5317
5318 /* Check for directly dependent types */
5319 if (pg_depend->classid == TypeRelationId)
5320 {
5321 /*
5322 * This must be an array, domain, or range containing the given
5323 * type, so recursively check for uses of this type. Note that
5324 * any error message will mention the original type not the
5325 * container; this is intentional.
5326 */
5327 find_composite_type_dependencies(pg_depend->objid,
5328 origRelation, origTypeName);
5329 continue;
5330 }
5331
5332 /* Else, ignore dependees that aren't user columns of relations */
5333 /* (we assume system columns are never of interesting types) */
5334 if (pg_depend->classid != RelationRelationId ||
5335 pg_depend->objsubid <= 0)
5336 continue;
5337
5338 rel = relation_open(pg_depend->objid, AccessShareLock);
5339 att = TupleDescAttr(rel->rd_att, pg_depend->objsubid - 1);
5340
5341 if (rel->rd_rel->relkind == RELKIND_RELATION ||
5342 rel->rd_rel->relkind == RELKIND_MATVIEW ||
5343 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5344 {
5345 if (origTypeName)
5346 ereport(ERROR,
5347 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5348 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
5349 origTypeName,
5350 RelationGetRelationName(rel),
5351 NameStr(att->attname))));
5352 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
5353 ereport(ERROR,
5354 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5355 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
5356 RelationGetRelationName(origRelation),
5357 RelationGetRelationName(rel),
5358 NameStr(att->attname))));
5359 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
5360 ereport(ERROR,
5361 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5362 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
5363 RelationGetRelationName(origRelation),
5364 RelationGetRelationName(rel),
5365 NameStr(att->attname))));
5366 else
5367 ereport(ERROR,
5368 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5369 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
5370 RelationGetRelationName(origRelation),
5371 RelationGetRelationName(rel),
5372 NameStr(att->attname))));
5373 }
5374 else if (OidIsValid(rel->rd_rel->reltype))
5375 {
5376 /*
5377 * A view or composite type itself isn't a problem, but we must
5378 * recursively check for indirect dependencies via its rowtype.
5379 */
5380 find_composite_type_dependencies(rel->rd_rel->reltype,
5381 origRelation, origTypeName);
5382 }
5383
5384 relation_close(rel, AccessShareLock);
5385 }
5386
5387 systable_endscan(depScan);
5388
5389 relation_close(depRel, AccessShareLock);
5390 }
5391
5392
5393 /*
5394 * find_typed_table_dependencies
5395 *
5396 * Check to see if a composite type is being used as the type of a
5397 * typed table. Abort if any are found and behavior is RESTRICT.
5398 * Else return the list of tables.
5399 */
5400 static List *
find_typed_table_dependencies(Oid typeOid,const char * typeName,DropBehavior behavior)5401 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
5402 {
5403 Relation classRel;
5404 ScanKeyData key[1];
5405 HeapScanDesc scan;
5406 HeapTuple tuple;
5407 List *result = NIL;
5408
5409 classRel = heap_open(RelationRelationId, AccessShareLock);
5410
5411 ScanKeyInit(&key[0],
5412 Anum_pg_class_reloftype,
5413 BTEqualStrategyNumber, F_OIDEQ,
5414 ObjectIdGetDatum(typeOid));
5415
5416 scan = heap_beginscan_catalog(classRel, 1, key);
5417
5418 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
5419 {
5420 if (behavior == DROP_RESTRICT)
5421 ereport(ERROR,
5422 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
5423 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
5424 typeName),
5425 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
5426 else
5427 result = lappend_oid(result, HeapTupleGetOid(tuple));
5428 }
5429
5430 heap_endscan(scan);
5431 heap_close(classRel, AccessShareLock);
5432
5433 return result;
5434 }
5435
5436
5437 /*
5438 * check_of_type
5439 *
5440 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
5441 * isn't suitable, throw an error. Currently, we require that the type
5442 * originated with CREATE TYPE AS. We could support any row type, but doing so
5443 * would require handling a number of extra corner cases in the DDL commands.
5444 * (Also, allowing domain-over-composite would open up a can of worms about
5445 * whether and how the domain's constraints should apply to derived tables.)
5446 */
5447 void
check_of_type(HeapTuple typetuple)5448 check_of_type(HeapTuple typetuple)
5449 {
5450 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
5451 bool typeOk = false;
5452
5453 if (typ->typtype == TYPTYPE_COMPOSITE)
5454 {
5455 Relation typeRelation;
5456
5457 Assert(OidIsValid(typ->typrelid));
5458 typeRelation = relation_open(typ->typrelid, AccessShareLock);
5459 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
5460
5461 /*
5462 * Close the parent rel, but keep our AccessShareLock on it until xact
5463 * commit. That will prevent someone else from deleting or ALTERing
5464 * the type before the typed table creation/conversion commits.
5465 */
5466 relation_close(typeRelation, NoLock);
5467 }
5468 if (!typeOk)
5469 ereport(ERROR,
5470 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5471 errmsg("type %s is not a composite type",
5472 format_type_be(HeapTupleGetOid(typetuple)))));
5473 }
5474
5475
5476 /*
5477 * ALTER TABLE ADD COLUMN
5478 *
5479 * Adds an additional attribute to a relation making the assumption that
5480 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
5481 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
5482 * AlterTableCmd's.
5483 *
5484 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
5485 * have to decide at runtime whether to recurse or not depending on whether we
5486 * actually add a column or merely merge with an existing column. (We can't
5487 * check this in a static pre-pass because it won't handle multiple inheritance
5488 * situations correctly.)
5489 */
5490 static void
ATPrepAddColumn(List ** wqueue,Relation rel,bool recurse,bool recursing,bool is_view,AlterTableCmd * cmd,LOCKMODE lockmode)5491 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
5492 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode)
5493 {
5494 if (rel->rd_rel->reloftype && !recursing)
5495 ereport(ERROR,
5496 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5497 errmsg("cannot add column to typed table")));
5498
5499 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
5500 ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
5501
5502 if (recurse && !is_view)
5503 cmd->subtype = AT_AddColumnRecurse;
5504 }
5505
5506 /*
5507 * Add a column to a table; this handles the AT_AddOids cases as well. The
5508 * return value is the address of the new column in the parent relation.
5509 */
5510 static ObjectAddress
ATExecAddColumn(List ** wqueue,AlteredTableInfo * tab,Relation rel,ColumnDef * colDef,bool isOid,bool recurse,bool recursing,bool if_not_exists,LOCKMODE lockmode)5511 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
5512 ColumnDef *colDef, bool isOid,
5513 bool recurse, bool recursing,
5514 bool if_not_exists, LOCKMODE lockmode)
5515 {
5516 Oid myrelid = RelationGetRelid(rel);
5517 Relation pgclass,
5518 attrdesc;
5519 HeapTuple reltup;
5520 FormData_pg_attribute attribute;
5521 int newattnum;
5522 char relkind;
5523 HeapTuple typeTuple;
5524 Oid typeOid;
5525 int32 typmod;
5526 Oid collOid;
5527 Form_pg_type tform;
5528 Expr *defval;
5529 List *children;
5530 ListCell *child;
5531 AclResult aclresult;
5532 ObjectAddress address;
5533
5534 /* At top level, permission check was done in ATPrepCmd, else do it */
5535 if (recursing)
5536 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5537
5538 if (rel->rd_rel->relispartition && !recursing)
5539 ereport(ERROR,
5540 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5541 errmsg("cannot add column to a partition")));
5542
5543 attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
5544
5545 /*
5546 * Are we adding the column to a recursion child? If so, check whether to
5547 * merge with an existing definition for the column. If we do merge, we
5548 * must not recurse. Children will already have the column, and recursing
5549 * into them would mess up attinhcount.
5550 */
5551 if (colDef->inhcount > 0)
5552 {
5553 HeapTuple tuple;
5554
5555 /* Does child already have a column by this name? */
5556 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
5557 if (HeapTupleIsValid(tuple))
5558 {
5559 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
5560 Oid ctypeId;
5561 int32 ctypmod;
5562 Oid ccollid;
5563
5564 /* Child column must match on type, typmod, and collation */
5565 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
5566 if (ctypeId != childatt->atttypid ||
5567 ctypmod != childatt->atttypmod)
5568 ereport(ERROR,
5569 (errcode(ERRCODE_DATATYPE_MISMATCH),
5570 errmsg("child table \"%s\" has different type for column \"%s\"",
5571 RelationGetRelationName(rel), colDef->colname)));
5572 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
5573 if (ccollid != childatt->attcollation)
5574 ereport(ERROR,
5575 (errcode(ERRCODE_COLLATION_MISMATCH),
5576 errmsg("child table \"%s\" has different collation for column \"%s\"",
5577 RelationGetRelationName(rel), colDef->colname),
5578 errdetail("\"%s\" versus \"%s\"",
5579 get_collation_name(ccollid),
5580 get_collation_name(childatt->attcollation))));
5581
5582 /* If it's OID, child column must actually be OID */
5583 if (isOid && childatt->attnum != ObjectIdAttributeNumber)
5584 ereport(ERROR,
5585 (errcode(ERRCODE_DATATYPE_MISMATCH),
5586 errmsg("child table \"%s\" has a conflicting \"%s\" column",
5587 RelationGetRelationName(rel), colDef->colname)));
5588
5589 /* Bump the existing child att's inhcount */
5590 childatt->attinhcount++;
5591 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
5592
5593 heap_freetuple(tuple);
5594
5595 /* Inform the user about the merge */
5596 ereport(NOTICE,
5597 (errmsg("merging definition of column \"%s\" for child \"%s\"",
5598 colDef->colname, RelationGetRelationName(rel))));
5599
5600 heap_close(attrdesc, RowExclusiveLock);
5601 return InvalidObjectAddress;
5602 }
5603 }
5604
5605 pgclass = heap_open(RelationRelationId, RowExclusiveLock);
5606
5607 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
5608 if (!HeapTupleIsValid(reltup))
5609 elog(ERROR, "cache lookup failed for relation %u", myrelid);
5610 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
5611
5612 /*
5613 * Cannot add identity column if table has children, because identity does
5614 * not inherit. (Adding column and identity separately will work.)
5615 */
5616 if (colDef->identity &&
5617 recurse &&
5618 find_inheritance_children(myrelid, NoLock) != NIL)
5619 ereport(ERROR,
5620 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5621 errmsg("cannot recursively add identity column to table that has child tables")));
5622
5623 /* skip if the name already exists and if_not_exists is true */
5624 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
5625 {
5626 heap_close(attrdesc, RowExclusiveLock);
5627 heap_freetuple(reltup);
5628 heap_close(pgclass, RowExclusiveLock);
5629 return InvalidObjectAddress;
5630 }
5631
5632 /* Determine the new attribute's number */
5633 if (isOid)
5634 newattnum = ObjectIdAttributeNumber;
5635 else
5636 {
5637 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
5638 if (newattnum > MaxHeapAttributeNumber)
5639 ereport(ERROR,
5640 (errcode(ERRCODE_TOO_MANY_COLUMNS),
5641 errmsg("tables can have at most %d columns",
5642 MaxHeapAttributeNumber)));
5643 }
5644
5645 typeTuple = typenameType(NULL, colDef->typeName, &typmod);
5646 tform = (Form_pg_type) GETSTRUCT(typeTuple);
5647 typeOid = HeapTupleGetOid(typeTuple);
5648
5649 aclresult = pg_type_aclcheck(typeOid, GetUserId(), ACL_USAGE);
5650 if (aclresult != ACLCHECK_OK)
5651 aclcheck_error_type(aclresult, typeOid);
5652
5653 collOid = GetColumnDefCollation(NULL, colDef, typeOid);
5654
5655 /* make sure datatype is legal for a column */
5656 CheckAttributeType(colDef->colname, typeOid, collOid,
5657 list_make1_oid(rel->rd_rel->reltype),
5658 false);
5659
5660 /* construct new attribute's pg_attribute entry */
5661 attribute.attrelid = myrelid;
5662 namestrcpy(&(attribute.attname), colDef->colname);
5663 attribute.atttypid = typeOid;
5664 attribute.attstattarget = (newattnum > 0) ? -1 : 0;
5665 attribute.attlen = tform->typlen;
5666 attribute.attcacheoff = -1;
5667 attribute.atttypmod = typmod;
5668 attribute.attnum = newattnum;
5669 attribute.attbyval = tform->typbyval;
5670 attribute.attndims = list_length(colDef->typeName->arrayBounds);
5671 attribute.attstorage = tform->typstorage;
5672 attribute.attalign = tform->typalign;
5673 attribute.attnotnull = colDef->is_not_null;
5674 attribute.atthasdef = false;
5675 attribute.atthasmissing = false;
5676 attribute.attidentity = colDef->identity;
5677 attribute.attisdropped = false;
5678 attribute.attislocal = colDef->is_local;
5679 attribute.attinhcount = colDef->inhcount;
5680 attribute.attcollation = collOid;
5681 /* attribute.attacl is handled by InsertPgAttributeTuple */
5682
5683 ReleaseSysCache(typeTuple);
5684
5685 InsertPgAttributeTuple(attrdesc, &attribute, NULL);
5686
5687 heap_close(attrdesc, RowExclusiveLock);
5688
5689 /*
5690 * Update pg_class tuple as appropriate
5691 */
5692 if (isOid)
5693 ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true;
5694 else
5695 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
5696
5697 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
5698
5699 heap_freetuple(reltup);
5700
5701 /* Post creation hook for new attribute */
5702 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
5703
5704 heap_close(pgclass, RowExclusiveLock);
5705
5706 /* Make the attribute's catalog entry visible */
5707 CommandCounterIncrement();
5708
5709 /*
5710 * Store the DEFAULT, if any, in the catalogs
5711 */
5712 if (colDef->raw_default)
5713 {
5714 RawColumnDefault *rawEnt;
5715
5716 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
5717 rawEnt->attnum = attribute.attnum;
5718 rawEnt->raw_default = copyObject(colDef->raw_default);
5719
5720 /*
5721 * Attempt to skip a complete table rewrite by storing the specified
5722 * DEFAULT value outside of the heap. This may be disabled inside
5723 * AddRelationNewConstraints if the optimization cannot be applied.
5724 */
5725 rawEnt->missingMode = true;
5726
5727 /*
5728 * This function is intended for CREATE TABLE, so it processes a
5729 * _list_ of defaults, but we just do one.
5730 */
5731 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
5732 false, true, false);
5733
5734 /* Make the additional catalog changes visible */
5735 CommandCounterIncrement();
5736
5737 /*
5738 * Did the request for a missing value work? If not we'll have to do a
5739 * rewrite
5740 */
5741 if (!rawEnt->missingMode)
5742 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
5743 }
5744
5745 /*
5746 * Tell Phase 3 to fill in the default expression, if there is one.
5747 *
5748 * If there is no default, Phase 3 doesn't have to do anything, because
5749 * that effectively means that the default is NULL. The heap tuple access
5750 * routines always check for attnum > # of attributes in tuple, and return
5751 * NULL if so, so without any modification of the tuple data we will get
5752 * the effect of NULL values in the new column.
5753 *
5754 * An exception occurs when the new column is of a domain type: the domain
5755 * might have a NOT NULL constraint, or a check constraint that indirectly
5756 * rejects nulls. If there are any domain constraints then we construct
5757 * an explicit NULL default value that will be passed through
5758 * CoerceToDomain processing. (This is a tad inefficient, since it causes
5759 * rewriting the table which we really don't have to do, but the present
5760 * design of domain processing doesn't offer any simple way of checking
5761 * the constraints more directly.)
5762 *
5763 * Note: we use build_column_default, and not just the cooked default
5764 * returned by AddRelationNewConstraints, so that the right thing happens
5765 * when a datatype's default applies.
5766 *
5767 * We skip this step completely for views and foreign tables. For a view,
5768 * we can only get here from CREATE OR REPLACE VIEW, which historically
5769 * doesn't set up defaults, not even for domain-typed columns. And in any
5770 * case we mustn't invoke Phase 3 on a view or foreign table, since they
5771 * have no storage.
5772 */
5773 if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE
5774 && relkind != RELKIND_FOREIGN_TABLE && attribute.attnum > 0)
5775 {
5776 /*
5777 * For an identity column, we can't use build_column_default(),
5778 * because the sequence ownership isn't set yet. So do it manually.
5779 */
5780 if (colDef->identity)
5781 {
5782 NextValueExpr *nve = makeNode(NextValueExpr);
5783
5784 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
5785 nve->typeId = typeOid;
5786
5787 defval = (Expr *) nve;
5788
5789 /* must do a rewrite for identity columns */
5790 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
5791 }
5792 else
5793 defval = (Expr *) build_column_default(rel, attribute.attnum);
5794
5795 if (!defval && DomainHasConstraints(typeOid))
5796 {
5797 Oid baseTypeId;
5798 int32 baseTypeMod;
5799 Oid baseTypeColl;
5800
5801 baseTypeMod = typmod;
5802 baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
5803 baseTypeColl = get_typcollation(baseTypeId);
5804 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
5805 defval = (Expr *) coerce_to_target_type(NULL,
5806 (Node *) defval,
5807 baseTypeId,
5808 typeOid,
5809 typmod,
5810 COERCION_ASSIGNMENT,
5811 COERCE_IMPLICIT_CAST,
5812 -1);
5813 if (defval == NULL) /* should not happen */
5814 elog(ERROR, "failed to coerce base type to domain");
5815 }
5816
5817 if (defval)
5818 {
5819 NewColumnValue *newval;
5820
5821 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
5822 newval->attnum = attribute.attnum;
5823 newval->expr = expression_planner(defval);
5824
5825 tab->newvals = lappend(tab->newvals, newval);
5826 }
5827
5828 if (DomainHasConstraints(typeOid))
5829 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
5830
5831 if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing)
5832 {
5833 /*
5834 * If the new column is NOT NULL, and there is no missing value,
5835 * tell Phase 3 it needs to test that. (Note we don't do this for
5836 * an OID column. OID will be marked not null, but since it's
5837 * filled specially, there's no need to test anything.)
5838 */
5839 tab->new_notnull |= colDef->is_not_null;
5840 }
5841 }
5842
5843 /*
5844 * If we are adding an OID column, we have to tell Phase 3 to rewrite the
5845 * table to fix that.
5846 */
5847 if (isOid)
5848 tab->rewrite |= AT_REWRITE_ALTER_OID;
5849
5850 /*
5851 * Add needed dependency entries for the new column.
5852 */
5853 add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
5854 add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
5855
5856 /*
5857 * Propagate to children as appropriate. Unlike most other ALTER
5858 * routines, we have to do this one level of recursion at a time; we can't
5859 * use find_all_inheritors to do it in one pass.
5860 */
5861 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
5862
5863 /*
5864 * If we are told not to recurse, there had better not be any child
5865 * tables; else the addition would put them out of step.
5866 */
5867 if (children && !recurse)
5868 ereport(ERROR,
5869 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5870 errmsg("column must be added to child tables too")));
5871
5872 /* Children should see column as singly inherited */
5873 if (!recursing)
5874 {
5875 colDef = copyObject(colDef);
5876 colDef->inhcount = 1;
5877 colDef->is_local = false;
5878 }
5879
5880 foreach(child, children)
5881 {
5882 Oid childrelid = lfirst_oid(child);
5883 Relation childrel;
5884 AlteredTableInfo *childtab;
5885
5886 /* find_inheritance_children already got lock */
5887 childrel = heap_open(childrelid, NoLock);
5888 CheckTableNotInUse(childrel, "ALTER TABLE");
5889
5890 /* Find or create work queue entry for this table */
5891 childtab = ATGetQueueEntry(wqueue, childrel);
5892
5893 /* Recurse to child; return value is ignored */
5894 ATExecAddColumn(wqueue, childtab, childrel,
5895 colDef, isOid, recurse, true,
5896 if_not_exists, lockmode);
5897
5898 heap_close(childrel, NoLock);
5899 }
5900
5901 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
5902 return address;
5903 }
5904
5905 /*
5906 * If a new or renamed column will collide with the name of an existing
5907 * column and if_not_exists is false then error out, else do nothing.
5908 */
5909 static bool
check_for_column_name_collision(Relation rel,const char * colname,bool if_not_exists)5910 check_for_column_name_collision(Relation rel, const char *colname,
5911 bool if_not_exists)
5912 {
5913 HeapTuple attTuple;
5914 int attnum;
5915
5916 /*
5917 * this test is deliberately not attisdropped-aware, since if one tries to
5918 * add a column matching a dropped column name, it's gonna fail anyway.
5919 */
5920 attTuple = SearchSysCache2(ATTNAME,
5921 ObjectIdGetDatum(RelationGetRelid(rel)),
5922 PointerGetDatum(colname));
5923 if (!HeapTupleIsValid(attTuple))
5924 return true;
5925
5926 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
5927 ReleaseSysCache(attTuple);
5928
5929 /*
5930 * We throw a different error message for conflicts with system column
5931 * names, since they are normally not shown and the user might otherwise
5932 * be confused about the reason for the conflict.
5933 */
5934 if (attnum <= 0)
5935 ereport(ERROR,
5936 (errcode(ERRCODE_DUPLICATE_COLUMN),
5937 errmsg("column name \"%s\" conflicts with a system column name",
5938 colname)));
5939 else
5940 {
5941 if (if_not_exists)
5942 {
5943 ereport(NOTICE,
5944 (errcode(ERRCODE_DUPLICATE_COLUMN),
5945 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
5946 colname, RelationGetRelationName(rel))));
5947 return false;
5948 }
5949
5950 ereport(ERROR,
5951 (errcode(ERRCODE_DUPLICATE_COLUMN),
5952 errmsg("column \"%s\" of relation \"%s\" already exists",
5953 colname, RelationGetRelationName(rel))));
5954 }
5955
5956 return true;
5957 }
5958
5959 /*
5960 * Install a column's dependency on its datatype.
5961 */
5962 static void
add_column_datatype_dependency(Oid relid,int32 attnum,Oid typid)5963 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
5964 {
5965 ObjectAddress myself,
5966 referenced;
5967
5968 myself.classId = RelationRelationId;
5969 myself.objectId = relid;
5970 myself.objectSubId = attnum;
5971 referenced.classId = TypeRelationId;
5972 referenced.objectId = typid;
5973 referenced.objectSubId = 0;
5974 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
5975 }
5976
5977 /*
5978 * Install a column's dependency on its collation.
5979 */
5980 static void
add_column_collation_dependency(Oid relid,int32 attnum,Oid collid)5981 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
5982 {
5983 ObjectAddress myself,
5984 referenced;
5985
5986 /* We know the default collation is pinned, so don't bother recording it */
5987 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
5988 {
5989 myself.classId = RelationRelationId;
5990 myself.objectId = relid;
5991 myself.objectSubId = attnum;
5992 referenced.classId = CollationRelationId;
5993 referenced.objectId = collid;
5994 referenced.objectSubId = 0;
5995 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
5996 }
5997 }
5998
5999 /*
6000 * ALTER TABLE SET WITH OIDS
6001 *
6002 * Basically this is an ADD COLUMN for the special OID column. We have
6003 * to cons up a ColumnDef node because the ADD COLUMN code needs one.
6004 */
6005 static void
ATPrepAddOids(List ** wqueue,Relation rel,bool recurse,AlterTableCmd * cmd,LOCKMODE lockmode)6006 ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode)
6007 {
6008 /* If we're recursing to a child table, the ColumnDef is already set up */
6009 if (cmd->def == NULL)
6010 {
6011 ColumnDef *cdef = makeNode(ColumnDef);
6012
6013 cdef->colname = pstrdup("oid");
6014 cdef->typeName = makeTypeNameFromOid(OIDOID, -1);
6015 cdef->inhcount = 0;
6016 cdef->is_local = true;
6017 cdef->is_not_null = true;
6018 cdef->storage = 0;
6019 cdef->location = -1;
6020 cmd->def = (Node *) cdef;
6021 }
6022 ATPrepAddColumn(wqueue, rel, recurse, false, false, cmd, lockmode);
6023
6024 if (recurse)
6025 cmd->subtype = AT_AddOidsRecurse;
6026 }
6027
6028 /*
6029 * ALTER TABLE ALTER COLUMN DROP NOT NULL
6030 *
6031 * Return the address of the modified column. If the column was already
6032 * nullable, InvalidObjectAddress is returned.
6033 */
6034
6035 static void
ATPrepDropNotNull(Relation rel,bool recurse,bool recursing)6036 ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
6037 {
6038 /*
6039 * If the parent is a partitioned table, like check constraints, we do not
6040 * support removing the NOT NULL while partitions exist.
6041 */
6042 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6043 {
6044 PartitionDesc partdesc = RelationGetPartitionDesc(rel);
6045
6046 Assert(partdesc != NULL);
6047 if (partdesc->nparts > 0 && !recurse && !recursing)
6048 ereport(ERROR,
6049 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6050 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
6051 errhint("Do not specify the ONLY keyword.")));
6052 }
6053 }
6054 static ObjectAddress
ATExecDropNotNull(Relation rel,const char * colName,LOCKMODE lockmode)6055 ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
6056 {
6057 HeapTuple tuple;
6058 AttrNumber attnum;
6059 Relation attr_rel;
6060 List *indexoidlist;
6061 ListCell *indexoidscan;
6062 ObjectAddress address;
6063
6064 /*
6065 * lookup the attribute
6066 */
6067 attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
6068
6069 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6070
6071 if (!HeapTupleIsValid(tuple))
6072 ereport(ERROR,
6073 (errcode(ERRCODE_UNDEFINED_COLUMN),
6074 errmsg("column \"%s\" of relation \"%s\" does not exist",
6075 colName, RelationGetRelationName(rel))));
6076
6077 attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
6078
6079 /* Prevent them from altering a system attribute */
6080 if (attnum <= 0)
6081 ereport(ERROR,
6082 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6083 errmsg("cannot alter system column \"%s\"",
6084 colName)));
6085
6086 if (get_attidentity(RelationGetRelid(rel), attnum))
6087 ereport(ERROR,
6088 (errcode(ERRCODE_SYNTAX_ERROR),
6089 errmsg("column \"%s\" of relation \"%s\" is an identity column",
6090 colName, RelationGetRelationName(rel))));
6091
6092 /*
6093 * Check that the attribute is not in a primary key
6094 *
6095 * Note: we'll throw error even if the pkey index is not valid.
6096 */
6097
6098 /* Loop over all indexes on the relation */
6099 indexoidlist = RelationGetIndexList(rel);
6100
6101 foreach(indexoidscan, indexoidlist)
6102 {
6103 Oid indexoid = lfirst_oid(indexoidscan);
6104 HeapTuple indexTuple;
6105 Form_pg_index indexStruct;
6106 int i;
6107
6108 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
6109 if (!HeapTupleIsValid(indexTuple))
6110 elog(ERROR, "cache lookup failed for index %u", indexoid);
6111 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
6112
6113 /* If the index is not a primary key, skip the check */
6114 if (indexStruct->indisprimary)
6115 {
6116 /*
6117 * Loop over each attribute in the primary key and see if it
6118 * matches the to-be-altered attribute
6119 */
6120 for (i = 0; i < indexStruct->indnkeyatts; i++)
6121 {
6122 if (indexStruct->indkey.values[i] == attnum)
6123 ereport(ERROR,
6124 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6125 errmsg("column \"%s\" is in a primary key",
6126 colName)));
6127 }
6128 }
6129
6130 ReleaseSysCache(indexTuple);
6131 }
6132
6133 list_free(indexoidlist);
6134
6135 /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
6136 if (rel->rd_rel->relispartition)
6137 {
6138 Oid parentId = get_partition_parent(RelationGetRelid(rel));
6139 Relation parent = heap_open(parentId, AccessShareLock);
6140 TupleDesc tupDesc = RelationGetDescr(parent);
6141 AttrNumber parent_attnum;
6142
6143 parent_attnum = get_attnum(parentId, colName);
6144 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
6145 ereport(ERROR,
6146 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6147 errmsg("column \"%s\" is marked NOT NULL in parent table",
6148 colName)));
6149 heap_close(parent, AccessShareLock);
6150 }
6151
6152 /*
6153 * Okay, actually perform the catalog change ... if needed
6154 */
6155 if (((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
6156 {
6157 ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = false;
6158
6159 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
6160
6161 ObjectAddressSubSet(address, RelationRelationId,
6162 RelationGetRelid(rel), attnum);
6163 }
6164 else
6165 address = InvalidObjectAddress;
6166
6167 InvokeObjectPostAlterHook(RelationRelationId,
6168 RelationGetRelid(rel), attnum);
6169
6170 heap_close(attr_rel, RowExclusiveLock);
6171
6172 return address;
6173 }
6174
6175 /*
6176 * ALTER TABLE ALTER COLUMN SET NOT NULL
6177 */
6178
6179 static void
ATPrepSetNotNull(Relation rel,bool recurse,bool recursing)6180 ATPrepSetNotNull(Relation rel, bool recurse, bool recursing)
6181 {
6182 /*
6183 * If the parent is a partitioned table, like check constraints, NOT NULL
6184 * constraints must be added to the child tables. Complain if requested
6185 * otherwise and partitions exist.
6186 */
6187 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6188 {
6189 PartitionDesc partdesc = RelationGetPartitionDesc(rel);
6190
6191 if (partdesc && partdesc->nparts > 0 && !recurse && !recursing)
6192 ereport(ERROR,
6193 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6194 errmsg("cannot add constraint to only the partitioned table when partitions exist"),
6195 errhint("Do not specify the ONLY keyword.")));
6196 }
6197 }
6198
6199 /*
6200 * Return the address of the modified column. If the column was already NOT
6201 * NULL, InvalidObjectAddress is returned.
6202 */
6203 static ObjectAddress
ATExecSetNotNull(AlteredTableInfo * tab,Relation rel,const char * colName,LOCKMODE lockmode)6204 ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
6205 const char *colName, LOCKMODE lockmode)
6206 {
6207 HeapTuple tuple;
6208 AttrNumber attnum;
6209 Relation attr_rel;
6210 ObjectAddress address;
6211
6212 /*
6213 * lookup the attribute
6214 */
6215 attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
6216
6217 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6218
6219 if (!HeapTupleIsValid(tuple))
6220 ereport(ERROR,
6221 (errcode(ERRCODE_UNDEFINED_COLUMN),
6222 errmsg("column \"%s\" of relation \"%s\" does not exist",
6223 colName, RelationGetRelationName(rel))));
6224
6225 attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
6226
6227 /* Prevent them from altering a system attribute */
6228 if (attnum <= 0)
6229 ereport(ERROR,
6230 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6231 errmsg("cannot alter system column \"%s\"",
6232 colName)));
6233
6234 /*
6235 * Okay, actually perform the catalog change ... if needed
6236 */
6237 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
6238 {
6239 ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = true;
6240
6241 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
6242
6243 /* Tell Phase 3 it needs to test the constraint */
6244 tab->new_notnull = true;
6245
6246 ObjectAddressSubSet(address, RelationRelationId,
6247 RelationGetRelid(rel), attnum);
6248 }
6249 else
6250 address = InvalidObjectAddress;
6251
6252 InvokeObjectPostAlterHook(RelationRelationId,
6253 RelationGetRelid(rel), attnum);
6254
6255 heap_close(attr_rel, RowExclusiveLock);
6256
6257 return address;
6258 }
6259
6260 /*
6261 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
6262 *
6263 * Return the address of the affected column.
6264 */
6265 static ObjectAddress
ATExecColumnDefault(Relation rel,const char * colName,Node * newDefault,LOCKMODE lockmode)6266 ATExecColumnDefault(Relation rel, const char *colName,
6267 Node *newDefault, LOCKMODE lockmode)
6268 {
6269 AttrNumber attnum;
6270 ObjectAddress address;
6271
6272 /*
6273 * get the number of the attribute
6274 */
6275 attnum = get_attnum(RelationGetRelid(rel), colName);
6276 if (attnum == InvalidAttrNumber)
6277 ereport(ERROR,
6278 (errcode(ERRCODE_UNDEFINED_COLUMN),
6279 errmsg("column \"%s\" of relation \"%s\" does not exist",
6280 colName, RelationGetRelationName(rel))));
6281
6282 /* Prevent them from altering a system attribute */
6283 if (attnum <= 0)
6284 ereport(ERROR,
6285 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6286 errmsg("cannot alter system column \"%s\"",
6287 colName)));
6288
6289 if (get_attidentity(RelationGetRelid(rel), attnum))
6290 ereport(ERROR,
6291 (errcode(ERRCODE_SYNTAX_ERROR),
6292 errmsg("column \"%s\" of relation \"%s\" is an identity column",
6293 colName, RelationGetRelationName(rel)),
6294 newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
6295
6296 /*
6297 * Remove any old default for the column. We use RESTRICT here for
6298 * safety, but at present we do not expect anything to depend on the
6299 * default.
6300 *
6301 * We treat removing the existing default as an internal operation when it
6302 * is preparatory to adding a new default, but as a user-initiated
6303 * operation when the user asked for a drop.
6304 */
6305 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
6306 newDefault == NULL ? false : true);
6307
6308 if (newDefault)
6309 {
6310 /* SET DEFAULT */
6311 RawColumnDefault *rawEnt;
6312
6313 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
6314 rawEnt->attnum = attnum;
6315 rawEnt->raw_default = newDefault;
6316 rawEnt->missingMode = false;
6317
6318 /*
6319 * This function is intended for CREATE TABLE, so it processes a
6320 * _list_ of defaults, but we just do one.
6321 */
6322 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
6323 false, true, false);
6324 }
6325
6326 ObjectAddressSubSet(address, RelationRelationId,
6327 RelationGetRelid(rel), attnum);
6328 return address;
6329 }
6330
6331 /*
6332 * ALTER TABLE ALTER COLUMN ADD IDENTITY
6333 *
6334 * Return the address of the affected column.
6335 */
6336 static ObjectAddress
ATExecAddIdentity(Relation rel,const char * colName,Node * def,LOCKMODE lockmode)6337 ATExecAddIdentity(Relation rel, const char *colName,
6338 Node *def, LOCKMODE lockmode)
6339 {
6340 Relation attrelation;
6341 HeapTuple tuple;
6342 Form_pg_attribute attTup;
6343 AttrNumber attnum;
6344 ObjectAddress address;
6345 ColumnDef *cdef = castNode(ColumnDef, def);
6346
6347 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6348
6349 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6350 if (!HeapTupleIsValid(tuple))
6351 ereport(ERROR,
6352 (errcode(ERRCODE_UNDEFINED_COLUMN),
6353 errmsg("column \"%s\" of relation \"%s\" does not exist",
6354 colName, RelationGetRelationName(rel))));
6355 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6356 attnum = attTup->attnum;
6357
6358 /* Can't alter a system attribute */
6359 if (attnum <= 0)
6360 ereport(ERROR,
6361 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6362 errmsg("cannot alter system column \"%s\"",
6363 colName)));
6364
6365 /*
6366 * Creating a column as identity implies NOT NULL, so adding the identity
6367 * to an existing column that is not NOT NULL would create a state that
6368 * cannot be reproduced without contortions.
6369 */
6370 if (!attTup->attnotnull)
6371 ereport(ERROR,
6372 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6373 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
6374 colName, RelationGetRelationName(rel))));
6375
6376 if (attTup->attidentity)
6377 ereport(ERROR,
6378 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6379 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
6380 colName, RelationGetRelationName(rel))));
6381
6382 if (attTup->atthasdef)
6383 ereport(ERROR,
6384 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6385 errmsg("column \"%s\" of relation \"%s\" already has a default value",
6386 colName, RelationGetRelationName(rel))));
6387
6388 attTup->attidentity = cdef->identity;
6389 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6390
6391 InvokeObjectPostAlterHook(RelationRelationId,
6392 RelationGetRelid(rel),
6393 attTup->attnum);
6394 ObjectAddressSubSet(address, RelationRelationId,
6395 RelationGetRelid(rel), attnum);
6396 heap_freetuple(tuple);
6397
6398 heap_close(attrelation, RowExclusiveLock);
6399
6400 return address;
6401 }
6402
6403 /*
6404 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
6405 *
6406 * Return the address of the affected column.
6407 */
6408 static ObjectAddress
ATExecSetIdentity(Relation rel,const char * colName,Node * def,LOCKMODE lockmode)6409 ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
6410 {
6411 ListCell *option;
6412 DefElem *generatedEl = NULL;
6413 HeapTuple tuple;
6414 Form_pg_attribute attTup;
6415 AttrNumber attnum;
6416 Relation attrelation;
6417 ObjectAddress address;
6418
6419 foreach(option, castNode(List, def))
6420 {
6421 DefElem *defel = lfirst_node(DefElem, option);
6422
6423 if (strcmp(defel->defname, "generated") == 0)
6424 {
6425 if (generatedEl)
6426 ereport(ERROR,
6427 (errcode(ERRCODE_SYNTAX_ERROR),
6428 errmsg("conflicting or redundant options")));
6429 generatedEl = defel;
6430 }
6431 else
6432 elog(ERROR, "option \"%s\" not recognized",
6433 defel->defname);
6434 }
6435
6436 /*
6437 * Even if there is nothing to change here, we run all the checks. There
6438 * will be a subsequent ALTER SEQUENCE that relies on everything being
6439 * there.
6440 */
6441
6442 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6443 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6444 if (!HeapTupleIsValid(tuple))
6445 ereport(ERROR,
6446 (errcode(ERRCODE_UNDEFINED_COLUMN),
6447 errmsg("column \"%s\" of relation \"%s\" does not exist",
6448 colName, RelationGetRelationName(rel))));
6449
6450 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6451 attnum = attTup->attnum;
6452
6453 if (attnum <= 0)
6454 ereport(ERROR,
6455 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6456 errmsg("cannot alter system column \"%s\"",
6457 colName)));
6458
6459 if (!attTup->attidentity)
6460 ereport(ERROR,
6461 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6462 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
6463 colName, RelationGetRelationName(rel))));
6464
6465 if (generatedEl)
6466 {
6467 attTup->attidentity = defGetInt32(generatedEl);
6468 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6469
6470 InvokeObjectPostAlterHook(RelationRelationId,
6471 RelationGetRelid(rel),
6472 attTup->attnum);
6473 ObjectAddressSubSet(address, RelationRelationId,
6474 RelationGetRelid(rel), attnum);
6475 }
6476 else
6477 address = InvalidObjectAddress;
6478
6479 heap_freetuple(tuple);
6480 heap_close(attrelation, RowExclusiveLock);
6481
6482 return address;
6483 }
6484
6485 /*
6486 * ALTER TABLE ALTER COLUMN DROP IDENTITY
6487 *
6488 * Return the address of the affected column.
6489 */
6490 static ObjectAddress
ATExecDropIdentity(Relation rel,const char * colName,bool missing_ok,LOCKMODE lockmode)6491 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
6492 {
6493 HeapTuple tuple;
6494 Form_pg_attribute attTup;
6495 AttrNumber attnum;
6496 Relation attrelation;
6497 ObjectAddress address;
6498 Oid seqid;
6499 ObjectAddress seqaddress;
6500
6501 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6502 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6503 if (!HeapTupleIsValid(tuple))
6504 ereport(ERROR,
6505 (errcode(ERRCODE_UNDEFINED_COLUMN),
6506 errmsg("column \"%s\" of relation \"%s\" does not exist",
6507 colName, RelationGetRelationName(rel))));
6508
6509 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6510 attnum = attTup->attnum;
6511
6512 if (attnum <= 0)
6513 ereport(ERROR,
6514 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6515 errmsg("cannot alter system column \"%s\"",
6516 colName)));
6517
6518 if (!attTup->attidentity)
6519 {
6520 if (!missing_ok)
6521 ereport(ERROR,
6522 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6523 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
6524 colName, RelationGetRelationName(rel))));
6525 else
6526 {
6527 ereport(NOTICE,
6528 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
6529 colName, RelationGetRelationName(rel))));
6530 heap_freetuple(tuple);
6531 heap_close(attrelation, RowExclusiveLock);
6532 return InvalidObjectAddress;
6533 }
6534 }
6535
6536 attTup->attidentity = '\0';
6537 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6538
6539 InvokeObjectPostAlterHook(RelationRelationId,
6540 RelationGetRelid(rel),
6541 attTup->attnum);
6542 ObjectAddressSubSet(address, RelationRelationId,
6543 RelationGetRelid(rel), attnum);
6544 heap_freetuple(tuple);
6545
6546 heap_close(attrelation, RowExclusiveLock);
6547
6548 /* drop the internal sequence */
6549 seqid = getOwnedSequence(RelationGetRelid(rel), attnum);
6550 deleteDependencyRecordsForClass(RelationRelationId, seqid,
6551 RelationRelationId, DEPENDENCY_INTERNAL);
6552 CommandCounterIncrement();
6553 seqaddress.classId = RelationRelationId;
6554 seqaddress.objectId = seqid;
6555 seqaddress.objectSubId = 0;
6556 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
6557
6558 return address;
6559 }
6560
6561 /*
6562 * ALTER TABLE ALTER COLUMN SET STATISTICS
6563 */
6564 static void
ATPrepSetStatistics(Relation rel,const char * colName,int16 colNum,Node * newValue,LOCKMODE lockmode)6565 ATPrepSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
6566 {
6567 /*
6568 * We do our own permission checking because (a) we want to allow SET
6569 * STATISTICS on indexes (for expressional index columns), and (b) we want
6570 * to allow SET STATISTICS on system catalogs without requiring
6571 * allowSystemTableMods to be turned on.
6572 */
6573 if (rel->rd_rel->relkind != RELKIND_RELATION &&
6574 rel->rd_rel->relkind != RELKIND_MATVIEW &&
6575 rel->rd_rel->relkind != RELKIND_INDEX &&
6576 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
6577 rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
6578 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
6579 ereport(ERROR,
6580 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6581 errmsg("\"%s\" is not a table, materialized view, index, or foreign table",
6582 RelationGetRelationName(rel))));
6583
6584 /*
6585 * We allow referencing columns by numbers only for indexes, since table
6586 * column numbers could contain gaps if columns are later dropped.
6587 */
6588 if (rel->rd_rel->relkind != RELKIND_INDEX &&
6589 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
6590 !colName)
6591 ereport(ERROR,
6592 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6593 errmsg("cannot refer to non-index column by number")));
6594
6595 /* Permissions checks */
6596 if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
6597 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6598 RelationGetRelationName(rel));
6599 }
6600
6601 /*
6602 * Return value is the address of the modified column
6603 */
6604 static ObjectAddress
ATExecSetStatistics(Relation rel,const char * colName,int16 colNum,Node * newValue,LOCKMODE lockmode)6605 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
6606 {
6607 int newtarget;
6608 Relation attrelation;
6609 HeapTuple tuple;
6610 Form_pg_attribute attrtuple;
6611 AttrNumber attnum;
6612 ObjectAddress address;
6613
6614 Assert(IsA(newValue, Integer));
6615 newtarget = intVal(newValue);
6616
6617 /*
6618 * Limit target to a sane range
6619 */
6620 if (newtarget < -1)
6621 {
6622 ereport(ERROR,
6623 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6624 errmsg("statistics target %d is too low",
6625 newtarget)));
6626 }
6627 else if (newtarget > 10000)
6628 {
6629 newtarget = 10000;
6630 ereport(WARNING,
6631 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6632 errmsg("lowering statistics target to %d",
6633 newtarget)));
6634 }
6635
6636 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6637
6638 if (colName)
6639 {
6640 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6641
6642 if (!HeapTupleIsValid(tuple))
6643 ereport(ERROR,
6644 (errcode(ERRCODE_UNDEFINED_COLUMN),
6645 errmsg("column \"%s\" of relation \"%s\" does not exist",
6646 colName, RelationGetRelationName(rel))));
6647 }
6648 else
6649 {
6650 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
6651
6652 if (!HeapTupleIsValid(tuple))
6653 ereport(ERROR,
6654 (errcode(ERRCODE_UNDEFINED_COLUMN),
6655 errmsg("column number %d of relation \"%s\" does not exist",
6656 colNum, RelationGetRelationName(rel))));
6657 }
6658
6659 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6660
6661 attnum = attrtuple->attnum;
6662 if (attnum <= 0)
6663 ereport(ERROR,
6664 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6665 errmsg("cannot alter system column \"%s\"",
6666 colName)));
6667
6668 if (rel->rd_rel->relkind == RELKIND_INDEX ||
6669 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
6670 {
6671 if (attnum > rel->rd_index->indnkeyatts)
6672 ereport(ERROR,
6673 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6674 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
6675 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
6676 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
6677 ereport(ERROR,
6678 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6679 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
6680 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
6681 errhint("Alter statistics on table column instead.")));
6682 }
6683
6684 attrtuple->attstattarget = newtarget;
6685
6686 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6687
6688 InvokeObjectPostAlterHook(RelationRelationId,
6689 RelationGetRelid(rel),
6690 attrtuple->attnum);
6691 ObjectAddressSubSet(address, RelationRelationId,
6692 RelationGetRelid(rel), attnum);
6693 heap_freetuple(tuple);
6694
6695 heap_close(attrelation, RowExclusiveLock);
6696
6697 return address;
6698 }
6699
6700 /*
6701 * Return value is the address of the modified column
6702 */
6703 static ObjectAddress
ATExecSetOptions(Relation rel,const char * colName,Node * options,bool isReset,LOCKMODE lockmode)6704 ATExecSetOptions(Relation rel, const char *colName, Node *options,
6705 bool isReset, LOCKMODE lockmode)
6706 {
6707 Relation attrelation;
6708 HeapTuple tuple,
6709 newtuple;
6710 Form_pg_attribute attrtuple;
6711 AttrNumber attnum;
6712 Datum datum,
6713 newOptions;
6714 bool isnull;
6715 ObjectAddress address;
6716 Datum repl_val[Natts_pg_attribute];
6717 bool repl_null[Natts_pg_attribute];
6718 bool repl_repl[Natts_pg_attribute];
6719
6720 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6721
6722 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
6723
6724 if (!HeapTupleIsValid(tuple))
6725 ereport(ERROR,
6726 (errcode(ERRCODE_UNDEFINED_COLUMN),
6727 errmsg("column \"%s\" of relation \"%s\" does not exist",
6728 colName, RelationGetRelationName(rel))));
6729 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6730
6731 attnum = attrtuple->attnum;
6732 if (attnum <= 0)
6733 ereport(ERROR,
6734 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6735 errmsg("cannot alter system column \"%s\"",
6736 colName)));
6737
6738 /* Generate new proposed attoptions (text array) */
6739 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
6740 &isnull);
6741 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
6742 castNode(List, options), NULL, NULL,
6743 false, isReset);
6744 /* Validate new options */
6745 (void) attribute_reloptions(newOptions, true);
6746
6747 /* Build new tuple. */
6748 memset(repl_null, false, sizeof(repl_null));
6749 memset(repl_repl, false, sizeof(repl_repl));
6750 if (newOptions != (Datum) 0)
6751 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
6752 else
6753 repl_null[Anum_pg_attribute_attoptions - 1] = true;
6754 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
6755 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
6756 repl_val, repl_null, repl_repl);
6757
6758 /* Update system catalog. */
6759 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
6760
6761 InvokeObjectPostAlterHook(RelationRelationId,
6762 RelationGetRelid(rel),
6763 attrtuple->attnum);
6764 ObjectAddressSubSet(address, RelationRelationId,
6765 RelationGetRelid(rel), attnum);
6766
6767 heap_freetuple(newtuple);
6768
6769 ReleaseSysCache(tuple);
6770
6771 heap_close(attrelation, RowExclusiveLock);
6772
6773 return address;
6774 }
6775
6776 /*
6777 * ALTER TABLE ALTER COLUMN SET STORAGE
6778 *
6779 * Return value is the address of the modified column
6780 */
6781 static ObjectAddress
ATExecSetStorage(Relation rel,const char * colName,Node * newValue,LOCKMODE lockmode)6782 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6783 {
6784 char *storagemode;
6785 char newstorage;
6786 Relation attrelation;
6787 HeapTuple tuple;
6788 Form_pg_attribute attrtuple;
6789 AttrNumber attnum;
6790 ObjectAddress address;
6791 ListCell *lc;
6792
6793 Assert(IsA(newValue, String));
6794 storagemode = strVal(newValue);
6795
6796 if (pg_strcasecmp(storagemode, "plain") == 0)
6797 newstorage = 'p';
6798 else if (pg_strcasecmp(storagemode, "external") == 0)
6799 newstorage = 'e';
6800 else if (pg_strcasecmp(storagemode, "extended") == 0)
6801 newstorage = 'x';
6802 else if (pg_strcasecmp(storagemode, "main") == 0)
6803 newstorage = 'm';
6804 else
6805 {
6806 ereport(ERROR,
6807 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6808 errmsg("invalid storage type \"%s\"",
6809 storagemode)));
6810 newstorage = 0; /* keep compiler quiet */
6811 }
6812
6813 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6814
6815 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6816
6817 if (!HeapTupleIsValid(tuple))
6818 ereport(ERROR,
6819 (errcode(ERRCODE_UNDEFINED_COLUMN),
6820 errmsg("column \"%s\" of relation \"%s\" does not exist",
6821 colName, RelationGetRelationName(rel))));
6822 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6823
6824 attnum = attrtuple->attnum;
6825 if (attnum <= 0)
6826 ereport(ERROR,
6827 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6828 errmsg("cannot alter system column \"%s\"",
6829 colName)));
6830
6831 /*
6832 * safety check: do not allow toasted storage modes unless column datatype
6833 * is TOAST-aware.
6834 */
6835 if (newstorage == 'p' || TypeIsToastable(attrtuple->atttypid))
6836 attrtuple->attstorage = newstorage;
6837 else
6838 ereport(ERROR,
6839 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6840 errmsg("column data type %s can only have storage PLAIN",
6841 format_type_be(attrtuple->atttypid))));
6842
6843 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6844
6845 InvokeObjectPostAlterHook(RelationRelationId,
6846 RelationGetRelid(rel),
6847 attrtuple->attnum);
6848
6849 heap_freetuple(tuple);
6850
6851 /*
6852 * Apply the change to indexes as well (only for simple index columns,
6853 * matching behavior of index.c ConstructTupleDescriptor()).
6854 */
6855 foreach(lc, RelationGetIndexList(rel))
6856 {
6857 Oid indexoid = lfirst_oid(lc);
6858 Relation indrel;
6859 int i;
6860 AttrNumber indattnum = 0;
6861
6862 indrel = index_open(indexoid, lockmode);
6863
6864 for (i = 0; i < indrel->rd_index->indnatts; i++)
6865 {
6866 if (indrel->rd_index->indkey.values[i] == attnum)
6867 {
6868 indattnum = i + 1;
6869 break;
6870 }
6871 }
6872
6873 if (indattnum == 0)
6874 {
6875 index_close(indrel, lockmode);
6876 continue;
6877 }
6878
6879 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
6880
6881 if (HeapTupleIsValid(tuple))
6882 {
6883 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6884 attrtuple->attstorage = newstorage;
6885
6886 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6887
6888 InvokeObjectPostAlterHook(RelationRelationId,
6889 RelationGetRelid(rel),
6890 attrtuple->attnum);
6891
6892 heap_freetuple(tuple);
6893 }
6894
6895 index_close(indrel, lockmode);
6896 }
6897
6898 heap_close(attrelation, RowExclusiveLock);
6899
6900 ObjectAddressSubSet(address, RelationRelationId,
6901 RelationGetRelid(rel), attnum);
6902 return address;
6903 }
6904
6905
6906 /*
6907 * ALTER TABLE DROP COLUMN
6908 *
6909 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
6910 * because we have to decide at runtime whether to recurse or not depending
6911 * on whether attinhcount goes to zero or not. (We can't check this in a
6912 * static pre-pass because it won't handle multiple inheritance situations
6913 * correctly.)
6914 */
6915 static void
ATPrepDropColumn(List ** wqueue,Relation rel,bool recurse,bool recursing,AlterTableCmd * cmd,LOCKMODE lockmode)6916 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6917 AlterTableCmd *cmd, LOCKMODE lockmode)
6918 {
6919 if (rel->rd_rel->reloftype && !recursing)
6920 ereport(ERROR,
6921 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6922 errmsg("cannot drop column from typed table")));
6923
6924 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6925 ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
6926
6927 if (recurse)
6928 cmd->subtype = AT_DropColumnRecurse;
6929 }
6930
6931 /*
6932 * Return value is the address of the dropped column.
6933 */
6934 static ObjectAddress
ATExecDropColumn(List ** wqueue,Relation rel,const char * colName,DropBehavior behavior,bool recurse,bool recursing,bool missing_ok,LOCKMODE lockmode)6935 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
6936 DropBehavior behavior,
6937 bool recurse, bool recursing,
6938 bool missing_ok, LOCKMODE lockmode)
6939 {
6940 HeapTuple tuple;
6941 Form_pg_attribute targetatt;
6942 AttrNumber attnum;
6943 List *children;
6944 ObjectAddress object;
6945 bool is_expr;
6946
6947 /* At top level, permission check was done in ATPrepCmd, else do it */
6948 if (recursing)
6949 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6950
6951 /*
6952 * get the number of the attribute
6953 */
6954 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
6955 if (!HeapTupleIsValid(tuple))
6956 {
6957 if (!missing_ok)
6958 {
6959 ereport(ERROR,
6960 (errcode(ERRCODE_UNDEFINED_COLUMN),
6961 errmsg("column \"%s\" of relation \"%s\" does not exist",
6962 colName, RelationGetRelationName(rel))));
6963 }
6964 else
6965 {
6966 ereport(NOTICE,
6967 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
6968 colName, RelationGetRelationName(rel))));
6969 return InvalidObjectAddress;
6970 }
6971 }
6972 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
6973
6974 attnum = targetatt->attnum;
6975
6976 /* Can't drop a system attribute, except OID */
6977 if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
6978 ereport(ERROR,
6979 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6980 errmsg("cannot drop system column \"%s\"",
6981 colName)));
6982
6983 /*
6984 * Don't drop inherited columns, unless recursing (presumably from a drop
6985 * of the parent column)
6986 */
6987 if (targetatt->attinhcount > 0 && !recursing)
6988 ereport(ERROR,
6989 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6990 errmsg("cannot drop inherited column \"%s\"",
6991 colName)));
6992
6993 /*
6994 * Don't drop columns used in the partition key, either. (If we let this
6995 * go through, the key column's dependencies would cause a cascaded drop
6996 * of the whole table, which is surely not what the user expected.)
6997 */
6998 if (has_partition_attrs(rel,
6999 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
7000 &is_expr))
7001 ereport(ERROR,
7002 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7003 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
7004 colName, RelationGetRelationName(rel))));
7005
7006 ReleaseSysCache(tuple);
7007
7008 /*
7009 * Propagate to children as appropriate. Unlike most other ALTER
7010 * routines, we have to do this one level of recursion at a time; we can't
7011 * use find_all_inheritors to do it in one pass.
7012 */
7013 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7014
7015 if (children)
7016 {
7017 Relation attr_rel;
7018 ListCell *child;
7019
7020 /*
7021 * In case of a partitioned table, the column must be dropped from the
7022 * partitions as well.
7023 */
7024 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
7025 ereport(ERROR,
7026 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7027 errmsg("cannot drop column from only the partitioned table when partitions exist"),
7028 errhint("Do not specify the ONLY keyword.")));
7029
7030 attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
7031 foreach(child, children)
7032 {
7033 Oid childrelid = lfirst_oid(child);
7034 Relation childrel;
7035 Form_pg_attribute childatt;
7036
7037 /* find_inheritance_children already got lock */
7038 childrel = heap_open(childrelid, NoLock);
7039 CheckTableNotInUse(childrel, "ALTER TABLE");
7040
7041 tuple = SearchSysCacheCopyAttName(childrelid, colName);
7042 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
7043 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
7044 colName, childrelid);
7045 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7046
7047 if (childatt->attinhcount <= 0) /* shouldn't happen */
7048 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
7049 childrelid, colName);
7050
7051 if (recurse)
7052 {
7053 /*
7054 * If the child column has other definition sources, just
7055 * decrement its inheritance count; if not, recurse to delete
7056 * it.
7057 */
7058 if (childatt->attinhcount == 1 && !childatt->attislocal)
7059 {
7060 /* Time to delete this child column, too */
7061 ATExecDropColumn(wqueue, childrel, colName,
7062 behavior, true, true,
7063 false, lockmode);
7064 }
7065 else
7066 {
7067 /* Child column must survive my deletion */
7068 childatt->attinhcount--;
7069
7070 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7071
7072 /* Make update visible */
7073 CommandCounterIncrement();
7074 }
7075 }
7076 else
7077 {
7078 /*
7079 * If we were told to drop ONLY in this table (no recursion),
7080 * we need to mark the inheritors' attributes as locally
7081 * defined rather than inherited.
7082 */
7083 childatt->attinhcount--;
7084 childatt->attislocal = true;
7085
7086 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7087
7088 /* Make update visible */
7089 CommandCounterIncrement();
7090 }
7091
7092 heap_freetuple(tuple);
7093
7094 heap_close(childrel, NoLock);
7095 }
7096 heap_close(attr_rel, RowExclusiveLock);
7097 }
7098
7099 /*
7100 * Perform the actual column deletion
7101 */
7102 object.classId = RelationRelationId;
7103 object.objectId = RelationGetRelid(rel);
7104 object.objectSubId = attnum;
7105
7106 performDeletion(&object, behavior, 0);
7107
7108 /*
7109 * If we dropped the OID column, must adjust pg_class.relhasoids and tell
7110 * Phase 3 to physically get rid of the column. We formerly left the
7111 * column in place physically, but this caused subtle problems. See
7112 * http://archives.postgresql.org/pgsql-hackers/2009-02/msg00363.php
7113 */
7114 if (attnum == ObjectIdAttributeNumber)
7115 {
7116 Relation class_rel;
7117 Form_pg_class tuple_class;
7118 AlteredTableInfo *tab;
7119
7120 class_rel = heap_open(RelationRelationId, RowExclusiveLock);
7121
7122 tuple = SearchSysCacheCopy1(RELOID,
7123 ObjectIdGetDatum(RelationGetRelid(rel)));
7124 if (!HeapTupleIsValid(tuple))
7125 elog(ERROR, "cache lookup failed for relation %u",
7126 RelationGetRelid(rel));
7127 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
7128
7129 tuple_class->relhasoids = false;
7130 CatalogTupleUpdate(class_rel, &tuple->t_self, tuple);
7131
7132 heap_close(class_rel, RowExclusiveLock);
7133
7134 /* Find or create work queue entry for this table */
7135 tab = ATGetQueueEntry(wqueue, rel);
7136
7137 /* Tell Phase 3 to physically remove the OID column */
7138 tab->rewrite |= AT_REWRITE_ALTER_OID;
7139 }
7140
7141 return object;
7142 }
7143
7144 /*
7145 * ALTER TABLE ADD INDEX
7146 *
7147 * There is no such command in the grammar, but parse_utilcmd.c converts
7148 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
7149 * us schedule creation of the index at the appropriate time during ALTER.
7150 *
7151 * Return value is the address of the new index.
7152 */
7153 static ObjectAddress
ATExecAddIndex(AlteredTableInfo * tab,Relation rel,IndexStmt * stmt,bool is_rebuild,LOCKMODE lockmode)7154 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
7155 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
7156 {
7157 bool check_rights;
7158 bool skip_build;
7159 bool quiet;
7160 ObjectAddress address;
7161
7162 Assert(IsA(stmt, IndexStmt));
7163 Assert(!stmt->concurrent);
7164
7165 /* The IndexStmt has already been through transformIndexStmt */
7166 Assert(stmt->transformed);
7167
7168 /* suppress schema rights check when rebuilding existing index */
7169 check_rights = !is_rebuild;
7170 /* skip index build if phase 3 will do it or we're reusing an old one */
7171 skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode);
7172 /* suppress notices when rebuilding existing index */
7173 quiet = is_rebuild;
7174
7175 address = DefineIndex(RelationGetRelid(rel),
7176 stmt,
7177 InvalidOid, /* no predefined OID */
7178 InvalidOid, /* no parent index */
7179 InvalidOid, /* no parent constraint */
7180 true, /* is_alter_table */
7181 check_rights,
7182 false, /* check_not_in_use - we did it already */
7183 skip_build,
7184 quiet);
7185
7186 /*
7187 * If TryReuseIndex() stashed a relfilenode for us, we used it for the new
7188 * index instead of building from scratch. The DROP of the old edition of
7189 * this index will have scheduled the storage for deletion at commit, so
7190 * cancel that pending deletion.
7191 */
7192 if (OidIsValid(stmt->oldNode))
7193 {
7194 Relation irel = index_open(address.objectId, NoLock);
7195
7196 RelationPreserveStorage(irel->rd_node, true);
7197 index_close(irel, NoLock);
7198 }
7199
7200 return address;
7201 }
7202
7203 /*
7204 * ALTER TABLE ADD CONSTRAINT USING INDEX
7205 *
7206 * Returns the address of the new constraint.
7207 */
7208 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo * tab,Relation rel,IndexStmt * stmt,LOCKMODE lockmode)7209 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
7210 IndexStmt *stmt, LOCKMODE lockmode)
7211 {
7212 Oid index_oid = stmt->indexOid;
7213 Relation indexRel;
7214 char *indexName;
7215 IndexInfo *indexInfo;
7216 char *constraintName;
7217 char constraintType;
7218 ObjectAddress address;
7219 bits16 flags;
7220
7221 Assert(IsA(stmt, IndexStmt));
7222 Assert(OidIsValid(index_oid));
7223 Assert(stmt->isconstraint);
7224
7225 /*
7226 * Doing this on partitioned tables is not a simple feature to implement,
7227 * so let's punt for now.
7228 */
7229 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7230 ereport(ERROR,
7231 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7232 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
7233
7234 indexRel = index_open(index_oid, AccessShareLock);
7235
7236 indexName = pstrdup(RelationGetRelationName(indexRel));
7237
7238 indexInfo = BuildIndexInfo(indexRel);
7239
7240 /* this should have been checked at parse time */
7241 if (!indexInfo->ii_Unique)
7242 elog(ERROR, "index \"%s\" is not unique", indexName);
7243
7244 /*
7245 * Determine name to assign to constraint. We require a constraint to
7246 * have the same name as the underlying index; therefore, use the index's
7247 * existing name as the default constraint name, and if the user
7248 * explicitly gives some other name for the constraint, rename the index
7249 * to match.
7250 */
7251 constraintName = stmt->idxname;
7252 if (constraintName == NULL)
7253 constraintName = indexName;
7254 else if (strcmp(constraintName, indexName) != 0)
7255 {
7256 ereport(NOTICE,
7257 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
7258 indexName, constraintName)));
7259 RenameRelationInternal(index_oid, constraintName, false);
7260 }
7261
7262 /* Extra checks needed if making primary key */
7263 if (stmt->primary)
7264 index_check_primary_key(rel, indexInfo, true, stmt);
7265
7266 /* Note we currently don't support EXCLUSION constraints here */
7267 if (stmt->primary)
7268 constraintType = CONSTRAINT_PRIMARY;
7269 else
7270 constraintType = CONSTRAINT_UNIQUE;
7271
7272 /* Create the catalog entries for the constraint */
7273 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
7274 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
7275 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
7276 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
7277 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
7278
7279 address = index_constraint_create(rel,
7280 index_oid,
7281 InvalidOid,
7282 indexInfo,
7283 constraintName,
7284 constraintType,
7285 flags,
7286 allowSystemTableMods,
7287 false); /* is_internal */
7288
7289 index_close(indexRel, NoLock);
7290
7291 return address;
7292 }
7293
7294 /*
7295 * ALTER TABLE ADD CONSTRAINT
7296 *
7297 * Return value is the address of the new constraint; if no constraint was
7298 * added, InvalidObjectAddress is returned.
7299 */
7300 static ObjectAddress
ATExecAddConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * newConstraint,bool recurse,bool is_readd,LOCKMODE lockmode)7301 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
7302 Constraint *newConstraint, bool recurse, bool is_readd,
7303 LOCKMODE lockmode)
7304 {
7305 ObjectAddress address = InvalidObjectAddress;
7306
7307 Assert(IsA(newConstraint, Constraint));
7308
7309 /*
7310 * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
7311 * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
7312 * switch anyway to make it easier to add more code later.
7313 */
7314 switch (newConstraint->contype)
7315 {
7316 case CONSTR_CHECK:
7317 address =
7318 ATAddCheckConstraint(wqueue, tab, rel,
7319 newConstraint, recurse, false, is_readd,
7320 lockmode);
7321 break;
7322
7323 case CONSTR_FOREIGN:
7324
7325 /*
7326 * Note that we currently never recurse for FK constraints, so the
7327 * "recurse" flag is silently ignored.
7328 *
7329 * Assign or validate constraint name
7330 */
7331 if (newConstraint->conname)
7332 {
7333 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
7334 RelationGetRelid(rel),
7335 newConstraint->conname))
7336 ereport(ERROR,
7337 (errcode(ERRCODE_DUPLICATE_OBJECT),
7338 errmsg("constraint \"%s\" for relation \"%s\" already exists",
7339 newConstraint->conname,
7340 RelationGetRelationName(rel))));
7341 }
7342 else
7343 newConstraint->conname =
7344 ChooseConstraintName(RelationGetRelationName(rel),
7345 strVal(linitial(newConstraint->fk_attrs)),
7346 "fkey",
7347 RelationGetNamespace(rel),
7348 NIL);
7349
7350 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
7351 newConstraint, InvalidOid,
7352 recurse, false,
7353 lockmode);
7354 break;
7355
7356 default:
7357 elog(ERROR, "unrecognized constraint type: %d",
7358 (int) newConstraint->contype);
7359 }
7360
7361 return address;
7362 }
7363
7364 /*
7365 * Add a check constraint to a single table and its children. Returns the
7366 * address of the constraint added to the parent relation, if one gets added,
7367 * or InvalidObjectAddress otherwise.
7368 *
7369 * Subroutine for ATExecAddConstraint.
7370 *
7371 * We must recurse to child tables during execution, rather than using
7372 * ALTER TABLE's normal prep-time recursion. The reason is that all the
7373 * constraints *must* be given the same name, else they won't be seen as
7374 * related later. If the user didn't explicitly specify a name, then
7375 * AddRelationNewConstraints would normally assign different names to the
7376 * child constraints. To fix that, we must capture the name assigned at
7377 * the parent table and pass that down.
7378 */
7379 static ObjectAddress
ATAddCheckConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * constr,bool recurse,bool recursing,bool is_readd,LOCKMODE lockmode)7380 ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
7381 Constraint *constr, bool recurse, bool recursing,
7382 bool is_readd, LOCKMODE lockmode)
7383 {
7384 List *newcons;
7385 ListCell *lcon;
7386 List *children;
7387 ListCell *child;
7388 ObjectAddress address = InvalidObjectAddress;
7389
7390 /* At top level, permission check was done in ATPrepCmd, else do it */
7391 if (recursing)
7392 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7393
7394 /*
7395 * Call AddRelationNewConstraints to do the work, making sure it works on
7396 * a copy of the Constraint so transformExpr can't modify the original. It
7397 * returns a list of cooked constraints.
7398 *
7399 * If the constraint ends up getting merged with a pre-existing one, it's
7400 * omitted from the returned list, which is what we want: we do not need
7401 * to do any validation work. That can only happen at child tables,
7402 * though, since we disallow merging at the top level.
7403 */
7404 newcons = AddRelationNewConstraints(rel, NIL,
7405 list_make1(copyObject(constr)),
7406 recursing | is_readd, /* allow_merge */
7407 !recursing, /* is_local */
7408 is_readd); /* is_internal */
7409
7410 /* we don't expect more than one constraint here */
7411 Assert(list_length(newcons) <= 1);
7412
7413 /* Add each to-be-validated constraint to Phase 3's queue */
7414 foreach(lcon, newcons)
7415 {
7416 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
7417
7418 if (!ccon->skip_validation)
7419 {
7420 NewConstraint *newcon;
7421
7422 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
7423 newcon->name = ccon->name;
7424 newcon->contype = ccon->contype;
7425 newcon->qual = ccon->expr;
7426
7427 tab->constraints = lappend(tab->constraints, newcon);
7428 }
7429
7430 /* Save the actually assigned name if it was defaulted */
7431 if (constr->conname == NULL)
7432 constr->conname = ccon->name;
7433
7434 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7435 }
7436
7437 /* At this point we must have a locked-down name to use */
7438 Assert(constr->conname != NULL);
7439
7440 /* Advance command counter in case same table is visited multiple times */
7441 CommandCounterIncrement();
7442
7443 /*
7444 * If the constraint got merged with an existing constraint, we're done.
7445 * We mustn't recurse to child tables in this case, because they've
7446 * already got the constraint, and visiting them again would lead to an
7447 * incorrect value for coninhcount.
7448 */
7449 if (newcons == NIL)
7450 return address;
7451
7452 /*
7453 * If adding a NO INHERIT constraint, no need to find our children.
7454 */
7455 if (constr->is_no_inherit)
7456 return address;
7457
7458 /*
7459 * Propagate to children as appropriate. Unlike most other ALTER
7460 * routines, we have to do this one level of recursion at a time; we can't
7461 * use find_all_inheritors to do it in one pass.
7462 */
7463 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7464
7465 /*
7466 * Check if ONLY was specified with ALTER TABLE. If so, allow the
7467 * constraint creation only if there are no children currently. Error out
7468 * otherwise.
7469 */
7470 if (!recurse && children != NIL)
7471 ereport(ERROR,
7472 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7473 errmsg("constraint must be added to child tables too")));
7474
7475 foreach(child, children)
7476 {
7477 Oid childrelid = lfirst_oid(child);
7478 Relation childrel;
7479 AlteredTableInfo *childtab;
7480
7481 /* find_inheritance_children already got lock */
7482 childrel = heap_open(childrelid, NoLock);
7483 CheckTableNotInUse(childrel, "ALTER TABLE");
7484
7485 /* Find or create work queue entry for this table */
7486 childtab = ATGetQueueEntry(wqueue, childrel);
7487
7488 /* Recurse to child */
7489 ATAddCheckConstraint(wqueue, childtab, childrel,
7490 constr, recurse, true, is_readd, lockmode);
7491
7492 heap_close(childrel, NoLock);
7493 }
7494
7495 return address;
7496 }
7497
7498 /*
7499 * Add a foreign-key constraint to a single table; return the new constraint's
7500 * address.
7501 *
7502 * Subroutine for ATExecAddConstraint. Must already hold exclusive
7503 * lock on the rel, and have done appropriate validity checks for it.
7504 * We do permissions checks here, however.
7505 */
7506 static ObjectAddress
ATAddForeignKeyConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * fkconstraint,Oid parentConstr,bool recurse,bool recursing,LOCKMODE lockmode)7507 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
7508 Constraint *fkconstraint, Oid parentConstr,
7509 bool recurse, bool recursing, LOCKMODE lockmode)
7510 {
7511 Relation pkrel;
7512 int16 pkattnum[INDEX_MAX_KEYS];
7513 int16 fkattnum[INDEX_MAX_KEYS];
7514 Oid pktypoid[INDEX_MAX_KEYS];
7515 Oid fktypoid[INDEX_MAX_KEYS];
7516 Oid opclasses[INDEX_MAX_KEYS];
7517 Oid pfeqoperators[INDEX_MAX_KEYS];
7518 Oid ppeqoperators[INDEX_MAX_KEYS];
7519 Oid ffeqoperators[INDEX_MAX_KEYS];
7520 bool connoinherit;
7521 int i;
7522 int numfks,
7523 numpks;
7524 Oid indexOid;
7525 Oid constrOid;
7526 bool old_check_ok;
7527 ObjectAddress address;
7528 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
7529
7530 /*
7531 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
7532 * delete rows out from under us.
7533 */
7534 if (OidIsValid(fkconstraint->old_pktable_oid))
7535 pkrel = heap_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
7536 else
7537 pkrel = heap_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
7538
7539 /*
7540 * Validity checks (permission checks wait till we have the column
7541 * numbers)
7542 */
7543 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7544 ereport(ERROR,
7545 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7546 errmsg("cannot reference partitioned table \"%s\"",
7547 RelationGetRelationName(pkrel))));
7548
7549 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7550 {
7551 if (!recurse)
7552 ereport(ERROR,
7553 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7554 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
7555 RelationGetRelationName(rel),
7556 RelationGetRelationName(pkrel))));
7557 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
7558 ereport(ERROR,
7559 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7560 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
7561 RelationGetRelationName(rel),
7562 RelationGetRelationName(pkrel)),
7563 errdetail("This feature is not yet supported on partitioned tables.")));
7564 }
7565
7566 if (pkrel->rd_rel->relkind != RELKIND_RELATION)
7567 ereport(ERROR,
7568 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7569 errmsg("referenced relation \"%s\" is not a table",
7570 RelationGetRelationName(pkrel))));
7571
7572 if (!allowSystemTableMods && IsSystemRelation(pkrel))
7573 ereport(ERROR,
7574 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
7575 errmsg("permission denied: \"%s\" is a system catalog",
7576 RelationGetRelationName(pkrel))));
7577
7578 /*
7579 * References from permanent or unlogged tables to temp tables, and from
7580 * permanent tables to unlogged tables, are disallowed because the
7581 * referenced data can vanish out from under us. References from temp
7582 * tables to any other table type are also disallowed, because other
7583 * backends might need to run the RI triggers on the perm table, but they
7584 * can't reliably see tuples in the local buffers of other backends.
7585 */
7586 switch (rel->rd_rel->relpersistence)
7587 {
7588 case RELPERSISTENCE_PERMANENT:
7589 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
7590 ereport(ERROR,
7591 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7592 errmsg("constraints on permanent tables may reference only permanent tables")));
7593 break;
7594 case RELPERSISTENCE_UNLOGGED:
7595 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT
7596 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
7597 ereport(ERROR,
7598 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7599 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
7600 break;
7601 case RELPERSISTENCE_TEMP:
7602 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
7603 ereport(ERROR,
7604 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7605 errmsg("constraints on temporary tables may reference only temporary tables")));
7606 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
7607 ereport(ERROR,
7608 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7609 errmsg("constraints on temporary tables must involve temporary tables of this session")));
7610 break;
7611 }
7612
7613 /*
7614 * Look up the referencing attributes to make sure they exist, and record
7615 * their attnums and type OIDs.
7616 */
7617 MemSet(pkattnum, 0, sizeof(pkattnum));
7618 MemSet(fkattnum, 0, sizeof(fkattnum));
7619 MemSet(pktypoid, 0, sizeof(pktypoid));
7620 MemSet(fktypoid, 0, sizeof(fktypoid));
7621 MemSet(opclasses, 0, sizeof(opclasses));
7622 MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
7623 MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
7624 MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
7625
7626 numfks = transformColumnNameList(RelationGetRelid(rel),
7627 fkconstraint->fk_attrs,
7628 fkattnum, fktypoid);
7629
7630 /*
7631 * If the attribute list for the referenced table was omitted, lookup the
7632 * definition of the primary key and use it. Otherwise, validate the
7633 * supplied attribute list. In either case, discover the index OID and
7634 * index opclasses, and the attnums and type OIDs of the attributes.
7635 */
7636 if (fkconstraint->pk_attrs == NIL)
7637 {
7638 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
7639 &fkconstraint->pk_attrs,
7640 pkattnum, pktypoid,
7641 opclasses);
7642 }
7643 else
7644 {
7645 numpks = transformColumnNameList(RelationGetRelid(pkrel),
7646 fkconstraint->pk_attrs,
7647 pkattnum, pktypoid);
7648 /* Look for an index matching the column list */
7649 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
7650 opclasses);
7651 }
7652
7653 /*
7654 * Now we can check permissions.
7655 */
7656 checkFkeyPermissions(pkrel, pkattnum, numpks);
7657
7658 /*
7659 * Look up the equality operators to use in the constraint.
7660 *
7661 * Note that we have to be careful about the difference between the actual
7662 * PK column type and the opclass' declared input type, which might be
7663 * only binary-compatible with it. The declared opcintype is the right
7664 * thing to probe pg_amop with.
7665 */
7666 if (numfks != numpks)
7667 ereport(ERROR,
7668 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
7669 errmsg("number of referencing and referenced columns for foreign key disagree")));
7670
7671 /*
7672 * On the strength of a previous constraint, we might avoid scanning
7673 * tables to validate this one. See below.
7674 */
7675 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
7676 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
7677
7678 for (i = 0; i < numpks; i++)
7679 {
7680 Oid pktype = pktypoid[i];
7681 Oid fktype = fktypoid[i];
7682 Oid fktyped;
7683 HeapTuple cla_ht;
7684 Form_pg_opclass cla_tup;
7685 Oid amid;
7686 Oid opfamily;
7687 Oid opcintype;
7688 Oid pfeqop;
7689 Oid ppeqop;
7690 Oid ffeqop;
7691 int16 eqstrategy;
7692 Oid pfeqop_right;
7693
7694 /* We need several fields out of the pg_opclass entry */
7695 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
7696 if (!HeapTupleIsValid(cla_ht))
7697 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
7698 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
7699 amid = cla_tup->opcmethod;
7700 opfamily = cla_tup->opcfamily;
7701 opcintype = cla_tup->opcintype;
7702 ReleaseSysCache(cla_ht);
7703
7704 /*
7705 * Check it's a btree; currently this can never fail since no other
7706 * index AMs support unique indexes. If we ever did have other types
7707 * of unique indexes, we'd need a way to determine which operator
7708 * strategy number is equality. (Is it reasonable to insist that
7709 * every such index AM use btree's number for equality?)
7710 */
7711 if (amid != BTREE_AM_OID)
7712 elog(ERROR, "only b-tree indexes are supported for foreign keys");
7713 eqstrategy = BTEqualStrategyNumber;
7714
7715 /*
7716 * There had better be a primary equality operator for the index.
7717 * We'll use it for PK = PK comparisons.
7718 */
7719 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
7720 eqstrategy);
7721
7722 if (!OidIsValid(ppeqop))
7723 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
7724 eqstrategy, opcintype, opcintype, opfamily);
7725
7726 /*
7727 * Are there equality operators that take exactly the FK type? Assume
7728 * we should look through any domain here.
7729 */
7730 fktyped = getBaseType(fktype);
7731
7732 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
7733 eqstrategy);
7734 if (OidIsValid(pfeqop))
7735 {
7736 pfeqop_right = fktyped;
7737 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
7738 eqstrategy);
7739 }
7740 else
7741 {
7742 /* keep compiler quiet */
7743 pfeqop_right = InvalidOid;
7744 ffeqop = InvalidOid;
7745 }
7746
7747 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
7748 {
7749 /*
7750 * Otherwise, look for an implicit cast from the FK type to the
7751 * opcintype, and if found, use the primary equality operator.
7752 * This is a bit tricky because opcintype might be a polymorphic
7753 * type such as ANYARRAY or ANYENUM; so what we have to test is
7754 * whether the two actual column types can be concurrently cast to
7755 * that type. (Otherwise, we'd fail to reject combinations such
7756 * as int[] and point[].)
7757 */
7758 Oid input_typeids[2];
7759 Oid target_typeids[2];
7760
7761 input_typeids[0] = pktype;
7762 input_typeids[1] = fktype;
7763 target_typeids[0] = opcintype;
7764 target_typeids[1] = opcintype;
7765 if (can_coerce_type(2, input_typeids, target_typeids,
7766 COERCION_IMPLICIT))
7767 {
7768 pfeqop = ffeqop = ppeqop;
7769 pfeqop_right = opcintype;
7770 }
7771 }
7772
7773 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
7774 ereport(ERROR,
7775 (errcode(ERRCODE_DATATYPE_MISMATCH),
7776 errmsg("foreign key constraint \"%s\" "
7777 "cannot be implemented",
7778 fkconstraint->conname),
7779 errdetail("Key columns \"%s\" and \"%s\" "
7780 "are of incompatible types: %s and %s.",
7781 strVal(list_nth(fkconstraint->fk_attrs, i)),
7782 strVal(list_nth(fkconstraint->pk_attrs, i)),
7783 format_type_be(fktype),
7784 format_type_be(pktype))));
7785
7786 if (old_check_ok)
7787 {
7788 /*
7789 * When a pfeqop changes, revalidate the constraint. We could
7790 * permit intra-opfamily changes, but that adds subtle complexity
7791 * without any concrete benefit for core types. We need not
7792 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
7793 */
7794 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
7795 old_pfeqop_item = lnext(old_pfeqop_item);
7796 }
7797 if (old_check_ok)
7798 {
7799 Oid old_fktype;
7800 Oid new_fktype;
7801 CoercionPathType old_pathtype;
7802 CoercionPathType new_pathtype;
7803 Oid old_castfunc;
7804 Oid new_castfunc;
7805 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
7806 fkattnum[i] - 1);
7807
7808 /*
7809 * Identify coercion pathways from each of the old and new FK-side
7810 * column types to the right (foreign) operand type of the pfeqop.
7811 * We may assume that pg_constraint.conkey is not changing.
7812 */
7813 old_fktype = attr->atttypid;
7814 new_fktype = fktype;
7815 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
7816 &old_castfunc);
7817 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
7818 &new_castfunc);
7819
7820 /*
7821 * Upon a change to the cast from the FK column to its pfeqop
7822 * operand, revalidate the constraint. For this evaluation, a
7823 * binary coercion cast is equivalent to no cast at all. While
7824 * type implementors should design implicit casts with an eye
7825 * toward consistency of operations like equality, we cannot
7826 * assume here that they have done so.
7827 *
7828 * A function with a polymorphic argument could change behavior
7829 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
7830 * when the cast destination is polymorphic, we only avoid
7831 * revalidation if the input type has not changed at all. Given
7832 * just the core data types and operator classes, this requirement
7833 * prevents no would-be optimizations.
7834 *
7835 * If the cast converts from a base type to a domain thereon, then
7836 * that domain type must be the opcintype of the unique index.
7837 * Necessarily, the primary key column must then be of the domain
7838 * type. Since the constraint was previously valid, all values on
7839 * the foreign side necessarily exist on the primary side and in
7840 * turn conform to the domain. Consequently, we need not treat
7841 * domains specially here.
7842 *
7843 * Since we require that all collations share the same notion of
7844 * equality (which they do, because texteq reduces to bitwise
7845 * equality), we don't compare collation here.
7846 *
7847 * We need not directly consider the PK type. It's necessarily
7848 * binary coercible to the opcintype of the unique index column,
7849 * and ri_triggers.c will only deal with PK datums in terms of
7850 * that opcintype. Changing the opcintype also changes pfeqop.
7851 */
7852 old_check_ok = (new_pathtype == old_pathtype &&
7853 new_castfunc == old_castfunc &&
7854 (!IsPolymorphicType(pfeqop_right) ||
7855 new_fktype == old_fktype));
7856 }
7857
7858 pfeqoperators[i] = pfeqop;
7859 ppeqoperators[i] = ppeqop;
7860 ffeqoperators[i] = ffeqop;
7861 }
7862
7863 /*
7864 * FKs always inherit for partitioned tables, and never for legacy
7865 * inheritance.
7866 */
7867 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
7868
7869 /*
7870 * Record the FK constraint in pg_constraint.
7871 */
7872 constrOid = CreateConstraintEntry(fkconstraint->conname,
7873 RelationGetNamespace(rel),
7874 CONSTRAINT_FOREIGN,
7875 fkconstraint->deferrable,
7876 fkconstraint->initdeferred,
7877 fkconstraint->initially_valid,
7878 parentConstr,
7879 RelationGetRelid(rel),
7880 fkattnum,
7881 numfks,
7882 numfks,
7883 InvalidOid, /* not a domain constraint */
7884 indexOid,
7885 RelationGetRelid(pkrel),
7886 pkattnum,
7887 pfeqoperators,
7888 ppeqoperators,
7889 ffeqoperators,
7890 numpks,
7891 fkconstraint->fk_upd_action,
7892 fkconstraint->fk_del_action,
7893 fkconstraint->fk_matchtype,
7894 NULL, /* no exclusion constraint */
7895 NULL, /* no check constraint */
7896 NULL,
7897 NULL,
7898 true, /* islocal */
7899 0, /* inhcount */
7900 connoinherit, /* conNoInherit */
7901 false); /* is_internal */
7902 ObjectAddressSet(address, ConstraintRelationId, constrOid);
7903
7904 /*
7905 * Create the triggers that will enforce the constraint. We only want the
7906 * action triggers to appear for the parent partitioned relation, even
7907 * though the constraints also exist below.
7908 */
7909 createForeignKeyTriggers(rel, RelationGetRelid(pkrel), fkconstraint,
7910 constrOid, indexOid, !recursing);
7911
7912 /*
7913 * Tell Phase 3 to check that the constraint is satisfied by existing
7914 * rows. We can skip this during table creation, when requested explicitly
7915 * by specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
7916 * recreating a constraint following a SET DATA TYPE operation that did
7917 * not impugn its validity.
7918 */
7919 if (!old_check_ok && !fkconstraint->skip_validation)
7920 {
7921 NewConstraint *newcon;
7922
7923 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
7924 newcon->name = fkconstraint->conname;
7925 newcon->contype = CONSTR_FOREIGN;
7926 newcon->refrelid = RelationGetRelid(pkrel);
7927 newcon->refindid = indexOid;
7928 newcon->conid = constrOid;
7929 newcon->qual = (Node *) fkconstraint;
7930
7931 tab->constraints = lappend(tab->constraints, newcon);
7932 }
7933
7934 /*
7935 * When called on a partitioned table, recurse to create the constraint on
7936 * the partitions also.
7937 */
7938 if (recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7939 {
7940 PartitionDesc partdesc;
7941 Relation pg_constraint;
7942 List *cloned = NIL;
7943 ListCell *cell;
7944
7945 pg_constraint = heap_open(ConstraintRelationId, RowExclusiveLock);
7946 partdesc = RelationGetPartitionDesc(rel);
7947
7948 for (i = 0; i < partdesc->nparts; i++)
7949 {
7950 Oid partitionId = partdesc->oids[i];
7951 Relation partition = heap_open(partitionId, lockmode);
7952
7953 CheckTableNotInUse(partition, "ALTER TABLE");
7954
7955 CloneFkReferencing(pg_constraint, rel, partition,
7956 list_make1_oid(constrOid),
7957 &cloned);
7958
7959 heap_close(partition, NoLock);
7960 }
7961 heap_close(pg_constraint, RowExclusiveLock);
7962
7963 foreach(cell, cloned)
7964 {
7965 ClonedConstraint *cc = (ClonedConstraint *) lfirst(cell);
7966 Relation partition = heap_open(cc->relid, lockmode);
7967 AlteredTableInfo *childtab;
7968 NewConstraint *newcon;
7969
7970 /* Find or create work queue entry for this partition */
7971 childtab = ATGetQueueEntry(wqueue, partition);
7972
7973 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
7974 newcon->name = cc->constraint->conname;
7975 newcon->contype = CONSTR_FOREIGN;
7976 newcon->refrelid = cc->refrelid;
7977 newcon->refindid = cc->conindid;
7978 newcon->conid = cc->conid;
7979 newcon->qual = (Node *) fkconstraint;
7980
7981 childtab->constraints = lappend(childtab->constraints, newcon);
7982
7983 heap_close(partition, lockmode);
7984 }
7985 }
7986
7987 /*
7988 * Close pk table, but keep lock until we've committed.
7989 */
7990 heap_close(pkrel, NoLock);
7991
7992 return address;
7993 }
7994
7995 /*
7996 * CloneForeignKeyConstraints
7997 * Clone foreign keys from a partitioned table to a newly acquired
7998 * partition.
7999 *
8000 * relationId is a partition of parentId, so we can be certain that it has the
8001 * same columns with the same datatypes. The columns may be in different
8002 * order, though.
8003 *
8004 * The *cloned list is appended ClonedConstraint elements describing what was
8005 * created, for the purposes of validating the constraint in ALTER TABLE's
8006 * Phase 3.
8007 */
8008 void
CloneForeignKeyConstraints(Oid parentId,Oid relationId,List ** cloned)8009 CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned)
8010 {
8011 Relation pg_constraint;
8012 Relation parentRel;
8013 Relation rel;
8014 ScanKeyData key;
8015 SysScanDesc scan;
8016 HeapTuple tuple;
8017 List *clone = NIL;
8018
8019 parentRel = heap_open(parentId, NoLock); /* already got lock */
8020 /* see ATAddForeignKeyConstraint about lock level */
8021 rel = heap_open(relationId, AccessExclusiveLock);
8022 pg_constraint = heap_open(ConstraintRelationId, RowShareLock);
8023
8024 /* Obtain the list of constraints to clone or attach */
8025 ScanKeyInit(&key,
8026 Anum_pg_constraint_conrelid, BTEqualStrategyNumber,
8027 F_OIDEQ, ObjectIdGetDatum(parentId));
8028 scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
8029 NULL, 1, &key);
8030 while ((tuple = systable_getnext(scan)) != NULL)
8031 {
8032 Oid oid = HeapTupleGetOid(tuple);
8033
8034 clone = lappend_oid(clone, oid);
8035 }
8036 systable_endscan(scan);
8037
8038 /* Do the actual work, recursing to partitions as needed */
8039 CloneFkReferencing(pg_constraint, parentRel, rel, clone, cloned);
8040
8041 /* We're done. Clean up */
8042 heap_close(parentRel, NoLock);
8043 heap_close(rel, NoLock); /* keep lock till commit */
8044 heap_close(pg_constraint, RowShareLock);
8045 }
8046
8047 /*
8048 * CloneFkReferencing
8049 * Recursive subroutine for CloneForeignKeyConstraints, referencing side
8050 *
8051 * Clone the given list of FK constraints when a partition is attached on the
8052 * referencing side of those constraints.
8053 *
8054 * When cloning foreign keys to a partition, it may happen that equivalent
8055 * constraints already exist in the partition for some of them. We can skip
8056 * creating a clone in that case, and instead just attach the existing
8057 * constraint to the one in the parent.
8058 *
8059 * This function recurses to partitions, if the new partition is partitioned;
8060 * of course, only do this for FKs that were actually cloned.
8061 */
8062 static void
CloneFkReferencing(Relation pg_constraint,Relation parentRel,Relation partRel,List * clone,List ** cloned)8063 CloneFkReferencing(Relation pg_constraint, Relation parentRel,
8064 Relation partRel, List *clone, List **cloned)
8065 {
8066 AttrNumber *attmap;
8067 List *partFKs;
8068 List *subclone = NIL;
8069 ListCell *cell;
8070
8071 /*
8072 * The constraint key may differ, if the columns in the partition are
8073 * different. This map is used to convert them.
8074 */
8075 attmap = convert_tuples_by_name_map(RelationGetDescr(partRel),
8076 RelationGetDescr(parentRel),
8077 gettext_noop("could not convert row type"));
8078
8079 partFKs = copyObject(RelationGetFKeyList(partRel));
8080
8081 foreach(cell, clone)
8082 {
8083 Oid parentConstrOid = lfirst_oid(cell);
8084 Form_pg_constraint constrForm;
8085 HeapTuple tuple;
8086 int numfks;
8087 AttrNumber conkey[INDEX_MAX_KEYS];
8088 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
8089 AttrNumber confkey[INDEX_MAX_KEYS];
8090 Oid conpfeqop[INDEX_MAX_KEYS];
8091 Oid conppeqop[INDEX_MAX_KEYS];
8092 Oid conffeqop[INDEX_MAX_KEYS];
8093 Constraint *fkconstraint;
8094 bool attach_it;
8095 Oid constrOid;
8096 ObjectAddress parentAddr,
8097 childAddr;
8098 ListCell *cell;
8099 int i;
8100
8101 tuple = SearchSysCache1(CONSTROID, parentConstrOid);
8102 if (!HeapTupleIsValid(tuple))
8103 elog(ERROR, "cache lookup failed for constraint %u",
8104 parentConstrOid);
8105 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
8106
8107 /* only foreign keys */
8108 if (constrForm->contype != CONSTRAINT_FOREIGN)
8109 {
8110 ReleaseSysCache(tuple);
8111 continue;
8112 }
8113
8114 ObjectAddressSet(parentAddr, ConstraintRelationId, parentConstrOid);
8115
8116 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
8117 conpfeqop, conppeqop, conffeqop);
8118 for (i = 0; i < numfks; i++)
8119 mapped_conkey[i] = attmap[conkey[i] - 1];
8120
8121 /*
8122 * Before creating a new constraint, see whether any existing FKs are
8123 * fit for the purpose. If one is, attach the parent constraint to it,
8124 * and don't clone anything. This way we avoid the expensive
8125 * verification step and don't end up with a duplicate FK. This also
8126 * means we don't consider this constraint when recursing to
8127 * partitions.
8128 */
8129 attach_it = false;
8130 foreach(cell, partFKs)
8131 {
8132 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, cell);
8133 Form_pg_constraint partConstr;
8134 HeapTuple partcontup;
8135 Relation trigrel;
8136 HeapTuple trigtup;
8137 SysScanDesc scan;
8138 ScanKeyData key;
8139
8140 attach_it = true;
8141
8142 /*
8143 * Do some quick & easy initial checks. If any of these fail, we
8144 * cannot use this constraint, but keep looking.
8145 */
8146 if (fk->confrelid != constrForm->confrelid || fk->nkeys != numfks)
8147 {
8148 attach_it = false;
8149 continue;
8150 }
8151 for (i = 0; i < numfks; i++)
8152 {
8153 if (fk->conkey[i] != mapped_conkey[i] ||
8154 fk->confkey[i] != confkey[i] ||
8155 fk->conpfeqop[i] != conpfeqop[i])
8156 {
8157 attach_it = false;
8158 break;
8159 }
8160 }
8161 if (!attach_it)
8162 continue;
8163
8164 /*
8165 * Looks good so far; do some more extensive checks. Presumably
8166 * the check for 'convalidated' could be dropped, since we don't
8167 * really care about that, but let's be careful for now.
8168 */
8169 partcontup = SearchSysCache1(CONSTROID,
8170 ObjectIdGetDatum(fk->conoid));
8171 if (!HeapTupleIsValid(partcontup))
8172 elog(ERROR, "cache lookup failed for constraint %u",
8173 fk->conoid);
8174 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
8175 if (OidIsValid(partConstr->conparentid) ||
8176 !partConstr->convalidated ||
8177 partConstr->condeferrable != constrForm->condeferrable ||
8178 partConstr->condeferred != constrForm->condeferred ||
8179 partConstr->confupdtype != constrForm->confupdtype ||
8180 partConstr->confdeltype != constrForm->confdeltype ||
8181 partConstr->confmatchtype != constrForm->confmatchtype)
8182 {
8183 ReleaseSysCache(partcontup);
8184 attach_it = false;
8185 continue;
8186 }
8187
8188 ReleaseSysCache(partcontup);
8189
8190 /*
8191 * Looks good! Attach this constraint. The action triggers in
8192 * the new partition become redundant -- the parent table already
8193 * has equivalent ones, and those will be able to reach the
8194 * partition. Remove the ones in the partition. We identify them
8195 * because they have our constraint OID, as well as being on the
8196 * referenced rel.
8197 */
8198 trigrel = heap_open(TriggerRelationId, RowExclusiveLock);
8199 ScanKeyInit(&key,
8200 Anum_pg_trigger_tgconstraint,
8201 BTEqualStrategyNumber, F_OIDEQ,
8202 ObjectIdGetDatum(fk->conoid));
8203
8204 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
8205 NULL, 1, &key);
8206 while ((trigtup = systable_getnext(scan)) != NULL)
8207 {
8208 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
8209 ObjectAddress trigger;
8210
8211 if (trgform->tgconstrrelid != fk->conrelid)
8212 continue;
8213 if (trgform->tgrelid != fk->confrelid)
8214 continue;
8215
8216 /*
8217 * The constraint is originally set up to contain this trigger
8218 * as an implementation object, so there's a dependency record
8219 * that links the two; however, since the trigger is no longer
8220 * needed, we remove the dependency link in order to be able
8221 * to drop the trigger while keeping the constraint intact.
8222 */
8223 deleteDependencyRecordsFor(TriggerRelationId,
8224 HeapTupleGetOid(trigtup),
8225 false);
8226 /* make dependency deletion visible to performDeletion */
8227 CommandCounterIncrement();
8228 ObjectAddressSet(trigger, TriggerRelationId,
8229 HeapTupleGetOid(trigtup));
8230 performDeletion(&trigger, DROP_RESTRICT, 0);
8231 /* make trigger drop visible, in case the loop iterates */
8232 CommandCounterIncrement();
8233 }
8234
8235 systable_endscan(scan);
8236 heap_close(trigrel, RowExclusiveLock);
8237
8238 ConstraintSetParentConstraint(fk->conoid, parentConstrOid);
8239 CommandCounterIncrement();
8240 attach_it = true;
8241 break;
8242 }
8243
8244 /*
8245 * If we attached to an existing constraint, there is no need to
8246 * create a new one. In fact, there's no need to recurse for this
8247 * constraint to partitions, either.
8248 */
8249 if (attach_it)
8250 {
8251 ReleaseSysCache(tuple);
8252 continue;
8253 }
8254
8255 constrOid =
8256 CreateConstraintEntry(NameStr(constrForm->conname),
8257 constrForm->connamespace,
8258 CONSTRAINT_FOREIGN,
8259 constrForm->condeferrable,
8260 constrForm->condeferred,
8261 constrForm->convalidated,
8262 parentConstrOid,
8263 RelationGetRelid(partRel),
8264 mapped_conkey,
8265 numfks,
8266 numfks,
8267 InvalidOid, /* not a domain constraint */
8268 constrForm->conindid, /* same index */
8269 constrForm->confrelid, /* same foreign rel */
8270 confkey,
8271 conpfeqop,
8272 conppeqop,
8273 conffeqop,
8274 numfks,
8275 constrForm->confupdtype,
8276 constrForm->confdeltype,
8277 constrForm->confmatchtype,
8278 NULL,
8279 NULL,
8280 NULL,
8281 NULL,
8282 false,
8283 1, false, true);
8284 subclone = lappend_oid(subclone, constrOid);
8285
8286 ObjectAddressSet(childAddr, ConstraintRelationId, constrOid);
8287 recordDependencyOn(&childAddr, &parentAddr, DEPENDENCY_INTERNAL_AUTO);
8288
8289 fkconstraint = makeNode(Constraint);
8290 /* for now this is all we need */
8291 fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
8292 fkconstraint->fk_upd_action = constrForm->confupdtype;
8293 fkconstraint->fk_del_action = constrForm->confdeltype;
8294 fkconstraint->deferrable = constrForm->condeferrable;
8295 fkconstraint->initdeferred = constrForm->condeferred;
8296
8297 createForeignKeyTriggers(partRel, constrForm->confrelid, fkconstraint,
8298 constrOid, constrForm->conindid, false);
8299
8300 if (cloned)
8301 {
8302 ClonedConstraint *newc;
8303
8304 /*
8305 * Feed back caller about the constraints we created, so that they
8306 * can set up constraint verification.
8307 */
8308 newc = palloc(sizeof(ClonedConstraint));
8309 newc->relid = RelationGetRelid(partRel);
8310 newc->refrelid = constrForm->confrelid;
8311 newc->conindid = constrForm->conindid;
8312 newc->conid = constrOid;
8313 newc->constraint = fkconstraint;
8314
8315 *cloned = lappend(*cloned, newc);
8316 }
8317
8318 ReleaseSysCache(tuple);
8319 }
8320
8321 pfree(attmap);
8322 list_free_deep(partFKs);
8323
8324 /*
8325 * If the partition is partitioned, recurse to handle any constraints that
8326 * were cloned.
8327 */
8328 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
8329 subclone != NIL)
8330 {
8331 PartitionDesc partdesc = RelationGetPartitionDesc(partRel);
8332 int i;
8333
8334 for (i = 0; i < partdesc->nparts; i++)
8335 {
8336 Relation childRel;
8337
8338 childRel = heap_open(partdesc->oids[i], AccessExclusiveLock);
8339 CloneFkReferencing(pg_constraint,
8340 partRel,
8341 childRel,
8342 subclone,
8343 cloned);
8344 heap_close(childRel, NoLock); /* keep lock till commit */
8345 }
8346 }
8347 }
8348
8349 /*
8350 * ALTER TABLE ALTER CONSTRAINT
8351 *
8352 * Update the attributes of a constraint.
8353 *
8354 * Currently only works for Foreign Key constraints.
8355 *
8356 * If the constraint is modified, returns its address; otherwise, return
8357 * InvalidObjectAddress.
8358 */
8359 static ObjectAddress
ATExecAlterConstraint(Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode)8360 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
8361 bool recursing, LOCKMODE lockmode)
8362 {
8363 Constraint *cmdcon;
8364 Relation conrel;
8365 Relation tgrel;
8366 SysScanDesc scan;
8367 ScanKeyData skey[3];
8368 HeapTuple contuple;
8369 Form_pg_constraint currcon;
8370 ObjectAddress address;
8371 List *otherrelids = NIL;
8372 ListCell *lc;
8373
8374 cmdcon = castNode(Constraint, cmd->def);
8375
8376 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
8377 tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
8378
8379 /*
8380 * Find and check the target constraint
8381 */
8382 ScanKeyInit(&skey[0],
8383 Anum_pg_constraint_conrelid,
8384 BTEqualStrategyNumber, F_OIDEQ,
8385 ObjectIdGetDatum(RelationGetRelid(rel)));
8386 ScanKeyInit(&skey[1],
8387 Anum_pg_constraint_contypid,
8388 BTEqualStrategyNumber, F_OIDEQ,
8389 ObjectIdGetDatum(InvalidOid));
8390 ScanKeyInit(&skey[2],
8391 Anum_pg_constraint_conname,
8392 BTEqualStrategyNumber, F_NAMEEQ,
8393 CStringGetDatum(cmdcon->conname));
8394 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
8395 true, NULL, 3, skey);
8396
8397 /* There can be at most one matching row */
8398 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
8399 ereport(ERROR,
8400 (errcode(ERRCODE_UNDEFINED_OBJECT),
8401 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
8402 cmdcon->conname, RelationGetRelationName(rel))));
8403
8404 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
8405 if (currcon->contype != CONSTRAINT_FOREIGN)
8406 ereport(ERROR,
8407 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8408 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
8409 cmdcon->conname, RelationGetRelationName(rel))));
8410
8411 /*
8412 * If it's not the topmost constraint, raise an error.
8413 *
8414 * Altering a non-topmost constraint leaves some triggers untouched, since
8415 * they are not directly connected to this constraint; also, pg_dump would
8416 * ignore the deferrability status of the individual constraint, since it
8417 * only dumps topmost constraints. Avoid these problems by refusing this
8418 * operation and telling the user to alter the parent constraint instead.
8419 */
8420 if (OidIsValid(currcon->conparentid))
8421 {
8422 HeapTuple tp;
8423 Oid parent = currcon->conparentid;
8424 char *ancestorname = NULL;
8425 char *ancestortable = NULL;
8426
8427 /* Loop to find the topmost constraint */
8428 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
8429 {
8430 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
8431
8432 /* If no parent, this is the constraint we want */
8433 if (!OidIsValid(contup->conparentid))
8434 {
8435 ancestorname = pstrdup(NameStr(contup->conname));
8436 ancestortable = get_rel_name(contup->conrelid);
8437 ReleaseSysCache(tp);
8438 break;
8439 }
8440
8441 parent = contup->conparentid;
8442 ReleaseSysCache(tp);
8443 }
8444
8445 ereport(ERROR,
8446 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
8447 cmdcon->conname, RelationGetRelationName(rel)),
8448 ancestorname && ancestortable ?
8449 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
8450 cmdcon->conname, ancestorname, ancestortable) : 0,
8451 errhint("You may alter the constraint it derives from, instead.")));
8452 }
8453
8454 /*
8455 * Do the actual catalog work. We can skip changing if already in the
8456 * desired state, but not if a partitioned table: partitions need to be
8457 * processed regardless, in case they've had it locally changed.
8458 */
8459 address = InvalidObjectAddress;
8460 if (currcon->condeferrable != cmdcon->deferrable ||
8461 currcon->condeferred != cmdcon->initdeferred ||
8462 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8463 {
8464 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
8465 &otherrelids, lockmode))
8466 ObjectAddressSet(address, ConstraintRelationId,
8467 HeapTupleGetOid(contuple));
8468 }
8469
8470 /*
8471 * ATExecConstrRecurse already invalidated relcache for the relations
8472 * having the constraint itself; here we also invalidate for relations
8473 * that have any triggers that implement the constraint.
8474 */
8475 foreach(lc, otherrelids)
8476 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
8477
8478 systable_endscan(scan);
8479
8480 heap_close(conrel, RowExclusiveLock);
8481 heap_close(tgrel, RowExclusiveLock);
8482
8483 return address;
8484 }
8485
8486 /*
8487 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
8488 * constraint is altered.
8489 *
8490 * *otherrelids is appended OIDs of relations containing affected triggers.
8491 *
8492 * Note that we must recurse even when the values are correct, in case
8493 * indirect descendants have had their constraints altered locally.
8494 * (This could be avoided if we forbade altering constraints in partitions
8495 * but existing releases don't do that.)
8496 */
8497 static bool
ATExecAlterConstrRecurse(Constraint * cmdcon,Relation conrel,Relation tgrel,Relation rel,HeapTuple contuple,List ** otherrelids,LOCKMODE lockmode)8498 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
8499 Relation rel, HeapTuple contuple, List **otherrelids,
8500 LOCKMODE lockmode)
8501 {
8502 Form_pg_constraint currcon;
8503 Oid conoid;
8504 bool changed = false;
8505
8506 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
8507 conoid = HeapTupleGetOid(contuple);
8508
8509 /*
8510 * Update pg_constraint with the flags from cmdcon.
8511 *
8512 * If called to modify a constraint that's already in the desired state,
8513 * silently do nothing.
8514 */
8515 if (currcon->condeferrable != cmdcon->deferrable ||
8516 currcon->condeferred != cmdcon->initdeferred)
8517 {
8518 HeapTuple copyTuple;
8519 Form_pg_constraint copy_con;
8520 HeapTuple tgtuple;
8521 ScanKeyData tgkey;
8522 SysScanDesc tgscan;
8523
8524 /*
8525 * Now update the catalog, while we have the door open.
8526 */
8527 copyTuple = heap_copytuple(contuple);
8528 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
8529 copy_con->condeferrable = cmdcon->deferrable;
8530 copy_con->condeferred = cmdcon->initdeferred;
8531 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
8532
8533 InvokeObjectPostAlterHook(ConstraintRelationId,
8534 HeapTupleGetOid(contuple), 0);
8535
8536 heap_freetuple(copyTuple);
8537 changed = true;
8538
8539 /* Make new constraint flags visible to others */
8540 CacheInvalidateRelcache(rel);
8541
8542 /*
8543 * Now we need to update the multiple entries in pg_trigger that
8544 * implement the constraint.
8545 */
8546 ScanKeyInit(&tgkey,
8547 Anum_pg_trigger_tgconstraint,
8548 BTEqualStrategyNumber, F_OIDEQ,
8549 ObjectIdGetDatum(HeapTupleGetOid(contuple)));
8550 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
8551 NULL, 1, &tgkey);
8552 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
8553 {
8554 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
8555 Form_pg_trigger copy_tg;
8556 HeapTuple copyTuple;
8557
8558 /*
8559 * Remember OIDs of other relation(s) involved in FK constraint.
8560 * (Note: it's likely that we could skip forcing a relcache inval
8561 * for other rels that don't have a trigger whose properties
8562 * change, but let's be conservative.)
8563 */
8564 if (tgform->tgrelid != RelationGetRelid(rel))
8565 *otherrelids = list_append_unique_oid(*otherrelids,
8566 tgform->tgrelid);
8567
8568 /*
8569 * Update deferrability of RI_FKey_noaction_del,
8570 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
8571 * triggers, but not others; see createForeignKeyTriggers and
8572 * CreateFKCheckTrigger.
8573 */
8574 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
8575 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
8576 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
8577 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
8578 continue;
8579
8580 copyTuple = heap_copytuple(tgtuple);
8581 copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
8582
8583 copy_tg->tgdeferrable = cmdcon->deferrable;
8584 copy_tg->tginitdeferred = cmdcon->initdeferred;
8585 CatalogTupleUpdate(tgrel, ©Tuple->t_self, copyTuple);
8586
8587 InvokeObjectPostAlterHook(TriggerRelationId,
8588 HeapTupleGetOid(tgtuple), 0);
8589
8590 heap_freetuple(copyTuple);
8591 }
8592
8593 systable_endscan(tgscan);
8594 }
8595
8596 /*
8597 * If the referencing table is partitioned, we need to recurse and handle
8598 * every constraint that is a child of this one.
8599 *
8600 * (This assumes that the recurse flag is forcibly set for partitioned
8601 * tables, and not set for legacy inheritance, though we don't check for
8602 * that here.)
8603 */
8604 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8605 {
8606 ScanKeyData pkey;
8607 SysScanDesc pscan;
8608 HeapTuple childtup;
8609
8610 ScanKeyInit(&pkey,
8611 Anum_pg_constraint_conparentid,
8612 BTEqualStrategyNumber, F_OIDEQ,
8613 ObjectIdGetDatum(conoid));
8614
8615 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
8616 true, NULL, 1, &pkey);
8617
8618 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
8619 {
8620 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
8621 Relation childrel;
8622
8623 childrel = heap_open(childcon->conrelid, lockmode);
8624 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
8625 otherrelids, lockmode);
8626 heap_close(childrel, NoLock);
8627 }
8628
8629 systable_endscan(pscan);
8630 }
8631
8632 return changed;
8633 }
8634
8635 /*
8636 * ALTER TABLE VALIDATE CONSTRAINT
8637 *
8638 * XXX The reason we handle recursion here rather than at Phase 1 is because
8639 * there's no good way to skip recursing when handling foreign keys: there is
8640 * no need to lock children in that case, yet we wouldn't be able to avoid
8641 * doing so at that level.
8642 *
8643 * Return value is the address of the validated constraint. If the constraint
8644 * was already validated, InvalidObjectAddress is returned.
8645 */
8646 static ObjectAddress
ATExecValidateConstraint(List ** wqueue,Relation rel,char * constrName,bool recurse,bool recursing,LOCKMODE lockmode)8647 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
8648 bool recurse, bool recursing, LOCKMODE lockmode)
8649 {
8650 Relation conrel;
8651 SysScanDesc scan;
8652 ScanKeyData skey[3];
8653 HeapTuple tuple;
8654 Form_pg_constraint con;
8655 ObjectAddress address;
8656
8657 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
8658
8659 /*
8660 * Find and check the target constraint
8661 */
8662 ScanKeyInit(&skey[0],
8663 Anum_pg_constraint_conrelid,
8664 BTEqualStrategyNumber, F_OIDEQ,
8665 ObjectIdGetDatum(RelationGetRelid(rel)));
8666 ScanKeyInit(&skey[1],
8667 Anum_pg_constraint_contypid,
8668 BTEqualStrategyNumber, F_OIDEQ,
8669 ObjectIdGetDatum(InvalidOid));
8670 ScanKeyInit(&skey[2],
8671 Anum_pg_constraint_conname,
8672 BTEqualStrategyNumber, F_NAMEEQ,
8673 CStringGetDatum(constrName));
8674 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
8675 true, NULL, 3, skey);
8676
8677 /* There can be at most one matching row */
8678 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
8679 ereport(ERROR,
8680 (errcode(ERRCODE_UNDEFINED_OBJECT),
8681 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
8682 constrName, RelationGetRelationName(rel))));
8683
8684 con = (Form_pg_constraint) GETSTRUCT(tuple);
8685 if (con->contype != CONSTRAINT_FOREIGN &&
8686 con->contype != CONSTRAINT_CHECK)
8687 ereport(ERROR,
8688 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8689 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
8690 constrName, RelationGetRelationName(rel))));
8691
8692 if (!con->convalidated)
8693 {
8694 AlteredTableInfo *tab;
8695 HeapTuple copyTuple;
8696 Form_pg_constraint copy_con;
8697
8698 if (con->contype == CONSTRAINT_FOREIGN)
8699 {
8700 NewConstraint *newcon;
8701 Constraint *fkconstraint;
8702
8703 /* Queue validation for phase 3 */
8704 fkconstraint = makeNode(Constraint);
8705 /* for now this is all we need */
8706 fkconstraint->conname = constrName;
8707
8708 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
8709 newcon->name = constrName;
8710 newcon->contype = CONSTR_FOREIGN;
8711 newcon->refrelid = con->confrelid;
8712 newcon->refindid = con->conindid;
8713 newcon->conid = HeapTupleGetOid(tuple);
8714 newcon->qual = (Node *) fkconstraint;
8715
8716 /* Find or create work queue entry for this table */
8717 tab = ATGetQueueEntry(wqueue, rel);
8718 tab->constraints = lappend(tab->constraints, newcon);
8719
8720 /*
8721 * We disallow creating invalid foreign keys to or from
8722 * partitioned tables, so ignoring the recursion bit is okay.
8723 */
8724 }
8725 else if (con->contype == CONSTRAINT_CHECK)
8726 {
8727 List *children = NIL;
8728 ListCell *child;
8729 NewConstraint *newcon;
8730 bool isnull;
8731 Datum val;
8732 char *conbin;
8733
8734 /*
8735 * If we're recursing, the parent has already done this, so skip
8736 * it. Also, if the constraint is a NO INHERIT constraint, we
8737 * shouldn't try to look for it in the children.
8738 */
8739 if (!recursing && !con->connoinherit)
8740 children = find_all_inheritors(RelationGetRelid(rel),
8741 lockmode, NULL);
8742
8743 /*
8744 * For CHECK constraints, we must ensure that we only mark the
8745 * constraint as validated on the parent if it's already validated
8746 * on the children.
8747 *
8748 * We recurse before validating on the parent, to reduce risk of
8749 * deadlocks.
8750 */
8751 foreach(child, children)
8752 {
8753 Oid childoid = lfirst_oid(child);
8754 Relation childrel;
8755
8756 if (childoid == RelationGetRelid(rel))
8757 continue;
8758
8759 /*
8760 * If we are told not to recurse, there had better not be any
8761 * child tables, because we can't mark the constraint on the
8762 * parent valid unless it is valid for all child tables.
8763 */
8764 if (!recurse)
8765 ereport(ERROR,
8766 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8767 errmsg("constraint must be validated on child tables too")));
8768
8769 /* find_all_inheritors already got lock */
8770 childrel = heap_open(childoid, NoLock);
8771
8772 ATExecValidateConstraint(wqueue, childrel, constrName, false,
8773 true, lockmode);
8774 heap_close(childrel, NoLock);
8775 }
8776
8777 /* Queue validation for phase 3 */
8778 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
8779 newcon->name = constrName;
8780 newcon->contype = CONSTR_CHECK;
8781 newcon->refrelid = InvalidOid;
8782 newcon->refindid = InvalidOid;
8783 newcon->conid = HeapTupleGetOid(tuple);
8784
8785 val = SysCacheGetAttr(CONSTROID, tuple,
8786 Anum_pg_constraint_conbin, &isnull);
8787 if (isnull)
8788 elog(ERROR, "null conbin for constraint %u",
8789 HeapTupleGetOid(tuple));
8790
8791 conbin = TextDatumGetCString(val);
8792 newcon->qual = (Node *) stringToNode(conbin);
8793
8794 /* Find or create work queue entry for this table */
8795 tab = ATGetQueueEntry(wqueue, rel);
8796 tab->constraints = lappend(tab->constraints, newcon);
8797
8798 /*
8799 * Invalidate relcache so that others see the new validated
8800 * constraint.
8801 */
8802 CacheInvalidateRelcache(rel);
8803 }
8804
8805 /*
8806 * Now update the catalog, while we have the door open.
8807 */
8808 copyTuple = heap_copytuple(tuple);
8809 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
8810 copy_con->convalidated = true;
8811 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
8812
8813 InvokeObjectPostAlterHook(ConstraintRelationId,
8814 HeapTupleGetOid(tuple), 0);
8815
8816 heap_freetuple(copyTuple);
8817
8818 ObjectAddressSet(address, ConstraintRelationId,
8819 HeapTupleGetOid(tuple));
8820 }
8821 else
8822 address = InvalidObjectAddress; /* already validated */
8823
8824 systable_endscan(scan);
8825
8826 heap_close(conrel, RowExclusiveLock);
8827
8828 return address;
8829 }
8830
8831
8832 /*
8833 * transformColumnNameList - transform list of column names
8834 *
8835 * Lookup each name and return its attnum and type OID
8836 */
8837 static int
transformColumnNameList(Oid relId,List * colList,int16 * attnums,Oid * atttypids)8838 transformColumnNameList(Oid relId, List *colList,
8839 int16 *attnums, Oid *atttypids)
8840 {
8841 ListCell *l;
8842 int attnum;
8843
8844 attnum = 0;
8845 foreach(l, colList)
8846 {
8847 char *attname = strVal(lfirst(l));
8848 HeapTuple atttuple;
8849
8850 atttuple = SearchSysCacheAttName(relId, attname);
8851 if (!HeapTupleIsValid(atttuple))
8852 ereport(ERROR,
8853 (errcode(ERRCODE_UNDEFINED_COLUMN),
8854 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
8855 attname)));
8856 if (attnum >= INDEX_MAX_KEYS)
8857 ereport(ERROR,
8858 (errcode(ERRCODE_TOO_MANY_COLUMNS),
8859 errmsg("cannot have more than %d keys in a foreign key",
8860 INDEX_MAX_KEYS)));
8861 attnums[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->attnum;
8862 atttypids[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
8863 ReleaseSysCache(atttuple);
8864 attnum++;
8865 }
8866
8867 return attnum;
8868 }
8869
8870 /*
8871 * transformFkeyGetPrimaryKey -
8872 *
8873 * Look up the names, attnums, and types of the primary key attributes
8874 * for the pkrel. Also return the index OID and index opclasses of the
8875 * index supporting the primary key.
8876 *
8877 * All parameters except pkrel are output parameters. Also, the function
8878 * return value is the number of attributes in the primary key.
8879 *
8880 * Used when the column list in the REFERENCES specification is omitted.
8881 */
8882 static int
transformFkeyGetPrimaryKey(Relation pkrel,Oid * indexOid,List ** attnamelist,int16 * attnums,Oid * atttypids,Oid * opclasses)8883 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
8884 List **attnamelist,
8885 int16 *attnums, Oid *atttypids,
8886 Oid *opclasses)
8887 {
8888 List *indexoidlist;
8889 ListCell *indexoidscan;
8890 HeapTuple indexTuple = NULL;
8891 Form_pg_index indexStruct = NULL;
8892 Datum indclassDatum;
8893 bool isnull;
8894 oidvector *indclass;
8895 int i;
8896
8897 /*
8898 * Get the list of index OIDs for the table from the relcache, and look up
8899 * each one in the pg_index syscache until we find one marked primary key
8900 * (hopefully there isn't more than one such). Insist it's valid, too.
8901 */
8902 *indexOid = InvalidOid;
8903
8904 indexoidlist = RelationGetIndexList(pkrel);
8905
8906 foreach(indexoidscan, indexoidlist)
8907 {
8908 Oid indexoid = lfirst_oid(indexoidscan);
8909
8910 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
8911 if (!HeapTupleIsValid(indexTuple))
8912 elog(ERROR, "cache lookup failed for index %u", indexoid);
8913 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
8914 if (indexStruct->indisprimary && IndexIsValid(indexStruct))
8915 {
8916 /*
8917 * Refuse to use a deferrable primary key. This is per SQL spec,
8918 * and there would be a lot of interesting semantic problems if we
8919 * tried to allow it.
8920 */
8921 if (!indexStruct->indimmediate)
8922 ereport(ERROR,
8923 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8924 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
8925 RelationGetRelationName(pkrel))));
8926
8927 *indexOid = indexoid;
8928 break;
8929 }
8930 ReleaseSysCache(indexTuple);
8931 }
8932
8933 list_free(indexoidlist);
8934
8935 /*
8936 * Check that we found it
8937 */
8938 if (!OidIsValid(*indexOid))
8939 ereport(ERROR,
8940 (errcode(ERRCODE_UNDEFINED_OBJECT),
8941 errmsg("there is no primary key for referenced table \"%s\"",
8942 RelationGetRelationName(pkrel))));
8943
8944 /* Must get indclass the hard way */
8945 indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
8946 Anum_pg_index_indclass, &isnull);
8947 Assert(!isnull);
8948 indclass = (oidvector *) DatumGetPointer(indclassDatum);
8949
8950 /*
8951 * Now build the list of PK attributes from the indkey definition (we
8952 * assume a primary key cannot have expressional elements)
8953 */
8954 *attnamelist = NIL;
8955 for (i = 0; i < indexStruct->indnkeyatts; i++)
8956 {
8957 int pkattno = indexStruct->indkey.values[i];
8958
8959 attnums[i] = pkattno;
8960 atttypids[i] = attnumTypeId(pkrel, pkattno);
8961 opclasses[i] = indclass->values[i];
8962 *attnamelist = lappend(*attnamelist,
8963 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
8964 }
8965
8966 ReleaseSysCache(indexTuple);
8967
8968 return i;
8969 }
8970
8971 /*
8972 * transformFkeyCheckAttrs -
8973 *
8974 * Make sure that the attributes of a referenced table belong to a unique
8975 * (or primary key) constraint. Return the OID of the index supporting
8976 * the constraint, as well as the opclasses associated with the index
8977 * columns.
8978 */
8979 static Oid
transformFkeyCheckAttrs(Relation pkrel,int numattrs,int16 * attnums,Oid * opclasses)8980 transformFkeyCheckAttrs(Relation pkrel,
8981 int numattrs, int16 *attnums,
8982 Oid *opclasses) /* output parameter */
8983 {
8984 Oid indexoid = InvalidOid;
8985 bool found = false;
8986 bool found_deferrable = false;
8987 List *indexoidlist;
8988 ListCell *indexoidscan;
8989 int i,
8990 j;
8991
8992 /*
8993 * Reject duplicate appearances of columns in the referenced-columns list.
8994 * Such a case is forbidden by the SQL standard, and even if we thought it
8995 * useful to allow it, there would be ambiguity about how to match the
8996 * list to unique indexes (in particular, it'd be unclear which index
8997 * opclass goes with which FK column).
8998 */
8999 for (i = 0; i < numattrs; i++)
9000 {
9001 for (j = i + 1; j < numattrs; j++)
9002 {
9003 if (attnums[i] == attnums[j])
9004 ereport(ERROR,
9005 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9006 errmsg("foreign key referenced-columns list must not contain duplicates")));
9007 }
9008 }
9009
9010 /*
9011 * Get the list of index OIDs for the table from the relcache, and look up
9012 * each one in the pg_index syscache, and match unique indexes to the list
9013 * of attnums we are given.
9014 */
9015 indexoidlist = RelationGetIndexList(pkrel);
9016
9017 foreach(indexoidscan, indexoidlist)
9018 {
9019 HeapTuple indexTuple;
9020 Form_pg_index indexStruct;
9021
9022 indexoid = lfirst_oid(indexoidscan);
9023 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
9024 if (!HeapTupleIsValid(indexTuple))
9025 elog(ERROR, "cache lookup failed for index %u", indexoid);
9026 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
9027
9028 /*
9029 * Must have the right number of columns; must be unique and not a
9030 * partial index; forget it if there are any expressions, too. Invalid
9031 * indexes are out as well.
9032 */
9033 if (indexStruct->indnkeyatts == numattrs &&
9034 indexStruct->indisunique &&
9035 IndexIsValid(indexStruct) &&
9036 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
9037 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
9038 {
9039 Datum indclassDatum;
9040 bool isnull;
9041 oidvector *indclass;
9042
9043 /* Must get indclass the hard way */
9044 indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
9045 Anum_pg_index_indclass, &isnull);
9046 Assert(!isnull);
9047 indclass = (oidvector *) DatumGetPointer(indclassDatum);
9048
9049 /*
9050 * The given attnum list may match the index columns in any order.
9051 * Check for a match, and extract the appropriate opclasses while
9052 * we're at it.
9053 *
9054 * We know that attnums[] is duplicate-free per the test at the
9055 * start of this function, and we checked above that the number of
9056 * index columns agrees, so if we find a match for each attnums[]
9057 * entry then we must have a one-to-one match in some order.
9058 */
9059 for (i = 0; i < numattrs; i++)
9060 {
9061 found = false;
9062 for (j = 0; j < numattrs; j++)
9063 {
9064 if (attnums[i] == indexStruct->indkey.values[j])
9065 {
9066 opclasses[i] = indclass->values[j];
9067 found = true;
9068 break;
9069 }
9070 }
9071 if (!found)
9072 break;
9073 }
9074
9075 /*
9076 * Refuse to use a deferrable unique/primary key. This is per SQL
9077 * spec, and there would be a lot of interesting semantic problems
9078 * if we tried to allow it.
9079 */
9080 if (found && !indexStruct->indimmediate)
9081 {
9082 /*
9083 * Remember that we found an otherwise matching index, so that
9084 * we can generate a more appropriate error message.
9085 */
9086 found_deferrable = true;
9087 found = false;
9088 }
9089 }
9090 ReleaseSysCache(indexTuple);
9091 if (found)
9092 break;
9093 }
9094
9095 if (!found)
9096 {
9097 if (found_deferrable)
9098 ereport(ERROR,
9099 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9100 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
9101 RelationGetRelationName(pkrel))));
9102 else
9103 ereport(ERROR,
9104 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9105 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
9106 RelationGetRelationName(pkrel))));
9107 }
9108
9109 list_free(indexoidlist);
9110
9111 return indexoid;
9112 }
9113
9114 /*
9115 * findFkeyCast -
9116 *
9117 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
9118 * Caller has equal regard for binary coercibility and for an exact match.
9119 */
9120 static CoercionPathType
findFkeyCast(Oid targetTypeId,Oid sourceTypeId,Oid * funcid)9121 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
9122 {
9123 CoercionPathType ret;
9124
9125 if (targetTypeId == sourceTypeId)
9126 {
9127 ret = COERCION_PATH_RELABELTYPE;
9128 *funcid = InvalidOid;
9129 }
9130 else
9131 {
9132 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
9133 COERCION_IMPLICIT, funcid);
9134 if (ret == COERCION_PATH_NONE)
9135 /* A previously-relied-upon cast is now gone. */
9136 elog(ERROR, "could not find cast from %u to %u",
9137 sourceTypeId, targetTypeId);
9138 }
9139
9140 return ret;
9141 }
9142
9143 /*
9144 * Permissions checks on the referenced table for ADD FOREIGN KEY
9145 *
9146 * Note: we have already checked that the user owns the referencing table,
9147 * else we'd have failed much earlier; no additional checks are needed for it.
9148 */
9149 static void
checkFkeyPermissions(Relation rel,int16 * attnums,int natts)9150 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
9151 {
9152 Oid roleid = GetUserId();
9153 AclResult aclresult;
9154 int i;
9155
9156 /* Okay if we have relation-level REFERENCES permission */
9157 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
9158 ACL_REFERENCES);
9159 if (aclresult == ACLCHECK_OK)
9160 return;
9161 /* Else we must have REFERENCES on each column */
9162 for (i = 0; i < natts; i++)
9163 {
9164 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
9165 roleid, ACL_REFERENCES);
9166 if (aclresult != ACLCHECK_OK)
9167 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
9168 RelationGetRelationName(rel));
9169 }
9170 }
9171
9172 /*
9173 * Scan the existing rows in a table to verify they meet a proposed FK
9174 * constraint.
9175 *
9176 * Caller must have opened and locked both relations appropriately.
9177 */
9178 static void
validateForeignKeyConstraint(char * conname,Relation rel,Relation pkrel,Oid pkindOid,Oid constraintOid)9179 validateForeignKeyConstraint(char *conname,
9180 Relation rel,
9181 Relation pkrel,
9182 Oid pkindOid,
9183 Oid constraintOid)
9184 {
9185 HeapScanDesc scan;
9186 HeapTuple tuple;
9187 Trigger trig;
9188 Snapshot snapshot;
9189
9190 ereport(DEBUG1,
9191 (errmsg("validating foreign key constraint \"%s\"", conname)));
9192
9193 /*
9194 * Build a trigger call structure; we'll need it either way.
9195 */
9196 MemSet(&trig, 0, sizeof(trig));
9197 trig.tgoid = InvalidOid;
9198 trig.tgname = conname;
9199 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
9200 trig.tgisinternal = true;
9201 trig.tgconstrrelid = RelationGetRelid(pkrel);
9202 trig.tgconstrindid = pkindOid;
9203 trig.tgconstraint = constraintOid;
9204 trig.tgdeferrable = false;
9205 trig.tginitdeferred = false;
9206 /* we needn't fill in remaining fields */
9207
9208 /*
9209 * See if we can do it with a single LEFT JOIN query. A false result
9210 * indicates we must proceed with the fire-the-trigger method.
9211 */
9212 if (RI_Initial_Check(&trig, rel, pkrel))
9213 return;
9214
9215 /*
9216 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
9217 * if that tuple had just been inserted. If any of those fail, it should
9218 * ereport(ERROR) and that's that.
9219 */
9220 snapshot = RegisterSnapshot(GetLatestSnapshot());
9221 scan = heap_beginscan(rel, snapshot, 0, NULL);
9222
9223 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
9224 {
9225 FunctionCallInfoData fcinfo;
9226 TriggerData trigdata;
9227
9228 /*
9229 * Make a call to the trigger function
9230 *
9231 * No parameters are passed, but we do set a context
9232 */
9233 MemSet(&fcinfo, 0, sizeof(fcinfo));
9234
9235 /*
9236 * We assume RI_FKey_check_ins won't look at flinfo...
9237 */
9238 trigdata.type = T_TriggerData;
9239 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
9240 trigdata.tg_relation = rel;
9241 trigdata.tg_trigtuple = tuple;
9242 trigdata.tg_newtuple = NULL;
9243 trigdata.tg_trigger = &trig;
9244 trigdata.tg_trigtuplebuf = scan->rs_cbuf;
9245 trigdata.tg_newtuplebuf = InvalidBuffer;
9246
9247 fcinfo.context = (Node *) &trigdata;
9248
9249 RI_FKey_check_ins(&fcinfo);
9250 }
9251
9252 heap_endscan(scan);
9253 UnregisterSnapshot(snapshot);
9254 }
9255
9256 static void
CreateFKCheckTrigger(Oid myRelOid,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid,bool on_insert)9257 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
9258 Oid constraintOid, Oid indexOid, bool on_insert)
9259 {
9260 CreateTrigStmt *fk_trigger;
9261
9262 /*
9263 * Note: for a self-referential FK (referencing and referenced tables are
9264 * the same), it is important that the ON UPDATE action fires before the
9265 * CHECK action, since both triggers will fire on the same row during an
9266 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
9267 * state of the row. Triggers fire in name order, so we ensure this by
9268 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
9269 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
9270 */
9271 fk_trigger = makeNode(CreateTrigStmt);
9272 fk_trigger->trigname = "RI_ConstraintTrigger_c";
9273 fk_trigger->relation = NULL;
9274 fk_trigger->row = true;
9275 fk_trigger->timing = TRIGGER_TYPE_AFTER;
9276
9277 /* Either ON INSERT or ON UPDATE */
9278 if (on_insert)
9279 {
9280 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
9281 fk_trigger->events = TRIGGER_TYPE_INSERT;
9282 }
9283 else
9284 {
9285 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
9286 fk_trigger->events = TRIGGER_TYPE_UPDATE;
9287 }
9288
9289 fk_trigger->columns = NIL;
9290 fk_trigger->transitionRels = NIL;
9291 fk_trigger->whenClause = NULL;
9292 fk_trigger->isconstraint = true;
9293 fk_trigger->deferrable = fkconstraint->deferrable;
9294 fk_trigger->initdeferred = fkconstraint->initdeferred;
9295 fk_trigger->constrrel = NULL;
9296 fk_trigger->args = NIL;
9297
9298 (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid,
9299 indexOid, InvalidOid, InvalidOid, NULL, true, false);
9300
9301 /* Make changes-so-far visible */
9302 CommandCounterIncrement();
9303 }
9304
9305 /*
9306 * createForeignKeyActionTriggers
9307 * Create the referenced-side "action" triggers that implement a foreign
9308 * key.
9309 */
9310 static void
createForeignKeyActionTriggers(Relation rel,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid)9311 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
9312 Oid constraintOid, Oid indexOid)
9313 {
9314 CreateTrigStmt *fk_trigger;
9315
9316 /*
9317 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
9318 * DELETE action on the referenced table.
9319 */
9320 fk_trigger = makeNode(CreateTrigStmt);
9321 fk_trigger->trigname = "RI_ConstraintTrigger_a";
9322 fk_trigger->relation = NULL;
9323 fk_trigger->row = true;
9324 fk_trigger->timing = TRIGGER_TYPE_AFTER;
9325 fk_trigger->events = TRIGGER_TYPE_DELETE;
9326 fk_trigger->columns = NIL;
9327 fk_trigger->transitionRels = NIL;
9328 fk_trigger->whenClause = NULL;
9329 fk_trigger->isconstraint = true;
9330 fk_trigger->constrrel = NULL;
9331 switch (fkconstraint->fk_del_action)
9332 {
9333 case FKCONSTR_ACTION_NOACTION:
9334 fk_trigger->deferrable = fkconstraint->deferrable;
9335 fk_trigger->initdeferred = fkconstraint->initdeferred;
9336 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
9337 break;
9338 case FKCONSTR_ACTION_RESTRICT:
9339 fk_trigger->deferrable = false;
9340 fk_trigger->initdeferred = false;
9341 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
9342 break;
9343 case FKCONSTR_ACTION_CASCADE:
9344 fk_trigger->deferrable = false;
9345 fk_trigger->initdeferred = false;
9346 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
9347 break;
9348 case FKCONSTR_ACTION_SETNULL:
9349 fk_trigger->deferrable = false;
9350 fk_trigger->initdeferred = false;
9351 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
9352 break;
9353 case FKCONSTR_ACTION_SETDEFAULT:
9354 fk_trigger->deferrable = false;
9355 fk_trigger->initdeferred = false;
9356 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
9357 break;
9358 default:
9359 elog(ERROR, "unrecognized FK action type: %d",
9360 (int) fkconstraint->fk_del_action);
9361 break;
9362 }
9363 fk_trigger->args = NIL;
9364
9365 (void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel),
9366 constraintOid,
9367 indexOid, InvalidOid, InvalidOid, NULL, true, false);
9368
9369 /* Make changes-so-far visible */
9370 CommandCounterIncrement();
9371
9372 /*
9373 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
9374 * UPDATE action on the referenced table.
9375 */
9376 fk_trigger = makeNode(CreateTrigStmt);
9377 fk_trigger->trigname = "RI_ConstraintTrigger_a";
9378 fk_trigger->relation = NULL;
9379 fk_trigger->row = true;
9380 fk_trigger->timing = TRIGGER_TYPE_AFTER;
9381 fk_trigger->events = TRIGGER_TYPE_UPDATE;
9382 fk_trigger->columns = NIL;
9383 fk_trigger->transitionRels = NIL;
9384 fk_trigger->whenClause = NULL;
9385 fk_trigger->isconstraint = true;
9386 fk_trigger->constrrel = NULL;
9387 switch (fkconstraint->fk_upd_action)
9388 {
9389 case FKCONSTR_ACTION_NOACTION:
9390 fk_trigger->deferrable = fkconstraint->deferrable;
9391 fk_trigger->initdeferred = fkconstraint->initdeferred;
9392 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
9393 break;
9394 case FKCONSTR_ACTION_RESTRICT:
9395 fk_trigger->deferrable = false;
9396 fk_trigger->initdeferred = false;
9397 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
9398 break;
9399 case FKCONSTR_ACTION_CASCADE:
9400 fk_trigger->deferrable = false;
9401 fk_trigger->initdeferred = false;
9402 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
9403 break;
9404 case FKCONSTR_ACTION_SETNULL:
9405 fk_trigger->deferrable = false;
9406 fk_trigger->initdeferred = false;
9407 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
9408 break;
9409 case FKCONSTR_ACTION_SETDEFAULT:
9410 fk_trigger->deferrable = false;
9411 fk_trigger->initdeferred = false;
9412 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
9413 break;
9414 default:
9415 elog(ERROR, "unrecognized FK action type: %d",
9416 (int) fkconstraint->fk_upd_action);
9417 break;
9418 }
9419 fk_trigger->args = NIL;
9420
9421 (void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel),
9422 constraintOid,
9423 indexOid, InvalidOid, InvalidOid, NULL, true, false);
9424 }
9425
9426 /*
9427 * createForeignKeyCheckTriggers
9428 * Create the referencing-side "check" triggers that implement a foreign
9429 * key.
9430 */
9431 static void
createForeignKeyCheckTriggers(Oid myRelOid,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid)9432 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
9433 Constraint *fkconstraint, Oid constraintOid,
9434 Oid indexOid)
9435 {
9436 CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
9437 indexOid, true);
9438 CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
9439 indexOid, false);
9440 }
9441
9442 /*
9443 * Create the triggers that implement an FK constraint.
9444 *
9445 * NB: if you change any trigger properties here, see also
9446 * ATExecAlterConstraint.
9447 */
9448 void
createForeignKeyTriggers(Relation rel,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid,bool create_action)9449 createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
9450 Oid constraintOid, Oid indexOid, bool create_action)
9451 {
9452 /*
9453 * For the referenced side, create action triggers, if requested. (If the
9454 * referencing side is partitioned, there is still only one trigger, which
9455 * runs on the referenced side and points to the top of the referencing
9456 * hierarchy.)
9457 */
9458 if (create_action)
9459 createForeignKeyActionTriggers(rel, refRelOid, fkconstraint, constraintOid,
9460 indexOid);
9461
9462 /*
9463 * For the referencing side, create the check triggers. We only need
9464 * these on the partitions.
9465 */
9466 if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9467 createForeignKeyCheckTriggers(RelationGetRelid(rel), refRelOid,
9468 fkconstraint, constraintOid, indexOid);
9469
9470 CommandCounterIncrement();
9471 }
9472
9473 /*
9474 * ALTER TABLE DROP CONSTRAINT
9475 *
9476 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
9477 */
9478 static void
ATExecDropConstraint(Relation rel,const char * constrName,DropBehavior behavior,bool recurse,bool recursing,bool missing_ok,LOCKMODE lockmode)9479 ATExecDropConstraint(Relation rel, const char *constrName,
9480 DropBehavior behavior,
9481 bool recurse, bool recursing,
9482 bool missing_ok, LOCKMODE lockmode)
9483 {
9484 List *children;
9485 ListCell *child;
9486 Relation conrel;
9487 Form_pg_constraint con;
9488 SysScanDesc scan;
9489 ScanKeyData skey[3];
9490 HeapTuple tuple;
9491 bool found = false;
9492 bool is_no_inherit_constraint = false;
9493 char contype;
9494
9495 /* At top level, permission check was done in ATPrepCmd, else do it */
9496 if (recursing)
9497 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9498
9499 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
9500
9501 /*
9502 * Find and drop the target constraint
9503 */
9504 ScanKeyInit(&skey[0],
9505 Anum_pg_constraint_conrelid,
9506 BTEqualStrategyNumber, F_OIDEQ,
9507 ObjectIdGetDatum(RelationGetRelid(rel)));
9508 ScanKeyInit(&skey[1],
9509 Anum_pg_constraint_contypid,
9510 BTEqualStrategyNumber, F_OIDEQ,
9511 ObjectIdGetDatum(InvalidOid));
9512 ScanKeyInit(&skey[2],
9513 Anum_pg_constraint_conname,
9514 BTEqualStrategyNumber, F_NAMEEQ,
9515 CStringGetDatum(constrName));
9516 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
9517 true, NULL, 3, skey);
9518
9519 /* There can be at most one matching row */
9520 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
9521 {
9522 ObjectAddress conobj;
9523
9524 con = (Form_pg_constraint) GETSTRUCT(tuple);
9525
9526 /* Don't drop inherited constraints */
9527 if (con->coninhcount > 0 && !recursing)
9528 ereport(ERROR,
9529 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9530 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
9531 constrName, RelationGetRelationName(rel))));
9532
9533 is_no_inherit_constraint = con->connoinherit;
9534 contype = con->contype;
9535
9536 /*
9537 * If it's a foreign-key constraint, we'd better lock the referenced
9538 * table and check that that's not in use, just as we've already done
9539 * for the constrained table (else we might, eg, be dropping a trigger
9540 * that has unfired events). But we can/must skip that in the
9541 * self-referential case.
9542 */
9543 if (contype == CONSTRAINT_FOREIGN &&
9544 con->confrelid != RelationGetRelid(rel))
9545 {
9546 Relation frel;
9547
9548 /* Must match lock taken by RemoveTriggerById: */
9549 frel = heap_open(con->confrelid, AccessExclusiveLock);
9550 CheckTableNotInUse(frel, "ALTER TABLE");
9551 heap_close(frel, NoLock);
9552 }
9553
9554 /*
9555 * Perform the actual constraint deletion
9556 */
9557 conobj.classId = ConstraintRelationId;
9558 conobj.objectId = HeapTupleGetOid(tuple);
9559 conobj.objectSubId = 0;
9560
9561 performDeletion(&conobj, behavior, 0);
9562
9563 found = true;
9564 }
9565
9566 systable_endscan(scan);
9567
9568 if (!found)
9569 {
9570 if (!missing_ok)
9571 {
9572 ereport(ERROR,
9573 (errcode(ERRCODE_UNDEFINED_OBJECT),
9574 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
9575 constrName, RelationGetRelationName(rel))));
9576 }
9577 else
9578 {
9579 ereport(NOTICE,
9580 (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
9581 constrName, RelationGetRelationName(rel))));
9582 heap_close(conrel, RowExclusiveLock);
9583 return;
9584 }
9585 }
9586
9587 /*
9588 * For partitioned tables, non-CHECK inherited constraints are dropped via
9589 * the dependency mechanism, so we're done here.
9590 */
9591 if (contype != CONSTRAINT_CHECK &&
9592 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9593 {
9594 heap_close(conrel, RowExclusiveLock);
9595 return;
9596 }
9597
9598 /*
9599 * Propagate to children as appropriate. Unlike most other ALTER
9600 * routines, we have to do this one level of recursion at a time; we can't
9601 * use find_all_inheritors to do it in one pass.
9602 */
9603 if (!is_no_inherit_constraint)
9604 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
9605 else
9606 children = NIL;
9607
9608 /*
9609 * For a partitioned table, if partitions exist and we are told not to
9610 * recurse, it's a user error. It doesn't make sense to have a constraint
9611 * be defined only on the parent, especially if it's a partitioned table.
9612 */
9613 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
9614 children != NIL && !recurse)
9615 ereport(ERROR,
9616 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9617 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
9618 errhint("Do not specify the ONLY keyword.")));
9619
9620 foreach(child, children)
9621 {
9622 Oid childrelid = lfirst_oid(child);
9623 Relation childrel;
9624 HeapTuple copy_tuple;
9625
9626 /* find_inheritance_children already got lock */
9627 childrel = heap_open(childrelid, NoLock);
9628 CheckTableNotInUse(childrel, "ALTER TABLE");
9629
9630 ScanKeyInit(&skey[0],
9631 Anum_pg_constraint_conrelid,
9632 BTEqualStrategyNumber, F_OIDEQ,
9633 ObjectIdGetDatum(childrelid));
9634 ScanKeyInit(&skey[1],
9635 Anum_pg_constraint_contypid,
9636 BTEqualStrategyNumber, F_OIDEQ,
9637 ObjectIdGetDatum(InvalidOid));
9638 ScanKeyInit(&skey[2],
9639 Anum_pg_constraint_conname,
9640 BTEqualStrategyNumber, F_NAMEEQ,
9641 CStringGetDatum(constrName));
9642 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
9643 true, NULL, 3, skey);
9644
9645 /* There can be at most one matching row */
9646 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
9647 ereport(ERROR,
9648 (errcode(ERRCODE_UNDEFINED_OBJECT),
9649 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
9650 constrName,
9651 RelationGetRelationName(childrel))));
9652
9653 copy_tuple = heap_copytuple(tuple);
9654
9655 systable_endscan(scan);
9656
9657 con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
9658
9659 /* Right now only CHECK constraints can be inherited */
9660 if (con->contype != CONSTRAINT_CHECK)
9661 elog(ERROR, "inherited constraint is not a CHECK constraint");
9662
9663 if (con->coninhcount <= 0) /* shouldn't happen */
9664 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
9665 childrelid, constrName);
9666
9667 if (recurse)
9668 {
9669 /*
9670 * If the child constraint has other definition sources, just
9671 * decrement its inheritance count; if not, recurse to delete it.
9672 */
9673 if (con->coninhcount == 1 && !con->conislocal)
9674 {
9675 /* Time to delete this child constraint, too */
9676 ATExecDropConstraint(childrel, constrName, behavior,
9677 true, true,
9678 false, lockmode);
9679 }
9680 else
9681 {
9682 /* Child constraint must survive my deletion */
9683 con->coninhcount--;
9684 CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
9685
9686 /* Make update visible */
9687 CommandCounterIncrement();
9688 }
9689 }
9690 else
9691 {
9692 /*
9693 * If we were told to drop ONLY in this table (no recursion), we
9694 * need to mark the inheritors' constraints as locally defined
9695 * rather than inherited.
9696 */
9697 con->coninhcount--;
9698 con->conislocal = true;
9699
9700 CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
9701
9702 /* Make update visible */
9703 CommandCounterIncrement();
9704 }
9705
9706 heap_freetuple(copy_tuple);
9707
9708 heap_close(childrel, NoLock);
9709 }
9710
9711 heap_close(conrel, RowExclusiveLock);
9712 }
9713
9714 /*
9715 * ALTER COLUMN TYPE
9716 */
9717 static void
ATPrepAlterColumnType(List ** wqueue,AlteredTableInfo * tab,Relation rel,bool recurse,bool recursing,AlterTableCmd * cmd,LOCKMODE lockmode)9718 ATPrepAlterColumnType(List **wqueue,
9719 AlteredTableInfo *tab, Relation rel,
9720 bool recurse, bool recursing,
9721 AlterTableCmd *cmd, LOCKMODE lockmode)
9722 {
9723 char *colName = cmd->name;
9724 ColumnDef *def = (ColumnDef *) cmd->def;
9725 TypeName *typeName = def->typeName;
9726 Node *transform = def->cooked_default;
9727 HeapTuple tuple;
9728 Form_pg_attribute attTup;
9729 AttrNumber attnum;
9730 Oid targettype;
9731 int32 targettypmod;
9732 Oid targetcollid;
9733 NewColumnValue *newval;
9734 ParseState *pstate = make_parsestate(NULL);
9735 AclResult aclresult;
9736 bool is_expr;
9737
9738 if (rel->rd_rel->reloftype && !recursing)
9739 ereport(ERROR,
9740 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9741 errmsg("cannot alter column type of typed table")));
9742
9743 /* lookup the attribute so we can check inheritance status */
9744 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9745 if (!HeapTupleIsValid(tuple))
9746 ereport(ERROR,
9747 (errcode(ERRCODE_UNDEFINED_COLUMN),
9748 errmsg("column \"%s\" of relation \"%s\" does not exist",
9749 colName, RelationGetRelationName(rel))));
9750 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
9751 attnum = attTup->attnum;
9752
9753 /* Can't alter a system attribute */
9754 if (attnum <= 0)
9755 ereport(ERROR,
9756 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9757 errmsg("cannot alter system column \"%s\"",
9758 colName)));
9759
9760 /*
9761 * Don't alter inherited columns. At outer level, there had better not be
9762 * any inherited definition; when recursing, we assume this was checked at
9763 * the parent level (see below).
9764 */
9765 if (attTup->attinhcount > 0 && !recursing)
9766 ereport(ERROR,
9767 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9768 errmsg("cannot alter inherited column \"%s\"",
9769 colName)));
9770
9771 /* Don't alter columns used in the partition key */
9772 if (has_partition_attrs(rel,
9773 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9774 &is_expr))
9775 ereport(ERROR,
9776 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9777 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
9778 colName, RelationGetRelationName(rel))));
9779
9780 /* Look up the target type */
9781 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
9782
9783 aclresult = pg_type_aclcheck(targettype, GetUserId(), ACL_USAGE);
9784 if (aclresult != ACLCHECK_OK)
9785 aclcheck_error_type(aclresult, targettype);
9786
9787 /* And the collation */
9788 targetcollid = GetColumnDefCollation(NULL, def, targettype);
9789
9790 /* make sure datatype is legal for a column */
9791 CheckAttributeType(colName, targettype, targetcollid,
9792 list_make1_oid(rel->rd_rel->reltype),
9793 false);
9794
9795 if (tab->relkind == RELKIND_RELATION ||
9796 tab->relkind == RELKIND_PARTITIONED_TABLE)
9797 {
9798 /*
9799 * Set up an expression to transform the old data value to the new
9800 * type. If a USING option was given, use the expression as
9801 * transformed by transformAlterTableStmt, else just take the old
9802 * value and try to coerce it. We do this first so that type
9803 * incompatibility can be detected before we waste effort, and because
9804 * we need the expression to be parsed against the original table row
9805 * type.
9806 */
9807 if (!transform)
9808 {
9809 transform = (Node *) makeVar(1, attnum,
9810 attTup->atttypid, attTup->atttypmod,
9811 attTup->attcollation,
9812 0);
9813 }
9814
9815 transform = coerce_to_target_type(pstate,
9816 transform, exprType(transform),
9817 targettype, targettypmod,
9818 COERCION_ASSIGNMENT,
9819 COERCE_IMPLICIT_CAST,
9820 -1);
9821 if (transform == NULL)
9822 {
9823 /* error text depends on whether USING was specified or not */
9824 if (def->cooked_default != NULL)
9825 ereport(ERROR,
9826 (errcode(ERRCODE_DATATYPE_MISMATCH),
9827 errmsg("result of USING clause for column \"%s\""
9828 " cannot be cast automatically to type %s",
9829 colName, format_type_be(targettype)),
9830 errhint("You might need to add an explicit cast.")));
9831 else
9832 ereport(ERROR,
9833 (errcode(ERRCODE_DATATYPE_MISMATCH),
9834 errmsg("column \"%s\" cannot be cast automatically to type %s",
9835 colName, format_type_be(targettype)),
9836 /* translator: USING is SQL, don't translate it */
9837 errhint("You might need to specify \"USING %s::%s\".",
9838 quote_identifier(colName),
9839 format_type_with_typemod(targettype,
9840 targettypmod))));
9841 }
9842
9843 /* Fix collations after all else */
9844 assign_expr_collations(pstate, transform);
9845
9846 /* Plan the expr now so we can accurately assess the need to rewrite. */
9847 transform = (Node *) expression_planner((Expr *) transform);
9848
9849 /*
9850 * Add a work queue item to make ATRewriteTable update the column
9851 * contents.
9852 */
9853 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
9854 newval->attnum = attnum;
9855 newval->expr = (Expr *) transform;
9856
9857 tab->newvals = lappend(tab->newvals, newval);
9858 if (ATColumnChangeRequiresRewrite(transform, attnum))
9859 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
9860 }
9861 else if (transform)
9862 ereport(ERROR,
9863 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9864 errmsg("\"%s\" is not a table",
9865 RelationGetRelationName(rel))));
9866
9867 if (tab->relkind == RELKIND_COMPOSITE_TYPE ||
9868 tab->relkind == RELKIND_FOREIGN_TABLE)
9869 {
9870 /*
9871 * For composite types, do this check now. Tables will check it later
9872 * when the table is being rewritten.
9873 */
9874 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
9875 }
9876
9877 ReleaseSysCache(tuple);
9878
9879 /*
9880 * Recurse manually by queueing a new command for each child, if
9881 * necessary. We cannot apply ATSimpleRecursion here because we need to
9882 * remap attribute numbers in the USING expression, if any.
9883 *
9884 * If we are told not to recurse, there had better not be any child
9885 * tables; else the alter would put them out of step.
9886 */
9887 if (recurse)
9888 {
9889 Oid relid = RelationGetRelid(rel);
9890 List *child_oids,
9891 *child_numparents;
9892 ListCell *lo,
9893 *li;
9894
9895 child_oids = find_all_inheritors(relid, lockmode,
9896 &child_numparents);
9897
9898 /*
9899 * find_all_inheritors does the recursive search of the inheritance
9900 * hierarchy, so all we have to do is process all of the relids in the
9901 * list that it returns.
9902 */
9903 forboth(lo, child_oids, li, child_numparents)
9904 {
9905 Oid childrelid = lfirst_oid(lo);
9906 int numparents = lfirst_int(li);
9907 Relation childrel;
9908 HeapTuple childtuple;
9909 Form_pg_attribute childattTup;
9910
9911 if (childrelid == relid)
9912 continue;
9913
9914 /* find_all_inheritors already got lock */
9915 childrel = relation_open(childrelid, NoLock);
9916 CheckTableNotInUse(childrel, "ALTER TABLE");
9917
9918 /*
9919 * Verify that the child doesn't have any inherited definitions of
9920 * this column that came from outside this inheritance hierarchy.
9921 * (renameatt makes a similar test, though in a different way
9922 * because of its different recursion mechanism.)
9923 */
9924 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
9925 colName);
9926 if (!HeapTupleIsValid(childtuple))
9927 ereport(ERROR,
9928 (errcode(ERRCODE_UNDEFINED_COLUMN),
9929 errmsg("column \"%s\" of relation \"%s\" does not exist",
9930 colName, RelationGetRelationName(childrel))));
9931 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
9932
9933 if (childattTup->attinhcount > numparents)
9934 ereport(ERROR,
9935 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9936 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
9937 colName, RelationGetRelationName(childrel))));
9938
9939 ReleaseSysCache(childtuple);
9940
9941 /*
9942 * Remap the attribute numbers. If no USING expression was
9943 * specified, there is no need for this step.
9944 */
9945 if (def->cooked_default)
9946 {
9947 AttrNumber *attmap;
9948 bool found_whole_row;
9949
9950 /* create a copy to scribble on */
9951 cmd = copyObject(cmd);
9952
9953 attmap = convert_tuples_by_name_map(RelationGetDescr(childrel),
9954 RelationGetDescr(rel),
9955 gettext_noop("could not convert row type"));
9956 ((ColumnDef *) cmd->def)->cooked_default =
9957 map_variable_attnos(def->cooked_default,
9958 1, 0,
9959 attmap, RelationGetDescr(rel)->natts,
9960 InvalidOid, &found_whole_row);
9961 if (found_whole_row)
9962 ereport(ERROR,
9963 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9964 errmsg("cannot convert whole-row table reference"),
9965 errdetail("USING expression contains a whole-row table reference.")));
9966 pfree(attmap);
9967 }
9968 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
9969 relation_close(childrel, NoLock);
9970 }
9971 }
9972 else if (!recursing &&
9973 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
9974 ereport(ERROR,
9975 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9976 errmsg("type of inherited column \"%s\" must be changed in child tables too",
9977 colName)));
9978
9979 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
9980 ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
9981 }
9982
9983 /*
9984 * When the data type of a column is changed, a rewrite might not be required
9985 * if the new type is sufficiently identical to the old one, and the USING
9986 * clause isn't trying to insert some other value. It's safe to skip the
9987 * rewrite if the old type is binary coercible to the new type, or if the
9988 * new type is an unconstrained domain over the old type. In the case of a
9989 * constrained domain, we could get by with scanning the table and checking
9990 * the constraint rather than actually rewriting it, but we don't currently
9991 * try to do that.
9992 */
9993 static bool
ATColumnChangeRequiresRewrite(Node * expr,AttrNumber varattno)9994 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
9995 {
9996 Assert(expr != NULL);
9997
9998 for (;;)
9999 {
10000 /* only one varno, so no need to check that */
10001 if (IsA(expr, Var) &&((Var *) expr)->varattno == varattno)
10002 return false;
10003 else if (IsA(expr, RelabelType))
10004 expr = (Node *) ((RelabelType *) expr)->arg;
10005 else if (IsA(expr, CoerceToDomain))
10006 {
10007 CoerceToDomain *d = (CoerceToDomain *) expr;
10008
10009 if (DomainHasConstraints(d->resulttype))
10010 return true;
10011 expr = (Node *) d->arg;
10012 }
10013 else
10014 return true;
10015 }
10016 }
10017
10018 /*
10019 * ALTER COLUMN .. SET DATA TYPE
10020 *
10021 * Return the address of the modified column.
10022 */
10023 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode)10024 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
10025 AlterTableCmd *cmd, LOCKMODE lockmode)
10026 {
10027 char *colName = cmd->name;
10028 ColumnDef *def = (ColumnDef *) cmd->def;
10029 TypeName *typeName = def->typeName;
10030 HeapTuple heapTup;
10031 Form_pg_attribute attTup,
10032 attOldTup;
10033 AttrNumber attnum;
10034 HeapTuple typeTuple;
10035 Form_pg_type tform;
10036 Oid targettype;
10037 int32 targettypmod;
10038 Oid targetcollid;
10039 Node *defaultexpr;
10040 Relation attrelation;
10041 Relation depRel;
10042 ScanKeyData key[3];
10043 SysScanDesc scan;
10044 HeapTuple depTup;
10045 ObjectAddress address;
10046
10047 /*
10048 * Clear all the missing values if we're rewriting the table, since this
10049 * renders them pointless.
10050 */
10051 if (tab->rewrite)
10052 {
10053 Relation newrel;
10054
10055 newrel = heap_open(RelationGetRelid(rel), NoLock);
10056 RelationClearMissing(newrel);
10057 relation_close(newrel, NoLock);
10058 /* make sure we don't conflict with later attribute modifications */
10059 CommandCounterIncrement();
10060 }
10061
10062 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
10063
10064 /* Look up the target column */
10065 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
10066 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
10067 ereport(ERROR,
10068 (errcode(ERRCODE_UNDEFINED_COLUMN),
10069 errmsg("column \"%s\" of relation \"%s\" does not exist",
10070 colName, RelationGetRelationName(rel))));
10071 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
10072 attnum = attTup->attnum;
10073 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
10074
10075 /* Check for multiple ALTER TYPE on same column --- can't cope */
10076 if (attTup->atttypid != attOldTup->atttypid ||
10077 attTup->atttypmod != attOldTup->atttypmod)
10078 ereport(ERROR,
10079 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10080 errmsg("cannot alter type of column \"%s\" twice",
10081 colName)));
10082
10083 /* Look up the target type (should not fail, since prep found it) */
10084 typeTuple = typenameType(NULL, typeName, &targettypmod);
10085 tform = (Form_pg_type) GETSTRUCT(typeTuple);
10086 targettype = HeapTupleGetOid(typeTuple);
10087 /* And the collation */
10088 targetcollid = GetColumnDefCollation(NULL, def, targettype);
10089
10090 /*
10091 * If there is a default expression for the column, get it and ensure we
10092 * can coerce it to the new datatype. (We must do this before changing
10093 * the column type, because build_column_default itself will try to
10094 * coerce, and will not issue the error message we want if it fails.)
10095 *
10096 * We remove any implicit coercion steps at the top level of the old
10097 * default expression; this has been agreed to satisfy the principle of
10098 * least surprise. (The conversion to the new column type should act like
10099 * it started from what the user sees as the stored expression, and the
10100 * implicit coercions aren't going to be shown.)
10101 */
10102 if (attTup->atthasdef)
10103 {
10104 defaultexpr = build_column_default(rel, attnum);
10105 Assert(defaultexpr);
10106 defaultexpr = strip_implicit_coercions(defaultexpr);
10107 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
10108 defaultexpr, exprType(defaultexpr),
10109 targettype, targettypmod,
10110 COERCION_ASSIGNMENT,
10111 COERCE_IMPLICIT_CAST,
10112 -1);
10113 if (defaultexpr == NULL)
10114 ereport(ERROR,
10115 (errcode(ERRCODE_DATATYPE_MISMATCH),
10116 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
10117 colName, format_type_be(targettype))));
10118 }
10119 else
10120 defaultexpr = NULL;
10121
10122 /*
10123 * Find everything that depends on the column (constraints, indexes, etc),
10124 * and record enough information to let us recreate the objects.
10125 *
10126 * The actual recreation does not happen here, but only after we have
10127 * performed all the individual ALTER TYPE operations. We have to save
10128 * the info before executing ALTER TYPE, though, else the deparser will
10129 * get confused.
10130 */
10131 depRel = heap_open(DependRelationId, RowExclusiveLock);
10132
10133 ScanKeyInit(&key[0],
10134 Anum_pg_depend_refclassid,
10135 BTEqualStrategyNumber, F_OIDEQ,
10136 ObjectIdGetDatum(RelationRelationId));
10137 ScanKeyInit(&key[1],
10138 Anum_pg_depend_refobjid,
10139 BTEqualStrategyNumber, F_OIDEQ,
10140 ObjectIdGetDatum(RelationGetRelid(rel)));
10141 ScanKeyInit(&key[2],
10142 Anum_pg_depend_refobjsubid,
10143 BTEqualStrategyNumber, F_INT4EQ,
10144 Int32GetDatum((int32) attnum));
10145
10146 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
10147 NULL, 3, key);
10148
10149 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
10150 {
10151 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
10152 ObjectAddress foundObject;
10153
10154 /* We don't expect any PIN dependencies on columns */
10155 if (foundDep->deptype == DEPENDENCY_PIN)
10156 elog(ERROR, "cannot alter type of a pinned column");
10157
10158 foundObject.classId = foundDep->classid;
10159 foundObject.objectId = foundDep->objid;
10160 foundObject.objectSubId = foundDep->objsubid;
10161
10162 switch (getObjectClass(&foundObject))
10163 {
10164 case OCLASS_CLASS:
10165 {
10166 char relKind = get_rel_relkind(foundObject.objectId);
10167
10168 if (relKind == RELKIND_INDEX ||
10169 relKind == RELKIND_PARTITIONED_INDEX)
10170 {
10171 Assert(foundObject.objectSubId == 0);
10172 RememberIndexForRebuilding(foundObject.objectId, tab);
10173 }
10174 else if (relKind == RELKIND_SEQUENCE)
10175 {
10176 /*
10177 * This must be a SERIAL column's sequence. We need
10178 * not do anything to it.
10179 */
10180 Assert(foundObject.objectSubId == 0);
10181 }
10182 else
10183 {
10184 /* Not expecting any other direct dependencies... */
10185 elog(ERROR, "unexpected object depending on column: %s",
10186 getObjectDescription(&foundObject));
10187 }
10188 break;
10189 }
10190
10191 case OCLASS_CONSTRAINT:
10192 Assert(foundObject.objectSubId == 0);
10193 RememberConstraintForRebuilding(foundObject.objectId, tab);
10194 break;
10195
10196 case OCLASS_REWRITE:
10197 /* XXX someday see if we can cope with revising views */
10198 ereport(ERROR,
10199 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10200 errmsg("cannot alter type of a column used by a view or rule"),
10201 errdetail("%s depends on column \"%s\"",
10202 getObjectDescription(&foundObject),
10203 colName)));
10204 break;
10205
10206 case OCLASS_TRIGGER:
10207
10208 /*
10209 * A trigger can depend on a column because the column is
10210 * specified as an update target, or because the column is
10211 * used in the trigger's WHEN condition. The first case would
10212 * not require any extra work, but the second case would
10213 * require updating the WHEN expression, which will take a
10214 * significant amount of new code. Since we can't easily tell
10215 * which case applies, we punt for both. FIXME someday.
10216 */
10217 ereport(ERROR,
10218 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10219 errmsg("cannot alter type of a column used in a trigger definition"),
10220 errdetail("%s depends on column \"%s\"",
10221 getObjectDescription(&foundObject),
10222 colName)));
10223 break;
10224
10225 case OCLASS_POLICY:
10226
10227 /*
10228 * A policy can depend on a column because the column is
10229 * specified in the policy's USING or WITH CHECK qual
10230 * expressions. It might be possible to rewrite and recheck
10231 * the policy expression, but punt for now. It's certainly
10232 * easy enough to remove and recreate the policy; still, FIXME
10233 * someday.
10234 */
10235 ereport(ERROR,
10236 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10237 errmsg("cannot alter type of a column used in a policy definition"),
10238 errdetail("%s depends on column \"%s\"",
10239 getObjectDescription(&foundObject),
10240 colName)));
10241 break;
10242
10243 case OCLASS_DEFAULT:
10244
10245 /*
10246 * Ignore the column's default expression, since we will fix
10247 * it below.
10248 */
10249 Assert(defaultexpr);
10250 break;
10251
10252 case OCLASS_STATISTIC_EXT:
10253
10254 /*
10255 * Give the extended-stats machinery a chance to fix anything
10256 * that this column type change would break.
10257 */
10258 UpdateStatisticsForTypeChange(foundObject.objectId,
10259 RelationGetRelid(rel), attnum,
10260 attTup->atttypid, targettype);
10261 break;
10262
10263 case OCLASS_PROC:
10264 case OCLASS_TYPE:
10265 case OCLASS_CAST:
10266 case OCLASS_COLLATION:
10267 case OCLASS_CONVERSION:
10268 case OCLASS_LANGUAGE:
10269 case OCLASS_LARGEOBJECT:
10270 case OCLASS_OPERATOR:
10271 case OCLASS_OPCLASS:
10272 case OCLASS_OPFAMILY:
10273 case OCLASS_AM:
10274 case OCLASS_AMOP:
10275 case OCLASS_AMPROC:
10276 case OCLASS_SCHEMA:
10277 case OCLASS_TSPARSER:
10278 case OCLASS_TSDICT:
10279 case OCLASS_TSTEMPLATE:
10280 case OCLASS_TSCONFIG:
10281 case OCLASS_ROLE:
10282 case OCLASS_DATABASE:
10283 case OCLASS_TBLSPACE:
10284 case OCLASS_FDW:
10285 case OCLASS_FOREIGN_SERVER:
10286 case OCLASS_USER_MAPPING:
10287 case OCLASS_DEFACL:
10288 case OCLASS_EXTENSION:
10289 case OCLASS_EVENT_TRIGGER:
10290 case OCLASS_PUBLICATION:
10291 case OCLASS_PUBLICATION_REL:
10292 case OCLASS_SUBSCRIPTION:
10293 case OCLASS_TRANSFORM:
10294
10295 /*
10296 * We don't expect any of these sorts of objects to depend on
10297 * a column.
10298 */
10299 elog(ERROR, "unexpected object depending on column: %s",
10300 getObjectDescription(&foundObject));
10301 break;
10302
10303 /*
10304 * There's intentionally no default: case here; we want the
10305 * compiler to warn if a new OCLASS hasn't been handled above.
10306 */
10307 }
10308 }
10309
10310 systable_endscan(scan);
10311
10312 /*
10313 * Now scan for dependencies of this column on other things. The only
10314 * thing we should find is the dependency on the column datatype, which we
10315 * want to remove, and possibly a collation dependency.
10316 */
10317 ScanKeyInit(&key[0],
10318 Anum_pg_depend_classid,
10319 BTEqualStrategyNumber, F_OIDEQ,
10320 ObjectIdGetDatum(RelationRelationId));
10321 ScanKeyInit(&key[1],
10322 Anum_pg_depend_objid,
10323 BTEqualStrategyNumber, F_OIDEQ,
10324 ObjectIdGetDatum(RelationGetRelid(rel)));
10325 ScanKeyInit(&key[2],
10326 Anum_pg_depend_objsubid,
10327 BTEqualStrategyNumber, F_INT4EQ,
10328 Int32GetDatum((int32) attnum));
10329
10330 scan = systable_beginscan(depRel, DependDependerIndexId, true,
10331 NULL, 3, key);
10332
10333 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
10334 {
10335 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
10336
10337 if (foundDep->deptype != DEPENDENCY_NORMAL)
10338 elog(ERROR, "found unexpected dependency type '%c'",
10339 foundDep->deptype);
10340 if (!(foundDep->refclassid == TypeRelationId &&
10341 foundDep->refobjid == attTup->atttypid) &&
10342 !(foundDep->refclassid == CollationRelationId &&
10343 foundDep->refobjid == attTup->attcollation))
10344 elog(ERROR, "found unexpected dependency for column");
10345
10346 CatalogTupleDelete(depRel, &depTup->t_self);
10347 }
10348
10349 systable_endscan(scan);
10350
10351 heap_close(depRel, RowExclusiveLock);
10352
10353 /*
10354 * Here we go --- change the recorded column type and collation. (Note
10355 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
10356 * fix up the missing value if any. There shouldn't be any missing values
10357 * for anything except plain tables, but if there are, ignore them.
10358 */
10359 if (rel->rd_rel->relkind == RELKIND_RELATION && attTup->atthasmissing)
10360 {
10361 Datum missingval;
10362 bool missingNull;
10363
10364 /* if rewrite is true the missing value should already be cleared */
10365 Assert(tab->rewrite == 0);
10366
10367 /* Get the missing value datum */
10368 missingval = heap_getattr(heapTup,
10369 Anum_pg_attribute_attmissingval,
10370 attrelation->rd_att,
10371 &missingNull);
10372
10373 /* if it's a null array there is nothing to do */
10374
10375 if (! missingNull)
10376 {
10377 /*
10378 * Get the datum out of the array and repack it in a new array
10379 * built with the new type data. We assume that since the table
10380 * doesn't need rewriting, the actual Datum doesn't need to be
10381 * changed, only the array metadata.
10382 */
10383
10384 int one = 1;
10385 bool isNull;
10386 Datum valuesAtt[Natts_pg_attribute];
10387 bool nullsAtt[Natts_pg_attribute];
10388 bool replacesAtt[Natts_pg_attribute];
10389 HeapTuple newTup;
10390
10391 MemSet(valuesAtt, 0, sizeof(valuesAtt));
10392 MemSet(nullsAtt, false, sizeof(nullsAtt));
10393 MemSet(replacesAtt, false, sizeof(replacesAtt));
10394
10395 missingval = array_get_element(missingval,
10396 1,
10397 &one,
10398 0,
10399 attTup->attlen,
10400 attTup->attbyval,
10401 attTup->attalign,
10402 &isNull);
10403 missingval = PointerGetDatum(
10404 construct_array(&missingval,
10405 1,
10406 targettype,
10407 tform->typlen,
10408 tform->typbyval,
10409 tform->typalign));
10410
10411 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
10412 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
10413 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
10414
10415 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
10416 valuesAtt, nullsAtt, replacesAtt);
10417 heap_freetuple(heapTup);
10418 heapTup = newTup;
10419 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
10420 }
10421 }
10422
10423 attTup->atttypid = targettype;
10424 attTup->atttypmod = targettypmod;
10425 attTup->attcollation = targetcollid;
10426 attTup->attndims = list_length(typeName->arrayBounds);
10427 attTup->attlen = tform->typlen;
10428 attTup->attbyval = tform->typbyval;
10429 attTup->attalign = tform->typalign;
10430 attTup->attstorage = tform->typstorage;
10431
10432 ReleaseSysCache(typeTuple);
10433
10434 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
10435
10436 heap_close(attrelation, RowExclusiveLock);
10437
10438 /* Install dependencies on new datatype and collation */
10439 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
10440 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
10441
10442 /*
10443 * Drop any pg_statistic entry for the column, since it's now wrong type
10444 */
10445 RemoveStatistics(RelationGetRelid(rel), attnum);
10446
10447 InvokeObjectPostAlterHook(RelationRelationId,
10448 RelationGetRelid(rel), attnum);
10449
10450 /*
10451 * Update the default, if present, by brute force --- remove and re-add
10452 * the default. Probably unsafe to take shortcuts, since the new version
10453 * may well have additional dependencies. (It's okay to do this now,
10454 * rather than after other ALTER TYPE commands, since the default won't
10455 * depend on other column types.)
10456 */
10457 if (defaultexpr)
10458 {
10459 /* Must make new row visible since it will be updated again */
10460 CommandCounterIncrement();
10461
10462 /*
10463 * We use RESTRICT here for safety, but at present we do not expect
10464 * anything to depend on the default.
10465 */
10466 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
10467 true);
10468
10469 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
10470 }
10471
10472 ObjectAddressSubSet(address, RelationRelationId,
10473 RelationGetRelid(rel), attnum);
10474
10475 /* Cleanup */
10476 heap_freetuple(heapTup);
10477
10478 return address;
10479 }
10480
10481 /*
10482 * Subroutine for ATExecAlterColumnType: remember that a replica identity
10483 * needs to be reset.
10484 */
10485 static void
RememberReplicaIdentityForRebuilding(Oid indoid,AlteredTableInfo * tab)10486 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
10487 {
10488 if (!get_index_isreplident(indoid))
10489 return;
10490
10491 if (tab->replicaIdentityIndex)
10492 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
10493
10494 tab->replicaIdentityIndex = get_rel_name(indoid);
10495 }
10496
10497 /*
10498 * Subroutine for ATExecAlterColumnType: remember any clustered index.
10499 */
10500 static void
RememberClusterOnForRebuilding(Oid indoid,AlteredTableInfo * tab)10501 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
10502 {
10503 if (!get_index_isclustered(indoid))
10504 return;
10505
10506 if (tab->clusterOnIndex)
10507 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
10508
10509 tab->clusterOnIndex = get_rel_name(indoid);
10510 }
10511
10512 /*
10513 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
10514 * to be rebuilt (which we might already know).
10515 */
10516 static void
RememberConstraintForRebuilding(Oid conoid,AlteredTableInfo * tab)10517 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
10518 {
10519 /*
10520 * This de-duplication check is critical for two independent reasons: we
10521 * mustn't try to recreate the same constraint twice, and if a constraint
10522 * depends on more than one column whose type is to be altered, we must
10523 * capture its definition string before applying any of the column type
10524 * changes. ruleutils.c will get confused if we ask again later.
10525 */
10526 if (!list_member_oid(tab->changedConstraintOids, conoid))
10527 {
10528 /* OK, capture the constraint's existing definition string */
10529 char *defstring = pg_get_constraintdef_command(conoid);
10530 Oid indoid;
10531
10532 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
10533 conoid);
10534 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
10535 defstring);
10536
10537 /*
10538 * For the index of a constraint, if any, remember if it is used for
10539 * the table's replica identity or if it is a clustered index, so that
10540 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
10541 * those properties.
10542 */
10543 indoid = get_constraint_index(conoid);
10544 if (OidIsValid(indoid))
10545 {
10546 RememberReplicaIdentityForRebuilding(indoid, tab);
10547 RememberClusterOnForRebuilding(indoid, tab);
10548 }
10549 }
10550 }
10551
10552 /*
10553 * Subroutine for ATExecAlterColumnType: remember that an index needs
10554 * to be rebuilt (which we might already know).
10555 */
10556 static void
RememberIndexForRebuilding(Oid indoid,AlteredTableInfo * tab)10557 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
10558 {
10559 /*
10560 * This de-duplication check is critical for two independent reasons: we
10561 * mustn't try to recreate the same index twice, and if an index depends
10562 * on more than one column whose type is to be altered, we must capture
10563 * its definition string before applying any of the column type changes.
10564 * ruleutils.c will get confused if we ask again later.
10565 */
10566 if (!list_member_oid(tab->changedIndexOids, indoid))
10567 {
10568 /*
10569 * Before adding it as an index-to-rebuild, we'd better see if it
10570 * belongs to a constraint, and if so rebuild the constraint instead.
10571 * Typically this check fails, because constraint indexes normally
10572 * have only dependencies on their constraint. But it's possible for
10573 * such an index to also have direct dependencies on table columns,
10574 * for example with a partial exclusion constraint.
10575 */
10576 Oid conoid = get_index_constraint(indoid);
10577
10578 if (OidIsValid(conoid))
10579 {
10580 RememberConstraintForRebuilding(conoid, tab);
10581 }
10582 else
10583 {
10584 /* OK, capture the index's existing definition string */
10585 char *defstring = pg_get_indexdef_string(indoid);
10586
10587 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
10588 indoid);
10589 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
10590 defstring);
10591
10592 /*
10593 * Remember if this index is used for the table's replica identity
10594 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
10595 * can queue up commands necessary to restore those properties.
10596 */
10597 RememberReplicaIdentityForRebuilding(indoid, tab);
10598 RememberClusterOnForRebuilding(indoid, tab);
10599 }
10600 }
10601 }
10602
10603 /*
10604 * Returns the address of the modified column
10605 */
10606 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel,const char * colName,List * options,LOCKMODE lockmode)10607 ATExecAlterColumnGenericOptions(Relation rel,
10608 const char *colName,
10609 List *options,
10610 LOCKMODE lockmode)
10611 {
10612 Relation ftrel;
10613 Relation attrel;
10614 ForeignServer *server;
10615 ForeignDataWrapper *fdw;
10616 HeapTuple tuple;
10617 HeapTuple newtuple;
10618 bool isnull;
10619 Datum repl_val[Natts_pg_attribute];
10620 bool repl_null[Natts_pg_attribute];
10621 bool repl_repl[Natts_pg_attribute];
10622 Datum datum;
10623 Form_pg_foreign_table fttableform;
10624 Form_pg_attribute atttableform;
10625 AttrNumber attnum;
10626 ObjectAddress address;
10627
10628 if (options == NIL)
10629 return InvalidObjectAddress;
10630
10631 /* First, determine FDW validator associated to the foreign table. */
10632 ftrel = heap_open(ForeignTableRelationId, AccessShareLock);
10633 tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
10634 if (!HeapTupleIsValid(tuple))
10635 ereport(ERROR,
10636 (errcode(ERRCODE_UNDEFINED_OBJECT),
10637 errmsg("foreign table \"%s\" does not exist",
10638 RelationGetRelationName(rel))));
10639 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
10640 server = GetForeignServer(fttableform->ftserver);
10641 fdw = GetForeignDataWrapper(server->fdwid);
10642
10643 heap_close(ftrel, AccessShareLock);
10644 ReleaseSysCache(tuple);
10645
10646 attrel = heap_open(AttributeRelationId, RowExclusiveLock);
10647 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
10648 if (!HeapTupleIsValid(tuple))
10649 ereport(ERROR,
10650 (errcode(ERRCODE_UNDEFINED_COLUMN),
10651 errmsg("column \"%s\" of relation \"%s\" does not exist",
10652 colName, RelationGetRelationName(rel))));
10653
10654 /* Prevent them from altering a system attribute */
10655 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
10656 attnum = atttableform->attnum;
10657 if (attnum <= 0)
10658 ereport(ERROR,
10659 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10660 errmsg("cannot alter system column \"%s\"", colName)));
10661
10662
10663 /* Initialize buffers for new tuple values */
10664 memset(repl_val, 0, sizeof(repl_val));
10665 memset(repl_null, false, sizeof(repl_null));
10666 memset(repl_repl, false, sizeof(repl_repl));
10667
10668 /* Extract the current options */
10669 datum = SysCacheGetAttr(ATTNAME,
10670 tuple,
10671 Anum_pg_attribute_attfdwoptions,
10672 &isnull);
10673 if (isnull)
10674 datum = PointerGetDatum(NULL);
10675
10676 /* Transform the options */
10677 datum = transformGenericOptions(AttributeRelationId,
10678 datum,
10679 options,
10680 fdw->fdwvalidator);
10681
10682 if (PointerIsValid(DatumGetPointer(datum)))
10683 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
10684 else
10685 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
10686
10687 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
10688
10689 /* Everything looks good - update the tuple */
10690
10691 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
10692 repl_val, repl_null, repl_repl);
10693
10694 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
10695
10696 InvokeObjectPostAlterHook(RelationRelationId,
10697 RelationGetRelid(rel),
10698 atttableform->attnum);
10699 ObjectAddressSubSet(address, RelationRelationId,
10700 RelationGetRelid(rel), attnum);
10701
10702 ReleaseSysCache(tuple);
10703
10704 heap_close(attrel, RowExclusiveLock);
10705
10706 heap_freetuple(newtuple);
10707
10708 return address;
10709 }
10710
10711 /*
10712 * Cleanup after we've finished all the ALTER TYPE operations for a
10713 * particular relation. We have to drop and recreate all the indexes
10714 * and constraints that depend on the altered columns.
10715 */
10716 static void
ATPostAlterTypeCleanup(List ** wqueue,AlteredTableInfo * tab,LOCKMODE lockmode)10717 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
10718 {
10719 ObjectAddress obj;
10720 ObjectAddresses *objects;
10721 ListCell *def_item;
10722 ListCell *oid_item;
10723
10724 /*
10725 * Collect all the constraints and indexes to drop so we can process them
10726 * in a single call. That way we don't have to worry about dependencies
10727 * among them.
10728 */
10729 objects = new_object_addresses();
10730
10731 /*
10732 * Re-parse the index and constraint definitions, and attach them to the
10733 * appropriate work queue entries. We do this before dropping because in
10734 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
10735 * lock on the table the constraint is attached to, and we need to get
10736 * that before reparsing/dropping.
10737 *
10738 * We can't rely on the output of deparsing to tell us which relation to
10739 * operate on, because concurrent activity might have made the name
10740 * resolve differently. Instead, we've got to use the OID of the
10741 * constraint or index we're processing to figure out which relation to
10742 * operate on.
10743 */
10744 forboth(oid_item, tab->changedConstraintOids,
10745 def_item, tab->changedConstraintDefs)
10746 {
10747 Oid oldId = lfirst_oid(oid_item);
10748 HeapTuple tup;
10749 Form_pg_constraint con;
10750 Oid relid;
10751 Oid confrelid;
10752 char contype;
10753 bool conislocal;
10754
10755 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
10756 if (!HeapTupleIsValid(tup)) /* should not happen */
10757 elog(ERROR, "cache lookup failed for constraint %u", oldId);
10758 con = (Form_pg_constraint) GETSTRUCT(tup);
10759 if (OidIsValid(con->conrelid))
10760 relid = con->conrelid;
10761 else
10762 {
10763 /* must be a domain constraint */
10764 relid = get_typ_typrelid(getBaseType(con->contypid));
10765 if (!OidIsValid(relid))
10766 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
10767 }
10768 confrelid = con->confrelid;
10769 contype = con->contype;
10770 conislocal = con->conislocal;
10771 ReleaseSysCache(tup);
10772
10773 ObjectAddressSet(obj, ConstraintRelationId, oldId);
10774 add_exact_object_address(&obj, objects);
10775
10776 /*
10777 * If the constraint is inherited (only), we don't want to inject a
10778 * new definition here; it'll get recreated when ATAddCheckConstraint
10779 * recurses from adding the parent table's constraint. But we had to
10780 * carry the info this far so that we can drop the constraint below.
10781 */
10782 if (!conislocal)
10783 continue;
10784
10785 /*
10786 * When rebuilding an FK constraint that references the table we're
10787 * modifying, we might not yet have any lock on the FK's table, so get
10788 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
10789 * step, so there's no value in asking for anything weaker.
10790 */
10791 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
10792 LockRelationOid(relid, AccessExclusiveLock);
10793
10794 ATPostAlterTypeParse(oldId, relid, confrelid,
10795 (char *) lfirst(def_item),
10796 wqueue, lockmode, tab->rewrite);
10797 }
10798 forboth(oid_item, tab->changedIndexOids,
10799 def_item, tab->changedIndexDefs)
10800 {
10801 Oid oldId = lfirst_oid(oid_item);
10802 Oid relid;
10803
10804 relid = IndexGetRelation(oldId, false);
10805 ATPostAlterTypeParse(oldId, relid, InvalidOid,
10806 (char *) lfirst(def_item),
10807 wqueue, lockmode, tab->rewrite);
10808
10809 ObjectAddressSet(obj, RelationRelationId, oldId);
10810 add_exact_object_address(&obj, objects);
10811 }
10812
10813 /*
10814 * Queue up command to restore replica identity index marking
10815 */
10816 if (tab->replicaIdentityIndex)
10817 {
10818 AlterTableCmd *cmd = makeNode(AlterTableCmd);
10819 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
10820
10821 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
10822 subcmd->name = tab->replicaIdentityIndex;
10823 cmd->subtype = AT_ReplicaIdentity;
10824 cmd->def = (Node *) subcmd;
10825
10826 /* do it after indexes and constraints */
10827 tab->subcmds[AT_PASS_OLD_CONSTR] =
10828 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
10829 }
10830
10831 /*
10832 * Queue up command to restore marking of index used for cluster.
10833 */
10834 if (tab->clusterOnIndex)
10835 {
10836 AlterTableCmd *cmd = makeNode(AlterTableCmd);
10837
10838 cmd->subtype = AT_ClusterOn;
10839 cmd->name = tab->clusterOnIndex;
10840
10841 /* do it after indexes and constraints */
10842 tab->subcmds[AT_PASS_OLD_CONSTR] =
10843 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
10844 }
10845
10846 /*
10847 * It should be okay to use DROP_RESTRICT here, since nothing else should
10848 * be depending on these objects.
10849 */
10850 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
10851
10852 free_object_addresses(objects);
10853
10854 /*
10855 * The objects will get recreated during subsequent passes over the work
10856 * queue.
10857 */
10858 }
10859
10860 static void
ATPostAlterTypeParse(Oid oldId,Oid oldRelId,Oid refRelId,char * cmd,List ** wqueue,LOCKMODE lockmode,bool rewrite)10861 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
10862 List **wqueue, LOCKMODE lockmode, bool rewrite)
10863 {
10864 List *raw_parsetree_list;
10865 List *querytree_list;
10866 ListCell *list_item;
10867 Relation rel;
10868
10869 /*
10870 * We expect that we will get only ALTER TABLE and CREATE INDEX
10871 * statements. Hence, there is no need to pass them through
10872 * parse_analyze() or the rewriter, but instead we need to pass them
10873 * through parse_utilcmd.c to make them ready for execution.
10874 */
10875 raw_parsetree_list = raw_parser(cmd);
10876 querytree_list = NIL;
10877 foreach(list_item, raw_parsetree_list)
10878 {
10879 RawStmt *rs = lfirst_node(RawStmt, list_item);
10880 Node *stmt = rs->stmt;
10881
10882 if (IsA(stmt, IndexStmt))
10883 querytree_list = lappend(querytree_list,
10884 transformIndexStmt(oldRelId,
10885 (IndexStmt *) stmt,
10886 cmd));
10887 else if (IsA(stmt, AlterTableStmt))
10888 querytree_list = list_concat(querytree_list,
10889 transformAlterTableStmt(oldRelId,
10890 (AlterTableStmt *) stmt,
10891 cmd));
10892 else
10893 querytree_list = lappend(querytree_list, stmt);
10894 }
10895
10896 /* Caller should already have acquired whatever lock we need. */
10897 rel = relation_open(oldRelId, NoLock);
10898
10899 /*
10900 * Attach each generated command to the proper place in the work queue.
10901 * Note this could result in creation of entirely new work-queue entries.
10902 *
10903 * Also note that we have to tweak the command subtypes, because it turns
10904 * out that re-creation of indexes and constraints has to act a bit
10905 * differently from initial creation.
10906 */
10907 foreach(list_item, querytree_list)
10908 {
10909 Node *stm = (Node *) lfirst(list_item);
10910 AlteredTableInfo *tab;
10911
10912 tab = ATGetQueueEntry(wqueue, rel);
10913
10914 if (IsA(stm, IndexStmt))
10915 {
10916 IndexStmt *stmt = (IndexStmt *) stm;
10917 AlterTableCmd *newcmd;
10918
10919 if (!rewrite)
10920 TryReuseIndex(oldId, stmt);
10921 /* keep the index's comment */
10922 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
10923
10924 newcmd = makeNode(AlterTableCmd);
10925 newcmd->subtype = AT_ReAddIndex;
10926 newcmd->def = (Node *) stmt;
10927 tab->subcmds[AT_PASS_OLD_INDEX] =
10928 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
10929 }
10930 else if (IsA(stm, AlterTableStmt))
10931 {
10932 AlterTableStmt *stmt = (AlterTableStmt *) stm;
10933 ListCell *lcmd;
10934
10935 foreach(lcmd, stmt->cmds)
10936 {
10937 AlterTableCmd *cmd = castNode(AlterTableCmd, lfirst(lcmd));
10938
10939 if (cmd->subtype == AT_AddIndex)
10940 {
10941 IndexStmt *indstmt;
10942 Oid indoid;
10943
10944 indstmt = castNode(IndexStmt, cmd->def);
10945 indoid = get_constraint_index(oldId);
10946
10947 if (!rewrite)
10948 TryReuseIndex(indoid, indstmt);
10949 /* keep any comment on the index */
10950 indstmt->idxcomment = GetComment(indoid,
10951 RelationRelationId, 0);
10952
10953 cmd->subtype = AT_ReAddIndex;
10954 tab->subcmds[AT_PASS_OLD_INDEX] =
10955 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
10956
10957 /* recreate any comment on the constraint */
10958 RebuildConstraintComment(tab,
10959 AT_PASS_OLD_INDEX,
10960 oldId,
10961 rel,
10962 NIL,
10963 indstmt->idxname);
10964 }
10965 else if (cmd->subtype == AT_AddConstraint)
10966 {
10967 Constraint *con = castNode(Constraint, cmd->def);
10968
10969 con->old_pktable_oid = refRelId;
10970 /* rewriting neither side of a FK */
10971 if (con->contype == CONSTR_FOREIGN &&
10972 !rewrite && tab->rewrite == 0)
10973 TryReuseForeignKey(oldId, con);
10974 cmd->subtype = AT_ReAddConstraint;
10975 tab->subcmds[AT_PASS_OLD_CONSTR] =
10976 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
10977
10978 /* recreate any comment on the constraint */
10979 RebuildConstraintComment(tab,
10980 AT_PASS_OLD_CONSTR,
10981 oldId,
10982 rel,
10983 NIL,
10984 con->conname);
10985 }
10986 else
10987 elog(ERROR, "unexpected statement subtype: %d",
10988 (int) cmd->subtype);
10989 }
10990 }
10991 else if (IsA(stm, AlterDomainStmt))
10992 {
10993 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
10994
10995 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
10996 {
10997 Constraint *con = castNode(Constraint, stmt->def);
10998 AlterTableCmd *cmd = makeNode(AlterTableCmd);
10999
11000 cmd->subtype = AT_ReAddDomainConstraint;
11001 cmd->def = (Node *) stmt;
11002 tab->subcmds[AT_PASS_OLD_CONSTR] =
11003 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
11004
11005 /* recreate any comment on the constraint */
11006 RebuildConstraintComment(tab,
11007 AT_PASS_OLD_CONSTR,
11008 oldId,
11009 NULL,
11010 stmt->typeName,
11011 con->conname);
11012 }
11013 else
11014 elog(ERROR, "unexpected statement subtype: %d",
11015 (int) stmt->subtype);
11016 }
11017 else
11018 elog(ERROR, "unexpected statement type: %d",
11019 (int) nodeTag(stm));
11020 }
11021
11022 relation_close(rel, NoLock);
11023 }
11024
11025 /*
11026 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
11027 * for a table or domain constraint that is being rebuilt.
11028 *
11029 * objid is the OID of the constraint.
11030 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
11031 * as a string list) for a domain constraint.
11032 * (We could dig that info, as well as the conname, out of the pg_constraint
11033 * entry; but callers already have them so might as well pass them.)
11034 */
11035 static void
RebuildConstraintComment(AlteredTableInfo * tab,int pass,Oid objid,Relation rel,List * domname,const char * conname)11036 RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
11037 Relation rel, List *domname,
11038 const char *conname)
11039 {
11040 CommentStmt *cmd;
11041 char *comment_str;
11042 AlterTableCmd *newcmd;
11043
11044 /* Look for comment for object wanted, and leave if none */
11045 comment_str = GetComment(objid, ConstraintRelationId, 0);
11046 if (comment_str == NULL)
11047 return;
11048
11049 /* Build CommentStmt node, copying all input data for safety */
11050 cmd = makeNode(CommentStmt);
11051 if (rel)
11052 {
11053 cmd->objtype = OBJECT_TABCONSTRAINT;
11054 cmd->object = (Node *)
11055 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
11056 makeString(pstrdup(RelationGetRelationName(rel))),
11057 makeString(pstrdup(conname)));
11058 }
11059 else
11060 {
11061 cmd->objtype = OBJECT_DOMCONSTRAINT;
11062 cmd->object = (Node *)
11063 list_make2(makeTypeNameFromNameList(copyObject(domname)),
11064 makeString(pstrdup(conname)));
11065 }
11066 cmd->comment = comment_str;
11067
11068 /* Append it to list of commands */
11069 newcmd = makeNode(AlterTableCmd);
11070 newcmd->subtype = AT_ReAddComment;
11071 newcmd->def = (Node *) cmd;
11072 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
11073 }
11074
11075 /*
11076 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
11077 * for the real analysis, then mutates the IndexStmt based on that verdict.
11078 */
11079 static void
TryReuseIndex(Oid oldId,IndexStmt * stmt)11080 TryReuseIndex(Oid oldId, IndexStmt *stmt)
11081 {
11082 if (CheckIndexCompatible(oldId,
11083 stmt->accessMethod,
11084 stmt->indexParams,
11085 stmt->excludeOpNames))
11086 {
11087 Relation irel = index_open(oldId, NoLock);
11088
11089 /* If it's a partitioned index, there is no storage to share. */
11090 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
11091 stmt->oldNode = irel->rd_node.relNode;
11092 index_close(irel, NoLock);
11093 }
11094 }
11095
11096 /*
11097 * Subroutine for ATPostAlterTypeParse().
11098 *
11099 * Stash the old P-F equality operator into the Constraint node, for possible
11100 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
11101 * this constraint can be skipped.
11102 */
11103 static void
TryReuseForeignKey(Oid oldId,Constraint * con)11104 TryReuseForeignKey(Oid oldId, Constraint *con)
11105 {
11106 HeapTuple tup;
11107 Datum adatum;
11108 bool isNull;
11109 ArrayType *arr;
11110 Oid *rawarr;
11111 int numkeys;
11112 int i;
11113
11114 Assert(con->contype == CONSTR_FOREIGN);
11115 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
11116
11117 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
11118 if (!HeapTupleIsValid(tup)) /* should not happen */
11119 elog(ERROR, "cache lookup failed for constraint %u", oldId);
11120
11121 adatum = SysCacheGetAttr(CONSTROID, tup,
11122 Anum_pg_constraint_conpfeqop, &isNull);
11123 if (isNull)
11124 elog(ERROR, "null conpfeqop for constraint %u", oldId);
11125 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
11126 numkeys = ARR_DIMS(arr)[0];
11127 /* test follows the one in ri_FetchConstraintInfo() */
11128 if (ARR_NDIM(arr) != 1 ||
11129 ARR_HASNULL(arr) ||
11130 ARR_ELEMTYPE(arr) != OIDOID)
11131 elog(ERROR, "conpfeqop is not a 1-D Oid array");
11132 rawarr = (Oid *) ARR_DATA_PTR(arr);
11133
11134 /* stash a List of the operator Oids in our Constraint node */
11135 for (i = 0; i < numkeys; i++)
11136 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
11137
11138 ReleaseSysCache(tup);
11139 }
11140
11141 /*
11142 * ALTER TABLE OWNER
11143 *
11144 * recursing is true if we are recursing from a table to its indexes,
11145 * sequences, or toast table. We don't allow the ownership of those things to
11146 * be changed separately from the parent table. Also, we can skip permission
11147 * checks (this is necessary not just an optimization, else we'd fail to
11148 * handle toast tables properly).
11149 *
11150 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
11151 * free-standing composite type.
11152 */
11153 void
ATExecChangeOwner(Oid relationOid,Oid newOwnerId,bool recursing,LOCKMODE lockmode)11154 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
11155 {
11156 Relation target_rel;
11157 Relation class_rel;
11158 HeapTuple tuple;
11159 Form_pg_class tuple_class;
11160
11161 /*
11162 * Get exclusive lock till end of transaction on the target table. Use
11163 * relation_open so that we can work on indexes and sequences.
11164 */
11165 target_rel = relation_open(relationOid, lockmode);
11166
11167 /* Get its pg_class tuple, too */
11168 class_rel = heap_open(RelationRelationId, RowExclusiveLock);
11169
11170 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
11171 if (!HeapTupleIsValid(tuple))
11172 elog(ERROR, "cache lookup failed for relation %u", relationOid);
11173 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
11174
11175 /* Can we change the ownership of this tuple? */
11176 switch (tuple_class->relkind)
11177 {
11178 case RELKIND_RELATION:
11179 case RELKIND_VIEW:
11180 case RELKIND_MATVIEW:
11181 case RELKIND_FOREIGN_TABLE:
11182 case RELKIND_PARTITIONED_TABLE:
11183 /* ok to change owner */
11184 break;
11185 case RELKIND_INDEX:
11186 if (!recursing)
11187 {
11188 /*
11189 * Because ALTER INDEX OWNER used to be allowed, and in fact
11190 * is generated by old versions of pg_dump, we give a warning
11191 * and do nothing rather than erroring out. Also, to avoid
11192 * unnecessary chatter while restoring those old dumps, say
11193 * nothing at all if the command would be a no-op anyway.
11194 */
11195 if (tuple_class->relowner != newOwnerId)
11196 ereport(WARNING,
11197 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11198 errmsg("cannot change owner of index \"%s\"",
11199 NameStr(tuple_class->relname)),
11200 errhint("Change the ownership of the index's table, instead.")));
11201 /* quick hack to exit via the no-op path */
11202 newOwnerId = tuple_class->relowner;
11203 }
11204 break;
11205 case RELKIND_PARTITIONED_INDEX:
11206 if (recursing)
11207 break;
11208 ereport(ERROR,
11209 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11210 errmsg("cannot change owner of index \"%s\"",
11211 NameStr(tuple_class->relname)),
11212 errhint("Change the ownership of the index's table, instead.")));
11213 break;
11214 case RELKIND_SEQUENCE:
11215 if (!recursing &&
11216 tuple_class->relowner != newOwnerId)
11217 {
11218 /* if it's an owned sequence, disallow changing it by itself */
11219 Oid tableId;
11220 int32 colId;
11221
11222 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
11223 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
11224 ereport(ERROR,
11225 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11226 errmsg("cannot change owner of sequence \"%s\"",
11227 NameStr(tuple_class->relname)),
11228 errdetail("Sequence \"%s\" is linked to table \"%s\".",
11229 NameStr(tuple_class->relname),
11230 get_rel_name(tableId))));
11231 }
11232 break;
11233 case RELKIND_COMPOSITE_TYPE:
11234 if (recursing)
11235 break;
11236 ereport(ERROR,
11237 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11238 errmsg("\"%s\" is a composite type",
11239 NameStr(tuple_class->relname)),
11240 errhint("Use ALTER TYPE instead.")));
11241 break;
11242 case RELKIND_TOASTVALUE:
11243 if (recursing)
11244 break;
11245 /* FALL THRU */
11246 default:
11247 ereport(ERROR,
11248 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11249 errmsg("\"%s\" is not a table, view, sequence, or foreign table",
11250 NameStr(tuple_class->relname))));
11251 }
11252
11253 /*
11254 * If the new owner is the same as the existing owner, consider the
11255 * command to have succeeded. This is for dump restoration purposes.
11256 */
11257 if (tuple_class->relowner != newOwnerId)
11258 {
11259 Datum repl_val[Natts_pg_class];
11260 bool repl_null[Natts_pg_class];
11261 bool repl_repl[Natts_pg_class];
11262 Acl *newAcl;
11263 Datum aclDatum;
11264 bool isNull;
11265 HeapTuple newtuple;
11266
11267 /* skip permission checks when recursing to index or toast table */
11268 if (!recursing)
11269 {
11270 /* Superusers can always do it */
11271 if (!superuser())
11272 {
11273 Oid namespaceOid = tuple_class->relnamespace;
11274 AclResult aclresult;
11275
11276 /* Otherwise, must be owner of the existing object */
11277 if (!pg_class_ownercheck(relationOid, GetUserId()))
11278 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
11279 RelationGetRelationName(target_rel));
11280
11281 /* Must be able to become new owner */
11282 check_is_member_of_role(GetUserId(), newOwnerId);
11283
11284 /* New owner must have CREATE privilege on namespace */
11285 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
11286 ACL_CREATE);
11287 if (aclresult != ACLCHECK_OK)
11288 aclcheck_error(aclresult, OBJECT_SCHEMA,
11289 get_namespace_name(namespaceOid));
11290 }
11291 }
11292
11293 memset(repl_null, false, sizeof(repl_null));
11294 memset(repl_repl, false, sizeof(repl_repl));
11295
11296 repl_repl[Anum_pg_class_relowner - 1] = true;
11297 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
11298
11299 /*
11300 * Determine the modified ACL for the new owner. This is only
11301 * necessary when the ACL is non-null.
11302 */
11303 aclDatum = SysCacheGetAttr(RELOID, tuple,
11304 Anum_pg_class_relacl,
11305 &isNull);
11306 if (!isNull)
11307 {
11308 newAcl = aclnewowner(DatumGetAclP(aclDatum),
11309 tuple_class->relowner, newOwnerId);
11310 repl_repl[Anum_pg_class_relacl - 1] = true;
11311 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
11312 }
11313
11314 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
11315
11316 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
11317
11318 heap_freetuple(newtuple);
11319
11320 /*
11321 * We must similarly update any per-column ACLs to reflect the new
11322 * owner; for neatness reasons that's split out as a subroutine.
11323 */
11324 change_owner_fix_column_acls(relationOid,
11325 tuple_class->relowner,
11326 newOwnerId);
11327
11328 /*
11329 * Update owner dependency reference, if any. A composite type has
11330 * none, because it's tracked for the pg_type entry instead of here;
11331 * indexes and TOAST tables don't have their own entries either.
11332 */
11333 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
11334 tuple_class->relkind != RELKIND_INDEX &&
11335 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
11336 tuple_class->relkind != RELKIND_TOASTVALUE)
11337 changeDependencyOnOwner(RelationRelationId, relationOid,
11338 newOwnerId);
11339
11340 /*
11341 * Also change the ownership of the table's row type, if it has one
11342 */
11343 if (tuple_class->relkind != RELKIND_INDEX &&
11344 tuple_class->relkind != RELKIND_PARTITIONED_INDEX)
11345 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
11346
11347 /*
11348 * If we are operating on a table or materialized view, also change
11349 * the ownership of any indexes and sequences that belong to the
11350 * relation, as well as its toast table (if it has one).
11351 */
11352 if (tuple_class->relkind == RELKIND_RELATION ||
11353 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
11354 tuple_class->relkind == RELKIND_MATVIEW ||
11355 tuple_class->relkind == RELKIND_TOASTVALUE)
11356 {
11357 List *index_oid_list;
11358 ListCell *i;
11359
11360 /* Find all the indexes belonging to this relation */
11361 index_oid_list = RelationGetIndexList(target_rel);
11362
11363 /* For each index, recursively change its ownership */
11364 foreach(i, index_oid_list)
11365 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
11366
11367 list_free(index_oid_list);
11368 }
11369
11370 /* If it has a toast table, recurse to change its ownership */
11371 if (tuple_class->reltoastrelid != InvalidOid)
11372 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
11373 true, lockmode);
11374
11375 /* If it has dependent sequences, recurse to change them too */
11376 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
11377 }
11378
11379 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
11380
11381 ReleaseSysCache(tuple);
11382 heap_close(class_rel, RowExclusiveLock);
11383 relation_close(target_rel, NoLock);
11384 }
11385
11386 /*
11387 * change_owner_fix_column_acls
11388 *
11389 * Helper function for ATExecChangeOwner. Scan the columns of the table
11390 * and fix any non-null column ACLs to reflect the new owner.
11391 */
11392 static void
change_owner_fix_column_acls(Oid relationOid,Oid oldOwnerId,Oid newOwnerId)11393 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
11394 {
11395 Relation attRelation;
11396 SysScanDesc scan;
11397 ScanKeyData key[1];
11398 HeapTuple attributeTuple;
11399
11400 attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
11401 ScanKeyInit(&key[0],
11402 Anum_pg_attribute_attrelid,
11403 BTEqualStrategyNumber, F_OIDEQ,
11404 ObjectIdGetDatum(relationOid));
11405 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
11406 true, NULL, 1, key);
11407 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
11408 {
11409 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
11410 Datum repl_val[Natts_pg_attribute];
11411 bool repl_null[Natts_pg_attribute];
11412 bool repl_repl[Natts_pg_attribute];
11413 Acl *newAcl;
11414 Datum aclDatum;
11415 bool isNull;
11416 HeapTuple newtuple;
11417
11418 /* Ignore dropped columns */
11419 if (att->attisdropped)
11420 continue;
11421
11422 aclDatum = heap_getattr(attributeTuple,
11423 Anum_pg_attribute_attacl,
11424 RelationGetDescr(attRelation),
11425 &isNull);
11426 /* Null ACLs do not require changes */
11427 if (isNull)
11428 continue;
11429
11430 memset(repl_null, false, sizeof(repl_null));
11431 memset(repl_repl, false, sizeof(repl_repl));
11432
11433 newAcl = aclnewowner(DatumGetAclP(aclDatum),
11434 oldOwnerId, newOwnerId);
11435 repl_repl[Anum_pg_attribute_attacl - 1] = true;
11436 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
11437
11438 newtuple = heap_modify_tuple(attributeTuple,
11439 RelationGetDescr(attRelation),
11440 repl_val, repl_null, repl_repl);
11441
11442 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
11443
11444 heap_freetuple(newtuple);
11445 }
11446 systable_endscan(scan);
11447 heap_close(attRelation, RowExclusiveLock);
11448 }
11449
11450 /*
11451 * change_owner_recurse_to_sequences
11452 *
11453 * Helper function for ATExecChangeOwner. Examines pg_depend searching
11454 * for sequences that are dependent on serial columns, and changes their
11455 * ownership.
11456 */
11457 static void
change_owner_recurse_to_sequences(Oid relationOid,Oid newOwnerId,LOCKMODE lockmode)11458 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
11459 {
11460 Relation depRel;
11461 SysScanDesc scan;
11462 ScanKeyData key[2];
11463 HeapTuple tup;
11464
11465 /*
11466 * SERIAL sequences are those having an auto dependency on one of the
11467 * table's columns (we don't care *which* column, exactly).
11468 */
11469 depRel = heap_open(DependRelationId, AccessShareLock);
11470
11471 ScanKeyInit(&key[0],
11472 Anum_pg_depend_refclassid,
11473 BTEqualStrategyNumber, F_OIDEQ,
11474 ObjectIdGetDatum(RelationRelationId));
11475 ScanKeyInit(&key[1],
11476 Anum_pg_depend_refobjid,
11477 BTEqualStrategyNumber, F_OIDEQ,
11478 ObjectIdGetDatum(relationOid));
11479 /* we leave refobjsubid unspecified */
11480
11481 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
11482 NULL, 2, key);
11483
11484 while (HeapTupleIsValid(tup = systable_getnext(scan)))
11485 {
11486 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
11487 Relation seqRel;
11488
11489 /* skip dependencies other than auto dependencies on columns */
11490 if (depForm->refobjsubid == 0 ||
11491 depForm->classid != RelationRelationId ||
11492 depForm->objsubid != 0 ||
11493 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
11494 continue;
11495
11496 /* Use relation_open just in case it's an index */
11497 seqRel = relation_open(depForm->objid, lockmode);
11498
11499 /* skip non-sequence relations */
11500 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
11501 {
11502 /* No need to keep the lock */
11503 relation_close(seqRel, lockmode);
11504 continue;
11505 }
11506
11507 /* We don't need to close the sequence while we alter it. */
11508 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
11509
11510 /* Now we can close it. Keep the lock till end of transaction. */
11511 relation_close(seqRel, NoLock);
11512 }
11513
11514 systable_endscan(scan);
11515
11516 relation_close(depRel, AccessShareLock);
11517 }
11518
11519 /*
11520 * ALTER TABLE CLUSTER ON
11521 *
11522 * The only thing we have to do is to change the indisclustered bits.
11523 *
11524 * Return the address of the new clustering index.
11525 */
11526 static ObjectAddress
ATExecClusterOn(Relation rel,const char * indexName,LOCKMODE lockmode)11527 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
11528 {
11529 Oid indexOid;
11530 ObjectAddress address;
11531
11532 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
11533
11534 if (!OidIsValid(indexOid))
11535 ereport(ERROR,
11536 (errcode(ERRCODE_UNDEFINED_OBJECT),
11537 errmsg("index \"%s\" for table \"%s\" does not exist",
11538 indexName, RelationGetRelationName(rel))));
11539
11540 /* Check index is valid to cluster on */
11541 check_index_is_clusterable(rel, indexOid, false, lockmode);
11542
11543 /* And do the work */
11544 mark_index_clustered(rel, indexOid, false);
11545
11546 ObjectAddressSet(address,
11547 RelationRelationId, indexOid);
11548
11549 return address;
11550 }
11551
11552 /*
11553 * ALTER TABLE SET WITHOUT CLUSTER
11554 *
11555 * We have to find any indexes on the table that have indisclustered bit
11556 * set and turn it off.
11557 */
11558 static void
ATExecDropCluster(Relation rel,LOCKMODE lockmode)11559 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
11560 {
11561 mark_index_clustered(rel, InvalidOid, false);
11562 }
11563
11564 /*
11565 * ALTER TABLE SET TABLESPACE
11566 */
11567 static void
ATPrepSetTableSpace(AlteredTableInfo * tab,Relation rel,const char * tablespacename,LOCKMODE lockmode)11568 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
11569 {
11570 Oid tablespaceId;
11571
11572 /* Check that the tablespace exists */
11573 tablespaceId = get_tablespace_oid(tablespacename, false);
11574
11575 /* Check permissions except when moving to database's default */
11576 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
11577 {
11578 AclResult aclresult;
11579
11580 aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
11581 if (aclresult != ACLCHECK_OK)
11582 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
11583 }
11584
11585 /* Save info for Phase 3 to do the real work */
11586 if (OidIsValid(tab->newTableSpace))
11587 ereport(ERROR,
11588 (errcode(ERRCODE_SYNTAX_ERROR),
11589 errmsg("cannot have multiple SET TABLESPACE subcommands")));
11590
11591 tab->newTableSpace = tablespaceId;
11592 }
11593
11594 /*
11595 * Set, reset, or replace reloptions.
11596 */
11597 static void
ATExecSetRelOptions(Relation rel,List * defList,AlterTableType operation,LOCKMODE lockmode)11598 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
11599 LOCKMODE lockmode)
11600 {
11601 Oid relid;
11602 Relation pgclass;
11603 HeapTuple tuple;
11604 HeapTuple newtuple;
11605 Datum datum;
11606 bool isnull;
11607 Datum newOptions;
11608 Datum repl_val[Natts_pg_class];
11609 bool repl_null[Natts_pg_class];
11610 bool repl_repl[Natts_pg_class];
11611 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
11612
11613 if (defList == NIL && operation != AT_ReplaceRelOptions)
11614 return; /* nothing to do */
11615
11616 pgclass = heap_open(RelationRelationId, RowExclusiveLock);
11617
11618 /* Fetch heap tuple */
11619 relid = RelationGetRelid(rel);
11620 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
11621 if (!HeapTupleIsValid(tuple))
11622 elog(ERROR, "cache lookup failed for relation %u", relid);
11623
11624 if (operation == AT_ReplaceRelOptions)
11625 {
11626 /*
11627 * If we're supposed to replace the reloptions list, we just pretend
11628 * there were none before.
11629 */
11630 datum = (Datum) 0;
11631 isnull = true;
11632 }
11633 else
11634 {
11635 /* Get the old reloptions */
11636 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
11637 &isnull);
11638 }
11639
11640 /* Generate new proposed reloptions (text array) */
11641 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
11642 defList, NULL, validnsps, false,
11643 operation == AT_ResetRelOptions);
11644
11645 /* Validate */
11646 switch (rel->rd_rel->relkind)
11647 {
11648 case RELKIND_RELATION:
11649 case RELKIND_TOASTVALUE:
11650 case RELKIND_MATVIEW:
11651 case RELKIND_PARTITIONED_TABLE:
11652 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
11653 break;
11654 case RELKIND_VIEW:
11655 (void) view_reloptions(newOptions, true);
11656 break;
11657 case RELKIND_INDEX:
11658 case RELKIND_PARTITIONED_INDEX:
11659 (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
11660 break;
11661 default:
11662 ereport(ERROR,
11663 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11664 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
11665 RelationGetRelationName(rel))));
11666 break;
11667 }
11668
11669 /* Special-case validation of view options */
11670 if (rel->rd_rel->relkind == RELKIND_VIEW)
11671 {
11672 Query *view_query = get_view_query(rel);
11673 List *view_options = untransformRelOptions(newOptions);
11674 ListCell *cell;
11675 bool check_option = false;
11676
11677 foreach(cell, view_options)
11678 {
11679 DefElem *defel = (DefElem *) lfirst(cell);
11680
11681 if (strcmp(defel->defname, "check_option") == 0)
11682 check_option = true;
11683 }
11684
11685 /*
11686 * If the check option is specified, look to see if the view is
11687 * actually auto-updatable or not.
11688 */
11689 if (check_option)
11690 {
11691 const char *view_updatable_error =
11692 view_query_is_auto_updatable(view_query, true);
11693
11694 if (view_updatable_error)
11695 ereport(ERROR,
11696 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11697 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
11698 errhint("%s", _(view_updatable_error))));
11699 }
11700 }
11701
11702 /*
11703 * All we need do here is update the pg_class row; the new options will be
11704 * propagated into relcaches during post-commit cache inval.
11705 */
11706 memset(repl_val, 0, sizeof(repl_val));
11707 memset(repl_null, false, sizeof(repl_null));
11708 memset(repl_repl, false, sizeof(repl_repl));
11709
11710 if (newOptions != (Datum) 0)
11711 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
11712 else
11713 repl_null[Anum_pg_class_reloptions - 1] = true;
11714
11715 repl_repl[Anum_pg_class_reloptions - 1] = true;
11716
11717 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
11718 repl_val, repl_null, repl_repl);
11719
11720 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
11721
11722 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
11723
11724 heap_freetuple(newtuple);
11725
11726 ReleaseSysCache(tuple);
11727
11728 /* repeat the whole exercise for the toast table, if there's one */
11729 if (OidIsValid(rel->rd_rel->reltoastrelid))
11730 {
11731 Relation toastrel;
11732 Oid toastid = rel->rd_rel->reltoastrelid;
11733
11734 toastrel = heap_open(toastid, lockmode);
11735
11736 /* Fetch heap tuple */
11737 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
11738 if (!HeapTupleIsValid(tuple))
11739 elog(ERROR, "cache lookup failed for relation %u", toastid);
11740
11741 if (operation == AT_ReplaceRelOptions)
11742 {
11743 /*
11744 * If we're supposed to replace the reloptions list, we just
11745 * pretend there were none before.
11746 */
11747 datum = (Datum) 0;
11748 isnull = true;
11749 }
11750 else
11751 {
11752 /* Get the old reloptions */
11753 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
11754 &isnull);
11755 }
11756
11757 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
11758 defList, "toast", validnsps, false,
11759 operation == AT_ResetRelOptions);
11760
11761 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
11762
11763 memset(repl_val, 0, sizeof(repl_val));
11764 memset(repl_null, false, sizeof(repl_null));
11765 memset(repl_repl, false, sizeof(repl_repl));
11766
11767 if (newOptions != (Datum) 0)
11768 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
11769 else
11770 repl_null[Anum_pg_class_reloptions - 1] = true;
11771
11772 repl_repl[Anum_pg_class_reloptions - 1] = true;
11773
11774 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
11775 repl_val, repl_null, repl_repl);
11776
11777 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
11778
11779 InvokeObjectPostAlterHookArg(RelationRelationId,
11780 RelationGetRelid(toastrel), 0,
11781 InvalidOid, true);
11782
11783 heap_freetuple(newtuple);
11784
11785 ReleaseSysCache(tuple);
11786
11787 heap_close(toastrel, NoLock);
11788 }
11789
11790 heap_close(pgclass, RowExclusiveLock);
11791 }
11792
11793 /*
11794 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
11795 * rewriting to be done, so we just want to copy the data as fast as possible.
11796 */
11797 static void
ATExecSetTableSpace(Oid tableOid,Oid newTableSpace,LOCKMODE lockmode)11798 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
11799 {
11800 Relation rel;
11801 Oid oldTableSpace;
11802 Oid reltoastrelid;
11803 Oid newrelfilenode;
11804 RelFileNode newrnode;
11805 SMgrRelation dstrel;
11806 Relation pg_class;
11807 HeapTuple tuple;
11808 Form_pg_class rd_rel;
11809 ForkNumber forkNum;
11810 List *reltoastidxids = NIL;
11811 ListCell *lc;
11812
11813 /*
11814 * Need lock here in case we are recursing to toast table or index
11815 */
11816 rel = relation_open(tableOid, lockmode);
11817
11818 /*
11819 * No work if no change in tablespace.
11820 */
11821 oldTableSpace = rel->rd_rel->reltablespace;
11822 if (newTableSpace == oldTableSpace ||
11823 (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
11824 {
11825 InvokeObjectPostAlterHook(RelationRelationId,
11826 RelationGetRelid(rel), 0);
11827
11828 relation_close(rel, NoLock);
11829 return;
11830 }
11831
11832 /*
11833 * We cannot support moving mapped relations into different tablespaces.
11834 * (In particular this eliminates all shared catalogs.)
11835 */
11836 if (RelationIsMapped(rel))
11837 ereport(ERROR,
11838 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11839 errmsg("cannot move system relation \"%s\"",
11840 RelationGetRelationName(rel))));
11841
11842 /* Can't move a non-shared relation into pg_global */
11843 if (newTableSpace == GLOBALTABLESPACE_OID)
11844 ereport(ERROR,
11845 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
11846 errmsg("only shared relations can be placed in pg_global tablespace")));
11847
11848 /*
11849 * Don't allow moving temp tables of other backends ... their local buffer
11850 * manager is not going to cope.
11851 */
11852 if (RELATION_IS_OTHER_TEMP(rel))
11853 ereport(ERROR,
11854 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11855 errmsg("cannot move temporary tables of other sessions")));
11856
11857 reltoastrelid = rel->rd_rel->reltoastrelid;
11858 /* Fetch the list of indexes on toast relation if necessary */
11859 if (OidIsValid(reltoastrelid))
11860 {
11861 Relation toastRel = relation_open(reltoastrelid, lockmode);
11862
11863 reltoastidxids = RelationGetIndexList(toastRel);
11864 relation_close(toastRel, lockmode);
11865 }
11866
11867 /* Get a modifiable copy of the relation's pg_class row */
11868 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
11869
11870 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tableOid));
11871 if (!HeapTupleIsValid(tuple))
11872 elog(ERROR, "cache lookup failed for relation %u", tableOid);
11873 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
11874
11875 /*
11876 * Since we copy the file directly without looking at the shared buffers,
11877 * we'd better first flush out any pages of the source relation that are
11878 * in shared buffers. We assume no new changes will be made while we are
11879 * holding exclusive lock on the rel.
11880 */
11881 FlushRelationBuffers(rel);
11882
11883 /*
11884 * Relfilenodes are not unique in databases across tablespaces, so we need
11885 * to allocate a new one in the new tablespace.
11886 */
11887 newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
11888 rel->rd_rel->relpersistence);
11889
11890 /* Open old and new relation */
11891 newrnode = rel->rd_node;
11892 newrnode.relNode = newrelfilenode;
11893 newrnode.spcNode = newTableSpace;
11894 dstrel = smgropen(newrnode, rel->rd_backend);
11895
11896 RelationOpenSmgr(rel);
11897
11898 /*
11899 * Create and copy all forks of the relation, and schedule unlinking of
11900 * old physical files.
11901 *
11902 * NOTE: any conflict in relfilenode value will be caught in
11903 * RelationCreateStorage().
11904 */
11905 RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
11906
11907 /* copy main fork */
11908 copy_relation_data(rel->rd_smgr, dstrel, MAIN_FORKNUM,
11909 rel->rd_rel->relpersistence);
11910
11911 /* copy those extra forks that exist */
11912 for (forkNum = MAIN_FORKNUM + 1; forkNum <= MAX_FORKNUM; forkNum++)
11913 {
11914 if (smgrexists(rel->rd_smgr, forkNum))
11915 {
11916 smgrcreate(dstrel, forkNum, false);
11917
11918 /*
11919 * WAL log creation if the relation is persistent, or this is the
11920 * init fork of an unlogged relation.
11921 */
11922 if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
11923 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
11924 forkNum == INIT_FORKNUM))
11925 log_smgrcreate(&newrnode, forkNum);
11926 copy_relation_data(rel->rd_smgr, dstrel, forkNum,
11927 rel->rd_rel->relpersistence);
11928 }
11929 }
11930
11931 /* drop old relation, and close new one */
11932 RelationDropStorage(rel);
11933 smgrclose(dstrel);
11934
11935 /* update the pg_class row */
11936 rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
11937 rd_rel->relfilenode = newrelfilenode;
11938 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
11939
11940 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
11941
11942 heap_freetuple(tuple);
11943
11944 heap_close(pg_class, RowExclusiveLock);
11945
11946 relation_close(rel, NoLock);
11947
11948 /* Make sure the reltablespace change is visible */
11949 CommandCounterIncrement();
11950
11951 /* Move associated toast relation and/or indexes, too */
11952 if (OidIsValid(reltoastrelid))
11953 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
11954 foreach(lc, reltoastidxids)
11955 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
11956
11957 /* Clean up */
11958 list_free(reltoastidxids);
11959 }
11960
11961 /*
11962 * Special handling of ALTER TABLE SET TABLESPACE for partitioned indexes,
11963 * which have no storage (so not handled in Phase 3 like other relation types)
11964 */
11965 static void
ATExecPartedIdxSetTableSpace(Relation rel,Oid newTableSpace)11966 ATExecPartedIdxSetTableSpace(Relation rel, Oid newTableSpace)
11967 {
11968 HeapTuple tuple;
11969 Oid oldTableSpace;
11970 Relation pg_class;
11971 Form_pg_class rd_rel;
11972 Oid indexOid = RelationGetRelid(rel);
11973
11974 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
11975
11976 /* Can't allow a non-shared relation in pg_global */
11977 if (newTableSpace == GLOBALTABLESPACE_OID)
11978 ereport(ERROR,
11979 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
11980 errmsg("only shared relations can be placed in pg_global tablespace")));
11981
11982 /*
11983 * No work if no change in tablespace.
11984 */
11985 oldTableSpace = rel->rd_rel->reltablespace;
11986 if (newTableSpace == oldTableSpace ||
11987 (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
11988 {
11989 InvokeObjectPostAlterHook(RelationRelationId,
11990 indexOid, 0);
11991 return;
11992 }
11993
11994 /* Get a modifiable copy of the relation's pg_class row */
11995 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
11996
11997 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(indexOid));
11998 if (!HeapTupleIsValid(tuple))
11999 elog(ERROR, "cache lookup failed for relation %u", indexOid);
12000 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
12001
12002 /* update the pg_class row */
12003 rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
12004 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
12005
12006 /* Record dependency on tablespace */
12007 changeDependencyOnTablespace(RelationRelationId,
12008 indexOid, rd_rel->reltablespace);
12009
12010 InvokeObjectPostAlterHook(RelationRelationId, indexOid, 0);
12011
12012 heap_freetuple(tuple);
12013
12014 heap_close(pg_class, RowExclusiveLock);
12015
12016 /* Make sure the reltablespace change is visible */
12017 CommandCounterIncrement();
12018 }
12019
12020 /*
12021 * Alter Table ALL ... SET TABLESPACE
12022 *
12023 * Allows a user to move all objects of some type in a given tablespace in the
12024 * current database to another tablespace. Objects can be chosen based on the
12025 * owner of the object also, to allow users to move only their objects.
12026 * The user must have CREATE rights on the new tablespace, as usual. The main
12027 * permissions handling is done by the lower-level table move function.
12028 *
12029 * All to-be-moved objects are locked first. If NOWAIT is specified and the
12030 * lock can't be acquired then we ereport(ERROR).
12031 */
12032 Oid
AlterTableMoveAll(AlterTableMoveAllStmt * stmt)12033 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
12034 {
12035 List *relations = NIL;
12036 ListCell *l;
12037 ScanKeyData key[1];
12038 Relation rel;
12039 HeapScanDesc scan;
12040 HeapTuple tuple;
12041 Oid orig_tablespaceoid;
12042 Oid new_tablespaceoid;
12043 List *role_oids = roleSpecsToIds(stmt->roles);
12044
12045 /* Ensure we were not asked to move something we can't */
12046 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
12047 stmt->objtype != OBJECT_MATVIEW)
12048 ereport(ERROR,
12049 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
12050 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
12051
12052 /* Get the orig and new tablespace OIDs */
12053 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
12054 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
12055
12056 /* Can't move shared relations in to or out of pg_global */
12057 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
12058 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
12059 new_tablespaceoid == GLOBALTABLESPACE_OID)
12060 ereport(ERROR,
12061 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
12062 errmsg("cannot move relations in to or out of pg_global tablespace")));
12063
12064 /*
12065 * Must have CREATE rights on the new tablespace, unless it is the
12066 * database default tablespace (which all users implicitly have CREATE
12067 * rights on).
12068 */
12069 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
12070 {
12071 AclResult aclresult;
12072
12073 aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(),
12074 ACL_CREATE);
12075 if (aclresult != ACLCHECK_OK)
12076 aclcheck_error(aclresult, OBJECT_TABLESPACE,
12077 get_tablespace_name(new_tablespaceoid));
12078 }
12079
12080 /*
12081 * Now that the checks are done, check if we should set either to
12082 * InvalidOid because it is our database's default tablespace.
12083 */
12084 if (orig_tablespaceoid == MyDatabaseTableSpace)
12085 orig_tablespaceoid = InvalidOid;
12086
12087 if (new_tablespaceoid == MyDatabaseTableSpace)
12088 new_tablespaceoid = InvalidOid;
12089
12090 /* no-op */
12091 if (orig_tablespaceoid == new_tablespaceoid)
12092 return new_tablespaceoid;
12093
12094 /*
12095 * Walk the list of objects in the tablespace and move them. This will
12096 * only find objects in our database, of course.
12097 */
12098 ScanKeyInit(&key[0],
12099 Anum_pg_class_reltablespace,
12100 BTEqualStrategyNumber, F_OIDEQ,
12101 ObjectIdGetDatum(orig_tablespaceoid));
12102
12103 rel = heap_open(RelationRelationId, AccessShareLock);
12104 scan = heap_beginscan_catalog(rel, 1, key);
12105 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
12106 {
12107 Oid relOid = HeapTupleGetOid(tuple);
12108 Form_pg_class relForm;
12109
12110 relForm = (Form_pg_class) GETSTRUCT(tuple);
12111
12112 /*
12113 * Do not move objects in pg_catalog as part of this, if an admin
12114 * really wishes to do so, they can issue the individual ALTER
12115 * commands directly.
12116 *
12117 * Also, explicitly avoid any shared tables, temp tables, or TOAST
12118 * (TOAST will be moved with the main table).
12119 */
12120 if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared ||
12121 isAnyTempNamespace(relForm->relnamespace) ||
12122 relForm->relnamespace == PG_TOAST_NAMESPACE)
12123 continue;
12124
12125 /* Only move the object type requested */
12126 if ((stmt->objtype == OBJECT_TABLE &&
12127 relForm->relkind != RELKIND_RELATION &&
12128 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
12129 (stmt->objtype == OBJECT_INDEX &&
12130 relForm->relkind != RELKIND_INDEX &&
12131 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
12132 (stmt->objtype == OBJECT_MATVIEW &&
12133 relForm->relkind != RELKIND_MATVIEW))
12134 continue;
12135
12136 /* Check if we are only moving objects owned by certain roles */
12137 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
12138 continue;
12139
12140 /*
12141 * Handle permissions-checking here since we are locking the tables
12142 * and also to avoid doing a bunch of work only to fail part-way. Note
12143 * that permissions will also be checked by AlterTableInternal().
12144 *
12145 * Caller must be considered an owner on the table to move it.
12146 */
12147 if (!pg_class_ownercheck(relOid, GetUserId()))
12148 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
12149 NameStr(relForm->relname));
12150
12151 if (stmt->nowait &&
12152 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
12153 ereport(ERROR,
12154 (errcode(ERRCODE_OBJECT_IN_USE),
12155 errmsg("aborting because lock on relation \"%s.%s\" is not available",
12156 get_namespace_name(relForm->relnamespace),
12157 NameStr(relForm->relname))));
12158 else
12159 LockRelationOid(relOid, AccessExclusiveLock);
12160
12161 /* Add to our list of objects to move */
12162 relations = lappend_oid(relations, relOid);
12163 }
12164
12165 heap_endscan(scan);
12166 heap_close(rel, AccessShareLock);
12167
12168 if (relations == NIL)
12169 ereport(NOTICE,
12170 (errcode(ERRCODE_NO_DATA_FOUND),
12171 errmsg("no matching relations in tablespace \"%s\" found",
12172 orig_tablespaceoid == InvalidOid ? "(database default)" :
12173 get_tablespace_name(orig_tablespaceoid))));
12174
12175 /* Everything is locked, loop through and move all of the relations. */
12176 foreach(l, relations)
12177 {
12178 List *cmds = NIL;
12179 AlterTableCmd *cmd = makeNode(AlterTableCmd);
12180
12181 cmd->subtype = AT_SetTableSpace;
12182 cmd->name = stmt->new_tablespacename;
12183
12184 cmds = lappend(cmds, cmd);
12185
12186 EventTriggerAlterTableStart((Node *) stmt);
12187 /* OID is set by AlterTableInternal */
12188 AlterTableInternal(lfirst_oid(l), cmds, false);
12189 EventTriggerAlterTableEnd();
12190 }
12191
12192 return new_tablespaceoid;
12193 }
12194
12195 /*
12196 * Copy data, block by block
12197 */
12198 static void
copy_relation_data(SMgrRelation src,SMgrRelation dst,ForkNumber forkNum,char relpersistence)12199 copy_relation_data(SMgrRelation src, SMgrRelation dst,
12200 ForkNumber forkNum, char relpersistence)
12201 {
12202 PGAlignedBlock buf;
12203 Page page;
12204 bool use_wal;
12205 bool copying_initfork;
12206 BlockNumber nblocks;
12207 BlockNumber blkno;
12208
12209 page = (Page) buf.data;
12210
12211 /*
12212 * The init fork for an unlogged relation in many respects has to be
12213 * treated the same as normal relation, changes need to be WAL logged and
12214 * it needs to be synced to disk.
12215 */
12216 copying_initfork = relpersistence == RELPERSISTENCE_UNLOGGED &&
12217 forkNum == INIT_FORKNUM;
12218
12219 /*
12220 * We need to log the copied data in WAL iff WAL archiving/streaming is
12221 * enabled AND it's a permanent relation.
12222 */
12223 use_wal = XLogIsNeeded() &&
12224 (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork);
12225
12226 nblocks = smgrnblocks(src, forkNum);
12227
12228 for (blkno = 0; blkno < nblocks; blkno++)
12229 {
12230 /* If we got a cancel signal during the copy of the data, quit */
12231 CHECK_FOR_INTERRUPTS();
12232
12233 smgrread(src, forkNum, blkno, buf.data);
12234
12235 if (!PageIsVerified(page, blkno))
12236 ereport(ERROR,
12237 (errcode(ERRCODE_DATA_CORRUPTED),
12238 errmsg("invalid page in block %u of relation %s",
12239 blkno,
12240 relpathbackend(src->smgr_rnode.node,
12241 src->smgr_rnode.backend,
12242 forkNum))));
12243
12244 /*
12245 * WAL-log the copied page. Unfortunately we don't know what kind of a
12246 * page this is, so we have to log the full page including any unused
12247 * space.
12248 */
12249 if (use_wal)
12250 log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
12251
12252 PageSetChecksumInplace(page, blkno);
12253
12254 /*
12255 * Now write the page. We say isTemp = true even if it's not a temp
12256 * rel, because there's no need for smgr to schedule an fsync for this
12257 * write; we'll do it ourselves below.
12258 */
12259 smgrextend(dst, forkNum, blkno, buf.data, true);
12260 }
12261
12262 /*
12263 * If the rel is WAL-logged, must fsync before commit. We use heap_sync
12264 * to ensure that the toast table gets fsync'd too. (For a temp or
12265 * unlogged rel we don't care since the data will be gone after a crash
12266 * anyway.)
12267 *
12268 * It's obvious that we must do this when not WAL-logging the copy. It's
12269 * less obvious that we have to do it even if we did WAL-log the copied
12270 * pages. The reason is that since we're copying outside shared buffers, a
12271 * CHECKPOINT occurring during the copy has no way to flush the previously
12272 * written data to disk (indeed it won't know the new rel even exists). A
12273 * crash later on would replay WAL from the checkpoint, therefore it
12274 * wouldn't replay our earlier WAL entries. If we do not fsync those pages
12275 * here, they might still not be on disk when the crash occurs.
12276 */
12277 if (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork)
12278 smgrimmedsync(dst, forkNum);
12279 }
12280
12281 /*
12282 * ALTER TABLE ENABLE/DISABLE TRIGGER
12283 *
12284 * We just pass this off to trigger.c.
12285 */
12286 static void
ATExecEnableDisableTrigger(Relation rel,const char * trigname,char fires_when,bool skip_system,LOCKMODE lockmode)12287 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
12288 char fires_when, bool skip_system, LOCKMODE lockmode)
12289 {
12290 EnableDisableTrigger(rel, trigname, fires_when, skip_system, lockmode);
12291 }
12292
12293 /*
12294 * ALTER TABLE ENABLE/DISABLE RULE
12295 *
12296 * We just pass this off to rewriteDefine.c.
12297 */
12298 static void
ATExecEnableDisableRule(Relation rel,const char * rulename,char fires_when,LOCKMODE lockmode)12299 ATExecEnableDisableRule(Relation rel, const char *rulename,
12300 char fires_when, LOCKMODE lockmode)
12301 {
12302 EnableDisableRule(rel, rulename, fires_when);
12303 }
12304
12305 /*
12306 * ALTER TABLE INHERIT
12307 *
12308 * Add a parent to the child's parents. This verifies that all the columns and
12309 * check constraints of the parent appear in the child and that they have the
12310 * same data types and expressions.
12311 */
12312 static void
ATPrepAddInherit(Relation child_rel)12313 ATPrepAddInherit(Relation child_rel)
12314 {
12315 if (child_rel->rd_rel->reloftype)
12316 ereport(ERROR,
12317 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12318 errmsg("cannot change inheritance of typed table")));
12319
12320 if (child_rel->rd_rel->relispartition)
12321 ereport(ERROR,
12322 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12323 errmsg("cannot change inheritance of a partition")));
12324
12325 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12326 ereport(ERROR,
12327 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12328 errmsg("cannot change inheritance of partitioned table")));
12329 }
12330
12331 /*
12332 * Return the address of the new parent relation.
12333 */
12334 static ObjectAddress
ATExecAddInherit(Relation child_rel,RangeVar * parent,LOCKMODE lockmode)12335 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
12336 {
12337 Relation parent_rel;
12338 List *children;
12339 ObjectAddress address;
12340 const char *trigger_name;
12341
12342 /*
12343 * A self-exclusive lock is needed here. See the similar case in
12344 * MergeAttributes() for a full explanation.
12345 */
12346 parent_rel = heap_openrv(parent, ShareUpdateExclusiveLock);
12347
12348 /*
12349 * Must be owner of both parent and child -- child was checked by
12350 * ATSimplePermissions call in ATPrepCmd
12351 */
12352 ATSimplePermissions(parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12353
12354 /* Permanent rels cannot inherit from temporary ones */
12355 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
12356 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
12357 ereport(ERROR,
12358 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12359 errmsg("cannot inherit from temporary relation \"%s\"",
12360 RelationGetRelationName(parent_rel))));
12361
12362 /* If parent rel is temp, it must belong to this session */
12363 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
12364 !parent_rel->rd_islocaltemp)
12365 ereport(ERROR,
12366 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12367 errmsg("cannot inherit from temporary relation of another session")));
12368
12369 /* Ditto for the child */
12370 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
12371 !child_rel->rd_islocaltemp)
12372 ereport(ERROR,
12373 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12374 errmsg("cannot inherit to temporary relation of another session")));
12375
12376 /* Prevent partitioned tables from becoming inheritance parents */
12377 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12378 ereport(ERROR,
12379 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12380 errmsg("cannot inherit from partitioned table \"%s\"",
12381 parent->relname)));
12382
12383 /* Likewise for partitions */
12384 if (parent_rel->rd_rel->relispartition)
12385 ereport(ERROR,
12386 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12387 errmsg("cannot inherit from a partition")));
12388
12389 /*
12390 * Prevent circularity by seeing if proposed parent inherits from child.
12391 * (In particular, this disallows making a rel inherit from itself.)
12392 *
12393 * This is not completely bulletproof because of race conditions: in
12394 * multi-level inheritance trees, someone else could concurrently be
12395 * making another inheritance link that closes the loop but does not join
12396 * either of the rels we have locked. Preventing that seems to require
12397 * exclusive locks on the entire inheritance tree, which is a cure worse
12398 * than the disease. find_all_inheritors() will cope with circularity
12399 * anyway, so don't sweat it too much.
12400 *
12401 * We use weakest lock we can on child's children, namely AccessShareLock.
12402 */
12403 children = find_all_inheritors(RelationGetRelid(child_rel),
12404 AccessShareLock, NULL);
12405
12406 if (list_member_oid(children, RelationGetRelid(parent_rel)))
12407 ereport(ERROR,
12408 (errcode(ERRCODE_DUPLICATE_TABLE),
12409 errmsg("circular inheritance not allowed"),
12410 errdetail("\"%s\" is already a child of \"%s\".",
12411 parent->relname,
12412 RelationGetRelationName(child_rel))));
12413
12414 /* If parent has OIDs then child must have OIDs */
12415 if (parent_rel->rd_rel->relhasoids && !child_rel->rd_rel->relhasoids)
12416 ereport(ERROR,
12417 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12418 errmsg("table \"%s\" without OIDs cannot inherit from table \"%s\" with OIDs",
12419 RelationGetRelationName(child_rel),
12420 RelationGetRelationName(parent_rel))));
12421
12422 /*
12423 * If child_rel has row-level triggers with transition tables, we
12424 * currently don't allow it to become an inheritance child. See also
12425 * prohibitions in ATExecAttachPartition() and CreateTrigger().
12426 */
12427 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
12428 if (trigger_name != NULL)
12429 ereport(ERROR,
12430 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12431 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
12432 trigger_name, RelationGetRelationName(child_rel)),
12433 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies")));
12434
12435 /* OK to create inheritance */
12436 CreateInheritance(child_rel, parent_rel);
12437
12438 ObjectAddressSet(address, RelationRelationId,
12439 RelationGetRelid(parent_rel));
12440
12441 /* keep our lock on the parent relation until commit */
12442 heap_close(parent_rel, NoLock);
12443
12444 return address;
12445 }
12446
12447 /*
12448 * CreateInheritance
12449 * Catalog manipulation portion of creating inheritance between a child
12450 * table and a parent table.
12451 *
12452 * Common to ATExecAddInherit() and ATExecAttachPartition().
12453 */
12454 static void
CreateInheritance(Relation child_rel,Relation parent_rel)12455 CreateInheritance(Relation child_rel, Relation parent_rel)
12456 {
12457 Relation catalogRelation;
12458 SysScanDesc scan;
12459 ScanKeyData key;
12460 HeapTuple inheritsTuple;
12461 int32 inhseqno;
12462
12463 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
12464 catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
12465
12466 /*
12467 * Check for duplicates in the list of parents, and determine the highest
12468 * inhseqno already present; we'll use the next one for the new parent.
12469 * Also, if proposed child is a partition, it cannot already be
12470 * inheriting.
12471 *
12472 * Note: we do not reject the case where the child already inherits from
12473 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
12474 */
12475 ScanKeyInit(&key,
12476 Anum_pg_inherits_inhrelid,
12477 BTEqualStrategyNumber, F_OIDEQ,
12478 ObjectIdGetDatum(RelationGetRelid(child_rel)));
12479 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
12480 true, NULL, 1, &key);
12481
12482 /* inhseqno sequences start at 1 */
12483 inhseqno = 0;
12484 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
12485 {
12486 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
12487
12488 if (inh->inhparent == RelationGetRelid(parent_rel))
12489 ereport(ERROR,
12490 (errcode(ERRCODE_DUPLICATE_TABLE),
12491 errmsg("relation \"%s\" would be inherited from more than once",
12492 RelationGetRelationName(parent_rel))));
12493
12494 if (inh->inhseqno > inhseqno)
12495 inhseqno = inh->inhseqno;
12496 }
12497 systable_endscan(scan);
12498
12499 /* Match up the columns and bump attinhcount as needed */
12500 MergeAttributesIntoExisting(child_rel, parent_rel);
12501
12502 /* Match up the constraints and bump coninhcount as needed */
12503 MergeConstraintsIntoExisting(child_rel, parent_rel);
12504
12505 /*
12506 * OK, it looks valid. Make the catalog entries that show inheritance.
12507 */
12508 StoreCatalogInheritance1(RelationGetRelid(child_rel),
12509 RelationGetRelid(parent_rel),
12510 inhseqno + 1,
12511 catalogRelation,
12512 parent_rel->rd_rel->relkind ==
12513 RELKIND_PARTITIONED_TABLE);
12514
12515 /* Now we're done with pg_inherits */
12516 heap_close(catalogRelation, RowExclusiveLock);
12517 }
12518
12519 /*
12520 * Obtain the source-text form of the constraint expression for a check
12521 * constraint, given its pg_constraint tuple
12522 */
12523 static char *
decompile_conbin(HeapTuple contup,TupleDesc tupdesc)12524 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
12525 {
12526 Form_pg_constraint con;
12527 bool isnull;
12528 Datum attr;
12529 Datum expr;
12530
12531 con = (Form_pg_constraint) GETSTRUCT(contup);
12532 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
12533 if (isnull)
12534 elog(ERROR, "null conbin for constraint %u", HeapTupleGetOid(contup));
12535
12536 expr = DirectFunctionCall2(pg_get_expr, attr,
12537 ObjectIdGetDatum(con->conrelid));
12538 return TextDatumGetCString(expr);
12539 }
12540
12541 /*
12542 * Determine whether two check constraints are functionally equivalent
12543 *
12544 * The test we apply is to see whether they reverse-compile to the same
12545 * source string. This insulates us from issues like whether attributes
12546 * have the same physical column numbers in parent and child relations.
12547 */
12548 static bool
constraints_equivalent(HeapTuple a,HeapTuple b,TupleDesc tupleDesc)12549 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
12550 {
12551 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
12552 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
12553
12554 if (acon->condeferrable != bcon->condeferrable ||
12555 acon->condeferred != bcon->condeferred ||
12556 strcmp(decompile_conbin(a, tupleDesc),
12557 decompile_conbin(b, tupleDesc)) != 0)
12558 return false;
12559 else
12560 return true;
12561 }
12562
12563 /*
12564 * Check columns in child table match up with columns in parent, and increment
12565 * their attinhcount.
12566 *
12567 * Called by CreateInheritance
12568 *
12569 * Currently all parent columns must be found in child. Missing columns are an
12570 * error. One day we might consider creating new columns like CREATE TABLE
12571 * does. However, that is widely unpopular --- in the common use case of
12572 * partitioned tables it's a foot-gun.
12573 *
12574 * The data type must match exactly. If the parent column is NOT NULL then
12575 * the child must be as well. Defaults are not compared, however.
12576 */
12577 static void
MergeAttributesIntoExisting(Relation child_rel,Relation parent_rel)12578 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
12579 {
12580 Relation attrrel;
12581 AttrNumber parent_attno;
12582 int parent_natts;
12583 TupleDesc tupleDesc;
12584 HeapTuple tuple;
12585 bool child_is_partition = false;
12586
12587 attrrel = heap_open(AttributeRelationId, RowExclusiveLock);
12588
12589 tupleDesc = RelationGetDescr(parent_rel);
12590 parent_natts = tupleDesc->natts;
12591
12592 /* If parent_rel is a partitioned table, child_rel must be a partition */
12593 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12594 child_is_partition = true;
12595
12596 for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
12597 {
12598 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
12599 parent_attno - 1);
12600 char *attributeName = NameStr(attribute->attname);
12601
12602 /* Ignore dropped columns in the parent. */
12603 if (attribute->attisdropped)
12604 continue;
12605
12606 /* Find same column in child (matching on column name). */
12607 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
12608 attributeName);
12609 if (HeapTupleIsValid(tuple))
12610 {
12611 /* Check they are same type, typmod, and collation */
12612 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
12613
12614 if (attribute->atttypid != childatt->atttypid ||
12615 attribute->atttypmod != childatt->atttypmod)
12616 ereport(ERROR,
12617 (errcode(ERRCODE_DATATYPE_MISMATCH),
12618 errmsg("child table \"%s\" has different type for column \"%s\"",
12619 RelationGetRelationName(child_rel),
12620 attributeName)));
12621
12622 if (attribute->attcollation != childatt->attcollation)
12623 ereport(ERROR,
12624 (errcode(ERRCODE_COLLATION_MISMATCH),
12625 errmsg("child table \"%s\" has different collation for column \"%s\"",
12626 RelationGetRelationName(child_rel),
12627 attributeName)));
12628
12629 /*
12630 * Check child doesn't discard NOT NULL property. (Other
12631 * constraints are checked elsewhere.)
12632 */
12633 if (attribute->attnotnull && !childatt->attnotnull)
12634 ereport(ERROR,
12635 (errcode(ERRCODE_DATATYPE_MISMATCH),
12636 errmsg("column \"%s\" in child table must be marked NOT NULL",
12637 attributeName)));
12638
12639 /*
12640 * OK, bump the child column's inheritance count. (If we fail
12641 * later on, this change will just roll back.)
12642 */
12643 childatt->attinhcount++;
12644
12645 /*
12646 * In case of partitions, we must enforce that value of attislocal
12647 * is same in all partitions. (Note: there are only inherited
12648 * attributes in partitions)
12649 */
12650 if (child_is_partition)
12651 {
12652 Assert(childatt->attinhcount == 1);
12653 childatt->attislocal = false;
12654 }
12655
12656 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
12657 heap_freetuple(tuple);
12658 }
12659 else
12660 {
12661 ereport(ERROR,
12662 (errcode(ERRCODE_DATATYPE_MISMATCH),
12663 errmsg("child table is missing column \"%s\"",
12664 attributeName)));
12665 }
12666 }
12667
12668 /*
12669 * If the parent has an OID column, so must the child, and we'd better
12670 * update the child's attinhcount and attislocal the same as for normal
12671 * columns. We needn't check data type or not-nullness though.
12672 */
12673 if (tupleDesc->tdhasoid)
12674 {
12675 /*
12676 * Here we match by column number not name; the match *must* be the
12677 * system column, not some random column named "oid".
12678 */
12679 tuple = SearchSysCacheCopy2(ATTNUM,
12680 ObjectIdGetDatum(RelationGetRelid(child_rel)),
12681 Int16GetDatum(ObjectIdAttributeNumber));
12682 if (HeapTupleIsValid(tuple))
12683 {
12684 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
12685
12686 /* See comments above; these changes should be the same */
12687 childatt->attinhcount++;
12688
12689 if (child_is_partition)
12690 {
12691 Assert(childatt->attinhcount == 1);
12692 childatt->attislocal = false;
12693 }
12694
12695 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
12696 heap_freetuple(tuple);
12697 }
12698 else
12699 {
12700 ereport(ERROR,
12701 (errcode(ERRCODE_DATATYPE_MISMATCH),
12702 errmsg("child table is missing column \"%s\"",
12703 "oid")));
12704 }
12705 }
12706
12707 heap_close(attrrel, RowExclusiveLock);
12708 }
12709
12710 /*
12711 * Check constraints in child table match up with constraints in parent,
12712 * and increment their coninhcount.
12713 *
12714 * Constraints that are marked ONLY in the parent are ignored.
12715 *
12716 * Called by CreateInheritance
12717 *
12718 * Currently all constraints in parent must be present in the child. One day we
12719 * may consider adding new constraints like CREATE TABLE does.
12720 *
12721 * XXX This is O(N^2) which may be an issue with tables with hundreds of
12722 * constraints. As long as tables have more like 10 constraints it shouldn't be
12723 * a problem though. Even 100 constraints ought not be the end of the world.
12724 *
12725 * XXX See MergeWithExistingConstraint too if you change this code.
12726 */
12727 static void
MergeConstraintsIntoExisting(Relation child_rel,Relation parent_rel)12728 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
12729 {
12730 Relation catalog_relation;
12731 TupleDesc tuple_desc;
12732 SysScanDesc parent_scan;
12733 ScanKeyData parent_key;
12734 HeapTuple parent_tuple;
12735 bool child_is_partition = false;
12736
12737 catalog_relation = heap_open(ConstraintRelationId, RowExclusiveLock);
12738 tuple_desc = RelationGetDescr(catalog_relation);
12739
12740 /* If parent_rel is a partitioned table, child_rel must be a partition */
12741 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12742 child_is_partition = true;
12743
12744 /* Outer loop scans through the parent's constraint definitions */
12745 ScanKeyInit(&parent_key,
12746 Anum_pg_constraint_conrelid,
12747 BTEqualStrategyNumber, F_OIDEQ,
12748 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
12749 parent_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
12750 true, NULL, 1, &parent_key);
12751
12752 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
12753 {
12754 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
12755 SysScanDesc child_scan;
12756 ScanKeyData child_key;
12757 HeapTuple child_tuple;
12758 bool found = false;
12759
12760 if (parent_con->contype != CONSTRAINT_CHECK)
12761 continue;
12762
12763 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
12764 if (parent_con->connoinherit)
12765 continue;
12766
12767 /* Search for a child constraint matching this one */
12768 ScanKeyInit(&child_key,
12769 Anum_pg_constraint_conrelid,
12770 BTEqualStrategyNumber, F_OIDEQ,
12771 ObjectIdGetDatum(RelationGetRelid(child_rel)));
12772 child_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
12773 true, NULL, 1, &child_key);
12774
12775 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
12776 {
12777 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
12778 HeapTuple child_copy;
12779
12780 if (child_con->contype != CONSTRAINT_CHECK)
12781 continue;
12782
12783 if (strcmp(NameStr(parent_con->conname),
12784 NameStr(child_con->conname)) != 0)
12785 continue;
12786
12787 if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
12788 ereport(ERROR,
12789 (errcode(ERRCODE_DATATYPE_MISMATCH),
12790 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
12791 RelationGetRelationName(child_rel),
12792 NameStr(parent_con->conname))));
12793
12794 /* If the child constraint is "no inherit" then cannot merge */
12795 if (child_con->connoinherit)
12796 ereport(ERROR,
12797 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
12798 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
12799 NameStr(child_con->conname),
12800 RelationGetRelationName(child_rel))));
12801
12802 /*
12803 * If the child constraint is "not valid" then cannot merge with a
12804 * valid parent constraint
12805 */
12806 if (parent_con->convalidated && !child_con->convalidated)
12807 ereport(ERROR,
12808 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
12809 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
12810 NameStr(child_con->conname),
12811 RelationGetRelationName(child_rel))));
12812
12813 /*
12814 * OK, bump the child constraint's inheritance count. (If we fail
12815 * later on, this change will just roll back.)
12816 */
12817 child_copy = heap_copytuple(child_tuple);
12818 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
12819 child_con->coninhcount++;
12820
12821 /*
12822 * In case of partitions, an inherited constraint must be
12823 * inherited only once since it cannot have multiple parents and
12824 * it is never considered local.
12825 */
12826 if (child_is_partition)
12827 {
12828 Assert(child_con->coninhcount == 1);
12829 child_con->conislocal = false;
12830 }
12831
12832 CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
12833 heap_freetuple(child_copy);
12834
12835 found = true;
12836 break;
12837 }
12838
12839 systable_endscan(child_scan);
12840
12841 if (!found)
12842 ereport(ERROR,
12843 (errcode(ERRCODE_DATATYPE_MISMATCH),
12844 errmsg("child table is missing constraint \"%s\"",
12845 NameStr(parent_con->conname))));
12846 }
12847
12848 systable_endscan(parent_scan);
12849 heap_close(catalog_relation, RowExclusiveLock);
12850 }
12851
12852 /*
12853 * ALTER TABLE NO INHERIT
12854 *
12855 * Return value is the address of the relation that is no longer parent.
12856 */
12857 static ObjectAddress
ATExecDropInherit(Relation rel,RangeVar * parent,LOCKMODE lockmode)12858 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
12859 {
12860 ObjectAddress address;
12861 Relation parent_rel;
12862
12863 if (rel->rd_rel->relispartition)
12864 ereport(ERROR,
12865 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12866 errmsg("cannot change inheritance of a partition")));
12867
12868 /*
12869 * AccessShareLock on the parent is probably enough, seeing that DROP
12870 * TABLE doesn't lock parent tables at all. We need some lock since we'll
12871 * be inspecting the parent's schema.
12872 */
12873 parent_rel = heap_openrv(parent, AccessShareLock);
12874
12875 /*
12876 * We don't bother to check ownership of the parent table --- ownership of
12877 * the child is presumed enough rights.
12878 */
12879
12880 /* Off to RemoveInheritance() where most of the work happens */
12881 RemoveInheritance(rel, parent_rel);
12882
12883 ObjectAddressSet(address, RelationRelationId,
12884 RelationGetRelid(parent_rel));
12885
12886 /* keep our lock on the parent relation until commit */
12887 heap_close(parent_rel, NoLock);
12888
12889 return address;
12890 }
12891
12892 /*
12893 * RemoveInheritance
12894 *
12895 * Drop a parent from the child's parents. This just adjusts the attinhcount
12896 * and attislocal of the columns and removes the pg_inherit and pg_depend
12897 * entries.
12898 *
12899 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
12900 * up attislocal stays true, which means if a child is ever removed from a
12901 * parent then its columns will never be automatically dropped which may
12902 * surprise. But at least we'll never surprise by dropping columns someone
12903 * isn't expecting to be dropped which would actually mean data loss.
12904 *
12905 * coninhcount and conislocal for inherited constraints are adjusted in
12906 * exactly the same way.
12907 *
12908 * Common to ATExecDropInherit() and ATExecDetachPartition().
12909 */
12910 static void
RemoveInheritance(Relation child_rel,Relation parent_rel)12911 RemoveInheritance(Relation child_rel, Relation parent_rel)
12912 {
12913 Relation catalogRelation;
12914 SysScanDesc scan;
12915 ScanKeyData key[3];
12916 HeapTuple attributeTuple,
12917 constraintTuple;
12918 List *connames;
12919 bool found;
12920 bool child_is_partition = false;
12921
12922 /* If parent_rel is a partitioned table, child_rel must be a partition */
12923 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12924 child_is_partition = true;
12925
12926 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
12927 RelationGetRelid(parent_rel));
12928 if (!found)
12929 {
12930 if (child_is_partition)
12931 ereport(ERROR,
12932 (errcode(ERRCODE_UNDEFINED_TABLE),
12933 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
12934 RelationGetRelationName(child_rel),
12935 RelationGetRelationName(parent_rel))));
12936 else
12937 ereport(ERROR,
12938 (errcode(ERRCODE_UNDEFINED_TABLE),
12939 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
12940 RelationGetRelationName(parent_rel),
12941 RelationGetRelationName(child_rel))));
12942 }
12943
12944 /*
12945 * Search through child columns looking for ones matching parent rel
12946 */
12947 catalogRelation = heap_open(AttributeRelationId, RowExclusiveLock);
12948 ScanKeyInit(&key[0],
12949 Anum_pg_attribute_attrelid,
12950 BTEqualStrategyNumber, F_OIDEQ,
12951 ObjectIdGetDatum(RelationGetRelid(child_rel)));
12952 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
12953 true, NULL, 1, key);
12954 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
12955 {
12956 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
12957
12958 /* Ignore if dropped or not inherited */
12959 if (att->attisdropped)
12960 continue;
12961 if (att->attinhcount <= 0)
12962 continue;
12963
12964 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
12965 NameStr(att->attname)))
12966 {
12967 /* Decrement inhcount and possibly set islocal to true */
12968 HeapTuple copyTuple = heap_copytuple(attributeTuple);
12969 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
12970
12971 copy_att->attinhcount--;
12972 if (copy_att->attinhcount == 0)
12973 copy_att->attislocal = true;
12974
12975 CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
12976 heap_freetuple(copyTuple);
12977 }
12978 }
12979 systable_endscan(scan);
12980 heap_close(catalogRelation, RowExclusiveLock);
12981
12982 /*
12983 * Likewise, find inherited check constraints and disinherit them. To do
12984 * this, we first need a list of the names of the parent's check
12985 * constraints. (We cheat a bit by only checking for name matches,
12986 * assuming that the expressions will match.)
12987 */
12988 catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
12989 ScanKeyInit(&key[0],
12990 Anum_pg_constraint_conrelid,
12991 BTEqualStrategyNumber, F_OIDEQ,
12992 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
12993 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
12994 true, NULL, 1, key);
12995
12996 connames = NIL;
12997
12998 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
12999 {
13000 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
13001
13002 if (con->contype == CONSTRAINT_CHECK)
13003 connames = lappend(connames, pstrdup(NameStr(con->conname)));
13004 }
13005
13006 systable_endscan(scan);
13007
13008 /* Now scan the child's constraints */
13009 ScanKeyInit(&key[0],
13010 Anum_pg_constraint_conrelid,
13011 BTEqualStrategyNumber, F_OIDEQ,
13012 ObjectIdGetDatum(RelationGetRelid(child_rel)));
13013 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
13014 true, NULL, 1, key);
13015
13016 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
13017 {
13018 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
13019 bool match;
13020 ListCell *lc;
13021
13022 if (con->contype != CONSTRAINT_CHECK)
13023 continue;
13024
13025 match = false;
13026 foreach(lc, connames)
13027 {
13028 if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
13029 {
13030 match = true;
13031 break;
13032 }
13033 }
13034
13035 if (match)
13036 {
13037 /* Decrement inhcount and possibly set islocal to true */
13038 HeapTuple copyTuple = heap_copytuple(constraintTuple);
13039 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13040
13041 if (copy_con->coninhcount <= 0) /* shouldn't happen */
13042 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13043 RelationGetRelid(child_rel), NameStr(copy_con->conname));
13044
13045 copy_con->coninhcount--;
13046 if (copy_con->coninhcount == 0)
13047 copy_con->conislocal = true;
13048
13049 CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
13050 heap_freetuple(copyTuple);
13051 }
13052 }
13053
13054 systable_endscan(scan);
13055 heap_close(catalogRelation, RowExclusiveLock);
13056
13057 drop_parent_dependency(RelationGetRelid(child_rel),
13058 RelationRelationId,
13059 RelationGetRelid(parent_rel),
13060 child_dependency_type(child_is_partition));
13061
13062 /*
13063 * Post alter hook of this inherits. Since object_access_hook doesn't take
13064 * multiple object identifiers, we relay oid of parent relation using
13065 * auxiliary_id argument.
13066 */
13067 InvokeObjectPostAlterHookArg(InheritsRelationId,
13068 RelationGetRelid(child_rel), 0,
13069 RelationGetRelid(parent_rel), false);
13070 }
13071
13072 /*
13073 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
13074 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
13075 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
13076 * be TypeRelationId). There's no convenient way to do this, so go trawling
13077 * through pg_depend.
13078 */
13079 static void
drop_parent_dependency(Oid relid,Oid refclassid,Oid refobjid,DependencyType deptype)13080 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
13081 DependencyType deptype)
13082 {
13083 Relation catalogRelation;
13084 SysScanDesc scan;
13085 ScanKeyData key[3];
13086 HeapTuple depTuple;
13087
13088 catalogRelation = heap_open(DependRelationId, RowExclusiveLock);
13089
13090 ScanKeyInit(&key[0],
13091 Anum_pg_depend_classid,
13092 BTEqualStrategyNumber, F_OIDEQ,
13093 ObjectIdGetDatum(RelationRelationId));
13094 ScanKeyInit(&key[1],
13095 Anum_pg_depend_objid,
13096 BTEqualStrategyNumber, F_OIDEQ,
13097 ObjectIdGetDatum(relid));
13098 ScanKeyInit(&key[2],
13099 Anum_pg_depend_objsubid,
13100 BTEqualStrategyNumber, F_INT4EQ,
13101 Int32GetDatum(0));
13102
13103 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
13104 NULL, 3, key);
13105
13106 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
13107 {
13108 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
13109
13110 if (dep->refclassid == refclassid &&
13111 dep->refobjid == refobjid &&
13112 dep->refobjsubid == 0 &&
13113 dep->deptype == deptype)
13114 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
13115 }
13116
13117 systable_endscan(scan);
13118 heap_close(catalogRelation, RowExclusiveLock);
13119 }
13120
13121 /*
13122 * ALTER TABLE OF
13123 *
13124 * Attach a table to a composite type, as though it had been created with CREATE
13125 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
13126 * subject table must not have inheritance parents. These restrictions ensure
13127 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
13128 *
13129 * The address of the type is returned.
13130 */
13131 static ObjectAddress
ATExecAddOf(Relation rel,const TypeName * ofTypename,LOCKMODE lockmode)13132 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
13133 {
13134 Oid relid = RelationGetRelid(rel);
13135 Type typetuple;
13136 Oid typeid;
13137 Relation inheritsRelation,
13138 relationRelation;
13139 SysScanDesc scan;
13140 ScanKeyData key;
13141 AttrNumber table_attno,
13142 type_attno;
13143 TupleDesc typeTupleDesc,
13144 tableTupleDesc;
13145 ObjectAddress tableobj,
13146 typeobj;
13147 HeapTuple classtuple;
13148
13149 /* Validate the type. */
13150 typetuple = typenameType(NULL, ofTypename, NULL);
13151 check_of_type(typetuple);
13152 typeid = HeapTupleGetOid(typetuple);
13153
13154 /* Fail if the table has any inheritance parents. */
13155 inheritsRelation = heap_open(InheritsRelationId, AccessShareLock);
13156 ScanKeyInit(&key,
13157 Anum_pg_inherits_inhrelid,
13158 BTEqualStrategyNumber, F_OIDEQ,
13159 ObjectIdGetDatum(relid));
13160 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
13161 true, NULL, 1, &key);
13162 if (HeapTupleIsValid(systable_getnext(scan)))
13163 ereport(ERROR,
13164 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13165 errmsg("typed tables cannot inherit")));
13166 systable_endscan(scan);
13167 heap_close(inheritsRelation, AccessShareLock);
13168
13169 /*
13170 * Check the tuple descriptors for compatibility. Unlike inheritance, we
13171 * require that the order also match. However, attnotnull need not match.
13172 * Also unlike inheritance, we do not require matching relhasoids.
13173 */
13174 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
13175 tableTupleDesc = RelationGetDescr(rel);
13176 table_attno = 1;
13177 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
13178 {
13179 Form_pg_attribute type_attr,
13180 table_attr;
13181 const char *type_attname,
13182 *table_attname;
13183
13184 /* Get the next non-dropped type attribute. */
13185 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
13186 if (type_attr->attisdropped)
13187 continue;
13188 type_attname = NameStr(type_attr->attname);
13189
13190 /* Get the next non-dropped table attribute. */
13191 do
13192 {
13193 if (table_attno > tableTupleDesc->natts)
13194 ereport(ERROR,
13195 (errcode(ERRCODE_DATATYPE_MISMATCH),
13196 errmsg("table is missing column \"%s\"",
13197 type_attname)));
13198 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
13199 table_attno++;
13200 } while (table_attr->attisdropped);
13201 table_attname = NameStr(table_attr->attname);
13202
13203 /* Compare name. */
13204 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
13205 ereport(ERROR,
13206 (errcode(ERRCODE_DATATYPE_MISMATCH),
13207 errmsg("table has column \"%s\" where type requires \"%s\"",
13208 table_attname, type_attname)));
13209
13210 /* Compare type. */
13211 if (table_attr->atttypid != type_attr->atttypid ||
13212 table_attr->atttypmod != type_attr->atttypmod ||
13213 table_attr->attcollation != type_attr->attcollation)
13214 ereport(ERROR,
13215 (errcode(ERRCODE_DATATYPE_MISMATCH),
13216 errmsg("table \"%s\" has different type for column \"%s\"",
13217 RelationGetRelationName(rel), type_attname)));
13218 }
13219 DecrTupleDescRefCount(typeTupleDesc);
13220
13221 /* Any remaining columns at the end of the table had better be dropped. */
13222 for (; table_attno <= tableTupleDesc->natts; table_attno++)
13223 {
13224 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
13225 table_attno - 1);
13226
13227 if (!table_attr->attisdropped)
13228 ereport(ERROR,
13229 (errcode(ERRCODE_DATATYPE_MISMATCH),
13230 errmsg("table has extra column \"%s\"",
13231 NameStr(table_attr->attname))));
13232 }
13233
13234 /* If the table was already typed, drop the existing dependency. */
13235 if (rel->rd_rel->reloftype)
13236 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
13237 DEPENDENCY_NORMAL);
13238
13239 /* Record a dependency on the new type. */
13240 tableobj.classId = RelationRelationId;
13241 tableobj.objectId = relid;
13242 tableobj.objectSubId = 0;
13243 typeobj.classId = TypeRelationId;
13244 typeobj.objectId = typeid;
13245 typeobj.objectSubId = 0;
13246 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
13247
13248 /* Update pg_class.reloftype */
13249 relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
13250 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
13251 if (!HeapTupleIsValid(classtuple))
13252 elog(ERROR, "cache lookup failed for relation %u", relid);
13253 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
13254 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
13255
13256 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
13257
13258 heap_freetuple(classtuple);
13259 heap_close(relationRelation, RowExclusiveLock);
13260
13261 ReleaseSysCache(typetuple);
13262
13263 return typeobj;
13264 }
13265
13266 /*
13267 * ALTER TABLE NOT OF
13268 *
13269 * Detach a typed table from its originating type. Just clear reloftype and
13270 * remove the dependency.
13271 */
13272 static void
ATExecDropOf(Relation rel,LOCKMODE lockmode)13273 ATExecDropOf(Relation rel, LOCKMODE lockmode)
13274 {
13275 Oid relid = RelationGetRelid(rel);
13276 Relation relationRelation;
13277 HeapTuple tuple;
13278
13279 if (!OidIsValid(rel->rd_rel->reloftype))
13280 ereport(ERROR,
13281 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13282 errmsg("\"%s\" is not a typed table",
13283 RelationGetRelationName(rel))));
13284
13285 /*
13286 * We don't bother to check ownership of the type --- ownership of the
13287 * table is presumed enough rights. No lock required on the type, either.
13288 */
13289
13290 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
13291 DEPENDENCY_NORMAL);
13292
13293 /* Clear pg_class.reloftype */
13294 relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
13295 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
13296 if (!HeapTupleIsValid(tuple))
13297 elog(ERROR, "cache lookup failed for relation %u", relid);
13298 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
13299 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
13300
13301 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
13302
13303 heap_freetuple(tuple);
13304 heap_close(relationRelation, RowExclusiveLock);
13305 }
13306
13307 /*
13308 * relation_mark_replica_identity: Update a table's replica identity
13309 *
13310 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
13311 * index. Otherwise, it should be InvalidOid.
13312 */
13313 static void
relation_mark_replica_identity(Relation rel,char ri_type,Oid indexOid,bool is_internal)13314 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
13315 bool is_internal)
13316 {
13317 Relation pg_index;
13318 Relation pg_class;
13319 HeapTuple pg_class_tuple;
13320 HeapTuple pg_index_tuple;
13321 Form_pg_class pg_class_form;
13322 Form_pg_index pg_index_form;
13323
13324 ListCell *index;
13325
13326 /*
13327 * Check whether relreplident has changed, and update it if so.
13328 */
13329 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
13330 pg_class_tuple = SearchSysCacheCopy1(RELOID,
13331 ObjectIdGetDatum(RelationGetRelid(rel)));
13332 if (!HeapTupleIsValid(pg_class_tuple))
13333 elog(ERROR, "cache lookup failed for relation \"%s\"",
13334 RelationGetRelationName(rel));
13335 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
13336 if (pg_class_form->relreplident != ri_type)
13337 {
13338 pg_class_form->relreplident = ri_type;
13339 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
13340 }
13341 heap_close(pg_class, RowExclusiveLock);
13342 heap_freetuple(pg_class_tuple);
13343
13344 /*
13345 * Check whether the correct index is marked indisreplident; if so, we're
13346 * done.
13347 */
13348 if (OidIsValid(indexOid))
13349 {
13350 Assert(ri_type == REPLICA_IDENTITY_INDEX);
13351
13352 pg_index_tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
13353 if (!HeapTupleIsValid(pg_index_tuple))
13354 elog(ERROR, "cache lookup failed for index %u", indexOid);
13355 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
13356
13357 if (pg_index_form->indisreplident)
13358 {
13359 ReleaseSysCache(pg_index_tuple);
13360 return;
13361 }
13362 ReleaseSysCache(pg_index_tuple);
13363 }
13364
13365 /*
13366 * Clear the indisreplident flag from any index that had it previously,
13367 * and set it for any index that should have it now.
13368 */
13369 pg_index = heap_open(IndexRelationId, RowExclusiveLock);
13370 foreach(index, RelationGetIndexList(rel))
13371 {
13372 Oid thisIndexOid = lfirst_oid(index);
13373 bool dirty = false;
13374
13375 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
13376 ObjectIdGetDatum(thisIndexOid));
13377 if (!HeapTupleIsValid(pg_index_tuple))
13378 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
13379 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
13380
13381 /*
13382 * Unset the bit if set. We know it's wrong because we checked this
13383 * earlier.
13384 */
13385 if (pg_index_form->indisreplident)
13386 {
13387 dirty = true;
13388 pg_index_form->indisreplident = false;
13389 }
13390 else if (thisIndexOid == indexOid)
13391 {
13392 dirty = true;
13393 pg_index_form->indisreplident = true;
13394 }
13395
13396 if (dirty)
13397 {
13398 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
13399 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
13400 InvalidOid, is_internal);
13401 }
13402 heap_freetuple(pg_index_tuple);
13403 }
13404
13405 heap_close(pg_index, RowExclusiveLock);
13406 }
13407
13408 /*
13409 * ALTER TABLE <name> REPLICA IDENTITY ...
13410 */
13411 static void
ATExecReplicaIdentity(Relation rel,ReplicaIdentityStmt * stmt,LOCKMODE lockmode)13412 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
13413 {
13414 Oid indexOid;
13415 Relation indexRel;
13416 int key;
13417
13418 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
13419 {
13420 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
13421 return;
13422 }
13423 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
13424 {
13425 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
13426 return;
13427 }
13428 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
13429 {
13430 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
13431 return;
13432 }
13433 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
13434 {
13435 /* fallthrough */ ;
13436 }
13437 else
13438 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
13439
13440
13441 /* Check that the index exists */
13442 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
13443 if (!OidIsValid(indexOid))
13444 ereport(ERROR,
13445 (errcode(ERRCODE_UNDEFINED_OBJECT),
13446 errmsg("index \"%s\" for table \"%s\" does not exist",
13447 stmt->name, RelationGetRelationName(rel))));
13448
13449 indexRel = index_open(indexOid, ShareLock);
13450
13451 /* Check that the index is on the relation we're altering. */
13452 if (indexRel->rd_index == NULL ||
13453 indexRel->rd_index->indrelid != RelationGetRelid(rel))
13454 ereport(ERROR,
13455 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13456 errmsg("\"%s\" is not an index for table \"%s\"",
13457 RelationGetRelationName(indexRel),
13458 RelationGetRelationName(rel))));
13459 /* The AM must support uniqueness, and the index must in fact be unique. */
13460 if (!indexRel->rd_amroutine->amcanunique ||
13461 !indexRel->rd_index->indisunique)
13462 ereport(ERROR,
13463 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13464 errmsg("cannot use non-unique index \"%s\" as replica identity",
13465 RelationGetRelationName(indexRel))));
13466 /* Deferred indexes are not guaranteed to be always unique. */
13467 if (!indexRel->rd_index->indimmediate)
13468 ereport(ERROR,
13469 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13470 errmsg("cannot use non-immediate index \"%s\" as replica identity",
13471 RelationGetRelationName(indexRel))));
13472 /* Expression indexes aren't supported. */
13473 if (RelationGetIndexExpressions(indexRel) != NIL)
13474 ereport(ERROR,
13475 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13476 errmsg("cannot use expression index \"%s\" as replica identity",
13477 RelationGetRelationName(indexRel))));
13478 /* Predicate indexes aren't supported. */
13479 if (RelationGetIndexPredicate(indexRel) != NIL)
13480 ereport(ERROR,
13481 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13482 errmsg("cannot use partial index \"%s\" as replica identity",
13483 RelationGetRelationName(indexRel))));
13484 /* And neither are invalid indexes. */
13485 if (!IndexIsValid(indexRel->rd_index))
13486 ereport(ERROR,
13487 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13488 errmsg("cannot use invalid index \"%s\" as replica identity",
13489 RelationGetRelationName(indexRel))));
13490
13491 /* Check index for nullable columns. */
13492 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
13493 {
13494 int16 attno = indexRel->rd_index->indkey.values[key];
13495 Form_pg_attribute attr;
13496
13497 /* Allow OID column to be indexed; it's certainly not nullable */
13498 if (attno == ObjectIdAttributeNumber)
13499 continue;
13500
13501 /*
13502 * Reject any other system columns. (Going forward, we'll disallow
13503 * indexes containing such columns in the first place, but they might
13504 * exist in older branches.)
13505 */
13506 if (attno <= 0)
13507 ereport(ERROR,
13508 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
13509 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
13510 RelationGetRelationName(indexRel), attno)));
13511
13512 attr = TupleDescAttr(rel->rd_att, attno - 1);
13513 if (!attr->attnotnull)
13514 ereport(ERROR,
13515 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13516 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
13517 RelationGetRelationName(indexRel),
13518 NameStr(attr->attname))));
13519 }
13520
13521 /* This index is suitable for use as a replica identity. Mark it. */
13522 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
13523
13524 index_close(indexRel, NoLock);
13525 }
13526
13527 /*
13528 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
13529 */
13530 static void
ATExecEnableRowSecurity(Relation rel)13531 ATExecEnableRowSecurity(Relation rel)
13532 {
13533 Relation pg_class;
13534 Oid relid;
13535 HeapTuple tuple;
13536
13537 relid = RelationGetRelid(rel);
13538
13539 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
13540
13541 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
13542
13543 if (!HeapTupleIsValid(tuple))
13544 elog(ERROR, "cache lookup failed for relation %u", relid);
13545
13546 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = true;
13547 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13548
13549 heap_close(pg_class, RowExclusiveLock);
13550 heap_freetuple(tuple);
13551 }
13552
13553 static void
ATExecDisableRowSecurity(Relation rel)13554 ATExecDisableRowSecurity(Relation rel)
13555 {
13556 Relation pg_class;
13557 Oid relid;
13558 HeapTuple tuple;
13559
13560 relid = RelationGetRelid(rel);
13561
13562 /* Pull the record for this relation and update it */
13563 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
13564
13565 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
13566
13567 if (!HeapTupleIsValid(tuple))
13568 elog(ERROR, "cache lookup failed for relation %u", relid);
13569
13570 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = false;
13571 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13572
13573 heap_close(pg_class, RowExclusiveLock);
13574 heap_freetuple(tuple);
13575 }
13576
13577 /*
13578 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
13579 */
13580 static void
ATExecForceNoForceRowSecurity(Relation rel,bool force_rls)13581 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
13582 {
13583 Relation pg_class;
13584 Oid relid;
13585 HeapTuple tuple;
13586
13587 relid = RelationGetRelid(rel);
13588
13589 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
13590
13591 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
13592
13593 if (!HeapTupleIsValid(tuple))
13594 elog(ERROR, "cache lookup failed for relation %u", relid);
13595
13596 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
13597 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13598
13599 heap_close(pg_class, RowExclusiveLock);
13600 heap_freetuple(tuple);
13601 }
13602
13603 /*
13604 * ALTER FOREIGN TABLE <name> OPTIONS (...)
13605 */
13606 static void
ATExecGenericOptions(Relation rel,List * options)13607 ATExecGenericOptions(Relation rel, List *options)
13608 {
13609 Relation ftrel;
13610 ForeignServer *server;
13611 ForeignDataWrapper *fdw;
13612 HeapTuple tuple;
13613 bool isnull;
13614 Datum repl_val[Natts_pg_foreign_table];
13615 bool repl_null[Natts_pg_foreign_table];
13616 bool repl_repl[Natts_pg_foreign_table];
13617 Datum datum;
13618 Form_pg_foreign_table tableform;
13619
13620 if (options == NIL)
13621 return;
13622
13623 ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
13624
13625 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
13626 if (!HeapTupleIsValid(tuple))
13627 ereport(ERROR,
13628 (errcode(ERRCODE_UNDEFINED_OBJECT),
13629 errmsg("foreign table \"%s\" does not exist",
13630 RelationGetRelationName(rel))));
13631 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
13632 server = GetForeignServer(tableform->ftserver);
13633 fdw = GetForeignDataWrapper(server->fdwid);
13634
13635 memset(repl_val, 0, sizeof(repl_val));
13636 memset(repl_null, false, sizeof(repl_null));
13637 memset(repl_repl, false, sizeof(repl_repl));
13638
13639 /* Extract the current options */
13640 datum = SysCacheGetAttr(FOREIGNTABLEREL,
13641 tuple,
13642 Anum_pg_foreign_table_ftoptions,
13643 &isnull);
13644 if (isnull)
13645 datum = PointerGetDatum(NULL);
13646
13647 /* Transform the options */
13648 datum = transformGenericOptions(ForeignTableRelationId,
13649 datum,
13650 options,
13651 fdw->fdwvalidator);
13652
13653 if (PointerIsValid(DatumGetPointer(datum)))
13654 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
13655 else
13656 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
13657
13658 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
13659
13660 /* Everything looks good - update the tuple */
13661
13662 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
13663 repl_val, repl_null, repl_repl);
13664
13665 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
13666
13667 /*
13668 * Invalidate relcache so that all sessions will refresh any cached plans
13669 * that might depend on the old options.
13670 */
13671 CacheInvalidateRelcache(rel);
13672
13673 InvokeObjectPostAlterHook(ForeignTableRelationId,
13674 RelationGetRelid(rel), 0);
13675
13676 heap_close(ftrel, RowExclusiveLock);
13677
13678 heap_freetuple(tuple);
13679 }
13680
13681 /*
13682 * Preparation phase for SET LOGGED/UNLOGGED
13683 *
13684 * This verifies that we're not trying to change a temp table. Also,
13685 * existing foreign key constraints are checked to avoid ending up with
13686 * permanent tables referencing unlogged tables.
13687 *
13688 * Return value is false if the operation is a no-op (in which case the
13689 * checks are skipped), otherwise true.
13690 */
13691 static bool
ATPrepChangePersistence(Relation rel,bool toLogged)13692 ATPrepChangePersistence(Relation rel, bool toLogged)
13693 {
13694 Relation pg_constraint;
13695 HeapTuple tuple;
13696 SysScanDesc scan;
13697 ScanKeyData skey[1];
13698
13699 /*
13700 * Disallow changing status for a temp table. Also verify whether we can
13701 * get away with doing nothing; in such cases we don't need to run the
13702 * checks below, either.
13703 */
13704 switch (rel->rd_rel->relpersistence)
13705 {
13706 case RELPERSISTENCE_TEMP:
13707 ereport(ERROR,
13708 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13709 errmsg("cannot change logged status of table \"%s\" because it is temporary",
13710 RelationGetRelationName(rel)),
13711 errtable(rel)));
13712 break;
13713 case RELPERSISTENCE_PERMANENT:
13714 if (toLogged)
13715 /* nothing to do */
13716 return false;
13717 break;
13718 case RELPERSISTENCE_UNLOGGED:
13719 if (!toLogged)
13720 /* nothing to do */
13721 return false;
13722 break;
13723 }
13724
13725 /*
13726 * Check that the table is not part any publication when changing to
13727 * UNLOGGED as UNLOGGED tables can't be published.
13728 */
13729 if (!toLogged &&
13730 list_length(GetRelationPublications(RelationGetRelid(rel))) > 0)
13731 ereport(ERROR,
13732 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13733 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
13734 RelationGetRelationName(rel)),
13735 errdetail("Unlogged relations cannot be replicated.")));
13736
13737 /*
13738 * Check existing foreign key constraints to preserve the invariant that
13739 * permanent tables cannot reference unlogged ones. Self-referencing
13740 * foreign keys can safely be ignored.
13741 */
13742 pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
13743
13744 /*
13745 * Scan conrelid if changing to permanent, else confrelid. This also
13746 * determines whether a useful index exists.
13747 */
13748 ScanKeyInit(&skey[0],
13749 toLogged ? Anum_pg_constraint_conrelid :
13750 Anum_pg_constraint_confrelid,
13751 BTEqualStrategyNumber, F_OIDEQ,
13752 ObjectIdGetDatum(RelationGetRelid(rel)));
13753 scan = systable_beginscan(pg_constraint,
13754 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
13755 true, NULL, 1, skey);
13756
13757 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
13758 {
13759 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
13760
13761 if (con->contype == CONSTRAINT_FOREIGN)
13762 {
13763 Oid foreignrelid;
13764 Relation foreignrel;
13765
13766 /* the opposite end of what we used as scankey */
13767 foreignrelid = toLogged ? con->confrelid : con->conrelid;
13768
13769 /* ignore if self-referencing */
13770 if (RelationGetRelid(rel) == foreignrelid)
13771 continue;
13772
13773 foreignrel = relation_open(foreignrelid, AccessShareLock);
13774
13775 if (toLogged)
13776 {
13777 if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
13778 ereport(ERROR,
13779 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13780 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
13781 RelationGetRelationName(rel),
13782 RelationGetRelationName(foreignrel)),
13783 errtableconstraint(rel, NameStr(con->conname))));
13784 }
13785 else
13786 {
13787 if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
13788 ereport(ERROR,
13789 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13790 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
13791 RelationGetRelationName(rel),
13792 RelationGetRelationName(foreignrel)),
13793 errtableconstraint(rel, NameStr(con->conname))));
13794 }
13795
13796 relation_close(foreignrel, AccessShareLock);
13797 }
13798 }
13799
13800 systable_endscan(scan);
13801
13802 heap_close(pg_constraint, AccessShareLock);
13803
13804 return true;
13805 }
13806
13807 /*
13808 * Execute ALTER TABLE SET SCHEMA
13809 */
13810 ObjectAddress
AlterTableNamespace(AlterObjectSchemaStmt * stmt,Oid * oldschema)13811 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
13812 {
13813 Relation rel;
13814 Oid relid;
13815 Oid oldNspOid;
13816 Oid nspOid;
13817 RangeVar *newrv;
13818 ObjectAddresses *objsMoved;
13819 ObjectAddress myself;
13820
13821 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
13822 stmt->missing_ok ? RVR_MISSING_OK : 0,
13823 RangeVarCallbackForAlterRelation,
13824 (void *) stmt);
13825
13826 if (!OidIsValid(relid))
13827 {
13828 ereport(NOTICE,
13829 (errmsg("relation \"%s\" does not exist, skipping",
13830 stmt->relation->relname)));
13831 return InvalidObjectAddress;
13832 }
13833
13834 rel = relation_open(relid, NoLock);
13835
13836 oldNspOid = RelationGetNamespace(rel);
13837
13838 /* If it's an owned sequence, disallow moving it by itself. */
13839 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
13840 {
13841 Oid tableId;
13842 int32 colId;
13843
13844 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
13845 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
13846 ereport(ERROR,
13847 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13848 errmsg("cannot move an owned sequence into another schema"),
13849 errdetail("Sequence \"%s\" is linked to table \"%s\".",
13850 RelationGetRelationName(rel),
13851 get_rel_name(tableId))));
13852 }
13853
13854 /* Get and lock schema OID and check its permissions. */
13855 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
13856 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
13857
13858 /* common checks on switching namespaces */
13859 CheckSetNamespace(oldNspOid, nspOid);
13860
13861 objsMoved = new_object_addresses();
13862 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
13863 free_object_addresses(objsMoved);
13864
13865 ObjectAddressSet(myself, RelationRelationId, relid);
13866
13867 if (oldschema)
13868 *oldschema = oldNspOid;
13869
13870 /* close rel, but keep lock until commit */
13871 relation_close(rel, NoLock);
13872
13873 return myself;
13874 }
13875
13876 /*
13877 * The guts of relocating a table or materialized view to another namespace:
13878 * besides moving the relation itself, its dependent objects are relocated to
13879 * the new schema.
13880 */
13881 void
AlterTableNamespaceInternal(Relation rel,Oid oldNspOid,Oid nspOid,ObjectAddresses * objsMoved)13882 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
13883 ObjectAddresses *objsMoved)
13884 {
13885 Relation classRel;
13886
13887 Assert(objsMoved != NULL);
13888
13889 /* OK, modify the pg_class row and pg_depend entry */
13890 classRel = heap_open(RelationRelationId, RowExclusiveLock);
13891
13892 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
13893 nspOid, true, objsMoved);
13894
13895 /* Fix the table's row type too */
13896 AlterTypeNamespaceInternal(rel->rd_rel->reltype,
13897 nspOid, false, false, objsMoved);
13898
13899 /* Fix other dependent stuff */
13900 if (rel->rd_rel->relkind == RELKIND_RELATION ||
13901 rel->rd_rel->relkind == RELKIND_MATVIEW ||
13902 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13903 {
13904 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
13905 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
13906 objsMoved, AccessExclusiveLock);
13907 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
13908 false, objsMoved);
13909 }
13910
13911 heap_close(classRel, RowExclusiveLock);
13912 }
13913
13914 /*
13915 * The guts of relocating a relation to another namespace: fix the pg_class
13916 * entry, and the pg_depend entry if any. Caller must already have
13917 * opened and write-locked pg_class.
13918 */
13919 void
AlterRelationNamespaceInternal(Relation classRel,Oid relOid,Oid oldNspOid,Oid newNspOid,bool hasDependEntry,ObjectAddresses * objsMoved)13920 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
13921 Oid oldNspOid, Oid newNspOid,
13922 bool hasDependEntry,
13923 ObjectAddresses *objsMoved)
13924 {
13925 HeapTuple classTup;
13926 Form_pg_class classForm;
13927 ObjectAddress thisobj;
13928 bool already_done = false;
13929
13930 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
13931 if (!HeapTupleIsValid(classTup))
13932 elog(ERROR, "cache lookup failed for relation %u", relOid);
13933 classForm = (Form_pg_class) GETSTRUCT(classTup);
13934
13935 Assert(classForm->relnamespace == oldNspOid);
13936
13937 thisobj.classId = RelationRelationId;
13938 thisobj.objectId = relOid;
13939 thisobj.objectSubId = 0;
13940
13941 /*
13942 * If the object has already been moved, don't move it again. If it's
13943 * already in the right place, don't move it, but still fire the object
13944 * access hook.
13945 */
13946 already_done = object_address_present(&thisobj, objsMoved);
13947 if (!already_done && oldNspOid != newNspOid)
13948 {
13949 /* check for duplicate name (more friendly than unique-index failure) */
13950 if (get_relname_relid(NameStr(classForm->relname),
13951 newNspOid) != InvalidOid)
13952 ereport(ERROR,
13953 (errcode(ERRCODE_DUPLICATE_TABLE),
13954 errmsg("relation \"%s\" already exists in schema \"%s\"",
13955 NameStr(classForm->relname),
13956 get_namespace_name(newNspOid))));
13957
13958 /* classTup is a copy, so OK to scribble on */
13959 classForm->relnamespace = newNspOid;
13960
13961 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
13962
13963 /* Update dependency on schema if caller said so */
13964 if (hasDependEntry &&
13965 changeDependencyFor(RelationRelationId,
13966 relOid,
13967 NamespaceRelationId,
13968 oldNspOid,
13969 newNspOid) != 1)
13970 elog(ERROR, "failed to change schema dependency for relation \"%s\"",
13971 NameStr(classForm->relname));
13972 }
13973 if (!already_done)
13974 {
13975 add_exact_object_address(&thisobj, objsMoved);
13976
13977 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
13978 }
13979
13980 heap_freetuple(classTup);
13981 }
13982
13983 /*
13984 * Move all indexes for the specified relation to another namespace.
13985 *
13986 * Note: we assume adequate permission checking was done by the caller,
13987 * and that the caller has a suitable lock on the owning relation.
13988 */
13989 static void
AlterIndexNamespaces(Relation classRel,Relation rel,Oid oldNspOid,Oid newNspOid,ObjectAddresses * objsMoved)13990 AlterIndexNamespaces(Relation classRel, Relation rel,
13991 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
13992 {
13993 List *indexList;
13994 ListCell *l;
13995
13996 indexList = RelationGetIndexList(rel);
13997
13998 foreach(l, indexList)
13999 {
14000 Oid indexOid = lfirst_oid(l);
14001 ObjectAddress thisobj;
14002
14003 thisobj.classId = RelationRelationId;
14004 thisobj.objectId = indexOid;
14005 thisobj.objectSubId = 0;
14006
14007 /*
14008 * Note: currently, the index will not have its own dependency on the
14009 * namespace, so we don't need to do changeDependencyFor(). There's no
14010 * row type in pg_type, either.
14011 *
14012 * XXX this objsMoved test may be pointless -- surely we have a single
14013 * dependency link from a relation to each index?
14014 */
14015 if (!object_address_present(&thisobj, objsMoved))
14016 {
14017 AlterRelationNamespaceInternal(classRel, indexOid,
14018 oldNspOid, newNspOid,
14019 false, objsMoved);
14020 add_exact_object_address(&thisobj, objsMoved);
14021 }
14022 }
14023
14024 list_free(indexList);
14025 }
14026
14027 /*
14028 * Move all identity and SERIAL-column sequences of the specified relation to another
14029 * namespace.
14030 *
14031 * Note: we assume adequate permission checking was done by the caller,
14032 * and that the caller has a suitable lock on the owning relation.
14033 */
14034 static void
AlterSeqNamespaces(Relation classRel,Relation rel,Oid oldNspOid,Oid newNspOid,ObjectAddresses * objsMoved,LOCKMODE lockmode)14035 AlterSeqNamespaces(Relation classRel, Relation rel,
14036 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
14037 LOCKMODE lockmode)
14038 {
14039 Relation depRel;
14040 SysScanDesc scan;
14041 ScanKeyData key[2];
14042 HeapTuple tup;
14043
14044 /*
14045 * SERIAL sequences are those having an auto dependency on one of the
14046 * table's columns (we don't care *which* column, exactly).
14047 */
14048 depRel = heap_open(DependRelationId, AccessShareLock);
14049
14050 ScanKeyInit(&key[0],
14051 Anum_pg_depend_refclassid,
14052 BTEqualStrategyNumber, F_OIDEQ,
14053 ObjectIdGetDatum(RelationRelationId));
14054 ScanKeyInit(&key[1],
14055 Anum_pg_depend_refobjid,
14056 BTEqualStrategyNumber, F_OIDEQ,
14057 ObjectIdGetDatum(RelationGetRelid(rel)));
14058 /* we leave refobjsubid unspecified */
14059
14060 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14061 NULL, 2, key);
14062
14063 while (HeapTupleIsValid(tup = systable_getnext(scan)))
14064 {
14065 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
14066 Relation seqRel;
14067
14068 /* skip dependencies other than auto dependencies on columns */
14069 if (depForm->refobjsubid == 0 ||
14070 depForm->classid != RelationRelationId ||
14071 depForm->objsubid != 0 ||
14072 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
14073 continue;
14074
14075 /* Use relation_open just in case it's an index */
14076 seqRel = relation_open(depForm->objid, lockmode);
14077
14078 /* skip non-sequence relations */
14079 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
14080 {
14081 /* No need to keep the lock */
14082 relation_close(seqRel, lockmode);
14083 continue;
14084 }
14085
14086 /* Fix the pg_class and pg_depend entries */
14087 AlterRelationNamespaceInternal(classRel, depForm->objid,
14088 oldNspOid, newNspOid,
14089 true, objsMoved);
14090
14091 /*
14092 * Sequences have entries in pg_type. We need to be careful to move
14093 * them to the new namespace, too.
14094 */
14095 AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype,
14096 newNspOid, false, false, objsMoved);
14097
14098 /* Now we can close it. Keep the lock till end of transaction. */
14099 relation_close(seqRel, NoLock);
14100 }
14101
14102 systable_endscan(scan);
14103
14104 relation_close(depRel, AccessShareLock);
14105 }
14106
14107
14108 /*
14109 * This code supports
14110 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
14111 *
14112 * Because we only support this for TEMP tables, it's sufficient to remember
14113 * the state in a backend-local data structure.
14114 */
14115
14116 /*
14117 * Register a newly-created relation's ON COMMIT action.
14118 */
14119 void
register_on_commit_action(Oid relid,OnCommitAction action)14120 register_on_commit_action(Oid relid, OnCommitAction action)
14121 {
14122 OnCommitItem *oc;
14123 MemoryContext oldcxt;
14124
14125 /*
14126 * We needn't bother registering the relation unless there is an ON COMMIT
14127 * action we need to take.
14128 */
14129 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
14130 return;
14131
14132 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
14133
14134 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
14135 oc->relid = relid;
14136 oc->oncommit = action;
14137 oc->creating_subid = GetCurrentSubTransactionId();
14138 oc->deleting_subid = InvalidSubTransactionId;
14139
14140 on_commits = lcons(oc, on_commits);
14141
14142 MemoryContextSwitchTo(oldcxt);
14143 }
14144
14145 /*
14146 * Unregister any ON COMMIT action when a relation is deleted.
14147 *
14148 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
14149 */
14150 void
remove_on_commit_action(Oid relid)14151 remove_on_commit_action(Oid relid)
14152 {
14153 ListCell *l;
14154
14155 foreach(l, on_commits)
14156 {
14157 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
14158
14159 if (oc->relid == relid)
14160 {
14161 oc->deleting_subid = GetCurrentSubTransactionId();
14162 break;
14163 }
14164 }
14165 }
14166
14167 /*
14168 * Perform ON COMMIT actions.
14169 *
14170 * This is invoked just before actually committing, since it's possible
14171 * to encounter errors.
14172 */
14173 void
PreCommit_on_commit_actions(void)14174 PreCommit_on_commit_actions(void)
14175 {
14176 ListCell *l;
14177 List *oids_to_truncate = NIL;
14178 List *oids_to_drop = NIL;
14179
14180 foreach(l, on_commits)
14181 {
14182 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
14183
14184 /* Ignore entry if already dropped in this xact */
14185 if (oc->deleting_subid != InvalidSubTransactionId)
14186 continue;
14187
14188 switch (oc->oncommit)
14189 {
14190 case ONCOMMIT_NOOP:
14191 case ONCOMMIT_PRESERVE_ROWS:
14192 /* Do nothing (there shouldn't be such entries, actually) */
14193 break;
14194 case ONCOMMIT_DELETE_ROWS:
14195
14196 /*
14197 * If this transaction hasn't accessed any temporary
14198 * relations, we can skip truncating ON COMMIT DELETE ROWS
14199 * tables, as they must still be empty.
14200 */
14201 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPREL))
14202 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
14203 break;
14204 case ONCOMMIT_DROP:
14205 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
14206 break;
14207 }
14208 }
14209
14210 /*
14211 * Truncate relations before dropping so that all dependencies between
14212 * relations are removed after they are worked on. Doing it like this
14213 * might be a waste as it is possible that a relation being truncated will
14214 * be dropped anyway due to its parent being dropped, but this makes the
14215 * code more robust because of not having to re-check that the relation
14216 * exists at truncation time.
14217 */
14218 if (oids_to_truncate != NIL)
14219 {
14220 heap_truncate(oids_to_truncate);
14221 CommandCounterIncrement(); /* XXX needed? */
14222 }
14223 if (oids_to_drop != NIL)
14224 {
14225 ObjectAddresses *targetObjects = new_object_addresses();
14226 ListCell *l;
14227
14228 foreach(l, oids_to_drop)
14229 {
14230 ObjectAddress object;
14231
14232 object.classId = RelationRelationId;
14233 object.objectId = lfirst_oid(l);
14234 object.objectSubId = 0;
14235
14236 Assert(!object_address_present(&object, targetObjects));
14237
14238 add_exact_object_address(&object, targetObjects);
14239 }
14240
14241 /*
14242 * Since this is an automatic drop, rather than one directly initiated
14243 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
14244 */
14245 performMultipleDeletions(targetObjects, DROP_CASCADE,
14246 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
14247
14248 #ifdef USE_ASSERT_CHECKING
14249
14250 /*
14251 * Note that table deletion will call remove_on_commit_action, so the
14252 * entry should get marked as deleted.
14253 */
14254 foreach(l, on_commits)
14255 {
14256 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
14257
14258 if (oc->oncommit != ONCOMMIT_DROP)
14259 continue;
14260
14261 Assert(oc->deleting_subid != InvalidSubTransactionId);
14262 }
14263 #endif
14264 }
14265 }
14266
14267 /*
14268 * Post-commit or post-abort cleanup for ON COMMIT management.
14269 *
14270 * All we do here is remove no-longer-needed OnCommitItem entries.
14271 *
14272 * During commit, remove entries that were deleted during this transaction;
14273 * during abort, remove those created during this transaction.
14274 */
14275 void
AtEOXact_on_commit_actions(bool isCommit)14276 AtEOXact_on_commit_actions(bool isCommit)
14277 {
14278 ListCell *cur_item;
14279 ListCell *prev_item;
14280
14281 prev_item = NULL;
14282 cur_item = list_head(on_commits);
14283
14284 while (cur_item != NULL)
14285 {
14286 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
14287
14288 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
14289 oc->creating_subid != InvalidSubTransactionId)
14290 {
14291 /* cur_item must be removed */
14292 on_commits = list_delete_cell(on_commits, cur_item, prev_item);
14293 pfree(oc);
14294 if (prev_item)
14295 cur_item = lnext(prev_item);
14296 else
14297 cur_item = list_head(on_commits);
14298 }
14299 else
14300 {
14301 /* cur_item must be preserved */
14302 oc->creating_subid = InvalidSubTransactionId;
14303 oc->deleting_subid = InvalidSubTransactionId;
14304 prev_item = cur_item;
14305 cur_item = lnext(prev_item);
14306 }
14307 }
14308 }
14309
14310 /*
14311 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
14312 *
14313 * During subabort, we can immediately remove entries created during this
14314 * subtransaction. During subcommit, just relabel entries marked during
14315 * this subtransaction as being the parent's responsibility.
14316 */
14317 void
AtEOSubXact_on_commit_actions(bool isCommit,SubTransactionId mySubid,SubTransactionId parentSubid)14318 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
14319 SubTransactionId parentSubid)
14320 {
14321 ListCell *cur_item;
14322 ListCell *prev_item;
14323
14324 prev_item = NULL;
14325 cur_item = list_head(on_commits);
14326
14327 while (cur_item != NULL)
14328 {
14329 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
14330
14331 if (!isCommit && oc->creating_subid == mySubid)
14332 {
14333 /* cur_item must be removed */
14334 on_commits = list_delete_cell(on_commits, cur_item, prev_item);
14335 pfree(oc);
14336 if (prev_item)
14337 cur_item = lnext(prev_item);
14338 else
14339 cur_item = list_head(on_commits);
14340 }
14341 else
14342 {
14343 /* cur_item must be preserved */
14344 if (oc->creating_subid == mySubid)
14345 oc->creating_subid = parentSubid;
14346 if (oc->deleting_subid == mySubid)
14347 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
14348 prev_item = cur_item;
14349 cur_item = lnext(prev_item);
14350 }
14351 }
14352 }
14353
14354 /*
14355 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
14356 * the relation to be locked only if (1) it's a plain table, materialized
14357 * view, or TOAST table and (2) the current user is the owner (or the
14358 * superuser). This meets the permission-checking needs of CLUSTER, REINDEX
14359 * TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it can be
14360 * used by all.
14361 */
14362 void
RangeVarCallbackOwnsTable(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)14363 RangeVarCallbackOwnsTable(const RangeVar *relation,
14364 Oid relId, Oid oldRelId, void *arg)
14365 {
14366 char relkind;
14367
14368 /* Nothing to do if the relation was not found. */
14369 if (!OidIsValid(relId))
14370 return;
14371
14372 /*
14373 * If the relation does exist, check whether it's an index. But note that
14374 * the relation might have been dropped between the time we did the name
14375 * lookup and now. In that case, there's nothing to do.
14376 */
14377 relkind = get_rel_relkind(relId);
14378 if (!relkind)
14379 return;
14380 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
14381 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
14382 ereport(ERROR,
14383 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14384 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
14385
14386 /* Check permissions */
14387 if (!pg_class_ownercheck(relId, GetUserId()))
14388 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)), relation->relname);
14389 }
14390
14391 /*
14392 * Callback to RangeVarGetRelidExtended(), similar to
14393 * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
14394 */
14395 void
RangeVarCallbackOwnsRelation(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)14396 RangeVarCallbackOwnsRelation(const RangeVar *relation,
14397 Oid relId, Oid oldRelId, void *arg)
14398 {
14399 HeapTuple tuple;
14400
14401 /* Nothing to do if the relation was not found. */
14402 if (!OidIsValid(relId))
14403 return;
14404
14405 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
14406 if (!HeapTupleIsValid(tuple)) /* should not happen */
14407 elog(ERROR, "cache lookup failed for relation %u", relId);
14408
14409 if (!pg_class_ownercheck(relId, GetUserId()))
14410 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
14411 relation->relname);
14412
14413 if (!allowSystemTableMods &&
14414 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
14415 ereport(ERROR,
14416 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
14417 errmsg("permission denied: \"%s\" is a system catalog",
14418 relation->relname)));
14419
14420 ReleaseSysCache(tuple);
14421 }
14422
14423 /*
14424 * Common RangeVarGetRelid callback for rename, set schema, and alter table
14425 * processing.
14426 */
14427 static void
RangeVarCallbackForAlterRelation(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)14428 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
14429 void *arg)
14430 {
14431 Node *stmt = (Node *) arg;
14432 ObjectType reltype;
14433 HeapTuple tuple;
14434 Form_pg_class classform;
14435 AclResult aclresult;
14436 char relkind;
14437
14438 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14439 if (!HeapTupleIsValid(tuple))
14440 return; /* concurrently dropped */
14441 classform = (Form_pg_class) GETSTRUCT(tuple);
14442 relkind = classform->relkind;
14443
14444 /* Must own relation. */
14445 if (!pg_class_ownercheck(relid, GetUserId()))
14446 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
14447
14448 /* No system table modifications unless explicitly allowed. */
14449 if (!allowSystemTableMods && IsSystemClass(relid, classform))
14450 ereport(ERROR,
14451 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
14452 errmsg("permission denied: \"%s\" is a system catalog",
14453 rv->relname)));
14454
14455 /*
14456 * Extract the specified relation type from the statement parse tree.
14457 *
14458 * Also, for ALTER .. RENAME, check permissions: the user must (still)
14459 * have CREATE rights on the containing namespace.
14460 */
14461 if (IsA(stmt, RenameStmt))
14462 {
14463 aclresult = pg_namespace_aclcheck(classform->relnamespace,
14464 GetUserId(), ACL_CREATE);
14465 if (aclresult != ACLCHECK_OK)
14466 aclcheck_error(aclresult, OBJECT_SCHEMA,
14467 get_namespace_name(classform->relnamespace));
14468 reltype = ((RenameStmt *) stmt)->renameType;
14469 }
14470 else if (IsA(stmt, AlterObjectSchemaStmt))
14471 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
14472
14473 else if (IsA(stmt, AlterTableStmt))
14474 reltype = ((AlterTableStmt *) stmt)->relkind;
14475 else
14476 {
14477 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
14478 reltype = OBJECT_TABLE; /* placate compiler */
14479 }
14480
14481 /*
14482 * For compatibility with prior releases, we allow ALTER TABLE to be used
14483 * with most other types of relations (but not composite types). We allow
14484 * similar flexibility for ALTER INDEX in the case of RENAME, but not
14485 * otherwise. Otherwise, the user must select the correct form of the
14486 * command for the relation at issue.
14487 */
14488 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
14489 ereport(ERROR,
14490 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14491 errmsg("\"%s\" is not a sequence", rv->relname)));
14492
14493 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
14494 ereport(ERROR,
14495 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14496 errmsg("\"%s\" is not a view", rv->relname)));
14497
14498 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
14499 ereport(ERROR,
14500 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14501 errmsg("\"%s\" is not a materialized view", rv->relname)));
14502
14503 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
14504 ereport(ERROR,
14505 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14506 errmsg("\"%s\" is not a foreign table", rv->relname)));
14507
14508 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
14509 ereport(ERROR,
14510 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14511 errmsg("\"%s\" is not a composite type", rv->relname)));
14512
14513 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
14514 relkind != RELKIND_PARTITIONED_INDEX
14515 && !IsA(stmt, RenameStmt))
14516 ereport(ERROR,
14517 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14518 errmsg("\"%s\" is not an index", rv->relname)));
14519
14520 /*
14521 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
14522 * TYPE for that.
14523 */
14524 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
14525 ereport(ERROR,
14526 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14527 errmsg("\"%s\" is a composite type", rv->relname),
14528 errhint("Use ALTER TYPE instead.")));
14529
14530 /*
14531 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
14532 * to a different schema, such as indexes and TOAST tables.
14533 */
14534 if (IsA(stmt, AlterObjectSchemaStmt) &&
14535 relkind != RELKIND_RELATION &&
14536 relkind != RELKIND_VIEW &&
14537 relkind != RELKIND_MATVIEW &&
14538 relkind != RELKIND_SEQUENCE &&
14539 relkind != RELKIND_FOREIGN_TABLE &&
14540 relkind != RELKIND_PARTITIONED_TABLE)
14541 ereport(ERROR,
14542 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14543 errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table",
14544 rv->relname)));
14545
14546 ReleaseSysCache(tuple);
14547 }
14548
14549 /*
14550 * Transform any expressions present in the partition key
14551 *
14552 * Returns a transformed PartitionSpec, as well as the strategy code
14553 */
14554 static PartitionSpec *
transformPartitionSpec(Relation rel,PartitionSpec * partspec,char * strategy)14555 transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
14556 {
14557 PartitionSpec *newspec;
14558 ParseState *pstate;
14559 RangeTblEntry *rte;
14560 ListCell *l;
14561
14562 newspec = makeNode(PartitionSpec);
14563
14564 newspec->strategy = partspec->strategy;
14565 newspec->partParams = NIL;
14566 newspec->location = partspec->location;
14567
14568 /* Parse partitioning strategy name */
14569 if (pg_strcasecmp(partspec->strategy, "hash") == 0)
14570 *strategy = PARTITION_STRATEGY_HASH;
14571 else if (pg_strcasecmp(partspec->strategy, "list") == 0)
14572 *strategy = PARTITION_STRATEGY_LIST;
14573 else if (pg_strcasecmp(partspec->strategy, "range") == 0)
14574 *strategy = PARTITION_STRATEGY_RANGE;
14575 else
14576 ereport(ERROR,
14577 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
14578 errmsg("unrecognized partitioning strategy \"%s\"",
14579 partspec->strategy)));
14580
14581 /* Check valid number of columns for strategy */
14582 if (*strategy == PARTITION_STRATEGY_LIST &&
14583 list_length(partspec->partParams) != 1)
14584 ereport(ERROR,
14585 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14586 errmsg("cannot use \"list\" partition strategy with more than one column")));
14587
14588 /*
14589 * Create a dummy ParseState and insert the target relation as its sole
14590 * rangetable entry. We need a ParseState for transformExpr.
14591 */
14592 pstate = make_parsestate(NULL);
14593 rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
14594 addRTEtoQuery(pstate, rte, true, true, true);
14595
14596 /* take care of any partition expressions */
14597 foreach(l, partspec->partParams)
14598 {
14599 PartitionElem *pelem = castNode(PartitionElem, lfirst(l));
14600
14601 if (pelem->expr)
14602 {
14603 /* Copy, to avoid scribbling on the input */
14604 pelem = copyObject(pelem);
14605
14606 /* Now do parse transformation of the expression */
14607 pelem->expr = transformExpr(pstate, pelem->expr,
14608 EXPR_KIND_PARTITION_EXPRESSION);
14609
14610 /* we have to fix its collations too */
14611 assign_expr_collations(pstate, pelem->expr);
14612 }
14613
14614 newspec->partParams = lappend(newspec->partParams, pelem);
14615 }
14616
14617 return newspec;
14618 }
14619
14620 /*
14621 * Compute per-partition-column information from a list of PartitionElems.
14622 * Expressions in the PartitionElems must be parse-analyzed already.
14623 */
14624 static void
ComputePartitionAttrs(Relation rel,List * partParams,AttrNumber * partattrs,List ** partexprs,Oid * partopclass,Oid * partcollation,char strategy)14625 ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
14626 List **partexprs, Oid *partopclass, Oid *partcollation,
14627 char strategy)
14628 {
14629 int attn;
14630 ListCell *lc;
14631 Oid am_oid;
14632
14633 attn = 0;
14634 foreach(lc, partParams)
14635 {
14636 PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
14637 Oid atttype;
14638 Oid attcollation;
14639
14640 if (pelem->name != NULL)
14641 {
14642 /* Simple attribute reference */
14643 HeapTuple atttuple;
14644 Form_pg_attribute attform;
14645
14646 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
14647 pelem->name);
14648 if (!HeapTupleIsValid(atttuple))
14649 ereport(ERROR,
14650 (errcode(ERRCODE_UNDEFINED_COLUMN),
14651 errmsg("column \"%s\" named in partition key does not exist",
14652 pelem->name)));
14653 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
14654
14655 if (attform->attnum <= 0)
14656 ereport(ERROR,
14657 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14658 errmsg("cannot use system column \"%s\" in partition key",
14659 pelem->name)));
14660
14661 partattrs[attn] = attform->attnum;
14662 atttype = attform->atttypid;
14663 attcollation = attform->attcollation;
14664 ReleaseSysCache(atttuple);
14665 }
14666 else
14667 {
14668 /* Expression */
14669 Node *expr = pelem->expr;
14670
14671 Assert(expr != NULL);
14672 atttype = exprType(expr);
14673 attcollation = exprCollation(expr);
14674
14675 /*
14676 * The expression must be of a storable type (e.g., not RECORD).
14677 * The test is the same as for whether a table column is of a safe
14678 * type (which is why we needn't check for the non-expression
14679 * case).
14680 */
14681 CheckAttributeType("partition key",
14682 atttype, attcollation,
14683 NIL, false);
14684
14685 /*
14686 * Strip any top-level COLLATE clause. This ensures that we treat
14687 * "x COLLATE y" and "(x COLLATE y)" alike.
14688 */
14689 while (IsA(expr, CollateExpr))
14690 expr = (Node *) ((CollateExpr *) expr)->arg;
14691
14692 if (IsA(expr, Var) &&
14693 ((Var *) expr)->varattno > 0)
14694 {
14695 /*
14696 * User wrote "(column)" or "(column COLLATE something)".
14697 * Treat it like simple attribute anyway.
14698 */
14699 partattrs[attn] = ((Var *) expr)->varattno;
14700 }
14701 else
14702 {
14703 Bitmapset *expr_attrs = NULL;
14704 int i;
14705
14706 partattrs[attn] = 0; /* marks the column as expression */
14707 *partexprs = lappend(*partexprs, expr);
14708
14709 /*
14710 * Try to simplify the expression before checking for
14711 * mutability. The main practical value of doing it in this
14712 * order is that an inline-able SQL-language function will be
14713 * accepted if its expansion is immutable, whether or not the
14714 * function itself is marked immutable.
14715 *
14716 * Note that expression_planner does not change the passed in
14717 * expression destructively and we have already saved the
14718 * expression to be stored into the catalog above.
14719 */
14720 expr = (Node *) expression_planner((Expr *) expr);
14721
14722 /*
14723 * Partition expression cannot contain mutable functions,
14724 * because a given row must always map to the same partition
14725 * as long as there is no change in the partition boundary
14726 * structure.
14727 */
14728 if (contain_mutable_functions(expr))
14729 ereport(ERROR,
14730 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14731 errmsg("functions in partition key expression must be marked IMMUTABLE")));
14732
14733 /*
14734 * transformPartitionSpec() should have already rejected
14735 * subqueries, aggregates, window functions, and SRFs, based
14736 * on the EXPR_KIND_ for partition expressions.
14737 */
14738
14739 /*
14740 * Cannot have expressions containing whole-row references or
14741 * system column references.
14742 */
14743 pull_varattnos(expr, 1, &expr_attrs);
14744 if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
14745 expr_attrs))
14746 ereport(ERROR,
14747 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14748 errmsg("partition key expressions cannot contain whole-row references")));
14749 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
14750 {
14751 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
14752 expr_attrs))
14753 ereport(ERROR,
14754 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14755 errmsg("partition key expressions cannot contain system column references")));
14756 }
14757
14758 /*
14759 * While it is not exactly *wrong* for a partition expression
14760 * to be a constant, it seems better to reject such keys.
14761 */
14762 if (IsA(expr, Const))
14763 ereport(ERROR,
14764 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
14765 errmsg("cannot use constant expression as partition key")));
14766 }
14767 }
14768
14769 /*
14770 * Apply collation override if any
14771 */
14772 if (pelem->collation)
14773 attcollation = get_collation_oid(pelem->collation, false);
14774
14775 /*
14776 * Check we have a collation iff it's a collatable type. The only
14777 * expected failures here are (1) COLLATE applied to a noncollatable
14778 * type, or (2) partition expression had an unresolved collation. But
14779 * we might as well code this to be a complete consistency check.
14780 */
14781 if (type_is_collatable(atttype))
14782 {
14783 if (!OidIsValid(attcollation))
14784 ereport(ERROR,
14785 (errcode(ERRCODE_INDETERMINATE_COLLATION),
14786 errmsg("could not determine which collation to use for partition expression"),
14787 errhint("Use the COLLATE clause to set the collation explicitly.")));
14788 }
14789 else
14790 {
14791 if (OidIsValid(attcollation))
14792 ereport(ERROR,
14793 (errcode(ERRCODE_DATATYPE_MISMATCH),
14794 errmsg("collations are not supported by type %s",
14795 format_type_be(atttype))));
14796 }
14797
14798 partcollation[attn] = attcollation;
14799
14800 /*
14801 * Identify the appropriate operator class. For list and range
14802 * partitioning, we use a btree operator class; hash partitioning uses
14803 * a hash operator class.
14804 */
14805 if (strategy == PARTITION_STRATEGY_HASH)
14806 am_oid = HASH_AM_OID;
14807 else
14808 am_oid = BTREE_AM_OID;
14809
14810 if (!pelem->opclass)
14811 {
14812 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
14813
14814 if (!OidIsValid(partopclass[attn]))
14815 {
14816 if (strategy == PARTITION_STRATEGY_HASH)
14817 ereport(ERROR,
14818 (errcode(ERRCODE_UNDEFINED_OBJECT),
14819 errmsg("data type %s has no default hash operator class",
14820 format_type_be(atttype)),
14821 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
14822 else
14823 ereport(ERROR,
14824 (errcode(ERRCODE_UNDEFINED_OBJECT),
14825 errmsg("data type %s has no default btree operator class",
14826 format_type_be(atttype)),
14827 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
14828
14829 }
14830 }
14831 else
14832 partopclass[attn] = ResolveOpClass(pelem->opclass,
14833 atttype,
14834 am_oid == HASH_AM_OID ? "hash" : "btree",
14835 am_oid);
14836
14837 attn++;
14838 }
14839 }
14840
14841 /*
14842 * PartConstraintImpliedByRelConstraint
14843 * Do scanrel's existing constraints imply the partition constraint?
14844 *
14845 * "Existing constraints" include its check constraints and column-level
14846 * NOT NULL constraints. partConstraint describes the partition constraint,
14847 * in implicit-AND form.
14848 */
14849 bool
PartConstraintImpliedByRelConstraint(Relation scanrel,List * partConstraint)14850 PartConstraintImpliedByRelConstraint(Relation scanrel,
14851 List *partConstraint)
14852 {
14853 List *existConstraint = NIL;
14854 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
14855 int num_check,
14856 i;
14857
14858 if (constr && constr->has_not_null)
14859 {
14860 int natts = scanrel->rd_att->natts;
14861
14862 for (i = 1; i <= natts; i++)
14863 {
14864 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
14865
14866 if (att->attnotnull && !att->attisdropped)
14867 {
14868 NullTest *ntest = makeNode(NullTest);
14869
14870 ntest->arg = (Expr *) makeVar(1,
14871 i,
14872 att->atttypid,
14873 att->atttypmod,
14874 att->attcollation,
14875 0);
14876 ntest->nulltesttype = IS_NOT_NULL;
14877
14878 /*
14879 * argisrow=false is correct even for a composite column,
14880 * because attnotnull does not represent a SQL-spec IS NOT
14881 * NULL test in such a case, just IS DISTINCT FROM NULL.
14882 */
14883 ntest->argisrow = false;
14884 ntest->location = -1;
14885 existConstraint = lappend(existConstraint, ntest);
14886 }
14887 }
14888 }
14889
14890 num_check = (constr != NULL) ? constr->num_check : 0;
14891 for (i = 0; i < num_check; i++)
14892 {
14893 Node *cexpr;
14894
14895 /*
14896 * If this constraint hasn't been fully validated yet, we must ignore
14897 * it here.
14898 */
14899 if (!constr->check[i].ccvalid)
14900 continue;
14901
14902 cexpr = stringToNode(constr->check[i].ccbin);
14903
14904 /*
14905 * Run each expression through const-simplification and
14906 * canonicalization. It is necessary, because we will be comparing it
14907 * to similarly-processed partition constraint expressions, and may
14908 * fail to detect valid matches without this.
14909 */
14910 cexpr = eval_const_expressions(NULL, cexpr);
14911 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
14912
14913 existConstraint = list_concat(existConstraint,
14914 make_ands_implicit((Expr *) cexpr));
14915 }
14916
14917 /*
14918 * Try to make the proof. Since we are comparing CHECK constraints, we
14919 * need to use weak implication, i.e., we assume existConstraint is
14920 * not-false and try to prove the same for partConstraint.
14921 *
14922 * Note that predicate_implied_by assumes its first argument is known
14923 * immutable. That should always be true for partition constraints, so we
14924 * don't test it here.
14925 */
14926 return predicate_implied_by(partConstraint, existConstraint, true);
14927 }
14928
14929 /*
14930 * QueuePartitionConstraintValidation
14931 *
14932 * Add an entry to wqueue to have the given partition constraint validated by
14933 * Phase 3, for the given relation, and all its children.
14934 *
14935 * We first verify whether the given constraint is implied by pre-existing
14936 * relation constraints; if it is, there's no need to scan the table to
14937 * validate, so don't queue in that case.
14938 */
14939 static void
QueuePartitionConstraintValidation(List ** wqueue,Relation scanrel,List * partConstraint,bool validate_default)14940 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
14941 List *partConstraint,
14942 bool validate_default)
14943 {
14944 /*
14945 * Based on the table's existing constraints, determine whether or not we
14946 * may skip scanning the table.
14947 */
14948 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
14949 {
14950 if (!validate_default)
14951 ereport(INFO,
14952 (errmsg("partition constraint for table \"%s\" is implied by existing constraints",
14953 RelationGetRelationName(scanrel))));
14954 else
14955 ereport(INFO,
14956 (errmsg("updated partition constraint for default partition \"%s\" is implied by existing constraints",
14957 RelationGetRelationName(scanrel))));
14958 return;
14959 }
14960
14961 /*
14962 * Constraints proved insufficient. For plain relations, queue a
14963 * validation item now; for partitioned tables, recurse to process each
14964 * partition.
14965 */
14966 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
14967 {
14968 AlteredTableInfo *tab;
14969
14970 /* Grab a work queue entry. */
14971 tab = ATGetQueueEntry(wqueue, scanrel);
14972 Assert(tab->partition_constraint == NULL);
14973 tab->partition_constraint = (Expr *) linitial(partConstraint);
14974 tab->validate_default = validate_default;
14975 }
14976 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14977 {
14978 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel);
14979 int i;
14980
14981 for (i = 0; i < partdesc->nparts; i++)
14982 {
14983 Relation part_rel;
14984 bool found_whole_row;
14985 List *thisPartConstraint;
14986
14987 /*
14988 * This is the minimum lock we need to prevent deadlocks.
14989 */
14990 part_rel = heap_open(partdesc->oids[i], AccessExclusiveLock);
14991
14992 /*
14993 * Adjust the constraint for scanrel so that it matches this
14994 * partition's attribute numbers.
14995 */
14996 thisPartConstraint =
14997 map_partition_varattnos(partConstraint, 1,
14998 part_rel, scanrel, &found_whole_row);
14999 /* There can never be a whole-row reference here */
15000 if (found_whole_row)
15001 elog(ERROR, "unexpected whole-row reference found in partition constraint");
15002
15003 QueuePartitionConstraintValidation(wqueue, part_rel,
15004 thisPartConstraint,
15005 validate_default);
15006 heap_close(part_rel, NoLock); /* keep lock till commit */
15007 }
15008 }
15009 }
15010
15011 /*
15012 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
15013 *
15014 * Return the address of the newly attached partition.
15015 */
15016 static ObjectAddress
ATExecAttachPartition(List ** wqueue,Relation rel,PartitionCmd * cmd)15017 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
15018 {
15019 Relation attachrel,
15020 catalog;
15021 List *attachrel_children;
15022 List *partConstraint;
15023 SysScanDesc scan;
15024 ScanKeyData skey;
15025 AttrNumber attno;
15026 int natts;
15027 TupleDesc tupleDesc;
15028 ObjectAddress address;
15029 const char *trigger_name;
15030 bool found_whole_row;
15031 Oid defaultPartOid;
15032 List *partBoundConstraint;
15033 List *cloned;
15034 ListCell *l;
15035
15036 /*
15037 * We must lock the default partition if one exists, because attaching a
15038 * new partition will change its partition constraint.
15039 */
15040 defaultPartOid =
15041 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel));
15042 if (OidIsValid(defaultPartOid))
15043 LockRelationOid(defaultPartOid, AccessExclusiveLock);
15044
15045 attachrel = heap_openrv(cmd->name, AccessExclusiveLock);
15046
15047 /*
15048 * XXX I think it'd be a good idea to grab locks on all tables referenced
15049 * by FKs at this point also.
15050 */
15051
15052 /*
15053 * Must be owner of both parent and source table -- parent was checked by
15054 * ATSimplePermissions call in ATPrepCmd
15055 */
15056 ATSimplePermissions(attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
15057
15058 /* A partition can only have one parent */
15059 if (attachrel->rd_rel->relispartition)
15060 ereport(ERROR,
15061 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15062 errmsg("\"%s\" is already a partition",
15063 RelationGetRelationName(attachrel))));
15064
15065 if (OidIsValid(attachrel->rd_rel->reloftype))
15066 ereport(ERROR,
15067 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15068 errmsg("cannot attach a typed table as partition")));
15069
15070 /*
15071 * Table being attached should not already be part of inheritance; either
15072 * as a child table...
15073 */
15074 catalog = heap_open(InheritsRelationId, AccessShareLock);
15075 ScanKeyInit(&skey,
15076 Anum_pg_inherits_inhrelid,
15077 BTEqualStrategyNumber, F_OIDEQ,
15078 ObjectIdGetDatum(RelationGetRelid(attachrel)));
15079 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
15080 NULL, 1, &skey);
15081 if (HeapTupleIsValid(systable_getnext(scan)))
15082 ereport(ERROR,
15083 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15084 errmsg("cannot attach inheritance child as partition")));
15085 systable_endscan(scan);
15086
15087 /* ...or as a parent table (except the case when it is partitioned) */
15088 ScanKeyInit(&skey,
15089 Anum_pg_inherits_inhparent,
15090 BTEqualStrategyNumber, F_OIDEQ,
15091 ObjectIdGetDatum(RelationGetRelid(attachrel)));
15092 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
15093 1, &skey);
15094 if (HeapTupleIsValid(systable_getnext(scan)) &&
15095 attachrel->rd_rel->relkind == RELKIND_RELATION)
15096 ereport(ERROR,
15097 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15098 errmsg("cannot attach inheritance parent as partition")));
15099 systable_endscan(scan);
15100 heap_close(catalog, AccessShareLock);
15101
15102 /*
15103 * Prevent circularity by seeing if rel is a partition of attachrel. (In
15104 * particular, this disallows making a rel a partition of itself.)
15105 *
15106 * We do that by checking if rel is a member of the list of attachrel's
15107 * partitions provided the latter is partitioned at all. We want to avoid
15108 * having to construct this list again, so we request the strongest lock
15109 * on all partitions. We need the strongest lock, because we may decide
15110 * to scan them if we find out that the table being attached (or its leaf
15111 * partitions) may contain rows that violate the partition constraint. If
15112 * the table has a constraint that would prevent such rows, which by
15113 * definition is present in all the partitions, we need not scan the
15114 * table, nor its partitions. But we cannot risk a deadlock by taking a
15115 * weaker lock now and the stronger one only when needed.
15116 */
15117 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
15118 AccessExclusiveLock, NULL);
15119 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
15120 ereport(ERROR,
15121 (errcode(ERRCODE_DUPLICATE_TABLE),
15122 errmsg("circular inheritance not allowed"),
15123 errdetail("\"%s\" is already a child of \"%s\".",
15124 RelationGetRelationName(rel),
15125 RelationGetRelationName(attachrel))));
15126
15127 /* If the parent is permanent, so must be all of its partitions. */
15128 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
15129 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
15130 ereport(ERROR,
15131 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15132 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
15133 RelationGetRelationName(rel))));
15134
15135 /* Temp parent cannot have a partition that is itself not a temp */
15136 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15137 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
15138 ereport(ERROR,
15139 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15140 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
15141 RelationGetRelationName(rel))));
15142
15143 /* If the parent is temp, it must belong to this session */
15144 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15145 !rel->rd_islocaltemp)
15146 ereport(ERROR,
15147 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15148 errmsg("cannot attach as partition of temporary relation of another session")));
15149
15150 /* Ditto for the partition */
15151 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15152 !attachrel->rd_islocaltemp)
15153 ereport(ERROR,
15154 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15155 errmsg("cannot attach temporary relation of another session as partition")));
15156
15157 /* If parent has OIDs then child must have OIDs */
15158 if (rel->rd_rel->relhasoids && !attachrel->rd_rel->relhasoids)
15159 ereport(ERROR,
15160 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15161 errmsg("cannot attach table \"%s\" without OIDs as partition of"
15162 " table \"%s\" with OIDs", RelationGetRelationName(attachrel),
15163 RelationGetRelationName(rel))));
15164
15165 /* OTOH, if parent doesn't have them, do not allow in attachrel either */
15166 if (attachrel->rd_rel->relhasoids && !rel->rd_rel->relhasoids)
15167 ereport(ERROR,
15168 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15169 errmsg("cannot attach table \"%s\" with OIDs as partition of table"
15170 " \"%s\" without OIDs", RelationGetRelationName(attachrel),
15171 RelationGetRelationName(rel))));
15172
15173 /* Check if there are any columns in attachrel that aren't in the parent */
15174 tupleDesc = RelationGetDescr(attachrel);
15175 natts = tupleDesc->natts;
15176 for (attno = 1; attno <= natts; attno++)
15177 {
15178 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
15179 char *attributeName = NameStr(attribute->attname);
15180
15181 /* Ignore dropped */
15182 if (attribute->attisdropped)
15183 continue;
15184
15185 /* Try to find the column in parent (matching on column name) */
15186 if (!SearchSysCacheExists2(ATTNAME,
15187 ObjectIdGetDatum(RelationGetRelid(rel)),
15188 CStringGetDatum(attributeName)))
15189 ereport(ERROR,
15190 (errcode(ERRCODE_DATATYPE_MISMATCH),
15191 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
15192 RelationGetRelationName(attachrel), attributeName,
15193 RelationGetRelationName(rel)),
15194 errdetail("The new partition may contain only the columns present in parent.")));
15195 }
15196
15197 /*
15198 * If child_rel has row-level triggers with transition tables, we
15199 * currently don't allow it to become a partition. See also prohibitions
15200 * in ATExecAddInherit() and CreateTrigger().
15201 */
15202 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
15203 if (trigger_name != NULL)
15204 ereport(ERROR,
15205 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15206 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
15207 trigger_name, RelationGetRelationName(attachrel)),
15208 errdetail("ROW triggers with transition tables are not supported on partitions")));
15209
15210 /*
15211 * Check that the new partition's bound is valid and does not overlap any
15212 * of existing partitions of the parent - note that it does not return on
15213 * error.
15214 */
15215 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
15216 cmd->bound);
15217
15218 /* OK to create inheritance. Rest of the checks performed there */
15219 CreateInheritance(attachrel, rel);
15220
15221 /* Update the pg_class entry. */
15222 StorePartitionBound(attachrel, rel, cmd->bound);
15223
15224 /* Ensure there exists a correct set of indexes in the partition. */
15225 AttachPartitionEnsureIndexes(rel, attachrel);
15226
15227 /* and triggers */
15228 CloneRowTriggersToPartition(rel, attachrel);
15229
15230 /*
15231 * Clone foreign key constraints, and setup for Phase 3 to verify them.
15232 */
15233 cloned = NIL;
15234 CloneForeignKeyConstraints(RelationGetRelid(rel),
15235 RelationGetRelid(attachrel), &cloned);
15236 foreach(l, cloned)
15237 {
15238 ClonedConstraint *clonedcon = lfirst(l);
15239 NewConstraint *newcon;
15240 Relation clonedrel;
15241 AlteredTableInfo *parttab;
15242
15243 clonedrel = relation_open(clonedcon->relid, NoLock);
15244 parttab = ATGetQueueEntry(wqueue, clonedrel);
15245
15246 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
15247 newcon->name = clonedcon->constraint->conname;
15248 newcon->contype = CONSTR_FOREIGN;
15249 newcon->refrelid = clonedcon->refrelid;
15250 newcon->refindid = clonedcon->conindid;
15251 newcon->conid = clonedcon->conid;
15252 newcon->qual = (Node *) clonedcon->constraint;
15253
15254 parttab->constraints = lappend(parttab->constraints, newcon);
15255
15256 relation_close(clonedrel, NoLock);
15257 }
15258
15259 /*
15260 * Generate partition constraint from the partition bound specification.
15261 * If the parent itself is a partition, make sure to include its
15262 * constraint as well.
15263 */
15264 partBoundConstraint = get_qual_from_partbound(attachrel, rel, cmd->bound);
15265 partConstraint = list_concat(partBoundConstraint,
15266 RelationGetPartitionQual(rel));
15267
15268 /* Skip validation if there are no constraints to validate. */
15269 if (partConstraint)
15270 {
15271 /*
15272 * Run the partition quals through const-simplification similar to
15273 * check constraints. We skip canonicalize_qual, though, because
15274 * partition quals should be in canonical form already.
15275 */
15276 partConstraint =
15277 (List *) eval_const_expressions(NULL,
15278 (Node *) partConstraint);
15279
15280 /* XXX this sure looks wrong */
15281 partConstraint = list_make1(make_ands_explicit(partConstraint));
15282
15283 /*
15284 * Adjust the generated constraint to match this partition's attribute
15285 * numbers.
15286 */
15287 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
15288 rel, &found_whole_row);
15289 /* There can never be a whole-row reference here */
15290 if (found_whole_row)
15291 elog(ERROR,
15292 "unexpected whole-row reference found in partition key");
15293
15294 /* Validate partition constraints against the table being attached. */
15295 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
15296 false);
15297 }
15298
15299 /*
15300 * If we're attaching a partition other than the default partition and a
15301 * default one exists, then that partition's partition constraint changes,
15302 * so add an entry to the work queue to validate it, too. (We must not do
15303 * this when the partition being attached is the default one; we already
15304 * did it above!)
15305 */
15306 if (OidIsValid(defaultPartOid))
15307 {
15308 Relation defaultrel;
15309 List *defPartConstraint;
15310
15311 Assert(!cmd->bound->is_default);
15312
15313 /* we already hold a lock on the default partition */
15314 defaultrel = heap_open(defaultPartOid, NoLock);
15315 defPartConstraint =
15316 get_proposed_default_constraint(partBoundConstraint);
15317 /*
15318 * Map the Vars in the constraint expression from rel's attnos to
15319 * defaultrel's.
15320 */
15321 defPartConstraint =
15322 map_partition_varattnos(defPartConstraint,
15323 1, defaultrel, rel, NULL);
15324 QueuePartitionConstraintValidation(wqueue, defaultrel,
15325 defPartConstraint, true);
15326
15327 /* keep our lock until commit. */
15328 heap_close(defaultrel, NoLock);
15329 }
15330
15331 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
15332
15333 /*
15334 * If the partition we just attached is partitioned itself, invalidate
15335 * relcache for all descendent partitions too to ensure that their
15336 * rd_partcheck expression trees are rebuilt; partitions already locked
15337 * at the beginning of this function.
15338 */
15339 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15340 {
15341 ListCell *l;
15342
15343 foreach(l, attachrel_children)
15344 {
15345 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
15346 }
15347 }
15348
15349 /* keep our lock until commit */
15350 heap_close(attachrel, NoLock);
15351
15352 return address;
15353 }
15354
15355 /*
15356 * AttachPartitionEnsureIndexes
15357 * subroutine for ATExecAttachPartition to create/match indexes
15358 *
15359 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
15360 * PARTITION: every partition must have an index attached to each index on the
15361 * partitioned table.
15362 */
15363 static void
AttachPartitionEnsureIndexes(Relation rel,Relation attachrel)15364 AttachPartitionEnsureIndexes(Relation rel, Relation attachrel)
15365 {
15366 List *idxes;
15367 List *attachRelIdxs;
15368 Relation *attachrelIdxRels;
15369 IndexInfo **attachInfos;
15370 int i;
15371 ListCell *cell;
15372 MemoryContext cxt;
15373 MemoryContext oldcxt;
15374
15375 cxt = AllocSetContextCreate(CurrentMemoryContext,
15376 "AttachPartitionEnsureIndexes",
15377 ALLOCSET_DEFAULT_SIZES);
15378 oldcxt = MemoryContextSwitchTo(cxt);
15379
15380 idxes = RelationGetIndexList(rel);
15381 attachRelIdxs = RelationGetIndexList(attachrel);
15382 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
15383 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
15384
15385 /* Build arrays of all existing indexes and their IndexInfos */
15386 i = 0;
15387 foreach(cell, attachRelIdxs)
15388 {
15389 Oid cldIdxId = lfirst_oid(cell);
15390
15391 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
15392 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
15393 i++;
15394 }
15395
15396 /*
15397 * If we're attaching a foreign table, we must fail if any of the indexes
15398 * is a constraint index; otherwise, there's nothing to do here. Do this
15399 * before starting work, to avoid wasting the effort of building a few
15400 * non-unique indexes before coming across a unique one.
15401 */
15402 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
15403 {
15404 foreach(cell, idxes)
15405 {
15406 Oid idx = lfirst_oid(cell);
15407 Relation idxRel = index_open(idx, AccessShareLock);
15408
15409 if (idxRel->rd_index->indisunique ||
15410 idxRel->rd_index->indisprimary)
15411 ereport(ERROR,
15412 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15413 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
15414 RelationGetRelationName(attachrel),
15415 RelationGetRelationName(rel)),
15416 errdetail("Table \"%s\" contains unique indexes.",
15417 RelationGetRelationName(rel))));
15418 index_close(idxRel, AccessShareLock);
15419 }
15420
15421 goto out;
15422 }
15423
15424 /*
15425 * For each index on the partitioned table, find a matching one in the
15426 * partition-to-be; if one is not found, create one.
15427 */
15428 foreach(cell, idxes)
15429 {
15430 Oid idx = lfirst_oid(cell);
15431 Relation idxRel = index_open(idx, AccessShareLock);
15432 IndexInfo *info;
15433 AttrNumber *attmap;
15434 bool found = false;
15435 Oid constraintOid;
15436
15437 /*
15438 * Ignore indexes in the partitioned table other than partitioned
15439 * indexes.
15440 */
15441 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15442 {
15443 index_close(idxRel, AccessShareLock);
15444 continue;
15445 }
15446
15447 /* construct an indexinfo to compare existing indexes against */
15448 info = BuildIndexInfo(idxRel);
15449 attmap = convert_tuples_by_name_map(RelationGetDescr(attachrel),
15450 RelationGetDescr(rel),
15451 gettext_noop("could not convert row type"));
15452 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
15453
15454 /*
15455 * Scan the list of existing indexes in the partition-to-be, and mark
15456 * the first matching, unattached one we find, if any, as partition of
15457 * the parent index. If we find one, we're done.
15458 */
15459 for (i = 0; i < list_length(attachRelIdxs); i++)
15460 {
15461 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
15462 Oid cldConstrOid = InvalidOid;
15463
15464 /* does this index have a parent? if so, can't use it */
15465 if (attachrelIdxRels[i]->rd_rel->relispartition)
15466 continue;
15467
15468 if (CompareIndexInfo(attachInfos[i], info,
15469 attachrelIdxRels[i]->rd_indcollation,
15470 idxRel->rd_indcollation,
15471 attachrelIdxRels[i]->rd_opfamily,
15472 idxRel->rd_opfamily,
15473 attmap,
15474 RelationGetDescr(rel)->natts))
15475 {
15476 /*
15477 * If this index is being created in the parent because of a
15478 * constraint, then the child needs to have a constraint also,
15479 * so look for one. If there is no such constraint, this
15480 * index is no good, so keep looking.
15481 */
15482 if (OidIsValid(constraintOid))
15483 {
15484 cldConstrOid =
15485 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
15486 cldIdxId);
15487 /* no dice */
15488 if (!OidIsValid(cldConstrOid))
15489 continue;
15490 }
15491
15492 /* bingo. */
15493 IndexSetParentIndex(attachrelIdxRels[i], idx);
15494 if (OidIsValid(constraintOid))
15495 ConstraintSetParentConstraint(cldConstrOid, constraintOid);
15496 found = true;
15497 break;
15498 }
15499 }
15500
15501 /*
15502 * If no suitable index was found in the partition-to-be, create one
15503 * now.
15504 */
15505 if (!found)
15506 {
15507 IndexStmt *stmt;
15508 Oid constraintOid;
15509
15510 stmt = generateClonedIndexStmt(NULL, RelationGetRelid(attachrel),
15511 idxRel, attmap,
15512 RelationGetDescr(rel)->natts,
15513 &constraintOid);
15514 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
15515 RelationGetRelid(idxRel),
15516 constraintOid,
15517 true, false, false, false, false);
15518 }
15519
15520 index_close(idxRel, AccessShareLock);
15521 }
15522
15523 out:
15524 /* Clean up. */
15525 for (i = 0; i < list_length(attachRelIdxs); i++)
15526 index_close(attachrelIdxRels[i], AccessShareLock);
15527 MemoryContextSwitchTo(oldcxt);
15528 MemoryContextDelete(cxt);
15529 }
15530
15531 /*
15532 * isPartitionTrigger
15533 * Subroutine for CloneRowTriggersToPartition: determine whether
15534 * the given trigger has been cloned from another one.
15535 *
15536 * We use pg_depend as a proxy for this, since we don't have any direct
15537 * evidence. This is an ugly hack to cope with a catalog deficiency.
15538 * Keep away from children. Do not stare with naked eyes. Do not propagate.
15539 */
15540 static bool
isPartitionTrigger(Oid trigger_oid)15541 isPartitionTrigger(Oid trigger_oid)
15542 {
15543 Relation pg_depend;
15544 ScanKeyData key[2];
15545 SysScanDesc scan;
15546 HeapTuple tup;
15547 bool found = false;
15548
15549 pg_depend = heap_open(DependRelationId, AccessShareLock);
15550
15551 ScanKeyInit(&key[0], Anum_pg_depend_classid,
15552 BTEqualStrategyNumber,
15553 F_OIDEQ,
15554 ObjectIdGetDatum(TriggerRelationId));
15555 ScanKeyInit(&key[1], Anum_pg_depend_objid,
15556 BTEqualStrategyNumber,
15557 F_OIDEQ,
15558 ObjectIdGetDatum(trigger_oid));
15559
15560 scan = systable_beginscan(pg_depend, DependDependerIndexId,
15561 true, NULL, 2, key);
15562 while ((tup = systable_getnext(scan)) != NULL)
15563 {
15564 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(tup);
15565
15566 if (dep->refclassid == TriggerRelationId)
15567 {
15568 found = true;
15569 break;
15570 }
15571 }
15572
15573 systable_endscan(scan);
15574 heap_close(pg_depend, AccessShareLock);
15575
15576 return found;
15577 }
15578
15579 /*
15580 * CloneRowTriggersToPartition
15581 * subroutine for ATExecAttachPartition/DefineRelation to create row
15582 * triggers on partitions
15583 */
15584 static void
CloneRowTriggersToPartition(Relation parent,Relation partition)15585 CloneRowTriggersToPartition(Relation parent, Relation partition)
15586 {
15587 Relation pg_trigger;
15588 ScanKeyData key;
15589 SysScanDesc scan;
15590 HeapTuple tuple;
15591 MemoryContext perTupCxt;
15592
15593 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
15594 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
15595 pg_trigger = heap_open(TriggerRelationId, RowExclusiveLock);
15596 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
15597 true, NULL, 1, &key);
15598
15599 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
15600 "clone trig", ALLOCSET_SMALL_SIZES);
15601
15602 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
15603 {
15604 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
15605 CreateTrigStmt *trigStmt;
15606 Node *qual = NULL;
15607 Datum value;
15608 bool isnull;
15609 List *cols = NIL;
15610 List *trigargs = NIL;
15611 MemoryContext oldcxt;
15612
15613 /*
15614 * Ignore statement-level triggers; those are not cloned.
15615 */
15616 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
15617 continue;
15618
15619 /*
15620 * Internal triggers require careful examination. Ideally, we don't
15621 * clone them.
15622 *
15623 * However, if our parent is a partitioned relation, there might be
15624 * internal triggers that need cloning. In that case, we must
15625 * skip clone it if the trigger on parent depends on another trigger.
15626 *
15627 * Note we dare not verify that the other trigger belongs to an
15628 * ancestor relation of our parent, because that creates deadlock
15629 * opportunities.
15630 */
15631 if (trigForm->tgisinternal &&
15632 (!parent->rd_rel->relispartition ||
15633 !isPartitionTrigger(HeapTupleGetOid(tuple))))
15634 continue;
15635
15636 /*
15637 * Complain if we find an unexpected trigger type.
15638 */
15639 if (!TRIGGER_FOR_AFTER(trigForm->tgtype))
15640 elog(ERROR, "unexpected trigger \"%s\" found",
15641 NameStr(trigForm->tgname));
15642
15643 /* Use short-lived context for CREATE TRIGGER */
15644 oldcxt = MemoryContextSwitchTo(perTupCxt);
15645
15646 /*
15647 * If there is a WHEN clause, generate a 'cooked' version of it that's
15648 * appropriate for the partition.
15649 */
15650 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
15651 RelationGetDescr(pg_trigger), &isnull);
15652 if (!isnull)
15653 {
15654 bool found_whole_row;
15655
15656 qual = stringToNode(TextDatumGetCString(value));
15657 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
15658 partition, parent,
15659 &found_whole_row);
15660 if (found_whole_row)
15661 elog(ERROR, "unexpected whole-row reference found in trigger WHEN clause");
15662 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
15663 partition, parent,
15664 &found_whole_row);
15665 if (found_whole_row)
15666 elog(ERROR, "unexpected whole-row reference found in trigger WHEN clause");
15667 }
15668
15669 /*
15670 * If there is a column list, transform it to a list of column names.
15671 * Note we don't need to map this list in any way ...
15672 */
15673 if (trigForm->tgattr.dim1 > 0)
15674 {
15675 int i;
15676
15677 for (i = 0; i < trigForm->tgattr.dim1; i++)
15678 {
15679 Form_pg_attribute col;
15680
15681 col = TupleDescAttr(parent->rd_att,
15682 trigForm->tgattr.values[i] - 1);
15683 cols = lappend(cols,
15684 makeString(pstrdup(NameStr(col->attname))));
15685 }
15686 }
15687
15688 /* Reconstruct trigger arguments list. */
15689 if (trigForm->tgnargs > 0)
15690 {
15691 char *p;
15692 int i;
15693
15694 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
15695 RelationGetDescr(pg_trigger), &isnull);
15696 if (isnull)
15697 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
15698 NameStr(trigForm->tgname), RelationGetRelationName(partition));
15699
15700 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
15701
15702 for (i = 0; i < trigForm->tgnargs; i++)
15703 {
15704 trigargs = lappend(trigargs, makeString(pstrdup(p)));
15705 p += strlen(p) + 1;
15706 }
15707 }
15708
15709 trigStmt = makeNode(CreateTrigStmt);
15710 trigStmt->trigname = NameStr(trigForm->tgname);
15711 trigStmt->relation = NULL;
15712 trigStmt->funcname = NULL; /* passed separately */
15713 trigStmt->args = trigargs;
15714 trigStmt->row = true;
15715 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
15716 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
15717 trigStmt->columns = cols;
15718 trigStmt->whenClause = NULL; /* passed separately */
15719 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
15720 trigStmt->transitionRels = NIL; /* not supported at present */
15721 trigStmt->deferrable = trigForm->tgdeferrable;
15722 trigStmt->initdeferred = trigForm->tginitdeferred;
15723 trigStmt->constrrel = NULL; /* passed separately */
15724
15725 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
15726 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
15727 trigForm->tgfoid, HeapTupleGetOid(tuple), qual,
15728 false, true, trigForm->tgenabled);
15729
15730 MemoryContextSwitchTo(oldcxt);
15731 MemoryContextReset(perTupCxt);
15732 }
15733
15734 MemoryContextDelete(perTupCxt);
15735
15736 systable_endscan(scan);
15737 heap_close(pg_trigger, RowExclusiveLock);
15738 }
15739
15740 /*
15741 * ALTER TABLE DETACH PARTITION
15742 *
15743 * Return the address of the relation that is no longer a partition of rel.
15744 */
15745 static ObjectAddress
ATExecDetachPartition(Relation rel,RangeVar * name)15746 ATExecDetachPartition(Relation rel, RangeVar *name)
15747 {
15748 Relation partRel,
15749 classRel;
15750 HeapTuple tuple,
15751 newtuple;
15752 Datum new_val[Natts_pg_class];
15753 bool new_null[Natts_pg_class],
15754 new_repl[Natts_pg_class];
15755 ObjectAddress address;
15756 Oid defaultPartOid;
15757 List *indexes;
15758 List *fks;
15759 ListCell *cell;
15760
15761 /*
15762 * We must lock the default partition, because detaching this partition
15763 * will change its partition constraint.
15764 */
15765 defaultPartOid =
15766 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel));
15767 if (OidIsValid(defaultPartOid))
15768 LockRelationOid(defaultPartOid, AccessExclusiveLock);
15769
15770 partRel = heap_openrv(name, ShareUpdateExclusiveLock);
15771
15772 /* All inheritance related checks are performed within the function */
15773 RemoveInheritance(partRel, rel);
15774
15775 /* Update pg_class tuple */
15776 classRel = heap_open(RelationRelationId, RowExclusiveLock);
15777 tuple = SearchSysCacheCopy1(RELOID,
15778 ObjectIdGetDatum(RelationGetRelid(partRel)));
15779 if (!HeapTupleIsValid(tuple))
15780 elog(ERROR, "cache lookup failed for relation %u",
15781 RelationGetRelid(partRel));
15782 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
15783
15784 /* Clear relpartbound and reset relispartition */
15785 memset(new_val, 0, sizeof(new_val));
15786 memset(new_null, false, sizeof(new_null));
15787 memset(new_repl, false, sizeof(new_repl));
15788 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
15789 new_null[Anum_pg_class_relpartbound - 1] = true;
15790 new_repl[Anum_pg_class_relpartbound - 1] = true;
15791 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
15792 new_val, new_null, new_repl);
15793
15794 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
15795 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
15796 heap_freetuple(newtuple);
15797
15798 if (OidIsValid(defaultPartOid))
15799 {
15800 /*
15801 * If the relation being detached is the default partition itself,
15802 * remove it from the parent's pg_partitioned_table entry.
15803 *
15804 * If not, we must invalidate default partition's relcache entry, as
15805 * in StorePartitionBound: its partition constraint depends on every
15806 * other partition's partition constraint.
15807 */
15808 if (RelationGetRelid(partRel) == defaultPartOid)
15809 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
15810 else
15811 CacheInvalidateRelcacheByRelid(defaultPartOid);
15812 }
15813
15814 /* detach indexes too */
15815 indexes = RelationGetIndexList(partRel);
15816 foreach(cell, indexes)
15817 {
15818 Oid idxid = lfirst_oid(cell);
15819 Relation idx;
15820 Oid constrOid;
15821
15822 if (!has_superclass(idxid))
15823 continue;
15824
15825 Assert((IndexGetRelation(get_partition_parent(idxid), false) ==
15826 RelationGetRelid(rel)));
15827
15828 idx = index_open(idxid, AccessExclusiveLock);
15829 IndexSetParentIndex(idx, InvalidOid);
15830
15831 /* If there's a constraint associated with the index, detach it too */
15832 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
15833 idxid);
15834 if (OidIsValid(constrOid))
15835 ConstraintSetParentConstraint(constrOid, InvalidOid);
15836
15837 index_close(idx, NoLock);
15838 }
15839 heap_close(classRel, RowExclusiveLock);
15840
15841 /* Drop any triggers that were cloned on creation/attach. */
15842 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
15843
15844 /*
15845 * Detach any foreign keys that are inherited. This includes creating
15846 * additional action triggers.
15847 */
15848 fks = copyObject(RelationGetFKeyList(partRel));
15849 foreach(cell, fks)
15850 {
15851 ForeignKeyCacheInfo *fk = lfirst(cell);
15852 HeapTuple contup;
15853 Form_pg_constraint conform;
15854 Constraint *fkconstraint;
15855
15856 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
15857 if (!HeapTupleIsValid(contup))
15858 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
15859 conform = (Form_pg_constraint) GETSTRUCT(contup);
15860
15861 /* consider only the inherited foreign keys */
15862 if (conform->contype != CONSTRAINT_FOREIGN ||
15863 !OidIsValid(conform->conparentid))
15864 {
15865 ReleaseSysCache(contup);
15866 continue;
15867 }
15868
15869 /* unset conparentid and adjust conislocal, coninhcount, etc. */
15870 ConstraintSetParentConstraint(fk->conoid, InvalidOid);
15871
15872 /*
15873 * Make the action triggers on the referenced relation. When this was
15874 * a partition the action triggers pointed to the parent rel (they
15875 * still do), but now we need separate ones of our own.
15876 */
15877 fkconstraint = makeNode(Constraint);
15878 fkconstraint->conname = pstrdup(NameStr(conform->conname));
15879 fkconstraint->fk_upd_action = conform->confupdtype;
15880 fkconstraint->fk_del_action = conform->confdeltype;
15881 fkconstraint->deferrable = conform->condeferrable;
15882 fkconstraint->initdeferred = conform->condeferred;
15883
15884 createForeignKeyActionTriggers(partRel, conform->confrelid,
15885 fkconstraint, fk->conoid,
15886 conform->conindid);
15887
15888 ReleaseSysCache(contup);
15889 }
15890 list_free_deep(fks);
15891
15892 /*
15893 * Invalidate the parent's relcache so that the partition is no longer
15894 * included in its partition descriptor.
15895 */
15896 CacheInvalidateRelcache(rel);
15897
15898 /*
15899 * If the partition we just detached is partitioned itself, invalidate
15900 * relcache for all descendent partitions too to ensure that their
15901 * rd_partcheck expression trees are rebuilt; must lock partitions
15902 * before doing so, using the same lockmode as what partRel has been
15903 * locked with by the caller.
15904 */
15905 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15906 {
15907 List *children;
15908
15909 children = find_all_inheritors(RelationGetRelid(partRel),
15910 AccessExclusiveLock, NULL);
15911 foreach(cell, children)
15912 {
15913 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
15914 }
15915 }
15916
15917 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
15918
15919 /* keep our lock until commit */
15920 heap_close(partRel, NoLock);
15921
15922 return address;
15923 }
15924
15925 /*
15926 * DropClonedTriggersFromPartition
15927 * subroutine for ATExecDetachPartition to remove any triggers that were
15928 * cloned to the partition when it was created-as-partition or attached.
15929 * This undoes what CloneRowTriggersToPartition did.
15930 */
15931 static void
DropClonedTriggersFromPartition(Oid partitionId)15932 DropClonedTriggersFromPartition(Oid partitionId)
15933 {
15934 ScanKeyData skey;
15935 SysScanDesc scan;
15936 HeapTuple trigtup;
15937 Relation tgrel;
15938 ObjectAddresses *objects;
15939
15940 objects = new_object_addresses();
15941
15942 /*
15943 * Scan pg_trigger to search for all triggers on this rel.
15944 */
15945 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
15946 F_OIDEQ, ObjectIdGetDatum(partitionId));
15947 tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
15948 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
15949 true, NULL, 1, &skey);
15950 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
15951 {
15952 Oid trigoid = HeapTupleGetOid(trigtup);
15953 ObjectAddress trig;
15954
15955 /* Ignore triggers that weren't cloned */
15956 if (!isPartitionTrigger(trigoid))
15957 continue;
15958
15959 /*
15960 * This is ugly, but necessary: remove the dependency markings on the
15961 * trigger so that it can be removed.
15962 */
15963 deleteDependencyRecordsForClass(TriggerRelationId, trigoid,
15964 TriggerRelationId,
15965 DEPENDENCY_INTERNAL_AUTO);
15966 deleteDependencyRecordsForClass(TriggerRelationId, trigoid,
15967 RelationRelationId,
15968 DEPENDENCY_INTERNAL_AUTO);
15969
15970 /* remember this trigger to remove it below */
15971 ObjectAddressSet(trig, TriggerRelationId, trigoid);
15972 add_exact_object_address(&trig, objects);
15973 }
15974
15975 /* make the dependency removal visible to the deletion below */
15976 CommandCounterIncrement();
15977 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
15978
15979 /* done */
15980 free_object_addresses(objects);
15981 systable_endscan(scan);
15982 heap_close(tgrel, RowExclusiveLock);
15983 }
15984
15985 /*
15986 * Before acquiring lock on an index, acquire the same lock on the owning
15987 * table.
15988 */
15989 struct AttachIndexCallbackState
15990 {
15991 Oid partitionOid;
15992 Oid parentTblOid;
15993 bool lockedParentTbl;
15994 };
15995
15996 static void
RangeVarCallbackForAttachIndex(const RangeVar * rv,Oid relOid,Oid oldRelOid,void * arg)15997 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
15998 void *arg)
15999 {
16000 struct AttachIndexCallbackState *state;
16001 Form_pg_class classform;
16002 HeapTuple tuple;
16003
16004 state = (struct AttachIndexCallbackState *) arg;
16005
16006 if (!state->lockedParentTbl)
16007 {
16008 LockRelationOid(state->parentTblOid, AccessShareLock);
16009 state->lockedParentTbl = true;
16010 }
16011
16012 /*
16013 * If we previously locked some other heap, and the name we're looking up
16014 * no longer refers to an index on that relation, release the now-useless
16015 * lock. XXX maybe we should do *after* we verify whether the index does
16016 * not actually belong to the same relation ...
16017 */
16018 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
16019 {
16020 UnlockRelationOid(state->partitionOid, AccessShareLock);
16021 state->partitionOid = InvalidOid;
16022 }
16023
16024 /* Didn't find a relation, so no need for locking or permission checks. */
16025 if (!OidIsValid(relOid))
16026 return;
16027
16028 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
16029 if (!HeapTupleIsValid(tuple))
16030 return; /* concurrently dropped, so nothing to do */
16031 classform = (Form_pg_class) GETSTRUCT(tuple);
16032 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
16033 classform->relkind != RELKIND_INDEX)
16034 ereport(ERROR,
16035 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16036 errmsg("\"%s\" is not an index", rv->relname)));
16037 ReleaseSysCache(tuple);
16038
16039 /*
16040 * Since we need only examine the heap's tupledesc, an access share lock
16041 * on it (preventing any DDL) is sufficient.
16042 */
16043 state->partitionOid = IndexGetRelation(relOid, false);
16044 LockRelationOid(state->partitionOid, AccessShareLock);
16045 }
16046
16047 /*
16048 * ALTER INDEX i1 ATTACH PARTITION i2
16049 */
16050 static ObjectAddress
ATExecAttachPartitionIdx(List ** wqueue,Relation parentIdx,RangeVar * name)16051 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
16052 {
16053 Relation partIdx;
16054 Relation partTbl;
16055 Relation parentTbl;
16056 ObjectAddress address;
16057 Oid partIdxId;
16058 Oid currParent;
16059 struct AttachIndexCallbackState state;
16060
16061 /*
16062 * We need to obtain lock on the index 'name' to modify it, but we also
16063 * need to read its owning table's tuple descriptor -- so we need to lock
16064 * both. To avoid deadlocks, obtain lock on the table before doing so on
16065 * the index. Furthermore, we need to examine the parent table of the
16066 * partition, so lock that one too.
16067 */
16068 state.partitionOid = InvalidOid;
16069 state.parentTblOid = parentIdx->rd_index->indrelid;
16070 state.lockedParentTbl = false;
16071 partIdxId =
16072 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
16073 RangeVarCallbackForAttachIndex,
16074 (void *) &state);
16075 /* Not there? */
16076 if (!OidIsValid(partIdxId))
16077 ereport(ERROR,
16078 (errcode(ERRCODE_UNDEFINED_OBJECT),
16079 errmsg("index \"%s\" does not exist", name->relname)));
16080
16081 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
16082 partIdx = relation_open(partIdxId, AccessExclusiveLock);
16083
16084 /* we already hold locks on both tables, so this is safe: */
16085 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
16086 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
16087
16088 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
16089
16090 /* Silently do nothing if already in the right state */
16091 currParent = partIdx->rd_rel->relispartition ?
16092 get_partition_parent(partIdxId) : InvalidOid;
16093 if (currParent != RelationGetRelid(parentIdx))
16094 {
16095 IndexInfo *childInfo;
16096 IndexInfo *parentInfo;
16097 AttrNumber *attmap;
16098 bool found;
16099 int i;
16100 PartitionDesc partDesc;
16101 Oid constraintOid,
16102 cldConstrId = InvalidOid;
16103
16104 /*
16105 * If this partition already has an index attached, refuse the
16106 * operation.
16107 */
16108 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
16109
16110 if (OidIsValid(currParent))
16111 ereport(ERROR,
16112 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16113 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
16114 RelationGetRelationName(partIdx),
16115 RelationGetRelationName(parentIdx)),
16116 errdetail("Index \"%s\" is already attached to another index.",
16117 RelationGetRelationName(partIdx))));
16118
16119 /* Make sure it indexes a partition of the other index's table */
16120 partDesc = RelationGetPartitionDesc(parentTbl);
16121 found = false;
16122 for (i = 0; i < partDesc->nparts; i++)
16123 {
16124 if (partDesc->oids[i] == state.partitionOid)
16125 {
16126 found = true;
16127 break;
16128 }
16129 }
16130 if (!found)
16131 ereport(ERROR,
16132 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16133 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
16134 RelationGetRelationName(partIdx),
16135 RelationGetRelationName(parentIdx)),
16136 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
16137 RelationGetRelationName(partIdx),
16138 RelationGetRelationName(parentTbl))));
16139
16140 /* Ensure the indexes are compatible */
16141 childInfo = BuildIndexInfo(partIdx);
16142 parentInfo = BuildIndexInfo(parentIdx);
16143 attmap = convert_tuples_by_name_map(RelationGetDescr(partTbl),
16144 RelationGetDescr(parentTbl),
16145 gettext_noop("could not convert row type"));
16146 if (!CompareIndexInfo(childInfo, parentInfo,
16147 partIdx->rd_indcollation,
16148 parentIdx->rd_indcollation,
16149 partIdx->rd_opfamily,
16150 parentIdx->rd_opfamily,
16151 attmap,
16152 RelationGetDescr(parentTbl)->natts))
16153 ereport(ERROR,
16154 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16155 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
16156 RelationGetRelationName(partIdx),
16157 RelationGetRelationName(parentIdx)),
16158 errdetail("The index definitions do not match.")));
16159
16160 /*
16161 * If there is a constraint in the parent, make sure there is one in
16162 * the child too.
16163 */
16164 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
16165 RelationGetRelid(parentIdx));
16166
16167 if (OidIsValid(constraintOid))
16168 {
16169 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
16170 partIdxId);
16171 if (!OidIsValid(cldConstrId))
16172 ereport(ERROR,
16173 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16174 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
16175 RelationGetRelationName(partIdx),
16176 RelationGetRelationName(parentIdx)),
16177 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
16178 RelationGetRelationName(parentIdx),
16179 RelationGetRelationName(parentTbl),
16180 RelationGetRelationName(partIdx))));
16181 }
16182
16183 /* All good -- do it */
16184 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
16185 if (OidIsValid(constraintOid))
16186 ConstraintSetParentConstraint(cldConstrId, constraintOid);
16187
16188 pfree(attmap);
16189
16190 validatePartitionedIndex(parentIdx, parentTbl);
16191 }
16192
16193 relation_close(parentTbl, AccessShareLock);
16194 /* keep these locks till commit */
16195 relation_close(partTbl, NoLock);
16196 relation_close(partIdx, NoLock);
16197
16198 return address;
16199 }
16200
16201 /*
16202 * Verify whether the given partition already contains an index attached
16203 * to the given partitioned index. If so, raise an error.
16204 */
16205 static void
refuseDupeIndexAttach(Relation parentIdx,Relation partIdx,Relation partitionTbl)16206 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
16207 {
16208 Relation pg_inherits;
16209 ScanKeyData key;
16210 HeapTuple tuple;
16211 SysScanDesc scan;
16212
16213 pg_inherits = heap_open(InheritsRelationId, AccessShareLock);
16214 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
16215 BTEqualStrategyNumber, F_OIDEQ,
16216 ObjectIdGetDatum(RelationGetRelid(parentIdx)));
16217 scan = systable_beginscan(pg_inherits, InheritsParentIndexId, true,
16218 NULL, 1, &key);
16219 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
16220 {
16221 Form_pg_inherits inhForm;
16222 Oid tab;
16223
16224 inhForm = (Form_pg_inherits) GETSTRUCT(tuple);
16225 tab = IndexGetRelation(inhForm->inhrelid, false);
16226 if (tab == RelationGetRelid(partitionTbl))
16227 ereport(ERROR,
16228 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16229 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
16230 RelationGetRelationName(partIdx),
16231 RelationGetRelationName(parentIdx)),
16232 errdetail("Another index is already attached for partition \"%s\".",
16233 RelationGetRelationName(partitionTbl))));
16234 }
16235
16236 systable_endscan(scan);
16237 heap_close(pg_inherits, AccessShareLock);
16238 }
16239
16240 /*
16241 * Verify whether the set of attached partition indexes to a parent index on
16242 * a partitioned table is complete. If it is, mark the parent index valid.
16243 *
16244 * This should be called each time a partition index is attached.
16245 */
16246 static void
validatePartitionedIndex(Relation partedIdx,Relation partedTbl)16247 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
16248 {
16249 Relation inheritsRel;
16250 SysScanDesc scan;
16251 ScanKeyData key;
16252 int tuples = 0;
16253 HeapTuple inhTup;
16254 bool updated = false;
16255
16256 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
16257
16258 /*
16259 * Scan pg_inherits for this parent index. Count each valid index we find
16260 * (verifying the pg_index entry for each), and if we reach the total
16261 * amount we expect, we can mark this parent index as valid.
16262 */
16263 inheritsRel = heap_open(InheritsRelationId, AccessShareLock);
16264 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
16265 BTEqualStrategyNumber, F_OIDEQ,
16266 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
16267 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
16268 NULL, 1, &key);
16269 while ((inhTup = systable_getnext(scan)) != NULL)
16270 {
16271 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
16272 HeapTuple indTup;
16273 Form_pg_index indexForm;
16274
16275 indTup = SearchSysCache1(INDEXRELID,
16276 ObjectIdGetDatum(inhForm->inhrelid));
16277 if (!HeapTupleIsValid(indTup))
16278 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
16279 indexForm = (Form_pg_index) GETSTRUCT(indTup);
16280 if (IndexIsValid(indexForm))
16281 tuples += 1;
16282 ReleaseSysCache(indTup);
16283 }
16284
16285 /* Done with pg_inherits */
16286 systable_endscan(scan);
16287 heap_close(inheritsRel, AccessShareLock);
16288
16289 /*
16290 * If we found as many inherited indexes as the partitioned table has
16291 * partitions, we're good; update pg_index to set indisvalid.
16292 */
16293 if (tuples == RelationGetPartitionDesc(partedTbl)->nparts)
16294 {
16295 Relation idxRel;
16296 HeapTuple newtup;
16297
16298 idxRel = heap_open(IndexRelationId, RowExclusiveLock);
16299
16300 newtup = heap_copytuple(partedIdx->rd_indextuple);
16301 ((Form_pg_index) GETSTRUCT(newtup))->indisvalid = true;
16302 updated = true;
16303
16304 CatalogTupleUpdate(idxRel, &partedIdx->rd_indextuple->t_self, newtup);
16305
16306 heap_close(idxRel, RowExclusiveLock);
16307 }
16308
16309 /*
16310 * If this index is in turn a partition of a larger index, validating it
16311 * might cause the parent to become valid also. Try that.
16312 */
16313 if (updated && partedIdx->rd_rel->relispartition)
16314 {
16315 Oid parentIdxId,
16316 parentTblId;
16317 Relation parentIdx,
16318 parentTbl;
16319
16320 /* make sure we see the validation we just did */
16321 CommandCounterIncrement();
16322
16323 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx));
16324 parentTblId = get_partition_parent(RelationGetRelid(partedTbl));
16325 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
16326 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
16327 Assert(!parentIdx->rd_index->indisvalid);
16328
16329 validatePartitionedIndex(parentIdx, parentTbl);
16330
16331 relation_close(parentIdx, AccessExclusiveLock);
16332 relation_close(parentTbl, AccessExclusiveLock);
16333 }
16334 }
16335