1 /*-------------------------------------------------------------------------
2 *
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
5 *
6 * Portions Copyright (c) 1996-2017, 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/multixact.h"
20 #include "access/reloptions.h"
21 #include "access/relscan.h"
22 #include "access/sysattr.h"
23 #include "access/tupconvert.h"
24 #include "access/xact.h"
25 #include "access/xlog.h"
26 #include "catalog/catalog.h"
27 #include "catalog/dependency.h"
28 #include "catalog/heap.h"
29 #include "catalog/index.h"
30 #include "catalog/indexing.h"
31 #include "catalog/namespace.h"
32 #include "catalog/objectaccess.h"
33 #include "catalog/partition.h"
34 #include "catalog/pg_am.h"
35 #include "catalog/pg_collation.h"
36 #include "catalog/pg_constraint.h"
37 #include "catalog/pg_constraint_fn.h"
38 #include "catalog/pg_depend.h"
39 #include "catalog/pg_foreign_table.h"
40 #include "catalog/pg_inherits.h"
41 #include "catalog/pg_inherits_fn.h"
42 #include "catalog/pg_namespace.h"
43 #include "catalog/pg_opclass.h"
44 #include "catalog/pg_tablespace.h"
45 #include "catalog/pg_trigger.h"
46 #include "catalog/pg_type.h"
47 #include "catalog/pg_type_fn.h"
48 #include "catalog/storage.h"
49 #include "catalog/storage_xlog.h"
50 #include "catalog/toasting.h"
51 #include "commands/cluster.h"
52 #include "commands/comment.h"
53 #include "commands/defrem.h"
54 #include "commands/event_trigger.h"
55 #include "commands/policy.h"
56 #include "commands/sequence.h"
57 #include "commands/tablecmds.h"
58 #include "commands/tablespace.h"
59 #include "commands/trigger.h"
60 #include "commands/typecmds.h"
61 #include "commands/user.h"
62 #include "executor/executor.h"
63 #include "foreign/foreign.h"
64 #include "miscadmin.h"
65 #include "nodes/makefuncs.h"
66 #include "nodes/nodeFuncs.h"
67 #include "nodes/parsenodes.h"
68 #include "optimizer/clauses.h"
69 #include "optimizer/planner.h"
70 #include "optimizer/predtest.h"
71 #include "optimizer/prep.h"
72 #include "optimizer/var.h"
73 #include "parser/parse_clause.h"
74 #include "parser/parse_coerce.h"
75 #include "parser/parse_collate.h"
76 #include "parser/parse_expr.h"
77 #include "parser/parse_oper.h"
78 #include "parser/parse_relation.h"
79 #include "parser/parse_type.h"
80 #include "parser/parse_utilcmd.h"
81 #include "parser/parser.h"
82 #include "pgstat.h"
83 #include "rewrite/rewriteDefine.h"
84 #include "rewrite/rewriteHandler.h"
85 #include "rewrite/rewriteManip.h"
86 #include "storage/bufmgr.h"
87 #include "storage/lmgr.h"
88 #include "storage/lock.h"
89 #include "storage/predicate.h"
90 #include "storage/smgr.h"
91 #include "utils/acl.h"
92 #include "utils/builtins.h"
93 #include "utils/fmgroids.h"
94 #include "utils/inval.h"
95 #include "utils/lsyscache.h"
96 #include "utils/memutils.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 /* Objects to rebuild after completing ALTER TYPE operations */
172 List *changedConstraintOids; /* OIDs of constraints to rebuild */
173 List *changedConstraintDefs; /* string definitions of same */
174 List *changedIndexOids; /* OIDs of indexes to rebuild */
175 List *changedIndexDefs; /* string definitions of same */
176 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
177 char *clusterOnIndex; /* index to use for CLUSTER */
178 } AlteredTableInfo;
179
180 /* Struct describing one new constraint to check in Phase 3 scan */
181 /* Note: new NOT NULL constraints are handled elsewhere */
182 typedef struct NewConstraint
183 {
184 char *name; /* Constraint name, or NULL if none */
185 ConstrType contype; /* CHECK or FOREIGN */
186 Oid refrelid; /* PK rel, if FOREIGN */
187 Oid refindid; /* OID of PK's index, if FOREIGN */
188 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
189 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
190 ExprState *qualstate; /* Execution state for CHECK expr */
191 } NewConstraint;
192
193 /*
194 * Struct describing one new column value that needs to be computed during
195 * Phase 3 copy (this could be either a new column with a non-null default, or
196 * a column that we're changing the type of). Columns without such an entry
197 * are just copied from the old table during ATRewriteTable. Note that the
198 * expr is an expression over *old* table values.
199 */
200 typedef struct NewColumnValue
201 {
202 AttrNumber attnum; /* which column */
203 Expr *expr; /* expression to compute */
204 ExprState *exprstate; /* execution state */
205 } NewColumnValue;
206
207 /*
208 * Error-reporting support for RemoveRelations
209 */
210 struct dropmsgstrings
211 {
212 char kind;
213 int nonexistent_code;
214 const char *nonexistent_msg;
215 const char *skipping_msg;
216 const char *nota_msg;
217 const char *drophint_msg;
218 };
219
220 static const struct dropmsgstrings dropmsgstringarray[] = {
221 {RELKIND_RELATION,
222 ERRCODE_UNDEFINED_TABLE,
223 gettext_noop("table \"%s\" does not exist"),
224 gettext_noop("table \"%s\" does not exist, skipping"),
225 gettext_noop("\"%s\" is not a table"),
226 gettext_noop("Use DROP TABLE to remove a table.")},
227 {RELKIND_SEQUENCE,
228 ERRCODE_UNDEFINED_TABLE,
229 gettext_noop("sequence \"%s\" does not exist"),
230 gettext_noop("sequence \"%s\" does not exist, skipping"),
231 gettext_noop("\"%s\" is not a sequence"),
232 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
233 {RELKIND_VIEW,
234 ERRCODE_UNDEFINED_TABLE,
235 gettext_noop("view \"%s\" does not exist"),
236 gettext_noop("view \"%s\" does not exist, skipping"),
237 gettext_noop("\"%s\" is not a view"),
238 gettext_noop("Use DROP VIEW to remove a view.")},
239 {RELKIND_MATVIEW,
240 ERRCODE_UNDEFINED_TABLE,
241 gettext_noop("materialized view \"%s\" does not exist"),
242 gettext_noop("materialized view \"%s\" does not exist, skipping"),
243 gettext_noop("\"%s\" is not a materialized view"),
244 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
245 {RELKIND_INDEX,
246 ERRCODE_UNDEFINED_OBJECT,
247 gettext_noop("index \"%s\" does not exist"),
248 gettext_noop("index \"%s\" does not exist, skipping"),
249 gettext_noop("\"%s\" is not an index"),
250 gettext_noop("Use DROP INDEX to remove an index.")},
251 {RELKIND_COMPOSITE_TYPE,
252 ERRCODE_UNDEFINED_OBJECT,
253 gettext_noop("type \"%s\" does not exist"),
254 gettext_noop("type \"%s\" does not exist, skipping"),
255 gettext_noop("\"%s\" is not a type"),
256 gettext_noop("Use DROP TYPE to remove a type.")},
257 {RELKIND_FOREIGN_TABLE,
258 ERRCODE_UNDEFINED_OBJECT,
259 gettext_noop("foreign table \"%s\" does not exist"),
260 gettext_noop("foreign table \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a foreign table"),
262 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
263 {RELKIND_PARTITIONED_TABLE,
264 ERRCODE_UNDEFINED_TABLE,
265 gettext_noop("table \"%s\" does not exist"),
266 gettext_noop("table \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a table"),
268 gettext_noop("Use DROP TABLE to remove a table.")},
269 {'\0', 0, NULL, NULL, NULL, NULL}
270 };
271
272 struct DropRelationCallbackState
273 {
274 char relkind;
275 Oid heapOid;
276 Oid partParentOid;
277 bool concurrent;
278 };
279
280 /* Alter table target-type flags for ATSimplePermissions */
281 #define ATT_TABLE 0x0001
282 #define ATT_VIEW 0x0002
283 #define ATT_MATVIEW 0x0004
284 #define ATT_INDEX 0x0008
285 #define ATT_COMPOSITE_TYPE 0x0010
286 #define ATT_FOREIGN_TABLE 0x0020
287
288 /*
289 * Partition tables are expected to be dropped when the parent partitioned
290 * table gets dropped. Hence for partitioning we use AUTO dependency.
291 * Otherwise, for regular inheritance use NORMAL dependency.
292 */
293 #define child_dependency_type(child_is_partition) \
294 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
295
296 static void truncate_check_rel(Relation rel);
297 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
298 bool is_partition, List **supOids, List **supconstr,
299 int *supOidCount);
300 static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
301 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
302 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
303 static void StoreCatalogInheritance(Oid relationId, List *supers,
304 bool child_is_partition);
305 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
306 int32 seqNumber, Relation inhRelation,
307 bool child_is_partition);
308 static int findAttrByName(const char *attributeName, List *schema);
309 static void AlterIndexNamespaces(Relation classRel, Relation rel,
310 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
311 static void AlterSeqNamespaces(Relation classRel, Relation rel,
312 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
313 LOCKMODE lockmode);
314 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
315 bool recurse, bool recursing, LOCKMODE lockmode);
316 static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel,
317 char *constrName, bool recurse, bool recursing,
318 LOCKMODE lockmode);
319 static int transformColumnNameList(Oid relId, List *colList,
320 int16 *attnums, Oid *atttypids);
321 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
322 List **attnamelist,
323 int16 *attnums, Oid *atttypids,
324 Oid *opclasses);
325 static Oid transformFkeyCheckAttrs(Relation pkrel,
326 int numattrs, int16 *attnums,
327 Oid *opclasses);
328 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
329 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
330 Oid *funcid);
331 static void validateForeignKeyConstraint(char *conname,
332 Relation rel, Relation pkrel,
333 Oid pkindOid, Oid constraintOid);
334 static void createForeignKeyTriggers(Relation rel, Oid refRelOid,
335 Constraint *fkconstraint,
336 Oid constraintOid, Oid indexOid);
337 static void ATController(AlterTableStmt *parsetree,
338 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
339 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
340 bool recurse, bool recursing, LOCKMODE lockmode);
341 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode);
342 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
343 AlterTableCmd *cmd, LOCKMODE lockmode);
344 static void ATRewriteTables(AlterTableStmt *parsetree,
345 List **wqueue, LOCKMODE lockmode);
346 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
347 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
348 static void ATSimplePermissions(Relation rel, int allowed_targets);
349 static void ATWrongRelkindError(Relation rel, int allowed_targets);
350 static void ATSimpleRecursion(List **wqueue, Relation rel,
351 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
352 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
353 LOCKMODE lockmode);
354 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
355 DropBehavior behavior);
356 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
357 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode);
358 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
359 Relation rel, ColumnDef *colDef, bool isOid,
360 bool recurse, bool recursing,
361 bool if_not_exists, LOCKMODE lockmode);
362 static bool check_for_column_name_collision(Relation rel, const char *colname,
363 bool if_not_exists);
364 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
365 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
366 static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
367 AlterTableCmd *cmd, LOCKMODE lockmode);
368 static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
369 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
370 static void ATPrepSetNotNull(Relation rel, bool recurse, bool recursing);
371 static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
372 const char *colName, LOCKMODE lockmode);
373 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
374 Node *newDefault, LOCKMODE lockmode);
375 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
376 Node *def, LOCKMODE lockmode);
377 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
378 Node *def, LOCKMODE lockmode);
379 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
380 static void ATPrepSetStatistics(Relation rel, const char *colName,
381 Node *newValue, LOCKMODE lockmode);
382 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName,
383 Node *newValue, LOCKMODE lockmode);
384 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
385 Node *options, bool isReset, LOCKMODE lockmode);
386 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
387 Node *newValue, LOCKMODE lockmode);
388 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
389 AlterTableCmd *cmd, LOCKMODE lockmode);
390 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
391 DropBehavior behavior,
392 bool recurse, bool recursing,
393 bool missing_ok, LOCKMODE lockmode);
394 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
395 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
396 static ObjectAddress ATExecAddConstraint(List **wqueue,
397 AlteredTableInfo *tab, Relation rel,
398 Constraint *newConstraint, bool recurse, bool is_readd,
399 LOCKMODE lockmode);
400 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
401 IndexStmt *stmt, LOCKMODE lockmode);
402 static ObjectAddress ATAddCheckConstraint(List **wqueue,
403 AlteredTableInfo *tab, Relation rel,
404 Constraint *constr,
405 bool recurse, bool recursing, bool is_readd,
406 LOCKMODE lockmode);
407 static ObjectAddress ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
408 Constraint *fkconstraint, LOCKMODE lockmode);
409 static void ATExecDropConstraint(Relation rel, const char *constrName,
410 DropBehavior behavior,
411 bool recurse, bool recursing,
412 bool missing_ok, LOCKMODE lockmode);
413 static void ATPrepAlterColumnType(List **wqueue,
414 AlteredTableInfo *tab, Relation rel,
415 bool recurse, bool recursing,
416 AlterTableCmd *cmd, LOCKMODE lockmode);
417 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
418 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
419 AlterTableCmd *cmd, LOCKMODE lockmode);
420 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab,
421 DependencyType deptype);
422 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
423 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
424 List *options, LOCKMODE lockmode);
425 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
426 LOCKMODE lockmode);
427 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
428 char *cmd, List **wqueue, LOCKMODE lockmode,
429 bool rewrite);
430 static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
431 Oid objid, Relation rel, char *conname);
432 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
433 static void TryReuseForeignKey(Oid oldId, Constraint *con);
434 static void change_owner_fix_column_acls(Oid relationOid,
435 Oid oldOwnerId, Oid newOwnerId);
436 static void change_owner_recurse_to_sequences(Oid relationOid,
437 Oid newOwnerId, LOCKMODE lockmode);
438 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
439 LOCKMODE lockmode);
440 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
441 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
442 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
443 char *tablespacename, LOCKMODE lockmode);
444 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
445 static void ATExecSetRelOptions(Relation rel, List *defList,
446 AlterTableType operation,
447 LOCKMODE lockmode);
448 static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
449 char fires_when, bool skip_system, LOCKMODE lockmode);
450 static void ATExecEnableDisableRule(Relation rel, char *rulename,
451 char fires_when, LOCKMODE lockmode);
452 static void ATPrepAddInherit(Relation child_rel);
453 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
454 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
455 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
456 DependencyType deptype);
457 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
458 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
459 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
460 static void ATExecGenericOptions(Relation rel, List *options);
461 static void ATExecEnableRowSecurity(Relation rel);
462 static void ATExecDisableRowSecurity(Relation rel);
463 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
464
465 static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
466 ForkNumber forkNum, char relpersistence);
467 static const char *storage_name(char c);
468
469 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
470 Oid oldRelOid, void *arg);
471 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
472 Oid oldrelid, void *arg);
473 static bool is_partition_attr(Relation rel, AttrNumber attnum, bool *used_in_expr);
474 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
475 static void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
476 List **partexprs, Oid *partopclass, Oid *partcollation);
477 static void CreateInheritance(Relation child_rel, Relation parent_rel);
478 static void RemoveInheritance(Relation child_rel, Relation parent_rel);
479 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
480 PartitionCmd *cmd);
481 static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
482
483
484 /* ----------------------------------------------------------------
485 * DefineRelation
486 * Creates a new relation.
487 *
488 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
489 * The other arguments are used to extend the behavior for other cases:
490 * relkind: relkind to assign to the new relation
491 * ownerId: if not InvalidOid, use this as the new relation's owner.
492 * typaddress: if not null, it's set to the pg_type entry's address.
493 *
494 * Note that permissions checks are done against current user regardless of
495 * ownerId. A nonzero ownerId is used when someone is creating a relation
496 * "on behalf of" someone else, so we still want to see that the current user
497 * has permissions to do it.
498 *
499 * If successful, returns the address of the new relation.
500 * ----------------------------------------------------------------
501 */
502 ObjectAddress
DefineRelation(CreateStmt * stmt,char relkind,Oid ownerId,ObjectAddress * typaddress,const char * queryString)503 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
504 ObjectAddress *typaddress, const char *queryString)
505 {
506 char relname[NAMEDATALEN];
507 Oid namespaceId;
508 Oid relationId;
509 Oid tablespaceId;
510 Relation rel;
511 TupleDesc descriptor;
512 List *inheritOids;
513 List *old_constraints;
514 bool localHasOids;
515 int parentOidCount;
516 List *rawDefaults;
517 List *cookedDefaults;
518 Datum reloptions;
519 ListCell *listptr;
520 AttrNumber attnum;
521 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
522 Oid ofTypeId;
523 ObjectAddress address;
524
525 /*
526 * Truncate relname to appropriate length (probably a waste of time, as
527 * parser should have done this already).
528 */
529 StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
530
531 /*
532 * Check consistency of arguments
533 */
534 if (stmt->oncommit != ONCOMMIT_NOOP
535 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
536 ereport(ERROR,
537 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
538 errmsg("ON COMMIT can only be used on temporary tables")));
539
540 if (stmt->partspec != NULL)
541 {
542 if (relkind != RELKIND_RELATION)
543 elog(ERROR, "unexpected relkind: %d", (int) relkind);
544
545 relkind = RELKIND_PARTITIONED_TABLE;
546 }
547
548 /*
549 * Look up the namespace in which we are supposed to create the relation,
550 * check we have permission to create there, lock it against concurrent
551 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
552 * namespace is selected.
553 */
554 namespaceId =
555 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
556
557 /*
558 * Security check: disallow creating temp tables from security-restricted
559 * code. This is needed because calling code might not expect untrusted
560 * tables to appear in pg_temp at the front of its search path.
561 */
562 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
563 && InSecurityRestrictedOperation())
564 ereport(ERROR,
565 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
566 errmsg("cannot create temporary table within security-restricted operation")));
567
568 /*
569 * Select tablespace to use. If not specified, use default tablespace
570 * (which may in turn default to database's default).
571 */
572 if (stmt->tablespacename)
573 {
574 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
575 }
576 else
577 {
578 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence);
579 /* note InvalidOid is OK in this case */
580 }
581
582 /* Check permissions except when using database's default */
583 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
584 {
585 AclResult aclresult;
586
587 aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
588 ACL_CREATE);
589 if (aclresult != ACLCHECK_OK)
590 aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
591 get_tablespace_name(tablespaceId));
592 }
593
594 /* In all cases disallow placing user relations in pg_global */
595 if (tablespaceId == GLOBALTABLESPACE_OID)
596 ereport(ERROR,
597 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
598 errmsg("only shared relations can be placed in pg_global tablespace")));
599
600 /* Identify user ID that will own the table */
601 if (!OidIsValid(ownerId))
602 ownerId = GetUserId();
603
604 /*
605 * Parse and validate reloptions, if any.
606 */
607 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
608 true, false);
609
610 if (relkind == RELKIND_VIEW)
611 (void) view_reloptions(reloptions, true);
612 else
613 (void) heap_reloptions(relkind, reloptions, true);
614
615 if (stmt->ofTypename)
616 {
617 AclResult aclresult;
618
619 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
620
621 aclresult = pg_type_aclcheck(ofTypeId, GetUserId(), ACL_USAGE);
622 if (aclresult != ACLCHECK_OK)
623 aclcheck_error_type(aclresult, ofTypeId);
624 }
625 else
626 ofTypeId = InvalidOid;
627
628 /*
629 * Look up inheritance ancestors and generate relation schema, including
630 * inherited attributes. (Note that stmt->tableElts is destructively
631 * modified by MergeAttributes.)
632 */
633 stmt->tableElts =
634 MergeAttributes(stmt->tableElts, stmt->inhRelations,
635 stmt->relation->relpersistence,
636 stmt->partbound != NULL,
637 &inheritOids, &old_constraints, &parentOidCount);
638
639 /*
640 * Create a tuple descriptor from the relation schema. Note that this
641 * deals with column names, types, and NOT NULL constraints, but not
642 * default values or CHECK constraints; we handle those below.
643 */
644 descriptor = BuildDescForRelation(stmt->tableElts);
645
646 /*
647 * Notice that we allow OIDs here only for plain tables and partitioned
648 * tables, even though some other relkinds can support them. This is
649 * necessary because the default_with_oids GUC must apply only to plain
650 * tables and not any other relkind; doing otherwise would break existing
651 * pg_dump files. We could allow explicit "WITH OIDS" while not allowing
652 * default_with_oids to affect other relkinds, but it would complicate
653 * interpretOidsOption().
654 */
655 localHasOids = interpretOidsOption(stmt->options,
656 (relkind == RELKIND_RELATION ||
657 relkind == RELKIND_PARTITIONED_TABLE));
658 descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
659
660 /*
661 * If a partitioned table doesn't have the system OID column, then none of
662 * its partitions should have it.
663 */
664 if (stmt->partbound && parentOidCount == 0 && localHasOids)
665 ereport(ERROR,
666 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
667 errmsg("cannot create table with OIDs as partition of table without OIDs")));
668
669 /*
670 * Find columns with default values and prepare for insertion of the
671 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
672 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
673 * while raw defaults go into a list of RawColumnDefault structs that will
674 * be processed by AddRelationNewConstraints. (We can't deal with raw
675 * expressions until we can do transformExpr.)
676 *
677 * We can set the atthasdef flags now in the tuple descriptor; this just
678 * saves StoreAttrDefault from having to do an immediate update of the
679 * pg_attribute rows.
680 */
681 rawDefaults = NIL;
682 cookedDefaults = NIL;
683 attnum = 0;
684
685 foreach(listptr, stmt->tableElts)
686 {
687 ColumnDef *colDef = lfirst(listptr);
688
689 attnum++;
690
691 if (colDef->raw_default != NULL)
692 {
693 RawColumnDefault *rawEnt;
694
695 Assert(colDef->cooked_default == NULL);
696
697 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
698 rawEnt->attnum = attnum;
699 rawEnt->raw_default = colDef->raw_default;
700 rawDefaults = lappend(rawDefaults, rawEnt);
701 descriptor->attrs[attnum - 1]->atthasdef = true;
702 }
703 else if (colDef->cooked_default != NULL)
704 {
705 CookedConstraint *cooked;
706
707 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
708 cooked->contype = CONSTR_DEFAULT;
709 cooked->conoid = InvalidOid; /* until created */
710 cooked->name = NULL;
711 cooked->attnum = attnum;
712 cooked->expr = colDef->cooked_default;
713 cooked->skip_validation = false;
714 cooked->is_local = true; /* not used for defaults */
715 cooked->inhcount = 0; /* ditto */
716 cooked->is_no_inherit = false;
717 cookedDefaults = lappend(cookedDefaults, cooked);
718 descriptor->attrs[attnum - 1]->atthasdef = true;
719 }
720
721 if (colDef->identity)
722 descriptor->attrs[attnum - 1]->attidentity = colDef->identity;
723 }
724
725 /*
726 * Create the relation. Inherited defaults and constraints are passed in
727 * for immediate handling --- since they don't need parsing, they can be
728 * stored immediately.
729 */
730 relationId = heap_create_with_catalog(relname,
731 namespaceId,
732 tablespaceId,
733 InvalidOid,
734 InvalidOid,
735 ofTypeId,
736 ownerId,
737 descriptor,
738 list_concat(cookedDefaults,
739 old_constraints),
740 relkind,
741 stmt->relation->relpersistence,
742 false,
743 false,
744 localHasOids,
745 parentOidCount,
746 stmt->oncommit,
747 reloptions,
748 true,
749 allowSystemTableMods,
750 false,
751 typaddress);
752
753 /* Store inheritance information for new rel. */
754 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
755
756 /*
757 * We must bump the command counter to make the newly-created relation
758 * tuple visible for opening.
759 */
760 CommandCounterIncrement();
761
762 /*
763 * Open the new relation and acquire exclusive lock on it. This isn't
764 * really necessary for locking out other backends (since they can't see
765 * the new rel anyway until we commit), but it keeps the lock manager from
766 * complaining about deadlock risks.
767 */
768 rel = relation_open(relationId, AccessExclusiveLock);
769
770 /* Process and store partition bound, if any. */
771 if (stmt->partbound)
772 {
773 PartitionBoundSpec *bound;
774 ParseState *pstate;
775 Oid parentId = linitial_oid(inheritOids);
776 Relation parent;
777
778 /* Already have strong enough lock on the parent */
779 parent = heap_open(parentId, NoLock);
780
781 /*
782 * We are going to try to validate the partition bound specification
783 * against the partition key of parentRel, so it better have one.
784 */
785 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
786 ereport(ERROR,
787 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
788 errmsg("\"%s\" is not partitioned",
789 RelationGetRelationName(parent))));
790
791 /* Tranform the bound values */
792 pstate = make_parsestate(NULL);
793 pstate->p_sourcetext = queryString;
794
795 bound = transformPartitionBound(pstate, parent, stmt->partbound);
796
797 /*
798 * Check first that the new partition's bound is valid and does not
799 * overlap with any of existing partitions of the parent - note that
800 * it does not return on error.
801 */
802 check_new_partition_bound(relname, parent, bound);
803
804 /* Update the pg_class entry. */
805 StorePartitionBound(rel, parent, bound);
806
807 heap_close(parent, NoLock);
808
809 /*
810 * The code that follows may also update the pg_class tuple to update
811 * relnumchecks, so bump up the command counter to avoid the "already
812 * updated by self" error.
813 */
814 CommandCounterIncrement();
815 }
816
817 /*
818 * Process the partitioning specification (if any) and store the partition
819 * key information into the catalog.
820 */
821 if (stmt->partspec)
822 {
823 char strategy;
824 int partnatts;
825 AttrNumber partattrs[PARTITION_MAX_KEYS];
826 Oid partopclass[PARTITION_MAX_KEYS];
827 Oid partcollation[PARTITION_MAX_KEYS];
828 List *partexprs = NIL;
829
830 partnatts = list_length(stmt->partspec->partParams);
831
832 /* Protect fixed-size arrays here and in executor */
833 if (partnatts > PARTITION_MAX_KEYS)
834 ereport(ERROR,
835 (errcode(ERRCODE_TOO_MANY_COLUMNS),
836 errmsg("cannot partition using more than %d columns",
837 PARTITION_MAX_KEYS)));
838
839 /*
840 * We need to transform the raw parsetrees corresponding to partition
841 * expressions into executable expression trees. Like column defaults
842 * and CHECK constraints, we could not have done the transformation
843 * earlier.
844 */
845 stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
846 &strategy);
847
848 ComputePartitionAttrs(rel, stmt->partspec->partParams,
849 partattrs, &partexprs, partopclass,
850 partcollation);
851
852 StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
853 partopclass, partcollation);
854 }
855
856 /*
857 * Now add any newly specified column default values and CHECK constraints
858 * to the new relation. These are passed to us in the form of raw
859 * parsetrees; we need to transform them to executable expression trees
860 * before they can be added. The most convenient way to do that is to
861 * apply the parser's transformExpr routine, but transformExpr doesn't
862 * work unless we have a pre-existing relation. So, the transformation has
863 * to be postponed to this final step of CREATE TABLE.
864 */
865 if (rawDefaults || stmt->constraints)
866 AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
867 true, true, false);
868
869 ObjectAddressSet(address, RelationRelationId, relationId);
870
871 /*
872 * Clean up. We keep lock on new relation (although it shouldn't be
873 * visible to anyone else anyway, until commit).
874 */
875 relation_close(rel, NoLock);
876
877 return address;
878 }
879
880 /*
881 * Emit the right error or warning message for a "DROP" command issued on a
882 * non-existent relation
883 */
884 static void
DropErrorMsgNonExistent(RangeVar * rel,char rightkind,bool missing_ok)885 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
886 {
887 const struct dropmsgstrings *rentry;
888
889 if (rel->schemaname != NULL &&
890 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
891 {
892 if (!missing_ok)
893 {
894 ereport(ERROR,
895 (errcode(ERRCODE_UNDEFINED_SCHEMA),
896 errmsg("schema \"%s\" does not exist", rel->schemaname)));
897 }
898 else
899 {
900 ereport(NOTICE,
901 (errmsg("schema \"%s\" does not exist, skipping",
902 rel->schemaname)));
903 }
904 return;
905 }
906
907 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
908 {
909 if (rentry->kind == rightkind)
910 {
911 if (!missing_ok)
912 {
913 ereport(ERROR,
914 (errcode(rentry->nonexistent_code),
915 errmsg(rentry->nonexistent_msg, rel->relname)));
916 }
917 else
918 {
919 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
920 break;
921 }
922 }
923 }
924
925 Assert(rentry->kind != '\0'); /* Should be impossible */
926 }
927
928 /*
929 * Emit the right error message for a "DROP" command issued on a
930 * relation of the wrong type
931 */
932 static void
DropErrorMsgWrongType(const char * relname,char wrongkind,char rightkind)933 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
934 {
935 const struct dropmsgstrings *rentry;
936 const struct dropmsgstrings *wentry;
937
938 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
939 if (rentry->kind == rightkind)
940 break;
941 Assert(rentry->kind != '\0');
942
943 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
944 if (wentry->kind == wrongkind)
945 break;
946 /* wrongkind could be something we don't have in our table... */
947
948 ereport(ERROR,
949 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
950 errmsg(rentry->nota_msg, relname),
951 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
952 }
953
954 /*
955 * RemoveRelations
956 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
957 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
958 */
959 void
RemoveRelations(DropStmt * drop)960 RemoveRelations(DropStmt *drop)
961 {
962 ObjectAddresses *objects;
963 char relkind;
964 ListCell *cell;
965 int flags = 0;
966 LOCKMODE lockmode = AccessExclusiveLock;
967
968 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
969 if (drop->concurrent)
970 {
971 /*
972 * Note that for temporary relations this lock may get upgraded
973 * later on, but as no other session can access a temporary
974 * relation, this is actually fine.
975 */
976 lockmode = ShareUpdateExclusiveLock;
977 Assert(drop->removeType == OBJECT_INDEX);
978 if (list_length(drop->objects) != 1)
979 ereport(ERROR,
980 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
981 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
982 if (drop->behavior == DROP_CASCADE)
983 ereport(ERROR,
984 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
985 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
986 }
987
988 /*
989 * First we identify all the relations, then we delete them in a single
990 * performMultipleDeletions() call. This is to avoid unwanted DROP
991 * RESTRICT errors if one of the relations depends on another.
992 */
993
994 /* Determine required relkind */
995 switch (drop->removeType)
996 {
997 case OBJECT_TABLE:
998 relkind = RELKIND_RELATION;
999 break;
1000
1001 case OBJECT_INDEX:
1002 relkind = RELKIND_INDEX;
1003 break;
1004
1005 case OBJECT_SEQUENCE:
1006 relkind = RELKIND_SEQUENCE;
1007 break;
1008
1009 case OBJECT_VIEW:
1010 relkind = RELKIND_VIEW;
1011 break;
1012
1013 case OBJECT_MATVIEW:
1014 relkind = RELKIND_MATVIEW;
1015 break;
1016
1017 case OBJECT_FOREIGN_TABLE:
1018 relkind = RELKIND_FOREIGN_TABLE;
1019 break;
1020
1021 default:
1022 elog(ERROR, "unrecognized drop object type: %d",
1023 (int) drop->removeType);
1024 relkind = 0; /* keep compiler quiet */
1025 break;
1026 }
1027
1028 /* Lock and validate each relation; build a list of object addresses */
1029 objects = new_object_addresses();
1030
1031 foreach(cell, drop->objects)
1032 {
1033 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1034 Oid relOid;
1035 ObjectAddress obj;
1036 struct DropRelationCallbackState state;
1037
1038 /*
1039 * These next few steps are a great deal like relation_openrv, but we
1040 * don't bother building a relcache entry since we don't need it.
1041 *
1042 * Check for shared-cache-inval messages before trying to access the
1043 * relation. This is needed to cover the case where the name
1044 * identifies a rel that has been dropped and recreated since the
1045 * start of our transaction: if we don't flush the old syscache entry,
1046 * then we'll latch onto that entry and suffer an error later.
1047 */
1048 AcceptInvalidationMessages();
1049
1050 /* Look up the appropriate relation using namespace search. */
1051 state.relkind = relkind;
1052 state.heapOid = InvalidOid;
1053 state.partParentOid = InvalidOid;
1054 state.concurrent = drop->concurrent;
1055 relOid = RangeVarGetRelidExtended(rel, lockmode, true,
1056 false,
1057 RangeVarCallbackForDropRelation,
1058 (void *) &state);
1059
1060 /* Not there? */
1061 if (!OidIsValid(relOid))
1062 {
1063 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1064 continue;
1065 }
1066
1067 /*
1068 * Decide if concurrent mode needs to be used here or not. The
1069 * relation persistence cannot be known without its OID.
1070 */
1071 if (drop->concurrent &&
1072 get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
1073 {
1074 Assert(list_length(drop->objects) == 1 &&
1075 drop->removeType == OBJECT_INDEX);
1076 flags |= PERFORM_DELETION_CONCURRENTLY;
1077 }
1078
1079 /* OK, we're ready to delete this one */
1080 obj.classId = RelationRelationId;
1081 obj.objectId = relOid;
1082 obj.objectSubId = 0;
1083
1084 add_exact_object_address(&obj, objects);
1085 }
1086
1087 performMultipleDeletions(objects, drop->behavior, flags);
1088
1089 free_object_addresses(objects);
1090 }
1091
1092 /*
1093 * Before acquiring a table lock, check whether we have sufficient rights.
1094 * In the case of DROP INDEX, also try to lock the table before the index.
1095 * Also, if the table to be dropped is a partition, we try to lock the parent
1096 * first.
1097 */
1098 static void
RangeVarCallbackForDropRelation(const RangeVar * rel,Oid relOid,Oid oldRelOid,void * arg)1099 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1100 void *arg)
1101 {
1102 HeapTuple tuple;
1103 struct DropRelationCallbackState *state;
1104 char relkind;
1105 char expected_relkind;
1106 bool is_partition;
1107 Form_pg_class classform;
1108 LOCKMODE heap_lockmode;
1109
1110 state = (struct DropRelationCallbackState *) arg;
1111 relkind = state->relkind;
1112 heap_lockmode = state->concurrent ?
1113 ShareUpdateExclusiveLock : AccessExclusiveLock;
1114
1115 /*
1116 * If we previously locked some other index's heap, and the name we're
1117 * looking up no longer refers to that relation, release the now-useless
1118 * lock.
1119 */
1120 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1121 {
1122 UnlockRelationOid(state->heapOid, heap_lockmode);
1123 state->heapOid = InvalidOid;
1124 }
1125
1126 /*
1127 * Similarly, if we previously locked some other partition's heap, and the
1128 * name we're looking up no longer refers to that relation, release the
1129 * now-useless lock.
1130 */
1131 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1132 {
1133 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1134 state->partParentOid = InvalidOid;
1135 }
1136
1137 /* Didn't find a relation, so no need for locking or permission checks. */
1138 if (!OidIsValid(relOid))
1139 return;
1140
1141 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1142 if (!HeapTupleIsValid(tuple))
1143 return; /* concurrently dropped, so nothing to do */
1144 classform = (Form_pg_class) GETSTRUCT(tuple);
1145 is_partition = classform->relispartition;
1146
1147 /*
1148 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1149 * but RemoveRelations() can only pass one relkind for a given relation.
1150 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1151 * That means we must be careful before giving the wrong type error when
1152 * the relation is RELKIND_PARTITIONED_TABLE.
1153 */
1154 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1155 expected_relkind = RELKIND_RELATION;
1156 else
1157 expected_relkind = classform->relkind;
1158
1159 if (relkind != expected_relkind)
1160 DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
1161
1162 /* Allow DROP to either table owner or schema owner */
1163 if (!pg_class_ownercheck(relOid, GetUserId()) &&
1164 !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
1165 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
1166 rel->relname);
1167
1168 if (!allowSystemTableMods && IsSystemClass(relOid, classform))
1169 ereport(ERROR,
1170 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1171 errmsg("permission denied: \"%s\" is a system catalog",
1172 rel->relname)));
1173
1174 ReleaseSysCache(tuple);
1175
1176 /*
1177 * In DROP INDEX, attempt to acquire lock on the parent table before
1178 * locking the index. index_drop() will need this anyway, and since
1179 * regular queries lock tables before their indexes, we risk deadlock if
1180 * we do it the other way around. No error if we don't find a pg_index
1181 * entry, though --- the relation may have been dropped.
1182 */
1183 if (relkind == RELKIND_INDEX && relOid != oldRelOid)
1184 {
1185 state->heapOid = IndexGetRelation(relOid, true);
1186 if (OidIsValid(state->heapOid))
1187 LockRelationOid(state->heapOid, heap_lockmode);
1188 }
1189
1190 /*
1191 * Similarly, if the relation is a partition, we must acquire lock on its
1192 * parent before locking the partition. That's because queries lock the
1193 * parent before its partitions, so we risk deadlock it we do it the other
1194 * way around.
1195 */
1196 if (is_partition && relOid != oldRelOid)
1197 {
1198 state->partParentOid = get_partition_parent(relOid);
1199 if (OidIsValid(state->partParentOid))
1200 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1201 }
1202 }
1203
1204 /*
1205 * ExecuteTruncate
1206 * Executes a TRUNCATE command.
1207 *
1208 * This is a multi-relation truncate. We first open and grab exclusive
1209 * lock on all relations involved, checking permissions and otherwise
1210 * verifying that the relation is OK for truncation. In CASCADE mode,
1211 * relations having FK references to the targeted relations are automatically
1212 * added to the group; in RESTRICT mode, we check that all FK references are
1213 * internal to the group that's being truncated. Finally all the relations
1214 * are truncated and reindexed.
1215 */
1216 void
ExecuteTruncate(TruncateStmt * stmt)1217 ExecuteTruncate(TruncateStmt *stmt)
1218 {
1219 List *rels = NIL;
1220 List *relids = NIL;
1221 List *seq_relids = NIL;
1222 EState *estate;
1223 ResultRelInfo *resultRelInfos;
1224 ResultRelInfo *resultRelInfo;
1225 SubTransactionId mySubid;
1226 ListCell *cell;
1227
1228 /*
1229 * Open, exclusive-lock, and check all the explicitly-specified relations
1230 */
1231 foreach(cell, stmt->relations)
1232 {
1233 RangeVar *rv = lfirst(cell);
1234 Relation rel;
1235 bool recurse = rv->inh;
1236 Oid myrelid;
1237 LOCKMODE lockmode = AccessExclusiveLock;
1238
1239 rel = heap_openrv(rv, lockmode);
1240 myrelid = RelationGetRelid(rel);
1241 /* don't throw error for "TRUNCATE foo, foo" */
1242 if (list_member_oid(relids, myrelid))
1243 {
1244 heap_close(rel, lockmode);
1245 continue;
1246 }
1247 truncate_check_rel(rel);
1248 rels = lappend(rels, rel);
1249 relids = lappend_oid(relids, myrelid);
1250
1251 if (recurse)
1252 {
1253 ListCell *child;
1254 List *children;
1255
1256 children = find_all_inheritors(myrelid, lockmode, NULL);
1257
1258 foreach(child, children)
1259 {
1260 Oid childrelid = lfirst_oid(child);
1261
1262 if (list_member_oid(relids, childrelid))
1263 continue;
1264
1265 /* find_all_inheritors already got lock */
1266 rel = heap_open(childrelid, NoLock);
1267
1268 /*
1269 * It is possible that the parent table has children that are
1270 * temp tables of other backends. We cannot safely access
1271 * such tables (because of buffering issues), and the best
1272 * thing to do is to silently ignore them. Note that this
1273 * check is the same as one of the checks done in
1274 * truncate_check_rel() called below, still it is kept
1275 * here for simplicity.
1276 */
1277 if (RELATION_IS_OTHER_TEMP(rel))
1278 {
1279 heap_close(rel, lockmode);
1280 continue;
1281 }
1282
1283 truncate_check_rel(rel);
1284 rels = lappend(rels, rel);
1285 relids = lappend_oid(relids, childrelid);
1286 }
1287 }
1288 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1289 ereport(ERROR,
1290 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1291 errmsg("cannot truncate only a partitioned table"),
1292 errhint("Do not specify the ONLY keyword, or use truncate only on the partitions directly.")));
1293 }
1294
1295 /*
1296 * In CASCADE mode, suck in all referencing relations as well. This
1297 * requires multiple iterations to find indirectly-dependent relations. At
1298 * each phase, we need to exclusive-lock new rels before looking for their
1299 * dependencies, else we might miss something. Also, we check each rel as
1300 * soon as we open it, to avoid a faux pas such as holding lock for a long
1301 * time on a rel we have no permissions for.
1302 */
1303 if (stmt->behavior == DROP_CASCADE)
1304 {
1305 for (;;)
1306 {
1307 List *newrelids;
1308
1309 newrelids = heap_truncate_find_FKs(relids);
1310 if (newrelids == NIL)
1311 break; /* nothing else to add */
1312
1313 foreach(cell, newrelids)
1314 {
1315 Oid relid = lfirst_oid(cell);
1316 Relation rel;
1317
1318 rel = heap_open(relid, AccessExclusiveLock);
1319 ereport(NOTICE,
1320 (errmsg("truncate cascades to table \"%s\"",
1321 RelationGetRelationName(rel))));
1322 truncate_check_rel(rel);
1323 rels = lappend(rels, rel);
1324 relids = lappend_oid(relids, relid);
1325 }
1326 }
1327 }
1328
1329 /*
1330 * Check foreign key references. In CASCADE mode, this should be
1331 * unnecessary since we just pulled in all the references; but as a
1332 * cross-check, do it anyway if in an Assert-enabled build.
1333 */
1334 #ifdef USE_ASSERT_CHECKING
1335 heap_truncate_check_FKs(rels, false);
1336 #else
1337 if (stmt->behavior == DROP_RESTRICT)
1338 heap_truncate_check_FKs(rels, false);
1339 #endif
1340
1341 /*
1342 * If we are asked to restart sequences, find all the sequences, lock them
1343 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1344 * We want to do this early since it's pointless to do all the truncation
1345 * work only to fail on sequence permissions.
1346 */
1347 if (stmt->restart_seqs)
1348 {
1349 foreach(cell, rels)
1350 {
1351 Relation rel = (Relation) lfirst(cell);
1352 List *seqlist = getOwnedSequences(RelationGetRelid(rel), 0);
1353 ListCell *seqcell;
1354
1355 foreach(seqcell, seqlist)
1356 {
1357 Oid seq_relid = lfirst_oid(seqcell);
1358 Relation seq_rel;
1359
1360 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1361
1362 /* This check must match AlterSequence! */
1363 if (!pg_class_ownercheck(seq_relid, GetUserId()))
1364 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
1365 RelationGetRelationName(seq_rel));
1366
1367 seq_relids = lappend_oid(seq_relids, seq_relid);
1368
1369 relation_close(seq_rel, NoLock);
1370 }
1371 }
1372 }
1373
1374 /* Prepare to catch AFTER triggers. */
1375 AfterTriggerBeginQuery();
1376
1377 /*
1378 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1379 * each relation. We don't need to call ExecOpenIndices, though.
1380 */
1381 estate = CreateExecutorState();
1382 resultRelInfos = (ResultRelInfo *)
1383 palloc(list_length(rels) * sizeof(ResultRelInfo));
1384 resultRelInfo = resultRelInfos;
1385 foreach(cell, rels)
1386 {
1387 Relation rel = (Relation) lfirst(cell);
1388
1389 InitResultRelInfo(resultRelInfo,
1390 rel,
1391 0, /* dummy rangetable index */
1392 NULL,
1393 0);
1394 resultRelInfo++;
1395 }
1396 estate->es_result_relations = resultRelInfos;
1397 estate->es_num_result_relations = list_length(rels);
1398
1399 /*
1400 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
1401 * truncating (this is because one of them might throw an error). Also, if
1402 * we were to allow them to prevent statement execution, that would need
1403 * to be handled here.
1404 */
1405 resultRelInfo = resultRelInfos;
1406 foreach(cell, rels)
1407 {
1408 estate->es_result_relation_info = resultRelInfo;
1409 ExecBSTruncateTriggers(estate, resultRelInfo);
1410 resultRelInfo++;
1411 }
1412
1413 /*
1414 * OK, truncate each table.
1415 */
1416 mySubid = GetCurrentSubTransactionId();
1417
1418 foreach(cell, rels)
1419 {
1420 Relation rel = (Relation) lfirst(cell);
1421
1422 /* Skip partitioned tables as there is nothing to do */
1423 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1424 continue;
1425
1426 /*
1427 * Normally, we need a transaction-safe truncation here. However, if
1428 * the table was either created in the current (sub)transaction or has
1429 * a new relfilenode in the current (sub)transaction, then we can just
1430 * truncate it in-place, because a rollback would cause the whole
1431 * table or the current physical file to be thrown away anyway.
1432 */
1433 if (rel->rd_createSubid == mySubid ||
1434 rel->rd_newRelfilenodeSubid == mySubid)
1435 {
1436 /* Immediate, non-rollbackable truncation is OK */
1437 heap_truncate_one_rel(rel);
1438 }
1439 else
1440 {
1441 Oid heap_relid;
1442 Oid toast_relid;
1443 MultiXactId minmulti;
1444
1445 /*
1446 * This effectively deletes all rows in the table, and may be done
1447 * in a serializable transaction. In that case we must record a
1448 * rw-conflict in to this transaction from each transaction
1449 * holding a predicate lock on the table.
1450 */
1451 CheckTableForSerializableConflictIn(rel);
1452
1453 minmulti = GetOldestMultiXactId();
1454
1455 /*
1456 * Need the full transaction-safe pushups.
1457 *
1458 * Create a new empty storage file for the relation, and assign it
1459 * as the relfilenode value. The old storage file is scheduled for
1460 * deletion at commit.
1461 */
1462 RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence,
1463 RecentXmin, minmulti);
1464 if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
1465 heap_create_init_fork(rel);
1466
1467 heap_relid = RelationGetRelid(rel);
1468
1469 /*
1470 * The same for the toast table, if any.
1471 */
1472 toast_relid = rel->rd_rel->reltoastrelid;
1473 if (OidIsValid(toast_relid))
1474 {
1475 Relation toastrel = relation_open(toast_relid,
1476 AccessExclusiveLock);
1477
1478 RelationSetNewRelfilenode(toastrel,
1479 toastrel->rd_rel->relpersistence,
1480 RecentXmin, minmulti);
1481 if (toastrel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
1482 heap_create_init_fork(toastrel);
1483 heap_close(toastrel, NoLock);
1484 }
1485
1486 /*
1487 * Reconstruct the indexes to match, and we're done.
1488 */
1489 reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, 0);
1490 }
1491
1492 pgstat_count_truncate(rel);
1493 }
1494
1495 /*
1496 * Restart owned sequences if we were asked to.
1497 */
1498 foreach(cell, seq_relids)
1499 {
1500 Oid seq_relid = lfirst_oid(cell);
1501
1502 ResetSequence(seq_relid);
1503 }
1504
1505 /*
1506 * Process all AFTER STATEMENT TRUNCATE triggers.
1507 */
1508 resultRelInfo = resultRelInfos;
1509 foreach(cell, rels)
1510 {
1511 estate->es_result_relation_info = resultRelInfo;
1512 ExecASTruncateTriggers(estate, resultRelInfo);
1513 resultRelInfo++;
1514 }
1515
1516 /* Handle queued AFTER triggers */
1517 AfterTriggerEndQuery(estate);
1518
1519 /* We can clean up the EState now */
1520 FreeExecutorState(estate);
1521
1522 /* And close the rels (can't do this while EState still holds refs) */
1523 foreach(cell, rels)
1524 {
1525 Relation rel = (Relation) lfirst(cell);
1526
1527 heap_close(rel, NoLock);
1528 }
1529 }
1530
1531 /*
1532 * Check that a given rel is safe to truncate. Subroutine for ExecuteTruncate
1533 */
1534 static void
truncate_check_rel(Relation rel)1535 truncate_check_rel(Relation rel)
1536 {
1537 AclResult aclresult;
1538
1539 /*
1540 * Only allow truncate on regular tables and partitioned tables (although,
1541 * the latter are only being included here for the following checks; no
1542 * physical truncation will occur in their case.)
1543 */
1544 if (rel->rd_rel->relkind != RELKIND_RELATION &&
1545 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1546 ereport(ERROR,
1547 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1548 errmsg("\"%s\" is not a table",
1549 RelationGetRelationName(rel))));
1550
1551 /* Permissions checks */
1552 aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
1553 ACL_TRUNCATE);
1554 if (aclresult != ACLCHECK_OK)
1555 aclcheck_error(aclresult, ACL_KIND_CLASS,
1556 RelationGetRelationName(rel));
1557
1558 if (!allowSystemTableMods && IsSystemRelation(rel))
1559 ereport(ERROR,
1560 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1561 errmsg("permission denied: \"%s\" is a system catalog",
1562 RelationGetRelationName(rel))));
1563
1564 /*
1565 * Don't allow truncate on temp tables of other backends ... their local
1566 * buffer manager is not going to cope.
1567 */
1568 if (RELATION_IS_OTHER_TEMP(rel))
1569 ereport(ERROR,
1570 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1571 errmsg("cannot truncate temporary tables of other sessions")));
1572
1573 /*
1574 * Also check for active uses of the relation in the current transaction,
1575 * including open scans and pending AFTER trigger events.
1576 */
1577 CheckTableNotInUse(rel, "TRUNCATE");
1578 }
1579
1580 /*
1581 * storage_name
1582 * returns the name corresponding to a typstorage/attstorage enum value
1583 */
1584 static const char *
storage_name(char c)1585 storage_name(char c)
1586 {
1587 switch (c)
1588 {
1589 case 'p':
1590 return "PLAIN";
1591 case 'm':
1592 return "MAIN";
1593 case 'x':
1594 return "EXTENDED";
1595 case 'e':
1596 return "EXTERNAL";
1597 default:
1598 return "???";
1599 }
1600 }
1601
1602 /*----------
1603 * MergeAttributes
1604 * Returns new schema given initial schema and superclasses.
1605 *
1606 * Input arguments:
1607 * 'schema' is the column/attribute definition for the table. (It's a list
1608 * of ColumnDef's.) It is destructively changed.
1609 * 'supers' is a list of names (as RangeVar nodes) of parent relations.
1610 * 'relpersistence' is a persistence type of the table.
1611 * 'is_partition' tells if the table is a partition
1612 *
1613 * Output arguments:
1614 * 'supOids' receives a list of the OIDs of the parent relations.
1615 * 'supconstr' receives a list of constraints belonging to the parents,
1616 * updated as necessary to be valid for the child.
1617 * 'supOidCount' is set to the number of parents that have OID columns.
1618 *
1619 * Return value:
1620 * Completed schema list.
1621 *
1622 * Notes:
1623 * The order in which the attributes are inherited is very important.
1624 * Intuitively, the inherited attributes should come first. If a table
1625 * inherits from multiple parents, the order of those attributes are
1626 * according to the order of the parents specified in CREATE TABLE.
1627 *
1628 * Here's an example:
1629 *
1630 * create table person (name text, age int4, location point);
1631 * create table emp (salary int4, manager text) inherits(person);
1632 * create table student (gpa float8) inherits (person);
1633 * create table stud_emp (percent int4) inherits (emp, student);
1634 *
1635 * The order of the attributes of stud_emp is:
1636 *
1637 * person {1:name, 2:age, 3:location}
1638 * / \
1639 * {6:gpa} student emp {4:salary, 5:manager}
1640 * \ /
1641 * stud_emp {7:percent}
1642 *
1643 * If the same attribute name appears multiple times, then it appears
1644 * in the result table in the proper location for its first appearance.
1645 *
1646 * Constraints (including NOT NULL constraints) for the child table
1647 * are the union of all relevant constraints, from both the child schema
1648 * and parent tables.
1649 *
1650 * The default value for a child column is defined as:
1651 * (1) If the child schema specifies a default, that value is used.
1652 * (2) If neither the child nor any parent specifies a default, then
1653 * the column will not have a default.
1654 * (3) If conflicting defaults are inherited from different parents
1655 * (and not overridden by the child), an error is raised.
1656 * (4) Otherwise the inherited default is used.
1657 * Rule (3) is new in Postgres 7.1; in earlier releases you got a
1658 * rather arbitrary choice of which parent default to use.
1659 *----------
1660 */
1661 static List *
MergeAttributes(List * schema,List * supers,char relpersistence,bool is_partition,List ** supOids,List ** supconstr,int * supOidCount)1662 MergeAttributes(List *schema, List *supers, char relpersistence,
1663 bool is_partition, List **supOids, List **supconstr,
1664 int *supOidCount)
1665 {
1666 ListCell *entry;
1667 List *inhSchema = NIL;
1668 List *parentOids = NIL;
1669 List *constraints = NIL;
1670 int parentsWithOids = 0;
1671 bool have_bogus_defaults = false;
1672 int child_attno;
1673 static Node bogus_marker = {0}; /* marks conflicting defaults */
1674 List *saved_schema = NIL;
1675
1676 /*
1677 * Check for and reject tables with too many columns. We perform this
1678 * check relatively early for two reasons: (a) we don't run the risk of
1679 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
1680 * okay if we're processing <= 1600 columns, but could take minutes to
1681 * execute if the user attempts to create a table with hundreds of
1682 * thousands of columns.
1683 *
1684 * Note that we also need to check that we do not exceed this figure after
1685 * including columns from inherited relations.
1686 */
1687 if (list_length(schema) > MaxHeapAttributeNumber)
1688 ereport(ERROR,
1689 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1690 errmsg("tables can have at most %d columns",
1691 MaxHeapAttributeNumber)));
1692
1693 /*
1694 * Check for duplicate names in the explicit list of attributes.
1695 *
1696 * Although we might consider merging such entries in the same way that we
1697 * handle name conflicts for inherited attributes, it seems to make more
1698 * sense to assume such conflicts are errors.
1699 */
1700 foreach(entry, schema)
1701 {
1702 ColumnDef *coldef = lfirst(entry);
1703 ListCell *rest = lnext(entry);
1704 ListCell *prev = entry;
1705
1706 if (!is_partition && coldef->typeName == NULL)
1707 {
1708 /*
1709 * Typed table column option that does not belong to a column from
1710 * the type. This works because the columns from the type come
1711 * first in the list. (We omit this check for partition column
1712 * lists; those are processed separately below.)
1713 */
1714 ereport(ERROR,
1715 (errcode(ERRCODE_UNDEFINED_COLUMN),
1716 errmsg("column \"%s\" does not exist",
1717 coldef->colname)));
1718 }
1719
1720 while (rest != NULL)
1721 {
1722 ColumnDef *restdef = lfirst(rest);
1723 ListCell *next = lnext(rest); /* need to save it in case we
1724 * delete it */
1725
1726 if (strcmp(coldef->colname, restdef->colname) == 0)
1727 {
1728 if (coldef->is_from_type)
1729 {
1730 /*
1731 * merge the column options into the column from the type
1732 */
1733 coldef->is_not_null = restdef->is_not_null;
1734 coldef->raw_default = restdef->raw_default;
1735 coldef->cooked_default = restdef->cooked_default;
1736 coldef->constraints = restdef->constraints;
1737 coldef->is_from_type = false;
1738 list_delete_cell(schema, rest, prev);
1739 }
1740 else
1741 ereport(ERROR,
1742 (errcode(ERRCODE_DUPLICATE_COLUMN),
1743 errmsg("column \"%s\" specified more than once",
1744 coldef->colname)));
1745 }
1746 prev = rest;
1747 rest = next;
1748 }
1749 }
1750
1751 /*
1752 * In case of a partition, there are no new column definitions, only dummy
1753 * ColumnDefs created for column constraints. Set them aside for now and
1754 * process them at the end.
1755 */
1756 if (is_partition)
1757 {
1758 saved_schema = schema;
1759 schema = NIL;
1760 }
1761
1762 /*
1763 * Scan the parents left-to-right, and merge their attributes to form a
1764 * list of inherited attributes (inhSchema). Also check to see if we need
1765 * to inherit an OID column.
1766 */
1767 child_attno = 0;
1768 foreach(entry, supers)
1769 {
1770 RangeVar *parent = (RangeVar *) lfirst(entry);
1771 Relation relation;
1772 TupleDesc tupleDesc;
1773 TupleConstr *constr;
1774 AttrNumber *newattno;
1775 AttrNumber parent_attno;
1776
1777 /*
1778 * A self-exclusive lock is needed here. If two backends attempt to
1779 * add children to the same parent simultaneously, and that parent has
1780 * no pre-existing children, then both will attempt to update the
1781 * parent's relhassubclass field, leading to a "tuple concurrently
1782 * updated" error. Also, this interlocks against a concurrent ANALYZE
1783 * on the parent table, which might otherwise be attempting to clear
1784 * the parent's relhassubclass field, if its previous children were
1785 * recently dropped.
1786 *
1787 * If the child table is a partition, then we instead grab an
1788 * exclusive lock on the parent because its partition descriptor will
1789 * be changed by addition of the new partition.
1790 */
1791 if (!is_partition)
1792 relation = heap_openrv(parent, ShareUpdateExclusiveLock);
1793 else
1794 relation = heap_openrv(parent, AccessExclusiveLock);
1795
1796 /*
1797 * Check for active uses of the parent partitioned table in the
1798 * current transaction, such as being used in some manner by an
1799 * enclosing command.
1800 */
1801 if (is_partition)
1802 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
1803
1804 /*
1805 * We do not allow partitioned tables and partitions to participate in
1806 * regular inheritance.
1807 */
1808 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
1809 !is_partition)
1810 ereport(ERROR,
1811 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1812 errmsg("cannot inherit from partitioned table \"%s\"",
1813 parent->relname)));
1814 if (relation->rd_rel->relispartition && !is_partition)
1815 ereport(ERROR,
1816 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1817 errmsg("cannot inherit from partition \"%s\"",
1818 parent->relname)));
1819
1820 if (relation->rd_rel->relkind != RELKIND_RELATION &&
1821 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
1822 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1823 ereport(ERROR,
1824 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1825 errmsg("inherited relation \"%s\" is not a table or foreign table",
1826 parent->relname)));
1827
1828 /*
1829 * If the parent is permanent, so must be all of its partitions. Note
1830 * that inheritance allows that case.
1831 */
1832 if (is_partition &&
1833 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
1834 relpersistence == RELPERSISTENCE_TEMP)
1835 ereport(ERROR,
1836 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1837 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
1838 RelationGetRelationName(relation))));
1839
1840 /* Permanent rels cannot inherit from temporary ones */
1841 if (relpersistence != RELPERSISTENCE_TEMP &&
1842 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
1843 ereport(ERROR,
1844 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1845 errmsg(!is_partition
1846 ? "cannot inherit from temporary relation \"%s\""
1847 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
1848 parent->relname)));
1849
1850 /* If existing rel is temp, it must belong to this session */
1851 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
1852 !relation->rd_islocaltemp)
1853 ereport(ERROR,
1854 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1855 errmsg(!is_partition
1856 ? "cannot inherit from temporary relation of another session"
1857 : "cannot create as partition of temporary relation of another session")));
1858
1859 /*
1860 * We should have an UNDER permission flag for this, but for now,
1861 * demand that creator of a child table own the parent.
1862 */
1863 if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
1864 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
1865 RelationGetRelationName(relation));
1866
1867 /*
1868 * Reject duplications in the list of parents.
1869 */
1870 if (list_member_oid(parentOids, RelationGetRelid(relation)))
1871 ereport(ERROR,
1872 (errcode(ERRCODE_DUPLICATE_TABLE),
1873 errmsg("relation \"%s\" would be inherited from more than once",
1874 parent->relname)));
1875
1876 parentOids = lappend_oid(parentOids, RelationGetRelid(relation));
1877
1878 if (relation->rd_rel->relhasoids)
1879 parentsWithOids++;
1880
1881 tupleDesc = RelationGetDescr(relation);
1882 constr = tupleDesc->constr;
1883
1884 /*
1885 * newattno[] will contain the child-table attribute numbers for the
1886 * attributes of this parent table. (They are not the same for
1887 * parents after the first one, nor if we have dropped columns.)
1888 */
1889 newattno = (AttrNumber *)
1890 palloc0(tupleDesc->natts * sizeof(AttrNumber));
1891
1892 for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1893 parent_attno++)
1894 {
1895 Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
1896 char *attributeName = NameStr(attribute->attname);
1897 int exist_attno;
1898 ColumnDef *def;
1899
1900 /*
1901 * Ignore dropped columns in the parent.
1902 */
1903 if (attribute->attisdropped)
1904 continue; /* leave newattno entry as zero */
1905
1906 /*
1907 * Does it conflict with some previously inherited column?
1908 */
1909 exist_attno = findAttrByName(attributeName, inhSchema);
1910 if (exist_attno > 0)
1911 {
1912 Oid defTypeId;
1913 int32 deftypmod;
1914 Oid defCollId;
1915
1916 /*
1917 * Yes, try to merge the two column definitions. They must
1918 * have the same type, typmod, and collation.
1919 */
1920 ereport(NOTICE,
1921 (errmsg("merging multiple inherited definitions of column \"%s\"",
1922 attributeName)));
1923 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
1924 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
1925 if (defTypeId != attribute->atttypid ||
1926 deftypmod != attribute->atttypmod)
1927 ereport(ERROR,
1928 (errcode(ERRCODE_DATATYPE_MISMATCH),
1929 errmsg("inherited column \"%s\" has a type conflict",
1930 attributeName),
1931 errdetail("%s versus %s",
1932 format_type_with_typemod(defTypeId,
1933 deftypmod),
1934 format_type_with_typemod(attribute->atttypid,
1935 attribute->atttypmod))));
1936 defCollId = GetColumnDefCollation(NULL, def, defTypeId);
1937 if (defCollId != attribute->attcollation)
1938 ereport(ERROR,
1939 (errcode(ERRCODE_COLLATION_MISMATCH),
1940 errmsg("inherited column \"%s\" has a collation conflict",
1941 attributeName),
1942 errdetail("\"%s\" versus \"%s\"",
1943 get_collation_name(defCollId),
1944 get_collation_name(attribute->attcollation))));
1945
1946 /* Copy storage parameter */
1947 if (def->storage == 0)
1948 def->storage = attribute->attstorage;
1949 else if (def->storage != attribute->attstorage)
1950 ereport(ERROR,
1951 (errcode(ERRCODE_DATATYPE_MISMATCH),
1952 errmsg("inherited column \"%s\" has a storage parameter conflict",
1953 attributeName),
1954 errdetail("%s versus %s",
1955 storage_name(def->storage),
1956 storage_name(attribute->attstorage))));
1957
1958 def->inhcount++;
1959 /* Merge of NOT NULL constraints = OR 'em together */
1960 def->is_not_null |= attribute->attnotnull;
1961 /* Default and other constraints are handled below */
1962 newattno[parent_attno - 1] = exist_attno;
1963 }
1964 else
1965 {
1966 /*
1967 * No, create a new inherited column
1968 */
1969 def = makeNode(ColumnDef);
1970 def->colname = pstrdup(attributeName);
1971 def->typeName = makeTypeNameFromOid(attribute->atttypid,
1972 attribute->atttypmod);
1973 def->inhcount = 1;
1974 def->is_local = false;
1975 def->is_not_null = attribute->attnotnull;
1976 def->is_from_type = false;
1977 def->storage = attribute->attstorage;
1978 def->raw_default = NULL;
1979 def->cooked_default = NULL;
1980 def->collClause = NULL;
1981 def->collOid = attribute->attcollation;
1982 def->constraints = NIL;
1983 def->location = -1;
1984 inhSchema = lappend(inhSchema, def);
1985 newattno[parent_attno - 1] = ++child_attno;
1986 }
1987
1988 /*
1989 * Copy default if any
1990 */
1991 if (attribute->atthasdef)
1992 {
1993 Node *this_default = NULL;
1994 AttrDefault *attrdef;
1995 int i;
1996
1997 /* Find default in constraint structure */
1998 Assert(constr != NULL);
1999 attrdef = constr->defval;
2000 for (i = 0; i < constr->num_defval; i++)
2001 {
2002 if (attrdef[i].adnum == parent_attno)
2003 {
2004 this_default = stringToNode(attrdef[i].adbin);
2005 break;
2006 }
2007 }
2008 Assert(this_default != NULL);
2009
2010 /*
2011 * If default expr could contain any vars, we'd need to fix
2012 * 'em, but it can't; so default is ready to apply to child.
2013 *
2014 * If we already had a default from some prior parent, check
2015 * to see if they are the same. If so, no problem; if not,
2016 * mark the column as having a bogus default. Below, we will
2017 * complain if the bogus default isn't overridden by the child
2018 * schema.
2019 */
2020 Assert(def->raw_default == NULL);
2021 if (def->cooked_default == NULL)
2022 def->cooked_default = this_default;
2023 else if (!equal(def->cooked_default, this_default))
2024 {
2025 def->cooked_default = &bogus_marker;
2026 have_bogus_defaults = true;
2027 }
2028 }
2029 }
2030
2031 /*
2032 * Now copy the CHECK constraints of this parent, adjusting attnos
2033 * using the completed newattno[] map. Identically named constraints
2034 * are merged if possible, else we throw error.
2035 */
2036 if (constr && constr->num_check > 0)
2037 {
2038 ConstrCheck *check = constr->check;
2039 int i;
2040
2041 for (i = 0; i < constr->num_check; i++)
2042 {
2043 char *name = check[i].ccname;
2044 Node *expr;
2045 bool found_whole_row;
2046
2047 /* ignore if the constraint is non-inheritable */
2048 if (check[i].ccnoinherit)
2049 continue;
2050
2051 /* Adjust Vars to match new table's column numbering */
2052 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2053 1, 0,
2054 newattno, tupleDesc->natts,
2055 InvalidOid, &found_whole_row);
2056
2057 /*
2058 * For the moment we have to reject whole-row variables. We
2059 * could convert them, if we knew the new table's rowtype OID,
2060 * but that hasn't been assigned yet.
2061 */
2062 if (found_whole_row)
2063 ereport(ERROR,
2064 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2065 errmsg("cannot convert whole-row table reference"),
2066 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2067 name,
2068 RelationGetRelationName(relation))));
2069
2070 /* check for duplicate */
2071 if (!MergeCheckConstraint(constraints, name, expr))
2072 {
2073 /* nope, this is a new one */
2074 CookedConstraint *cooked;
2075
2076 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
2077 cooked->contype = CONSTR_CHECK;
2078 cooked->conoid = InvalidOid; /* until created */
2079 cooked->name = pstrdup(name);
2080 cooked->attnum = 0; /* not used for constraints */
2081 cooked->expr = expr;
2082 cooked->skip_validation = false;
2083 cooked->is_local = false;
2084 cooked->inhcount = 1;
2085 cooked->is_no_inherit = false;
2086 constraints = lappend(constraints, cooked);
2087 }
2088 }
2089 }
2090
2091 pfree(newattno);
2092
2093 /*
2094 * Close the parent rel, but keep our lock on it until xact commit.
2095 * That will prevent someone else from deleting or ALTERing the parent
2096 * before the child is committed.
2097 */
2098 heap_close(relation, NoLock);
2099 }
2100
2101 /*
2102 * If we had no inherited attributes, the result schema is just the
2103 * explicitly declared columns. Otherwise, we need to merge the declared
2104 * columns into the inherited schema list. Although, we never have any
2105 * explicitly declared columns if the table is a partition.
2106 */
2107 if (inhSchema != NIL)
2108 {
2109 int schema_attno = 0;
2110
2111 foreach(entry, schema)
2112 {
2113 ColumnDef *newdef = lfirst(entry);
2114 char *attributeName = newdef->colname;
2115 int exist_attno;
2116
2117 schema_attno++;
2118
2119 /*
2120 * Does it conflict with some previously inherited column?
2121 */
2122 exist_attno = findAttrByName(attributeName, inhSchema);
2123 if (exist_attno > 0)
2124 {
2125 ColumnDef *def;
2126 Oid defTypeId,
2127 newTypeId;
2128 int32 deftypmod,
2129 newtypmod;
2130 Oid defcollid,
2131 newcollid;
2132
2133 /*
2134 * Partitions have only one parent and have no column
2135 * definitions of their own, so conflict should never occur.
2136 */
2137 Assert(!is_partition);
2138
2139 /*
2140 * Yes, try to merge the two column definitions. They must
2141 * have the same type, typmod, and collation.
2142 */
2143 if (exist_attno == schema_attno)
2144 ereport(NOTICE,
2145 (errmsg("merging column \"%s\" with inherited definition",
2146 attributeName)));
2147 else
2148 ereport(NOTICE,
2149 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
2150 errdetail("User-specified column moved to the position of the inherited column.")));
2151 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2152 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2153 typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
2154 if (defTypeId != newTypeId || deftypmod != newtypmod)
2155 ereport(ERROR,
2156 (errcode(ERRCODE_DATATYPE_MISMATCH),
2157 errmsg("column \"%s\" has a type conflict",
2158 attributeName),
2159 errdetail("%s versus %s",
2160 format_type_with_typemod(defTypeId,
2161 deftypmod),
2162 format_type_with_typemod(newTypeId,
2163 newtypmod))));
2164 defcollid = GetColumnDefCollation(NULL, def, defTypeId);
2165 newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
2166 if (defcollid != newcollid)
2167 ereport(ERROR,
2168 (errcode(ERRCODE_COLLATION_MISMATCH),
2169 errmsg("column \"%s\" has a collation conflict",
2170 attributeName),
2171 errdetail("\"%s\" versus \"%s\"",
2172 get_collation_name(defcollid),
2173 get_collation_name(newcollid))));
2174
2175 /*
2176 * Identity is never inherited. The new column can have an
2177 * identity definition, so we always just take that one.
2178 */
2179 def->identity = newdef->identity;
2180
2181 /* Copy storage parameter */
2182 if (def->storage == 0)
2183 def->storage = newdef->storage;
2184 else if (newdef->storage != 0 && def->storage != newdef->storage)
2185 ereport(ERROR,
2186 (errcode(ERRCODE_DATATYPE_MISMATCH),
2187 errmsg("column \"%s\" has a storage parameter conflict",
2188 attributeName),
2189 errdetail("%s versus %s",
2190 storage_name(def->storage),
2191 storage_name(newdef->storage))));
2192
2193 /* Mark the column as locally defined */
2194 def->is_local = true;
2195 /* Merge of NOT NULL constraints = OR 'em together */
2196 def->is_not_null |= newdef->is_not_null;
2197 /* If new def has a default, override previous default */
2198 if (newdef->raw_default != NULL)
2199 {
2200 def->raw_default = newdef->raw_default;
2201 def->cooked_default = newdef->cooked_default;
2202 }
2203 }
2204 else
2205 {
2206 /*
2207 * No, attach new column to result schema
2208 */
2209 inhSchema = lappend(inhSchema, newdef);
2210 }
2211 }
2212
2213 schema = inhSchema;
2214
2215 /*
2216 * Check that we haven't exceeded the legal # of columns after merging
2217 * in inherited columns.
2218 */
2219 if (list_length(schema) > MaxHeapAttributeNumber)
2220 ereport(ERROR,
2221 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2222 errmsg("tables can have at most %d columns",
2223 MaxHeapAttributeNumber)));
2224 }
2225
2226 /*
2227 * Now that we have the column definition list for a partition, we can
2228 * check whether the columns referenced in the column constraint specs
2229 * actually exist. Also, we merge NOT NULL and defaults into each
2230 * corresponding column definition.
2231 */
2232 if (is_partition)
2233 {
2234 foreach(entry, saved_schema)
2235 {
2236 ColumnDef *restdef = lfirst(entry);
2237 bool found = false;
2238 ListCell *l;
2239
2240 foreach(l, schema)
2241 {
2242 ColumnDef *coldef = lfirst(l);
2243
2244 if (strcmp(coldef->colname, restdef->colname) == 0)
2245 {
2246 found = true;
2247 coldef->is_not_null |= restdef->is_not_null;
2248
2249 /*
2250 * Override the parent's default value for this column
2251 * (coldef->cooked_default) with the partition's local
2252 * definition (restdef->raw_default), if there's one. It
2253 * should be physically impossible to get a cooked default
2254 * in the local definition or a raw default in the
2255 * inherited definition, but make sure they're nulls, for
2256 * future-proofing.
2257 */
2258 Assert(restdef->cooked_default == NULL);
2259 Assert(coldef->raw_default == NULL);
2260 if (restdef->raw_default)
2261 {
2262 coldef->raw_default = restdef->raw_default;
2263 coldef->cooked_default = NULL;
2264 }
2265 }
2266 }
2267
2268 /* complain for constraints on columns not in parent */
2269 if (!found)
2270 ereport(ERROR,
2271 (errcode(ERRCODE_UNDEFINED_COLUMN),
2272 errmsg("column \"%s\" does not exist",
2273 restdef->colname)));
2274 }
2275 }
2276
2277 /*
2278 * If we found any conflicting parent default values, check to make sure
2279 * they were overridden by the child.
2280 */
2281 if (have_bogus_defaults)
2282 {
2283 foreach(entry, schema)
2284 {
2285 ColumnDef *def = lfirst(entry);
2286
2287 if (def->cooked_default == &bogus_marker)
2288 ereport(ERROR,
2289 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2290 errmsg("column \"%s\" inherits conflicting default values",
2291 def->colname),
2292 errhint("To resolve the conflict, specify a default explicitly.")));
2293 }
2294 }
2295
2296 *supOids = parentOids;
2297 *supconstr = constraints;
2298 *supOidCount = parentsWithOids;
2299 return schema;
2300 }
2301
2302
2303 /*
2304 * MergeCheckConstraint
2305 * Try to merge an inherited CHECK constraint with previous ones
2306 *
2307 * If we inherit identically-named constraints from multiple parents, we must
2308 * merge them, or throw an error if they don't have identical definitions.
2309 *
2310 * constraints is a list of CookedConstraint structs for previous constraints.
2311 *
2312 * Returns TRUE if merged (constraint is a duplicate), or FALSE if it's
2313 * got a so-far-unique name, or throws error if conflict.
2314 */
2315 static bool
MergeCheckConstraint(List * constraints,char * name,Node * expr)2316 MergeCheckConstraint(List *constraints, char *name, Node *expr)
2317 {
2318 ListCell *lc;
2319
2320 foreach(lc, constraints)
2321 {
2322 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
2323
2324 Assert(ccon->contype == CONSTR_CHECK);
2325
2326 /* Non-matching names never conflict */
2327 if (strcmp(ccon->name, name) != 0)
2328 continue;
2329
2330 if (equal(expr, ccon->expr))
2331 {
2332 /* OK to merge */
2333 ccon->inhcount++;
2334 return true;
2335 }
2336
2337 ereport(ERROR,
2338 (errcode(ERRCODE_DUPLICATE_OBJECT),
2339 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
2340 name)));
2341 }
2342
2343 return false;
2344 }
2345
2346
2347 /*
2348 * StoreCatalogInheritance
2349 * Updates the system catalogs with proper inheritance information.
2350 *
2351 * supers is a list of the OIDs of the new relation's direct ancestors.
2352 */
2353 static void
StoreCatalogInheritance(Oid relationId,List * supers,bool child_is_partition)2354 StoreCatalogInheritance(Oid relationId, List *supers,
2355 bool child_is_partition)
2356 {
2357 Relation relation;
2358 int32 seqNumber;
2359 ListCell *entry;
2360
2361 /*
2362 * sanity checks
2363 */
2364 AssertArg(OidIsValid(relationId));
2365
2366 if (supers == NIL)
2367 return;
2368
2369 /*
2370 * Store INHERITS information in pg_inherits using direct ancestors only.
2371 * Also enter dependencies on the direct ancestors, and make sure they are
2372 * marked with relhassubclass = true.
2373 *
2374 * (Once upon a time, both direct and indirect ancestors were found here
2375 * and then entered into pg_ipl. Since that catalog doesn't exist
2376 * anymore, there's no need to look for indirect ancestors.)
2377 */
2378 relation = heap_open(InheritsRelationId, RowExclusiveLock);
2379
2380 seqNumber = 1;
2381 foreach(entry, supers)
2382 {
2383 Oid parentOid = lfirst_oid(entry);
2384
2385 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
2386 child_is_partition);
2387 seqNumber++;
2388 }
2389
2390 heap_close(relation, RowExclusiveLock);
2391 }
2392
2393 /*
2394 * Make catalog entries showing relationId as being an inheritance child
2395 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
2396 */
2397 static void
StoreCatalogInheritance1(Oid relationId,Oid parentOid,int32 seqNumber,Relation inhRelation,bool child_is_partition)2398 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
2399 int32 seqNumber, Relation inhRelation,
2400 bool child_is_partition)
2401 {
2402 TupleDesc desc = RelationGetDescr(inhRelation);
2403 Datum values[Natts_pg_inherits];
2404 bool nulls[Natts_pg_inherits];
2405 ObjectAddress childobject,
2406 parentobject;
2407 HeapTuple tuple;
2408
2409 /*
2410 * Make the pg_inherits entry
2411 */
2412 values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
2413 values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
2414 values[Anum_pg_inherits_inhseqno - 1] = Int32GetDatum(seqNumber);
2415
2416 memset(nulls, 0, sizeof(nulls));
2417
2418 tuple = heap_form_tuple(desc, values, nulls);
2419
2420 CatalogTupleInsert(inhRelation, tuple);
2421
2422 heap_freetuple(tuple);
2423
2424 /*
2425 * Store a dependency too
2426 */
2427 parentobject.classId = RelationRelationId;
2428 parentobject.objectId = parentOid;
2429 parentobject.objectSubId = 0;
2430 childobject.classId = RelationRelationId;
2431 childobject.objectId = relationId;
2432 childobject.objectSubId = 0;
2433
2434 recordDependencyOn(&childobject, &parentobject,
2435 child_dependency_type(child_is_partition));
2436
2437 /*
2438 * Post creation hook of this inheritance. Since object_access_hook
2439 * doesn't take multiple object identifiers, we relay oid of parent
2440 * relation using auxiliary_id argument.
2441 */
2442 InvokeObjectPostAlterHookArg(InheritsRelationId,
2443 relationId, 0,
2444 parentOid, false);
2445
2446 /*
2447 * Mark the parent as having subclasses.
2448 */
2449 SetRelationHasSubclass(parentOid, true);
2450 }
2451
2452 /*
2453 * Look for an existing schema entry with the given name.
2454 *
2455 * Returns the index (starting with 1) if attribute already exists in schema,
2456 * 0 if it doesn't.
2457 */
2458 static int
findAttrByName(const char * attributeName,List * schema)2459 findAttrByName(const char *attributeName, List *schema)
2460 {
2461 ListCell *s;
2462 int i = 1;
2463
2464 foreach(s, schema)
2465 {
2466 ColumnDef *def = lfirst(s);
2467
2468 if (strcmp(attributeName, def->colname) == 0)
2469 return i;
2470
2471 i++;
2472 }
2473 return 0;
2474 }
2475
2476
2477 /*
2478 * SetRelationHasSubclass
2479 * Set the value of the relation's relhassubclass field in pg_class.
2480 *
2481 * NOTE: caller must be holding an appropriate lock on the relation.
2482 * ShareUpdateExclusiveLock is sufficient.
2483 *
2484 * NOTE: an important side-effect of this operation is that an SI invalidation
2485 * message is sent out to all backends --- including me --- causing plans
2486 * referencing the relation to be rebuilt with the new list of children.
2487 * This must happen even if we find that no change is needed in the pg_class
2488 * row.
2489 */
2490 void
SetRelationHasSubclass(Oid relationId,bool relhassubclass)2491 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
2492 {
2493 Relation relationRelation;
2494 HeapTuple tuple;
2495 Form_pg_class classtuple;
2496
2497 /*
2498 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
2499 */
2500 relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
2501 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
2502 if (!HeapTupleIsValid(tuple))
2503 elog(ERROR, "cache lookup failed for relation %u", relationId);
2504 classtuple = (Form_pg_class) GETSTRUCT(tuple);
2505
2506 if (classtuple->relhassubclass != relhassubclass)
2507 {
2508 classtuple->relhassubclass = relhassubclass;
2509 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
2510 }
2511 else
2512 {
2513 /* no need to change tuple, but force relcache rebuild anyway */
2514 CacheInvalidateRelcacheByTuple(tuple);
2515 }
2516
2517 heap_freetuple(tuple);
2518 heap_close(relationRelation, RowExclusiveLock);
2519 }
2520
2521 /*
2522 * renameatt_check - basic sanity checks before attribute rename
2523 */
2524 static void
renameatt_check(Oid myrelid,Form_pg_class classform,bool recursing)2525 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
2526 {
2527 char relkind = classform->relkind;
2528
2529 if (classform->reloftype && !recursing)
2530 ereport(ERROR,
2531 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2532 errmsg("cannot rename column of typed table")));
2533
2534 /*
2535 * Renaming the columns of sequences or toast tables doesn't actually
2536 * break anything from the system's point of view, since internal
2537 * references are by attnum. But it doesn't seem right to allow users to
2538 * change names that are hardcoded into the system, hence the following
2539 * restriction.
2540 */
2541 if (relkind != RELKIND_RELATION &&
2542 relkind != RELKIND_VIEW &&
2543 relkind != RELKIND_MATVIEW &&
2544 relkind != RELKIND_COMPOSITE_TYPE &&
2545 relkind != RELKIND_INDEX &&
2546 relkind != RELKIND_FOREIGN_TABLE &&
2547 relkind != RELKIND_PARTITIONED_TABLE)
2548 ereport(ERROR,
2549 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2550 errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table",
2551 NameStr(classform->relname))));
2552
2553 /*
2554 * permissions checking. only the owner of a class can change its schema.
2555 */
2556 if (!pg_class_ownercheck(myrelid, GetUserId()))
2557 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
2558 NameStr(classform->relname));
2559 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
2560 ereport(ERROR,
2561 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2562 errmsg("permission denied: \"%s\" is a system catalog",
2563 NameStr(classform->relname))));
2564 }
2565
2566 /*
2567 * renameatt_internal - workhorse for renameatt
2568 *
2569 * Return value is the attribute number in the 'myrelid' relation.
2570 */
2571 static AttrNumber
renameatt_internal(Oid myrelid,const char * oldattname,const char * newattname,bool recurse,bool recursing,int expected_parents,DropBehavior behavior)2572 renameatt_internal(Oid myrelid,
2573 const char *oldattname,
2574 const char *newattname,
2575 bool recurse,
2576 bool recursing,
2577 int expected_parents,
2578 DropBehavior behavior)
2579 {
2580 Relation targetrelation;
2581 Relation attrelation;
2582 HeapTuple atttup;
2583 Form_pg_attribute attform;
2584 AttrNumber attnum;
2585
2586 /*
2587 * Grab an exclusive lock on the target table, which we will NOT release
2588 * until end of transaction.
2589 */
2590 targetrelation = relation_open(myrelid, AccessExclusiveLock);
2591 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
2592
2593 /*
2594 * if the 'recurse' flag is set then we are supposed to rename this
2595 * attribute in all classes that inherit from 'relname' (as well as in
2596 * 'relname').
2597 *
2598 * any permissions or problems with duplicate attributes will cause the
2599 * whole transaction to abort, which is what we want -- all or nothing.
2600 */
2601 if (recurse)
2602 {
2603 List *child_oids,
2604 *child_numparents;
2605 ListCell *lo,
2606 *li;
2607
2608 /*
2609 * we need the number of parents for each child so that the recursive
2610 * calls to renameatt() can determine whether there are any parents
2611 * outside the inheritance hierarchy being processed.
2612 */
2613 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
2614 &child_numparents);
2615
2616 /*
2617 * find_all_inheritors does the recursive search of the inheritance
2618 * hierarchy, so all we have to do is process all of the relids in the
2619 * list that it returns.
2620 */
2621 forboth(lo, child_oids, li, child_numparents)
2622 {
2623 Oid childrelid = lfirst_oid(lo);
2624 int numparents = lfirst_int(li);
2625
2626 if (childrelid == myrelid)
2627 continue;
2628 /* note we need not recurse again */
2629 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
2630 }
2631 }
2632 else
2633 {
2634 /*
2635 * If we are told not to recurse, there had better not be any child
2636 * tables; else the rename would put them out of step.
2637 *
2638 * expected_parents will only be 0 if we are not already recursing.
2639 */
2640 if (expected_parents == 0 &&
2641 find_inheritance_children(myrelid, NoLock) != NIL)
2642 ereport(ERROR,
2643 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2644 errmsg("inherited column \"%s\" must be renamed in child tables too",
2645 oldattname)));
2646 }
2647
2648 /* rename attributes in typed tables of composite type */
2649 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
2650 {
2651 List *child_oids;
2652 ListCell *lo;
2653
2654 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
2655 RelationGetRelationName(targetrelation),
2656 behavior);
2657
2658 foreach(lo, child_oids)
2659 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
2660 }
2661
2662 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
2663
2664 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
2665 if (!HeapTupleIsValid(atttup))
2666 ereport(ERROR,
2667 (errcode(ERRCODE_UNDEFINED_COLUMN),
2668 errmsg("column \"%s\" does not exist",
2669 oldattname)));
2670 attform = (Form_pg_attribute) GETSTRUCT(atttup);
2671
2672 attnum = attform->attnum;
2673 if (attnum <= 0)
2674 ereport(ERROR,
2675 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2676 errmsg("cannot rename system column \"%s\"",
2677 oldattname)));
2678
2679 /*
2680 * if the attribute is inherited, forbid the renaming. if this is a
2681 * top-level call to renameatt(), then expected_parents will be 0, so the
2682 * effect of this code will be to prohibit the renaming if the attribute
2683 * is inherited at all. if this is a recursive call to renameatt(),
2684 * expected_parents will be the number of parents the current relation has
2685 * within the inheritance hierarchy being processed, so we'll prohibit the
2686 * renaming only if there are additional parents from elsewhere.
2687 */
2688 if (attform->attinhcount > expected_parents)
2689 ereport(ERROR,
2690 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2691 errmsg("cannot rename inherited column \"%s\"",
2692 oldattname)));
2693
2694 /* new name should not already exist */
2695 (void) check_for_column_name_collision(targetrelation, newattname, false);
2696
2697 /* apply the update */
2698 namestrcpy(&(attform->attname), newattname);
2699
2700 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
2701
2702 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
2703
2704 heap_freetuple(atttup);
2705
2706 heap_close(attrelation, RowExclusiveLock);
2707
2708 relation_close(targetrelation, NoLock); /* close rel but keep lock */
2709
2710 return attnum;
2711 }
2712
2713 /*
2714 * Perform permissions and integrity checks before acquiring a relation lock.
2715 */
2716 static void
RangeVarCallbackForRenameAttribute(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)2717 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
2718 void *arg)
2719 {
2720 HeapTuple tuple;
2721 Form_pg_class form;
2722
2723 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
2724 if (!HeapTupleIsValid(tuple))
2725 return; /* concurrently dropped */
2726 form = (Form_pg_class) GETSTRUCT(tuple);
2727 renameatt_check(relid, form, false);
2728 ReleaseSysCache(tuple);
2729 }
2730
2731 /*
2732 * renameatt - changes the name of an attribute in a relation
2733 *
2734 * The returned ObjectAddress is that of the renamed column.
2735 */
2736 ObjectAddress
renameatt(RenameStmt * stmt)2737 renameatt(RenameStmt *stmt)
2738 {
2739 Oid relid;
2740 AttrNumber attnum;
2741 ObjectAddress address;
2742
2743 /* lock level taken here should match renameatt_internal */
2744 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
2745 stmt->missing_ok, false,
2746 RangeVarCallbackForRenameAttribute,
2747 NULL);
2748
2749 if (!OidIsValid(relid))
2750 {
2751 ereport(NOTICE,
2752 (errmsg("relation \"%s\" does not exist, skipping",
2753 stmt->relation->relname)));
2754 return InvalidObjectAddress;
2755 }
2756
2757 attnum =
2758 renameatt_internal(relid,
2759 stmt->subname, /* old att name */
2760 stmt->newname, /* new att name */
2761 stmt->relation->inh, /* recursive? */
2762 false, /* recursing? */
2763 0, /* expected inhcount */
2764 stmt->behavior);
2765
2766 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
2767
2768 return address;
2769 }
2770
2771 /*
2772 * same logic as renameatt_internal
2773 */
2774 static ObjectAddress
rename_constraint_internal(Oid myrelid,Oid mytypid,const char * oldconname,const char * newconname,bool recurse,bool recursing,int expected_parents)2775 rename_constraint_internal(Oid myrelid,
2776 Oid mytypid,
2777 const char *oldconname,
2778 const char *newconname,
2779 bool recurse,
2780 bool recursing,
2781 int expected_parents)
2782 {
2783 Relation targetrelation = NULL;
2784 Oid constraintOid;
2785 HeapTuple tuple;
2786 Form_pg_constraint con;
2787 ObjectAddress address;
2788
2789 AssertArg(!myrelid || !mytypid);
2790
2791 if (mytypid)
2792 {
2793 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
2794 }
2795 else
2796 {
2797 targetrelation = relation_open(myrelid, AccessExclusiveLock);
2798
2799 /*
2800 * don't tell it whether we're recursing; we allow changing typed
2801 * tables here
2802 */
2803 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
2804
2805 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
2806 }
2807
2808 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
2809 if (!HeapTupleIsValid(tuple))
2810 elog(ERROR, "cache lookup failed for constraint %u",
2811 constraintOid);
2812 con = (Form_pg_constraint) GETSTRUCT(tuple);
2813
2814 if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
2815 {
2816 if (recurse)
2817 {
2818 List *child_oids,
2819 *child_numparents;
2820 ListCell *lo,
2821 *li;
2822
2823 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
2824 &child_numparents);
2825
2826 forboth(lo, child_oids, li, child_numparents)
2827 {
2828 Oid childrelid = lfirst_oid(lo);
2829 int numparents = lfirst_int(li);
2830
2831 if (childrelid == myrelid)
2832 continue;
2833
2834 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
2835 }
2836 }
2837 else
2838 {
2839 if (expected_parents == 0 &&
2840 find_inheritance_children(myrelid, NoLock) != NIL)
2841 ereport(ERROR,
2842 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2843 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
2844 oldconname)));
2845 }
2846
2847 if (con->coninhcount > expected_parents)
2848 ereport(ERROR,
2849 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2850 errmsg("cannot rename inherited constraint \"%s\"",
2851 oldconname)));
2852 }
2853
2854 if (con->conindid
2855 && (con->contype == CONSTRAINT_PRIMARY
2856 || con->contype == CONSTRAINT_UNIQUE
2857 || con->contype == CONSTRAINT_EXCLUSION))
2858 /* rename the index; this renames the constraint as well */
2859 RenameRelationInternal(con->conindid, newconname, false);
2860 else
2861 RenameConstraintById(constraintOid, newconname);
2862
2863 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
2864
2865 ReleaseSysCache(tuple);
2866
2867 if (targetrelation)
2868 {
2869 /*
2870 * Invalidate relcache so as others can see the new constraint name.
2871 */
2872 CacheInvalidateRelcache(targetrelation);
2873
2874 relation_close(targetrelation, NoLock); /* close rel but keep lock */
2875 }
2876
2877 return address;
2878 }
2879
2880 ObjectAddress
RenameConstraint(RenameStmt * stmt)2881 RenameConstraint(RenameStmt *stmt)
2882 {
2883 Oid relid = InvalidOid;
2884 Oid typid = InvalidOid;
2885
2886 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
2887 {
2888 Relation rel;
2889 HeapTuple tup;
2890
2891 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
2892 rel = heap_open(TypeRelationId, RowExclusiveLock);
2893 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
2894 if (!HeapTupleIsValid(tup))
2895 elog(ERROR, "cache lookup failed for type %u", typid);
2896 checkDomainOwner(tup);
2897 ReleaseSysCache(tup);
2898 heap_close(rel, NoLock);
2899 }
2900 else
2901 {
2902 /* lock level taken here should match rename_constraint_internal */
2903 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
2904 stmt->missing_ok, false,
2905 RangeVarCallbackForRenameAttribute,
2906 NULL);
2907 if (!OidIsValid(relid))
2908 {
2909 ereport(NOTICE,
2910 (errmsg("relation \"%s\" does not exist, skipping",
2911 stmt->relation->relname)));
2912 return InvalidObjectAddress;
2913 }
2914 }
2915
2916 return
2917 rename_constraint_internal(relid, typid,
2918 stmt->subname,
2919 stmt->newname,
2920 (stmt->relation &&
2921 stmt->relation->inh), /* recursive? */
2922 false, /* recursing? */
2923 0 /* expected inhcount */ );
2924
2925 }
2926
2927 /*
2928 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
2929 * RENAME
2930 */
2931 ObjectAddress
RenameRelation(RenameStmt * stmt)2932 RenameRelation(RenameStmt *stmt)
2933 {
2934 Oid relid;
2935 ObjectAddress address;
2936
2937 /*
2938 * Grab an exclusive lock on the target table, index, sequence, view,
2939 * materialized view, or foreign table, which we will NOT release until
2940 * end of transaction.
2941 *
2942 * Lock level used here should match RenameRelationInternal, to avoid lock
2943 * escalation.
2944 */
2945 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
2946 stmt->missing_ok, false,
2947 RangeVarCallbackForAlterRelation,
2948 (void *) stmt);
2949
2950 if (!OidIsValid(relid))
2951 {
2952 ereport(NOTICE,
2953 (errmsg("relation \"%s\" does not exist, skipping",
2954 stmt->relation->relname)));
2955 return InvalidObjectAddress;
2956 }
2957
2958 /* Do the work */
2959 RenameRelationInternal(relid, stmt->newname, false);
2960
2961 ObjectAddressSet(address, RelationRelationId, relid);
2962
2963 return address;
2964 }
2965
2966 /*
2967 * RenameRelationInternal - change the name of a relation
2968 */
2969 void
RenameRelationInternal(Oid myrelid,const char * newrelname,bool is_internal)2970 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
2971 {
2972 Relation targetrelation;
2973 Relation relrelation; /* for RELATION relation */
2974 HeapTuple reltup;
2975 Form_pg_class relform;
2976 Oid namespaceId;
2977
2978 /*
2979 * Grab an exclusive lock on the target table, index, sequence, view,
2980 * materialized view, or foreign table, which we will NOT release until
2981 * end of transaction.
2982 */
2983 targetrelation = relation_open(myrelid, AccessExclusiveLock);
2984 namespaceId = RelationGetNamespace(targetrelation);
2985
2986 /*
2987 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
2988 */
2989 relrelation = heap_open(RelationRelationId, RowExclusiveLock);
2990
2991 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
2992 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
2993 elog(ERROR, "cache lookup failed for relation %u", myrelid);
2994 relform = (Form_pg_class) GETSTRUCT(reltup);
2995
2996 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
2997 ereport(ERROR,
2998 (errcode(ERRCODE_DUPLICATE_TABLE),
2999 errmsg("relation \"%s\" already exists",
3000 newrelname)));
3001
3002 /*
3003 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
3004 * because it's a copy...)
3005 */
3006 namestrcpy(&(relform->relname), newrelname);
3007
3008 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
3009
3010 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
3011 InvalidOid, is_internal);
3012
3013 heap_freetuple(reltup);
3014 heap_close(relrelation, RowExclusiveLock);
3015
3016 /*
3017 * Also rename the associated type, if any.
3018 */
3019 if (OidIsValid(targetrelation->rd_rel->reltype))
3020 RenameTypeInternal(targetrelation->rd_rel->reltype,
3021 newrelname, namespaceId);
3022
3023 /*
3024 * Also rename the associated constraint, if any.
3025 */
3026 if (targetrelation->rd_rel->relkind == RELKIND_INDEX)
3027 {
3028 Oid constraintId = get_index_constraint(myrelid);
3029
3030 if (OidIsValid(constraintId))
3031 RenameConstraintById(constraintId, newrelname);
3032 }
3033
3034 /*
3035 * Close rel, but keep exclusive lock!
3036 */
3037 relation_close(targetrelation, NoLock);
3038 }
3039
3040 /*
3041 * Disallow ALTER TABLE (and similar commands) when the current backend has
3042 * any open reference to the target table besides the one just acquired by
3043 * the calling command; this implies there's an open cursor or active plan.
3044 * We need this check because our lock doesn't protect us against stomping
3045 * on our own foot, only other people's feet!
3046 *
3047 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
3048 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
3049 * possibly be relaxed to only error out for certain types of alterations.
3050 * But the use-case for allowing any of these things is not obvious, so we
3051 * won't work hard at it for now.
3052 *
3053 * We also reject these commands if there are any pending AFTER trigger events
3054 * for the rel. This is certainly necessary for the rewriting variants of
3055 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
3056 * events would try to fetch the wrong tuples. It might be overly cautious
3057 * in other cases, but again it seems better to err on the side of paranoia.
3058 *
3059 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
3060 * we are worried about active indexscans on the index. The trigger-event
3061 * check can be skipped, since we are doing no damage to the parent table.
3062 *
3063 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
3064 */
3065 void
CheckTableNotInUse(Relation rel,const char * stmt)3066 CheckTableNotInUse(Relation rel, const char *stmt)
3067 {
3068 int expected_refcnt;
3069
3070 expected_refcnt = rel->rd_isnailed ? 2 : 1;
3071 if (rel->rd_refcnt != expected_refcnt)
3072 ereport(ERROR,
3073 (errcode(ERRCODE_OBJECT_IN_USE),
3074 /* translator: first %s is a SQL command, eg ALTER TABLE */
3075 errmsg("cannot %s \"%s\" because "
3076 "it is being used by active queries in this session",
3077 stmt, RelationGetRelationName(rel))));
3078
3079 if (rel->rd_rel->relkind != RELKIND_INDEX &&
3080 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
3081 ereport(ERROR,
3082 (errcode(ERRCODE_OBJECT_IN_USE),
3083 /* translator: first %s is a SQL command, eg ALTER TABLE */
3084 errmsg("cannot %s \"%s\" because "
3085 "it has pending trigger events",
3086 stmt, RelationGetRelationName(rel))));
3087 }
3088
3089 /*
3090 * AlterTableLookupRelation
3091 * Look up, and lock, the OID for the relation named by an alter table
3092 * statement.
3093 */
3094 Oid
AlterTableLookupRelation(AlterTableStmt * stmt,LOCKMODE lockmode)3095 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
3096 {
3097 return RangeVarGetRelidExtended(stmt->relation, lockmode, stmt->missing_ok, false,
3098 RangeVarCallbackForAlterRelation,
3099 (void *) stmt);
3100 }
3101
3102 /*
3103 * AlterTable
3104 * Execute ALTER TABLE, which can be a list of subcommands
3105 *
3106 * ALTER TABLE is performed in three phases:
3107 * 1. Examine subcommands and perform pre-transformation checking.
3108 * 2. Update system catalogs.
3109 * 3. Scan table(s) to check new constraints, and optionally recopy
3110 * the data into new table(s).
3111 * Phase 3 is not performed unless one or more of the subcommands requires
3112 * it. The intention of this design is to allow multiple independent
3113 * updates of the table schema to be performed with only one pass over the
3114 * data.
3115 *
3116 * ATPrepCmd performs phase 1. A "work queue" entry is created for
3117 * each table to be affected (there may be multiple affected tables if the
3118 * commands traverse a table inheritance hierarchy). Also we do preliminary
3119 * validation of the subcommands, including parse transformation of those
3120 * expressions that need to be evaluated with respect to the old table
3121 * schema.
3122 *
3123 * ATRewriteCatalogs performs phase 2 for each affected table. (Note that
3124 * phases 2 and 3 normally do no explicit recursion, since phase 1 already
3125 * did it --- although some subcommands have to recurse in phase 2 instead.)
3126 * Certain subcommands need to be performed before others to avoid
3127 * unnecessary conflicts; for example, DROP COLUMN should come before
3128 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
3129 * lists, one for each logical "pass" of phase 2.
3130 *
3131 * ATRewriteTables performs phase 3 for those tables that need it.
3132 *
3133 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
3134 * the whole operation; we don't have to do anything special to clean up.
3135 *
3136 * The caller must lock the relation, with an appropriate lock level
3137 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
3138 * or higher. We pass the lock level down
3139 * so that we can apply it recursively to inherited tables. Note that the
3140 * lock level we want as we recurse might well be higher than required for
3141 * that specific subcommand. So we pass down the overall lock requirement,
3142 * rather than reassess it at lower levels.
3143 */
3144 void
AlterTable(Oid relid,LOCKMODE lockmode,AlterTableStmt * stmt)3145 AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt)
3146 {
3147 Relation rel;
3148
3149 /* Caller is required to provide an adequate lock. */
3150 rel = relation_open(relid, NoLock);
3151
3152 CheckTableNotInUse(rel, "ALTER TABLE");
3153
3154 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode);
3155 }
3156
3157 /*
3158 * AlterTableInternal
3159 *
3160 * ALTER TABLE with target specified by OID
3161 *
3162 * We do not reject if the relation is already open, because it's quite
3163 * likely that one or more layers of caller have it open. That means it
3164 * is unsafe to use this entry point for alterations that could break
3165 * existing query plans. On the assumption it's not used for such, we
3166 * don't have to reject pending AFTER triggers, either.
3167 */
3168 void
AlterTableInternal(Oid relid,List * cmds,bool recurse)3169 AlterTableInternal(Oid relid, List *cmds, bool recurse)
3170 {
3171 Relation rel;
3172 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
3173
3174 rel = relation_open(relid, lockmode);
3175
3176 EventTriggerAlterTableRelid(relid);
3177
3178 ATController(NULL, rel, cmds, recurse, lockmode);
3179 }
3180
3181 /*
3182 * AlterTableGetLockLevel
3183 *
3184 * Sets the overall lock level required for the supplied list of subcommands.
3185 * Policy for doing this set according to needs of AlterTable(), see
3186 * comments there for overall explanation.
3187 *
3188 * Function is called before and after parsing, so it must give same
3189 * answer each time it is called. Some subcommands are transformed
3190 * into other subcommand types, so the transform must never be made to a
3191 * lower lock level than previously assigned. All transforms are noted below.
3192 *
3193 * Since this is called before we lock the table we cannot use table metadata
3194 * to influence the type of lock we acquire.
3195 *
3196 * There should be no lockmodes hardcoded into the subcommand functions. All
3197 * lockmode decisions for ALTER TABLE are made here only. The one exception is
3198 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
3199 * and does not travel through this section of code and cannot be combined with
3200 * any of the subcommands given here.
3201 *
3202 * Note that Hot Standby only knows about AccessExclusiveLocks on the master
3203 * so any changes that might affect SELECTs running on standbys need to use
3204 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
3205 * have a solution for that also.
3206 *
3207 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
3208 * that takes a lock less than AccessExclusiveLock can change object definitions
3209 * while pg_dump is running. Be careful to check that the appropriate data is
3210 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
3211 * otherwise we might end up with an inconsistent dump that can't restore.
3212 */
3213 LOCKMODE
AlterTableGetLockLevel(List * cmds)3214 AlterTableGetLockLevel(List *cmds)
3215 {
3216 /*
3217 * This only works if we read catalog tables using MVCC snapshots.
3218 */
3219 ListCell *lcmd;
3220 LOCKMODE lockmode = ShareUpdateExclusiveLock;
3221
3222 foreach(lcmd, cmds)
3223 {
3224 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3225 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
3226
3227 switch (cmd->subtype)
3228 {
3229 /*
3230 * These subcommands rewrite the heap, so require full locks.
3231 */
3232 case AT_AddColumn: /* may rewrite heap, in some cases and visible
3233 * to SELECT */
3234 case AT_SetTableSpace: /* must rewrite heap */
3235 case AT_AlterColumnType: /* must rewrite heap */
3236 case AT_AddOids: /* must rewrite heap */
3237 cmd_lockmode = AccessExclusiveLock;
3238 break;
3239
3240 /*
3241 * These subcommands may require addition of toast tables. If
3242 * we add a toast table to a table currently being scanned, we
3243 * might miss data added to the new toast table by concurrent
3244 * insert transactions.
3245 */
3246 case AT_SetStorage: /* may add toast tables, see
3247 * ATRewriteCatalogs() */
3248 cmd_lockmode = AccessExclusiveLock;
3249 break;
3250
3251 /*
3252 * Removing constraints can affect SELECTs that have been
3253 * optimised assuming the constraint holds true.
3254 */
3255 case AT_DropConstraint: /* as DROP INDEX */
3256 case AT_DropNotNull: /* may change some SQL plans */
3257 cmd_lockmode = AccessExclusiveLock;
3258 break;
3259
3260 /*
3261 * Subcommands that may be visible to concurrent SELECTs
3262 */
3263 case AT_DropColumn: /* change visible to SELECT */
3264 case AT_AddColumnToView: /* CREATE VIEW */
3265 case AT_DropOids: /* calls AT_DropColumn */
3266 case AT_EnableAlwaysRule: /* may change SELECT rules */
3267 case AT_EnableReplicaRule: /* may change SELECT rules */
3268 case AT_EnableRule: /* may change SELECT rules */
3269 case AT_DisableRule: /* may change SELECT rules */
3270 cmd_lockmode = AccessExclusiveLock;
3271 break;
3272
3273 /*
3274 * Changing owner may remove implicit SELECT privileges
3275 */
3276 case AT_ChangeOwner: /* change visible to SELECT */
3277 cmd_lockmode = AccessExclusiveLock;
3278 break;
3279
3280 /*
3281 * Changing foreign table options may affect optimization.
3282 */
3283 case AT_GenericOptions:
3284 case AT_AlterColumnGenericOptions:
3285 cmd_lockmode = AccessExclusiveLock;
3286 break;
3287
3288 /*
3289 * These subcommands affect write operations only.
3290 */
3291 case AT_EnableTrig:
3292 case AT_EnableAlwaysTrig:
3293 case AT_EnableReplicaTrig:
3294 case AT_EnableTrigAll:
3295 case AT_EnableTrigUser:
3296 case AT_DisableTrig:
3297 case AT_DisableTrigAll:
3298 case AT_DisableTrigUser:
3299 cmd_lockmode = ShareRowExclusiveLock;
3300 break;
3301
3302 /*
3303 * These subcommands affect write operations only. XXX
3304 * Theoretically, these could be ShareRowExclusiveLock.
3305 */
3306 case AT_ColumnDefault:
3307 case AT_AlterConstraint:
3308 case AT_AddIndex: /* from ADD CONSTRAINT */
3309 case AT_AddIndexConstraint:
3310 case AT_ReplicaIdentity:
3311 case AT_SetNotNull:
3312 case AT_EnableRowSecurity:
3313 case AT_DisableRowSecurity:
3314 case AT_ForceRowSecurity:
3315 case AT_NoForceRowSecurity:
3316 case AT_AddIdentity:
3317 case AT_DropIdentity:
3318 case AT_SetIdentity:
3319 cmd_lockmode = AccessExclusiveLock;
3320 break;
3321
3322 case AT_AddConstraint:
3323 case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
3324 case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
3325 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
3326 if (IsA(cmd->def, Constraint))
3327 {
3328 Constraint *con = (Constraint *) cmd->def;
3329
3330 switch (con->contype)
3331 {
3332 case CONSTR_EXCLUSION:
3333 case CONSTR_PRIMARY:
3334 case CONSTR_UNIQUE:
3335
3336 /*
3337 * Cases essentially the same as CREATE INDEX. We
3338 * could reduce the lock strength to ShareLock if
3339 * we can work out how to allow concurrent catalog
3340 * updates. XXX Might be set down to
3341 * ShareRowExclusiveLock but requires further
3342 * analysis.
3343 */
3344 cmd_lockmode = AccessExclusiveLock;
3345 break;
3346 case CONSTR_FOREIGN:
3347
3348 /*
3349 * We add triggers to both tables when we add a
3350 * Foreign Key, so the lock level must be at least
3351 * as strong as CREATE TRIGGER.
3352 */
3353 cmd_lockmode = ShareRowExclusiveLock;
3354 break;
3355
3356 default:
3357 cmd_lockmode = AccessExclusiveLock;
3358 }
3359 }
3360 break;
3361
3362 /*
3363 * These subcommands affect inheritance behaviour. Queries
3364 * started before us will continue to see the old inheritance
3365 * behaviour, while queries started after we commit will see
3366 * new behaviour. No need to prevent reads or writes to the
3367 * subtable while we hook it up though. Changing the TupDesc
3368 * may be a problem, so keep highest lock.
3369 */
3370 case AT_AddInherit:
3371 case AT_DropInherit:
3372 cmd_lockmode = AccessExclusiveLock;
3373 break;
3374
3375 /*
3376 * These subcommands affect implicit row type conversion. They
3377 * have affects similar to CREATE/DROP CAST on queries. don't
3378 * provide for invalidating parse trees as a result of such
3379 * changes, so we keep these at AccessExclusiveLock.
3380 */
3381 case AT_AddOf:
3382 case AT_DropOf:
3383 cmd_lockmode = AccessExclusiveLock;
3384 break;
3385
3386 /*
3387 * Only used by CREATE OR REPLACE VIEW which must conflict
3388 * with an SELECTs currently using the view.
3389 */
3390 case AT_ReplaceRelOptions:
3391 cmd_lockmode = AccessExclusiveLock;
3392 break;
3393
3394 /*
3395 * These subcommands affect general strategies for performance
3396 * and maintenance, though don't change the semantic results
3397 * from normal data reads and writes. Delaying an ALTER TABLE
3398 * behind currently active writes only delays the point where
3399 * the new strategy begins to take effect, so there is no
3400 * benefit in waiting. In this case the minimum restriction
3401 * applies: we don't currently allow concurrent catalog
3402 * updates.
3403 */
3404 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
3405 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
3406 case AT_DropCluster: /* Uses MVCC in getIndexes() */
3407 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
3408 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
3409 cmd_lockmode = ShareUpdateExclusiveLock;
3410 break;
3411
3412 case AT_SetLogged:
3413 case AT_SetUnLogged:
3414 cmd_lockmode = AccessExclusiveLock;
3415 break;
3416
3417 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
3418 cmd_lockmode = ShareUpdateExclusiveLock;
3419 break;
3420
3421 /*
3422 * Rel options are more complex than first appears. Options
3423 * are set here for tables, views and indexes; for historical
3424 * reasons these can all be used with ALTER TABLE, so we can't
3425 * decide between them using the basic grammar.
3426 */
3427 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
3428 * getTables() */
3429 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
3430 * getTables() */
3431 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
3432 break;
3433
3434 case AT_AttachPartition:
3435 case AT_DetachPartition:
3436 cmd_lockmode = AccessExclusiveLock;
3437 break;
3438
3439 default: /* oops */
3440 elog(ERROR, "unrecognized alter table type: %d",
3441 (int) cmd->subtype);
3442 break;
3443 }
3444
3445 /*
3446 * Take the greatest lockmode from any subcommand
3447 */
3448 if (cmd_lockmode > lockmode)
3449 lockmode = cmd_lockmode;
3450 }
3451
3452 return lockmode;
3453 }
3454
3455 /*
3456 * ATController provides top level control over the phases.
3457 *
3458 * parsetree is passed in to allow it to be passed to event triggers
3459 * when requested.
3460 */
3461 static void
ATController(AlterTableStmt * parsetree,Relation rel,List * cmds,bool recurse,LOCKMODE lockmode)3462 ATController(AlterTableStmt *parsetree,
3463 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
3464 {
3465 List *wqueue = NIL;
3466 ListCell *lcmd;
3467
3468 /* Phase 1: preliminary examination of commands, create work queue */
3469 foreach(lcmd, cmds)
3470 {
3471 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3472
3473 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode);
3474 }
3475
3476 /* Close the relation, but keep lock until commit */
3477 relation_close(rel, NoLock);
3478
3479 /* Phase 2: update system catalogs */
3480 ATRewriteCatalogs(&wqueue, lockmode);
3481
3482 /* Phase 3: scan/rewrite tables as needed */
3483 ATRewriteTables(parsetree, &wqueue, lockmode);
3484 }
3485
3486 /*
3487 * ATPrepCmd
3488 *
3489 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
3490 * recursion and permission checks.
3491 *
3492 * Caller must have acquired appropriate lock type on relation already.
3493 * This lock should be held until commit.
3494 */
3495 static void
ATPrepCmd(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode)3496 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
3497 bool recurse, bool recursing, LOCKMODE lockmode)
3498 {
3499 AlteredTableInfo *tab;
3500 int pass = AT_PASS_UNSET;
3501
3502 /* Find or create work queue entry for this table */
3503 tab = ATGetQueueEntry(wqueue, rel);
3504
3505 /*
3506 * Copy the original subcommand for each table. This avoids conflicts
3507 * when different child tables need to make different parse
3508 * transformations (for example, the same column may have different column
3509 * numbers in different children).
3510 */
3511 cmd = copyObject(cmd);
3512
3513 /*
3514 * Do permissions checking, recursion to child tables if needed, and any
3515 * additional phase-1 processing needed.
3516 */
3517 switch (cmd->subtype)
3518 {
3519 case AT_AddColumn: /* ADD COLUMN */
3520 ATSimplePermissions(rel,
3521 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3522 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
3523 lockmode);
3524 /* Recursion occurs during execution phase */
3525 pass = AT_PASS_ADD_COL;
3526 break;
3527 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
3528 ATSimplePermissions(rel, ATT_VIEW);
3529 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
3530 lockmode);
3531 /* Recursion occurs during execution phase */
3532 pass = AT_PASS_ADD_COL;
3533 break;
3534 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
3535
3536 /*
3537 * We allow defaults on views so that INSERT into a view can have
3538 * default-ish behavior. This works because the rewriter
3539 * substitutes default values into INSERTs before it expands
3540 * rules.
3541 */
3542 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3543 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3544 /* No command-specific prep needed */
3545 pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
3546 break;
3547 case AT_AddIdentity:
3548 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3549 pass = AT_PASS_ADD_CONSTR;
3550 break;
3551 case AT_DropIdentity:
3552 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3553 pass = AT_PASS_DROP;
3554 break;
3555 case AT_SetIdentity:
3556 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3557 pass = AT_PASS_COL_ATTRS;
3558 break;
3559 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
3560 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3561 ATPrepDropNotNull(rel, recurse, recursing);
3562 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3563 /* No command-specific prep needed */
3564 pass = AT_PASS_DROP;
3565 break;
3566 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
3567 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3568 ATPrepSetNotNull(rel, recurse, recursing);
3569 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3570 /* No command-specific prep needed */
3571 pass = AT_PASS_ADD_CONSTR;
3572 break;
3573 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
3574 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3575 /* Performs own permission checks */
3576 ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode);
3577 pass = AT_PASS_MISC;
3578 break;
3579 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
3580 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
3581 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE);
3582 /* This command never recurses */
3583 pass = AT_PASS_MISC;
3584 break;
3585 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
3586 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
3587 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3588 /* No command-specific prep needed */
3589 pass = AT_PASS_MISC;
3590 break;
3591 case AT_DropColumn: /* DROP COLUMN */
3592 ATSimplePermissions(rel,
3593 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3594 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
3595 /* Recursion occurs during execution phase */
3596 pass = AT_PASS_DROP;
3597 break;
3598 case AT_AddIndex: /* ADD INDEX */
3599 ATSimplePermissions(rel, ATT_TABLE);
3600 /* This command never recurses */
3601 /* No command-specific prep needed */
3602 pass = AT_PASS_ADD_INDEX;
3603 break;
3604 case AT_AddConstraint: /* ADD CONSTRAINT */
3605 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3606 /* Recursion occurs during execution phase */
3607 /* No command-specific prep needed except saving recurse flag */
3608 if (recurse)
3609 cmd->subtype = AT_AddConstraintRecurse;
3610 pass = AT_PASS_ADD_CONSTR;
3611 break;
3612 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
3613 ATSimplePermissions(rel, ATT_TABLE);
3614 /* This command never recurses */
3615 /* No command-specific prep needed */
3616 pass = AT_PASS_ADD_CONSTR;
3617 break;
3618 case AT_DropConstraint: /* DROP CONSTRAINT */
3619 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3620 /* Recursion occurs during execution phase */
3621 /* No command-specific prep needed except saving recurse flag */
3622 if (recurse)
3623 cmd->subtype = AT_DropConstraintRecurse;
3624 pass = AT_PASS_DROP;
3625 break;
3626 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
3627 ATSimplePermissions(rel,
3628 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3629 /* Performs own recursion */
3630 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
3631 pass = AT_PASS_ALTER_TYPE;
3632 break;
3633 case AT_AlterColumnGenericOptions:
3634 ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
3635 /* This command never recurses */
3636 /* No command-specific prep needed */
3637 pass = AT_PASS_MISC;
3638 break;
3639 case AT_ChangeOwner: /* ALTER OWNER */
3640 /* This command never recurses */
3641 /* No command-specific prep needed */
3642 pass = AT_PASS_MISC;
3643 break;
3644 case AT_ClusterOn: /* CLUSTER ON */
3645 case AT_DropCluster: /* SET WITHOUT CLUSTER */
3646 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
3647 /* These commands never recurse */
3648 /* No command-specific prep needed */
3649 pass = AT_PASS_MISC;
3650 break;
3651 case AT_SetLogged: /* SET LOGGED */
3652 ATSimplePermissions(rel, ATT_TABLE);
3653 tab->chgPersistence = ATPrepChangePersistence(rel, true);
3654 /* force rewrite if necessary; see comment in ATRewriteTables */
3655 if (tab->chgPersistence)
3656 {
3657 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3658 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
3659 }
3660 pass = AT_PASS_MISC;
3661 break;
3662 case AT_SetUnLogged: /* SET UNLOGGED */
3663 ATSimplePermissions(rel, ATT_TABLE);
3664 tab->chgPersistence = ATPrepChangePersistence(rel, false);
3665 /* force rewrite if necessary; see comment in ATRewriteTables */
3666 if (tab->chgPersistence)
3667 {
3668 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3669 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
3670 }
3671 pass = AT_PASS_MISC;
3672 break;
3673 case AT_AddOids: /* SET WITH OIDS */
3674 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3675 if (!rel->rd_rel->relhasoids || recursing)
3676 ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
3677 /* Recursion occurs during execution phase */
3678 pass = AT_PASS_ADD_COL;
3679 break;
3680 case AT_DropOids: /* SET WITHOUT OIDS */
3681 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3682 /* Performs own recursion */
3683 if (rel->rd_rel->relhasoids)
3684 {
3685 AlterTableCmd *dropCmd = makeNode(AlterTableCmd);
3686
3687 dropCmd->subtype = AT_DropColumn;
3688 dropCmd->name = pstrdup("oid");
3689 dropCmd->behavior = cmd->behavior;
3690 ATPrepCmd(wqueue, rel, dropCmd, recurse, false, lockmode);
3691 }
3692 pass = AT_PASS_DROP;
3693 break;
3694 case AT_SetTableSpace: /* SET TABLESPACE */
3695 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX);
3696 /* This command never recurses */
3697 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
3698 pass = AT_PASS_MISC; /* doesn't actually matter */
3699 break;
3700 case AT_SetRelOptions: /* SET (...) */
3701 case AT_ResetRelOptions: /* RESET (...) */
3702 case AT_ReplaceRelOptions: /* reset them all, then set just these */
3703 ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
3704 /* This command never recurses */
3705 /* No command-specific prep needed */
3706 pass = AT_PASS_MISC;
3707 break;
3708 case AT_AddInherit: /* INHERIT */
3709 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3710 /* This command never recurses */
3711 ATPrepAddInherit(rel);
3712 pass = AT_PASS_MISC;
3713 break;
3714 case AT_DropInherit: /* NO INHERIT */
3715 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3716 /* This command never recurses */
3717 /* No command-specific prep needed */
3718 pass = AT_PASS_MISC;
3719 break;
3720 case AT_AlterConstraint: /* ALTER CONSTRAINT */
3721 ATSimplePermissions(rel, ATT_TABLE);
3722 pass = AT_PASS_MISC;
3723 break;
3724 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
3725 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3726 /* Recursion occurs during execution phase */
3727 /* No command-specific prep needed except saving recurse flag */
3728 if (recurse)
3729 cmd->subtype = AT_ValidateConstraintRecurse;
3730 pass = AT_PASS_MISC;
3731 break;
3732 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
3733 ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
3734 pass = AT_PASS_MISC;
3735 /* This command never recurses */
3736 /* No command-specific prep needed */
3737 break;
3738 case AT_EnableTrig: /* ENABLE TRIGGER variants */
3739 case AT_EnableAlwaysTrig:
3740 case AT_EnableReplicaTrig:
3741 case AT_EnableTrigAll:
3742 case AT_EnableTrigUser:
3743 case AT_DisableTrig: /* DISABLE TRIGGER variants */
3744 case AT_DisableTrigAll:
3745 case AT_DisableTrigUser:
3746 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3747 pass = AT_PASS_MISC;
3748 break;
3749 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
3750 case AT_EnableAlwaysRule:
3751 case AT_EnableReplicaRule:
3752 case AT_DisableRule:
3753 case AT_AddOf: /* OF */
3754 case AT_DropOf: /* NOT OF */
3755 case AT_EnableRowSecurity:
3756 case AT_DisableRowSecurity:
3757 case AT_ForceRowSecurity:
3758 case AT_NoForceRowSecurity:
3759 ATSimplePermissions(rel, ATT_TABLE);
3760 /* These commands never recurse */
3761 /* No command-specific prep needed */
3762 pass = AT_PASS_MISC;
3763 break;
3764 case AT_GenericOptions:
3765 ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
3766 /* No command-specific prep needed */
3767 pass = AT_PASS_MISC;
3768 break;
3769 case AT_AttachPartition:
3770 case AT_DetachPartition:
3771 ATSimplePermissions(rel, ATT_TABLE);
3772 /* No command-specific prep needed */
3773 pass = AT_PASS_MISC;
3774 break;
3775 default: /* oops */
3776 elog(ERROR, "unrecognized alter table type: %d",
3777 (int) cmd->subtype);
3778 pass = AT_PASS_UNSET; /* keep compiler quiet */
3779 break;
3780 }
3781 Assert(pass > AT_PASS_UNSET);
3782
3783 /* Add the subcommand to the appropriate list for phase 2 */
3784 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
3785 }
3786
3787 /*
3788 * ATRewriteCatalogs
3789 *
3790 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
3791 * dispatched in a "safe" execution order (designed to avoid unnecessary
3792 * conflicts).
3793 */
3794 static void
ATRewriteCatalogs(List ** wqueue,LOCKMODE lockmode)3795 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
3796 {
3797 int pass;
3798 ListCell *ltab;
3799
3800 /*
3801 * We process all the tables "in parallel", one pass at a time. This is
3802 * needed because we may have to propagate work from one table to another
3803 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
3804 * re-adding of the foreign key constraint to the other table). Work can
3805 * only be propagated into later passes, however.
3806 */
3807 for (pass = 0; pass < AT_NUM_PASSES; pass++)
3808 {
3809 /* Go through each table that needs to be processed */
3810 foreach(ltab, *wqueue)
3811 {
3812 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
3813 List *subcmds = tab->subcmds[pass];
3814 Relation rel;
3815 ListCell *lcmd;
3816
3817 if (subcmds == NIL)
3818 continue;
3819
3820 /*
3821 * Appropriate lock was obtained by phase 1, needn't get it again
3822 */
3823 rel = relation_open(tab->relid, NoLock);
3824
3825 foreach(lcmd, subcmds)
3826 ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd), lockmode);
3827
3828 /*
3829 * After the ALTER TYPE pass, do cleanup work (this is not done in
3830 * ATExecAlterColumnType since it should be done only once if
3831 * multiple columns of a table are altered).
3832 */
3833 if (pass == AT_PASS_ALTER_TYPE)
3834 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
3835
3836 relation_close(rel, NoLock);
3837 }
3838 }
3839
3840 /* Check to see if a toast table must be added. */
3841 foreach(ltab, *wqueue)
3842 {
3843 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
3844
3845 /*
3846 * If the table is source table of ATTACH PARTITION command, we did
3847 * not modify anything about it that will change its toasting
3848 * requirement, so no need to check.
3849 */
3850 if (((tab->relkind == RELKIND_RELATION ||
3851 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
3852 tab->partition_constraint == NULL) ||
3853 tab->relkind == RELKIND_MATVIEW)
3854 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
3855 }
3856 }
3857
3858 /*
3859 * ATExecCmd: dispatch a subcommand to appropriate execution routine
3860 */
3861 static void
ATExecCmd(List ** wqueue,AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode)3862 ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
3863 AlterTableCmd *cmd, LOCKMODE lockmode)
3864 {
3865 ObjectAddress address = InvalidObjectAddress;
3866
3867 switch (cmd->subtype)
3868 {
3869 case AT_AddColumn: /* ADD COLUMN */
3870 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
3871 address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
3872 false, false, false,
3873 cmd->missing_ok, lockmode);
3874 break;
3875 case AT_AddColumnRecurse:
3876 address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
3877 false, true, false,
3878 cmd->missing_ok, lockmode);
3879 break;
3880 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
3881 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
3882 break;
3883 case AT_AddIdentity:
3884 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
3885 break;
3886 case AT_SetIdentity:
3887 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
3888 break;
3889 case AT_DropIdentity:
3890 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
3891 break;
3892 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
3893 address = ATExecDropNotNull(rel, cmd->name, lockmode);
3894 break;
3895 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
3896 address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
3897 break;
3898 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
3899 address = ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
3900 break;
3901 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
3902 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
3903 break;
3904 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
3905 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
3906 break;
3907 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
3908 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
3909 break;
3910 case AT_DropColumn: /* DROP COLUMN */
3911 address = ATExecDropColumn(wqueue, rel, cmd->name,
3912 cmd->behavior, false, false,
3913 cmd->missing_ok, lockmode);
3914 break;
3915 case AT_DropColumnRecurse: /* DROP COLUMN with recursion */
3916 address = ATExecDropColumn(wqueue, rel, cmd->name,
3917 cmd->behavior, true, false,
3918 cmd->missing_ok, lockmode);
3919 break;
3920 case AT_AddIndex: /* ADD INDEX */
3921 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
3922 lockmode);
3923 break;
3924 case AT_ReAddIndex: /* ADD INDEX */
3925 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
3926 lockmode);
3927 break;
3928 case AT_AddConstraint: /* ADD CONSTRAINT */
3929 address =
3930 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3931 false, false, lockmode);
3932 break;
3933 case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
3934 address =
3935 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3936 true, false, lockmode);
3937 break;
3938 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
3939 address =
3940 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3941 true, true, lockmode);
3942 break;
3943 case AT_ReAddComment: /* Re-add existing comment */
3944 address = CommentObject((CommentStmt *) cmd->def);
3945 break;
3946 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
3947 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
3948 lockmode);
3949 break;
3950 case AT_AlterConstraint: /* ALTER CONSTRAINT */
3951 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
3952 break;
3953 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
3954 address = ATExecValidateConstraint(wqueue, rel, cmd->name, false,
3955 false, lockmode);
3956 break;
3957 case AT_ValidateConstraintRecurse: /* VALIDATE CONSTRAINT with
3958 * recursion */
3959 address = ATExecValidateConstraint(wqueue, rel, cmd->name, true,
3960 false, lockmode);
3961 break;
3962 case AT_DropConstraint: /* DROP CONSTRAINT */
3963 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
3964 false, false,
3965 cmd->missing_ok, lockmode);
3966 break;
3967 case AT_DropConstraintRecurse: /* DROP CONSTRAINT with recursion */
3968 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
3969 true, false,
3970 cmd->missing_ok, lockmode);
3971 break;
3972 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
3973 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
3974 break;
3975 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
3976 address =
3977 ATExecAlterColumnGenericOptions(rel, cmd->name,
3978 (List *) cmd->def, lockmode);
3979 break;
3980 case AT_ChangeOwner: /* ALTER OWNER */
3981 ATExecChangeOwner(RelationGetRelid(rel),
3982 get_rolespec_oid(cmd->newowner, false),
3983 false, lockmode);
3984 break;
3985 case AT_ClusterOn: /* CLUSTER ON */
3986 address = ATExecClusterOn(rel, cmd->name, lockmode);
3987 break;
3988 case AT_DropCluster: /* SET WITHOUT CLUSTER */
3989 ATExecDropCluster(rel, lockmode);
3990 break;
3991 case AT_SetLogged: /* SET LOGGED */
3992 case AT_SetUnLogged: /* SET UNLOGGED */
3993 break;
3994 case AT_AddOids: /* SET WITH OIDS */
3995 /* Use the ADD COLUMN code, unless prep decided to do nothing */
3996 if (cmd->def != NULL)
3997 address =
3998 ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
3999 true, false, false,
4000 cmd->missing_ok, lockmode);
4001 break;
4002 case AT_AddOidsRecurse: /* SET WITH OIDS */
4003 /* Use the ADD COLUMN code, unless prep decided to do nothing */
4004 if (cmd->def != NULL)
4005 address =
4006 ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
4007 true, true, false,
4008 cmd->missing_ok, lockmode);
4009 break;
4010 case AT_DropOids: /* SET WITHOUT OIDS */
4011
4012 /*
4013 * Nothing to do here; we'll have generated a DropColumn
4014 * subcommand to do the real work
4015 */
4016 break;
4017 case AT_SetTableSpace: /* SET TABLESPACE */
4018
4019 /*
4020 * Nothing to do here; Phase 3 does the work
4021 */
4022 break;
4023 case AT_SetRelOptions: /* SET (...) */
4024 case AT_ResetRelOptions: /* RESET (...) */
4025 case AT_ReplaceRelOptions: /* replace entire option list */
4026 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
4027 break;
4028 case AT_EnableTrig: /* ENABLE TRIGGER name */
4029 ATExecEnableDisableTrigger(rel, cmd->name,
4030 TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
4031 break;
4032 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
4033 ATExecEnableDisableTrigger(rel, cmd->name,
4034 TRIGGER_FIRES_ALWAYS, false, lockmode);
4035 break;
4036 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
4037 ATExecEnableDisableTrigger(rel, cmd->name,
4038 TRIGGER_FIRES_ON_REPLICA, false, lockmode);
4039 break;
4040 case AT_DisableTrig: /* DISABLE TRIGGER name */
4041 ATExecEnableDisableTrigger(rel, cmd->name,
4042 TRIGGER_DISABLED, false, lockmode);
4043 break;
4044 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
4045 ATExecEnableDisableTrigger(rel, NULL,
4046 TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
4047 break;
4048 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
4049 ATExecEnableDisableTrigger(rel, NULL,
4050 TRIGGER_DISABLED, false, lockmode);
4051 break;
4052 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
4053 ATExecEnableDisableTrigger(rel, NULL,
4054 TRIGGER_FIRES_ON_ORIGIN, true, lockmode);
4055 break;
4056 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
4057 ATExecEnableDisableTrigger(rel, NULL,
4058 TRIGGER_DISABLED, true, lockmode);
4059 break;
4060
4061 case AT_EnableRule: /* ENABLE RULE name */
4062 ATExecEnableDisableRule(rel, cmd->name,
4063 RULE_FIRES_ON_ORIGIN, lockmode);
4064 break;
4065 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
4066 ATExecEnableDisableRule(rel, cmd->name,
4067 RULE_FIRES_ALWAYS, lockmode);
4068 break;
4069 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
4070 ATExecEnableDisableRule(rel, cmd->name,
4071 RULE_FIRES_ON_REPLICA, lockmode);
4072 break;
4073 case AT_DisableRule: /* DISABLE RULE name */
4074 ATExecEnableDisableRule(rel, cmd->name,
4075 RULE_DISABLED, lockmode);
4076 break;
4077
4078 case AT_AddInherit:
4079 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
4080 break;
4081 case AT_DropInherit:
4082 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
4083 break;
4084 case AT_AddOf:
4085 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
4086 break;
4087 case AT_DropOf:
4088 ATExecDropOf(rel, lockmode);
4089 break;
4090 case AT_ReplicaIdentity:
4091 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
4092 break;
4093 case AT_EnableRowSecurity:
4094 ATExecEnableRowSecurity(rel);
4095 break;
4096 case AT_DisableRowSecurity:
4097 ATExecDisableRowSecurity(rel);
4098 break;
4099 case AT_ForceRowSecurity:
4100 ATExecForceNoForceRowSecurity(rel, true);
4101 break;
4102 case AT_NoForceRowSecurity:
4103 ATExecForceNoForceRowSecurity(rel, false);
4104 break;
4105 case AT_GenericOptions:
4106 ATExecGenericOptions(rel, (List *) cmd->def);
4107 break;
4108 case AT_AttachPartition:
4109 ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
4110 break;
4111 case AT_DetachPartition:
4112 ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
4113 break;
4114 default: /* oops */
4115 elog(ERROR, "unrecognized alter table type: %d",
4116 (int) cmd->subtype);
4117 break;
4118 }
4119
4120 /*
4121 * Report the subcommand to interested event triggers.
4122 */
4123 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
4124
4125 /*
4126 * Bump the command counter to ensure the next subcommand in the sequence
4127 * can see the changes so far
4128 */
4129 CommandCounterIncrement();
4130 }
4131
4132 /*
4133 * ATRewriteTables: ALTER TABLE phase 3
4134 */
4135 static void
ATRewriteTables(AlterTableStmt * parsetree,List ** wqueue,LOCKMODE lockmode)4136 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
4137 {
4138 ListCell *ltab;
4139
4140 /* Go through each table that needs to be checked or rewritten */
4141 foreach(ltab, *wqueue)
4142 {
4143 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4144
4145 /* Foreign tables have no storage, nor do partitioned tables. */
4146 if (tab->relkind == RELKIND_FOREIGN_TABLE ||
4147 tab->relkind == RELKIND_PARTITIONED_TABLE)
4148 continue;
4149
4150 /*
4151 * If we change column data types or add/remove OIDs, the operation
4152 * has to be propagated to tables that use this table's rowtype as a
4153 * column type. tab->newvals will also be non-NULL in the case where
4154 * we're adding a column with a default. We choose to forbid that
4155 * case as well, since composite types might eventually support
4156 * defaults.
4157 *
4158 * (Eventually we'll probably need to check for composite type
4159 * dependencies even when we're just scanning the table without a
4160 * rewrite, but at the moment a composite type does not enforce any
4161 * constraints, so it's not necessary/appropriate to enforce them just
4162 * during ALTER.)
4163 */
4164 if (tab->newvals != NIL || tab->rewrite > 0)
4165 {
4166 Relation rel;
4167
4168 rel = heap_open(tab->relid, NoLock);
4169 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
4170 heap_close(rel, NoLock);
4171 }
4172
4173 /*
4174 * We only need to rewrite the table if at least one column needs to
4175 * be recomputed, we are adding/removing the OID column, or we are
4176 * changing its persistence.
4177 *
4178 * There are two reasons for requiring a rewrite when changing
4179 * persistence: on one hand, we need to ensure that the buffers
4180 * belonging to each of the two relations are marked with or without
4181 * BM_PERMANENT properly. On the other hand, since rewriting creates
4182 * and assigns a new relfilenode, we automatically create or drop an
4183 * init fork for the relation as appropriate.
4184 */
4185 if (tab->rewrite > 0)
4186 {
4187 /* Build a temporary relation and copy data */
4188 Relation OldHeap;
4189 Oid OIDNewHeap;
4190 Oid NewTableSpace;
4191 char persistence;
4192
4193 OldHeap = heap_open(tab->relid, NoLock);
4194
4195 /*
4196 * We don't support rewriting of system catalogs; there are too
4197 * many corner cases and too little benefit. In particular this
4198 * is certainly not going to work for mapped catalogs.
4199 */
4200 if (IsSystemRelation(OldHeap))
4201 ereport(ERROR,
4202 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4203 errmsg("cannot rewrite system relation \"%s\"",
4204 RelationGetRelationName(OldHeap))));
4205
4206 if (RelationIsUsedAsCatalogTable(OldHeap))
4207 ereport(ERROR,
4208 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4209 errmsg("cannot rewrite table \"%s\" used as a catalog table",
4210 RelationGetRelationName(OldHeap))));
4211
4212 /*
4213 * Don't allow rewrite on temp tables of other backends ... their
4214 * local buffer manager is not going to cope.
4215 */
4216 if (RELATION_IS_OTHER_TEMP(OldHeap))
4217 ereport(ERROR,
4218 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4219 errmsg("cannot rewrite temporary tables of other sessions")));
4220
4221 /*
4222 * Select destination tablespace (same as original unless user
4223 * requested a change)
4224 */
4225 if (tab->newTableSpace)
4226 NewTableSpace = tab->newTableSpace;
4227 else
4228 NewTableSpace = OldHeap->rd_rel->reltablespace;
4229
4230 /*
4231 * Select persistence of transient table (same as original unless
4232 * user requested a change)
4233 */
4234 persistence = tab->chgPersistence ?
4235 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
4236
4237 heap_close(OldHeap, NoLock);
4238
4239 /*
4240 * Fire off an Event Trigger now, before actually rewriting the
4241 * table.
4242 *
4243 * We don't support Event Trigger for nested commands anywhere,
4244 * here included, and parsetree is given NULL when coming from
4245 * AlterTableInternal.
4246 *
4247 * And fire it only once.
4248 */
4249 if (parsetree)
4250 EventTriggerTableRewrite((Node *) parsetree,
4251 tab->relid,
4252 tab->rewrite);
4253
4254 /*
4255 * Create transient table that will receive the modified data.
4256 *
4257 * Ensure it is marked correctly as logged or unlogged. We have
4258 * to do this here so that buffers for the new relfilenode will
4259 * have the right persistence set, and at the same time ensure
4260 * that the original filenode's buffers will get read in with the
4261 * correct setting (i.e. the original one). Otherwise a rollback
4262 * after the rewrite would possibly result with buffers for the
4263 * original filenode having the wrong persistence setting.
4264 *
4265 * NB: This relies on swap_relation_files() also swapping the
4266 * persistence. That wouldn't work for pg_class, but that can't be
4267 * unlogged anyway.
4268 */
4269 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
4270 lockmode);
4271
4272 /*
4273 * Copy the heap data into the new table with the desired
4274 * modifications, and test the current data within the table
4275 * against new constraints generated by ALTER TABLE commands.
4276 */
4277 ATRewriteTable(tab, OIDNewHeap, lockmode);
4278
4279 /*
4280 * Swap the physical files of the old and new heaps, then rebuild
4281 * indexes and discard the old heap. We can use RecentXmin for
4282 * the table's new relfrozenxid because we rewrote all the tuples
4283 * in ATRewriteTable, so no older Xid remains in the table. Also,
4284 * we never try to swap toast tables by content, since we have no
4285 * interest in letting this code work on system catalogs.
4286 */
4287 finish_heap_swap(tab->relid, OIDNewHeap,
4288 false, false, true,
4289 !OidIsValid(tab->newTableSpace),
4290 RecentXmin,
4291 ReadNextMultiXactId(),
4292 persistence);
4293 }
4294 else
4295 {
4296 /*
4297 * Test the current data within the table against new constraints
4298 * generated by ALTER TABLE commands, but don't rebuild data.
4299 */
4300 if (tab->constraints != NIL || tab->new_notnull ||
4301 tab->partition_constraint != NULL)
4302 ATRewriteTable(tab, InvalidOid, lockmode);
4303
4304 /*
4305 * If we had SET TABLESPACE but no reason to reconstruct tuples,
4306 * just do a block-by-block copy.
4307 */
4308 if (tab->newTableSpace)
4309 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
4310 }
4311 }
4312
4313 /*
4314 * Foreign key constraints are checked in a final pass, since (a) it's
4315 * generally best to examine each one separately, and (b) it's at least
4316 * theoretically possible that we have changed both relations of the
4317 * foreign key, and we'd better have finished both rewrites before we try
4318 * to read the tables.
4319 */
4320 foreach(ltab, *wqueue)
4321 {
4322 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4323 Relation rel = NULL;
4324 ListCell *lcon;
4325
4326 foreach(lcon, tab->constraints)
4327 {
4328 NewConstraint *con = lfirst(lcon);
4329
4330 if (con->contype == CONSTR_FOREIGN)
4331 {
4332 Constraint *fkconstraint = (Constraint *) con->qual;
4333 Relation refrel;
4334
4335 if (rel == NULL)
4336 {
4337 /* Long since locked, no need for another */
4338 rel = heap_open(tab->relid, NoLock);
4339 }
4340
4341 refrel = heap_open(con->refrelid, RowShareLock);
4342
4343 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
4344 con->refindid,
4345 con->conid);
4346
4347 /*
4348 * No need to mark the constraint row as validated, we did
4349 * that when we inserted the row earlier.
4350 */
4351
4352 heap_close(refrel, NoLock);
4353 }
4354 }
4355
4356 if (rel)
4357 heap_close(rel, NoLock);
4358 }
4359 }
4360
4361 /*
4362 * ATRewriteTable: scan or rewrite one table
4363 *
4364 * OIDNewHeap is InvalidOid if we don't need to rewrite
4365 */
4366 static void
ATRewriteTable(AlteredTableInfo * tab,Oid OIDNewHeap,LOCKMODE lockmode)4367 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
4368 {
4369 Relation oldrel;
4370 Relation newrel;
4371 TupleDesc oldTupDesc;
4372 TupleDesc newTupDesc;
4373 bool needscan = false;
4374 List *notnull_attrs;
4375 int i;
4376 ListCell *l;
4377 EState *estate;
4378 CommandId mycid;
4379 BulkInsertState bistate;
4380 int hi_options;
4381 ExprState *partqualstate = NULL;
4382
4383 /*
4384 * Open the relation(s). We have surely already locked the existing
4385 * table.
4386 */
4387 oldrel = heap_open(tab->relid, NoLock);
4388 oldTupDesc = tab->oldDesc;
4389 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
4390
4391 if (OidIsValid(OIDNewHeap))
4392 newrel = heap_open(OIDNewHeap, lockmode);
4393 else
4394 newrel = NULL;
4395
4396 /*
4397 * Prepare a BulkInsertState and options for heap_insert. Because we're
4398 * building a new heap, we can skip WAL-logging and fsync it to disk at
4399 * the end instead (unless WAL-logging is required for archiving or
4400 * streaming replication). The FSM is empty too, so don't bother using it.
4401 */
4402 if (newrel)
4403 {
4404 mycid = GetCurrentCommandId(true);
4405 bistate = GetBulkInsertState();
4406
4407 hi_options = HEAP_INSERT_SKIP_FSM;
4408 if (!XLogIsNeeded())
4409 hi_options |= HEAP_INSERT_SKIP_WAL;
4410 }
4411 else
4412 {
4413 /* keep compiler quiet about using these uninitialized */
4414 mycid = 0;
4415 bistate = NULL;
4416 hi_options = 0;
4417 }
4418
4419 /*
4420 * Generate the constraint and default execution states
4421 */
4422
4423 estate = CreateExecutorState();
4424
4425 /* Build the needed expression execution states */
4426 foreach(l, tab->constraints)
4427 {
4428 NewConstraint *con = lfirst(l);
4429
4430 switch (con->contype)
4431 {
4432 case CONSTR_CHECK:
4433 needscan = true;
4434 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
4435 break;
4436 case CONSTR_FOREIGN:
4437 /* Nothing to do here */
4438 break;
4439 default:
4440 elog(ERROR, "unrecognized constraint type: %d",
4441 (int) con->contype);
4442 }
4443 }
4444
4445 /* Build expression execution states for partition check quals */
4446 if (tab->partition_constraint)
4447 {
4448 needscan = true;
4449 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
4450 }
4451
4452 foreach(l, tab->newvals)
4453 {
4454 NewColumnValue *ex = lfirst(l);
4455
4456 /* expr already planned */
4457 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
4458 }
4459
4460 notnull_attrs = NIL;
4461 if (newrel || tab->new_notnull)
4462 {
4463 /*
4464 * If we are rebuilding the tuples OR if we added any new NOT NULL
4465 * constraints, check all not-null constraints. This is a bit of
4466 * overkill but it minimizes risk of bugs, and heap_attisnull is a
4467 * pretty cheap test anyway.
4468 */
4469 for (i = 0; i < newTupDesc->natts; i++)
4470 {
4471 if (newTupDesc->attrs[i]->attnotnull &&
4472 !newTupDesc->attrs[i]->attisdropped)
4473 notnull_attrs = lappend_int(notnull_attrs, i);
4474 }
4475 if (notnull_attrs)
4476 needscan = true;
4477 }
4478
4479 if (newrel || needscan)
4480 {
4481 ExprContext *econtext;
4482 Datum *values;
4483 bool *isnull;
4484 TupleTableSlot *oldslot;
4485 TupleTableSlot *newslot;
4486 HeapScanDesc scan;
4487 HeapTuple tuple;
4488 MemoryContext oldCxt;
4489 List *dropped_attrs = NIL;
4490 ListCell *lc;
4491 Snapshot snapshot;
4492
4493 if (newrel)
4494 ereport(DEBUG1,
4495 (errmsg("rewriting table \"%s\"",
4496 RelationGetRelationName(oldrel))));
4497 else
4498 ereport(DEBUG1,
4499 (errmsg("verifying table \"%s\"",
4500 RelationGetRelationName(oldrel))));
4501
4502 if (newrel)
4503 {
4504 /*
4505 * All predicate locks on the tuples or pages are about to be made
4506 * invalid, because we move tuples around. Promote them to
4507 * relation locks.
4508 */
4509 TransferPredicateLocksToHeapRelation(oldrel);
4510 }
4511
4512 econtext = GetPerTupleExprContext(estate);
4513
4514 /*
4515 * Make tuple slots for old and new tuples. Note that even when the
4516 * tuples are the same, the tupDescs might not be (consider ADD COLUMN
4517 * without a default).
4518 */
4519 oldslot = MakeSingleTupleTableSlot(oldTupDesc);
4520 newslot = MakeSingleTupleTableSlot(newTupDesc);
4521
4522 /* Preallocate values/isnull arrays */
4523 i = Max(newTupDesc->natts, oldTupDesc->natts);
4524 values = (Datum *) palloc(i * sizeof(Datum));
4525 isnull = (bool *) palloc(i * sizeof(bool));
4526 memset(values, 0, i * sizeof(Datum));
4527 memset(isnull, true, i * sizeof(bool));
4528
4529 /*
4530 * Any attributes that are dropped according to the new tuple
4531 * descriptor can be set to NULL. We precompute the list of dropped
4532 * attributes to avoid needing to do so in the per-tuple loop.
4533 */
4534 for (i = 0; i < newTupDesc->natts; i++)
4535 {
4536 if (newTupDesc->attrs[i]->attisdropped)
4537 dropped_attrs = lappend_int(dropped_attrs, i);
4538 }
4539
4540 /*
4541 * Scan through the rows, generating a new row if needed and then
4542 * checking all the constraints.
4543 */
4544 snapshot = RegisterSnapshot(GetLatestSnapshot());
4545 scan = heap_beginscan(oldrel, snapshot, 0, NULL);
4546
4547 /*
4548 * Switch to per-tuple memory context and reset it for each tuple
4549 * produced, so we don't leak memory.
4550 */
4551 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
4552
4553 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
4554 {
4555 if (tab->rewrite > 0)
4556 {
4557 Oid tupOid = InvalidOid;
4558
4559 /* Extract data from old tuple */
4560 heap_deform_tuple(tuple, oldTupDesc, values, isnull);
4561 if (oldTupDesc->tdhasoid)
4562 tupOid = HeapTupleGetOid(tuple);
4563
4564 /* Set dropped attributes to null in new tuple */
4565 foreach(lc, dropped_attrs)
4566 isnull[lfirst_int(lc)] = true;
4567
4568 /*
4569 * Process supplied expressions to replace selected columns.
4570 * Expression inputs come from the old tuple.
4571 */
4572 ExecStoreTuple(tuple, oldslot, InvalidBuffer, false);
4573 econtext->ecxt_scantuple = oldslot;
4574
4575 foreach(l, tab->newvals)
4576 {
4577 NewColumnValue *ex = lfirst(l);
4578
4579 values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
4580 econtext,
4581 &isnull[ex->attnum - 1]);
4582 }
4583
4584 /*
4585 * Form the new tuple. Note that we don't explicitly pfree it,
4586 * since the per-tuple memory context will be reset shortly.
4587 */
4588 tuple = heap_form_tuple(newTupDesc, values, isnull);
4589
4590 /* Preserve OID, if any */
4591 if (newTupDesc->tdhasoid)
4592 HeapTupleSetOid(tuple, tupOid);
4593
4594 /*
4595 * Constraints might reference the tableoid column, so
4596 * initialize t_tableOid before evaluating them.
4597 */
4598 tuple->t_tableOid = RelationGetRelid(oldrel);
4599 }
4600
4601 /* Now check any constraints on the possibly-changed tuple */
4602 ExecStoreTuple(tuple, newslot, InvalidBuffer, false);
4603 econtext->ecxt_scantuple = newslot;
4604
4605 foreach(l, notnull_attrs)
4606 {
4607 int attn = lfirst_int(l);
4608
4609 if (heap_attisnull(tuple, attn + 1))
4610 ereport(ERROR,
4611 (errcode(ERRCODE_NOT_NULL_VIOLATION),
4612 errmsg("column \"%s\" contains null values",
4613 NameStr(newTupDesc->attrs[attn]->attname)),
4614 errtablecol(oldrel, attn + 1)));
4615 }
4616
4617 foreach(l, tab->constraints)
4618 {
4619 NewConstraint *con = lfirst(l);
4620
4621 switch (con->contype)
4622 {
4623 case CONSTR_CHECK:
4624 if (!ExecCheck(con->qualstate, econtext))
4625 ereport(ERROR,
4626 (errcode(ERRCODE_CHECK_VIOLATION),
4627 errmsg("check constraint \"%s\" is violated by some row",
4628 con->name),
4629 errtableconstraint(oldrel, con->name)));
4630 break;
4631 case CONSTR_FOREIGN:
4632 /* Nothing to do here */
4633 break;
4634 default:
4635 elog(ERROR, "unrecognized constraint type: %d",
4636 (int) con->contype);
4637 }
4638 }
4639
4640 if (partqualstate && !ExecCheck(partqualstate, econtext))
4641 ereport(ERROR,
4642 (errcode(ERRCODE_CHECK_VIOLATION),
4643 errmsg("partition constraint is violated by some row")));
4644
4645 /* Write the tuple out to the new relation */
4646 if (newrel)
4647 heap_insert(newrel, tuple, mycid, hi_options, bistate);
4648
4649 ResetExprContext(econtext);
4650
4651 CHECK_FOR_INTERRUPTS();
4652 }
4653
4654 MemoryContextSwitchTo(oldCxt);
4655 heap_endscan(scan);
4656 UnregisterSnapshot(snapshot);
4657
4658 ExecDropSingleTupleTableSlot(oldslot);
4659 ExecDropSingleTupleTableSlot(newslot);
4660 }
4661
4662 FreeExecutorState(estate);
4663
4664 heap_close(oldrel, NoLock);
4665 if (newrel)
4666 {
4667 FreeBulkInsertState(bistate);
4668
4669 /* If we skipped writing WAL, then we need to sync the heap. */
4670 if (hi_options & HEAP_INSERT_SKIP_WAL)
4671 heap_sync(newrel);
4672
4673 heap_close(newrel, NoLock);
4674 }
4675 }
4676
4677 /*
4678 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
4679 */
4680 static AlteredTableInfo *
ATGetQueueEntry(List ** wqueue,Relation rel)4681 ATGetQueueEntry(List **wqueue, Relation rel)
4682 {
4683 Oid relid = RelationGetRelid(rel);
4684 AlteredTableInfo *tab;
4685 ListCell *ltab;
4686
4687 foreach(ltab, *wqueue)
4688 {
4689 tab = (AlteredTableInfo *) lfirst(ltab);
4690 if (tab->relid == relid)
4691 return tab;
4692 }
4693
4694 /*
4695 * Not there, so add it. Note that we make a copy of the relation's
4696 * existing descriptor before anything interesting can happen to it.
4697 */
4698 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
4699 tab->relid = relid;
4700 tab->relkind = rel->rd_rel->relkind;
4701 tab->oldDesc = CreateTupleDescCopy(RelationGetDescr(rel));
4702 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4703 tab->chgPersistence = false;
4704
4705 *wqueue = lappend(*wqueue, tab);
4706
4707 return tab;
4708 }
4709
4710 /*
4711 * ATSimplePermissions
4712 *
4713 * - Ensure that it is a relation (or possibly a view)
4714 * - Ensure this user is the owner
4715 * - Ensure that it is not a system table
4716 */
4717 static void
ATSimplePermissions(Relation rel,int allowed_targets)4718 ATSimplePermissions(Relation rel, int allowed_targets)
4719 {
4720 int actual_target;
4721
4722 switch (rel->rd_rel->relkind)
4723 {
4724 case RELKIND_RELATION:
4725 case RELKIND_PARTITIONED_TABLE:
4726 actual_target = ATT_TABLE;
4727 break;
4728 case RELKIND_VIEW:
4729 actual_target = ATT_VIEW;
4730 break;
4731 case RELKIND_MATVIEW:
4732 actual_target = ATT_MATVIEW;
4733 break;
4734 case RELKIND_INDEX:
4735 actual_target = ATT_INDEX;
4736 break;
4737 case RELKIND_COMPOSITE_TYPE:
4738 actual_target = ATT_COMPOSITE_TYPE;
4739 break;
4740 case RELKIND_FOREIGN_TABLE:
4741 actual_target = ATT_FOREIGN_TABLE;
4742 break;
4743 default:
4744 actual_target = 0;
4745 break;
4746 }
4747
4748 /* Wrong target type? */
4749 if ((actual_target & allowed_targets) == 0)
4750 ATWrongRelkindError(rel, allowed_targets);
4751
4752 /* Permissions checks */
4753 if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
4754 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
4755 RelationGetRelationName(rel));
4756
4757 if (!allowSystemTableMods && IsSystemRelation(rel))
4758 ereport(ERROR,
4759 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4760 errmsg("permission denied: \"%s\" is a system catalog",
4761 RelationGetRelationName(rel))));
4762 }
4763
4764 /*
4765 * ATWrongRelkindError
4766 *
4767 * Throw an error when a relation has been determined to be of the wrong
4768 * type.
4769 */
4770 static void
ATWrongRelkindError(Relation rel,int allowed_targets)4771 ATWrongRelkindError(Relation rel, int allowed_targets)
4772 {
4773 char *msg;
4774
4775 switch (allowed_targets)
4776 {
4777 case ATT_TABLE:
4778 msg = _("\"%s\" is not a table");
4779 break;
4780 case ATT_TABLE | ATT_VIEW:
4781 msg = _("\"%s\" is not a table or view");
4782 break;
4783 case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
4784 msg = _("\"%s\" is not a table, view, or foreign table");
4785 break;
4786 case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
4787 msg = _("\"%s\" is not a table, view, materialized view, or index");
4788 break;
4789 case ATT_TABLE | ATT_MATVIEW:
4790 msg = _("\"%s\" is not a table or materialized view");
4791 break;
4792 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX:
4793 msg = _("\"%s\" is not a table, materialized view, or index");
4794 break;
4795 case ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE:
4796 msg = _("\"%s\" is not a table, materialized view, or foreign table");
4797 break;
4798 case ATT_TABLE | ATT_FOREIGN_TABLE:
4799 msg = _("\"%s\" is not a table or foreign table");
4800 break;
4801 case ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE:
4802 msg = _("\"%s\" is not a table, composite type, or foreign table");
4803 break;
4804 case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE:
4805 msg = _("\"%s\" is not a table, materialized view, index, or foreign table");
4806 break;
4807 case ATT_VIEW:
4808 msg = _("\"%s\" is not a view");
4809 break;
4810 case ATT_FOREIGN_TABLE:
4811 msg = _("\"%s\" is not a foreign table");
4812 break;
4813 default:
4814 /* shouldn't get here, add all necessary cases above */
4815 msg = _("\"%s\" is of the wrong type");
4816 break;
4817 }
4818
4819 ereport(ERROR,
4820 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
4821 errmsg(msg, RelationGetRelationName(rel))));
4822 }
4823
4824 /*
4825 * ATSimpleRecursion
4826 *
4827 * Simple table recursion sufficient for most ALTER TABLE operations.
4828 * All direct and indirect children are processed in an unspecified order.
4829 * Note that if a child inherits from the original table via multiple
4830 * inheritance paths, it will be visited just once.
4831 */
4832 static void
ATSimpleRecursion(List ** wqueue,Relation rel,AlterTableCmd * cmd,bool recurse,LOCKMODE lockmode)4833 ATSimpleRecursion(List **wqueue, Relation rel,
4834 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
4835 {
4836 /*
4837 * Propagate to children if desired. Only plain tables and foreign tables
4838 * have children, so no need to search for other relkinds.
4839 */
4840 if (recurse &&
4841 (rel->rd_rel->relkind == RELKIND_RELATION ||
4842 rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
4843 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
4844 {
4845 Oid relid = RelationGetRelid(rel);
4846 ListCell *child;
4847 List *children;
4848
4849 children = find_all_inheritors(relid, lockmode, NULL);
4850
4851 /*
4852 * find_all_inheritors does the recursive search of the inheritance
4853 * hierarchy, so all we have to do is process all of the relids in the
4854 * list that it returns.
4855 */
4856 foreach(child, children)
4857 {
4858 Oid childrelid = lfirst_oid(child);
4859 Relation childrel;
4860
4861 if (childrelid == relid)
4862 continue;
4863 /* find_all_inheritors already got lock */
4864 childrel = relation_open(childrelid, NoLock);
4865 CheckTableNotInUse(childrel, "ALTER TABLE");
4866 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
4867 relation_close(childrel, NoLock);
4868 }
4869 }
4870 }
4871
4872 /*
4873 * ATTypedTableRecursion
4874 *
4875 * Propagate ALTER TYPE operations to the typed tables of that type.
4876 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
4877 * recursion to inheritance children of the typed tables.
4878 */
4879 static void
ATTypedTableRecursion(List ** wqueue,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode)4880 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
4881 LOCKMODE lockmode)
4882 {
4883 ListCell *child;
4884 List *children;
4885
4886 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
4887
4888 children = find_typed_table_dependencies(rel->rd_rel->reltype,
4889 RelationGetRelationName(rel),
4890 cmd->behavior);
4891
4892 foreach(child, children)
4893 {
4894 Oid childrelid = lfirst_oid(child);
4895 Relation childrel;
4896
4897 childrel = relation_open(childrelid, lockmode);
4898 CheckTableNotInUse(childrel, "ALTER TABLE");
4899 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode);
4900 relation_close(childrel, NoLock);
4901 }
4902 }
4903
4904
4905 /*
4906 * find_composite_type_dependencies
4907 *
4908 * Check to see if the type "typeOid" is being used as a column in some table
4909 * (possibly nested several levels deep in composite types, arrays, etc!).
4910 * Eventually, we'd like to propagate the check or rewrite operation
4911 * into such tables, but for now, just error out if we find any.
4912 *
4913 * Caller should provide either the associated relation of a rowtype,
4914 * or a type name (not both) for use in the error message, if any.
4915 *
4916 * Note that "typeOid" is not necessarily a composite type; it could also be
4917 * another container type such as an array or range, or a domain over one of
4918 * these things. The name of this function is therefore somewhat historical,
4919 * but it's not worth changing.
4920 *
4921 * We assume that functions and views depending on the type are not reasons
4922 * to reject the ALTER. (How safe is this really?)
4923 */
4924 void
find_composite_type_dependencies(Oid typeOid,Relation origRelation,const char * origTypeName)4925 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
4926 const char *origTypeName)
4927 {
4928 Relation depRel;
4929 ScanKeyData key[2];
4930 SysScanDesc depScan;
4931 HeapTuple depTup;
4932
4933 /* since this function recurses, it could be driven to stack overflow */
4934 check_stack_depth();
4935
4936 /*
4937 * We scan pg_depend to find those things that depend on the given type.
4938 * (We assume we can ignore refobjsubid for a type.)
4939 */
4940 depRel = heap_open(DependRelationId, AccessShareLock);
4941
4942 ScanKeyInit(&key[0],
4943 Anum_pg_depend_refclassid,
4944 BTEqualStrategyNumber, F_OIDEQ,
4945 ObjectIdGetDatum(TypeRelationId));
4946 ScanKeyInit(&key[1],
4947 Anum_pg_depend_refobjid,
4948 BTEqualStrategyNumber, F_OIDEQ,
4949 ObjectIdGetDatum(typeOid));
4950
4951 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
4952 NULL, 2, key);
4953
4954 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
4955 {
4956 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
4957 Relation rel;
4958 Form_pg_attribute att;
4959
4960 /* Check for directly dependent types */
4961 if (pg_depend->classid == TypeRelationId)
4962 {
4963 /*
4964 * This must be an array, domain, or range containing the given
4965 * type, so recursively check for uses of this type. Note that
4966 * any error message will mention the original type not the
4967 * container; this is intentional.
4968 */
4969 find_composite_type_dependencies(pg_depend->objid,
4970 origRelation, origTypeName);
4971 continue;
4972 }
4973
4974 /* Else, ignore dependees that aren't user columns of relations */
4975 /* (we assume system columns are never of interesting types) */
4976 if (pg_depend->classid != RelationRelationId ||
4977 pg_depend->objsubid <= 0)
4978 continue;
4979
4980 rel = relation_open(pg_depend->objid, AccessShareLock);
4981 att = rel->rd_att->attrs[pg_depend->objsubid - 1];
4982
4983 if (rel->rd_rel->relkind == RELKIND_RELATION ||
4984 rel->rd_rel->relkind == RELKIND_MATVIEW ||
4985 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4986 {
4987 if (origTypeName)
4988 ereport(ERROR,
4989 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4990 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
4991 origTypeName,
4992 RelationGetRelationName(rel),
4993 NameStr(att->attname))));
4994 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
4995 ereport(ERROR,
4996 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4997 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
4998 RelationGetRelationName(origRelation),
4999 RelationGetRelationName(rel),
5000 NameStr(att->attname))));
5001 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
5002 ereport(ERROR,
5003 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5004 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
5005 RelationGetRelationName(origRelation),
5006 RelationGetRelationName(rel),
5007 NameStr(att->attname))));
5008 else
5009 ereport(ERROR,
5010 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5011 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
5012 RelationGetRelationName(origRelation),
5013 RelationGetRelationName(rel),
5014 NameStr(att->attname))));
5015 }
5016 else if (OidIsValid(rel->rd_rel->reltype))
5017 {
5018 /*
5019 * A view or composite type itself isn't a problem, but we must
5020 * recursively check for indirect dependencies via its rowtype.
5021 */
5022 find_composite_type_dependencies(rel->rd_rel->reltype,
5023 origRelation, origTypeName);
5024 }
5025
5026 relation_close(rel, AccessShareLock);
5027 }
5028
5029 systable_endscan(depScan);
5030
5031 relation_close(depRel, AccessShareLock);
5032 }
5033
5034
5035 /*
5036 * find_typed_table_dependencies
5037 *
5038 * Check to see if a composite type is being used as the type of a
5039 * typed table. Abort if any are found and behavior is RESTRICT.
5040 * Else return the list of tables.
5041 */
5042 static List *
find_typed_table_dependencies(Oid typeOid,const char * typeName,DropBehavior behavior)5043 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
5044 {
5045 Relation classRel;
5046 ScanKeyData key[1];
5047 HeapScanDesc scan;
5048 HeapTuple tuple;
5049 List *result = NIL;
5050
5051 classRel = heap_open(RelationRelationId, AccessShareLock);
5052
5053 ScanKeyInit(&key[0],
5054 Anum_pg_class_reloftype,
5055 BTEqualStrategyNumber, F_OIDEQ,
5056 ObjectIdGetDatum(typeOid));
5057
5058 scan = heap_beginscan_catalog(classRel, 1, key);
5059
5060 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
5061 {
5062 if (behavior == DROP_RESTRICT)
5063 ereport(ERROR,
5064 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
5065 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
5066 typeName),
5067 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
5068 else
5069 result = lappend_oid(result, HeapTupleGetOid(tuple));
5070 }
5071
5072 heap_endscan(scan);
5073 heap_close(classRel, AccessShareLock);
5074
5075 return result;
5076 }
5077
5078
5079 /*
5080 * check_of_type
5081 *
5082 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
5083 * isn't suitable, throw an error. Currently, we require that the type
5084 * originated with CREATE TYPE AS. We could support any row type, but doing so
5085 * would require handling a number of extra corner cases in the DDL commands.
5086 */
5087 void
check_of_type(HeapTuple typetuple)5088 check_of_type(HeapTuple typetuple)
5089 {
5090 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
5091 bool typeOk = false;
5092
5093 if (typ->typtype == TYPTYPE_COMPOSITE)
5094 {
5095 Relation typeRelation;
5096
5097 Assert(OidIsValid(typ->typrelid));
5098 typeRelation = relation_open(typ->typrelid, AccessShareLock);
5099 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
5100
5101 /*
5102 * Close the parent rel, but keep our AccessShareLock on it until xact
5103 * commit. That will prevent someone else from deleting or ALTERing
5104 * the type before the typed table creation/conversion commits.
5105 */
5106 relation_close(typeRelation, NoLock);
5107 }
5108 if (!typeOk)
5109 ereport(ERROR,
5110 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5111 errmsg("type %s is not a composite type",
5112 format_type_be(HeapTupleGetOid(typetuple)))));
5113 }
5114
5115
5116 /*
5117 * ALTER TABLE ADD COLUMN
5118 *
5119 * Adds an additional attribute to a relation making the assumption that
5120 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
5121 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
5122 * AlterTableCmd's.
5123 *
5124 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
5125 * have to decide at runtime whether to recurse or not depending on whether we
5126 * actually add a column or merely merge with an existing column. (We can't
5127 * check this in a static pre-pass because it won't handle multiple inheritance
5128 * situations correctly.)
5129 */
5130 static void
ATPrepAddColumn(List ** wqueue,Relation rel,bool recurse,bool recursing,bool is_view,AlterTableCmd * cmd,LOCKMODE lockmode)5131 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
5132 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode)
5133 {
5134 if (rel->rd_rel->reloftype && !recursing)
5135 ereport(ERROR,
5136 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5137 errmsg("cannot add column to typed table")));
5138
5139 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
5140 ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
5141
5142 if (recurse && !is_view)
5143 cmd->subtype = AT_AddColumnRecurse;
5144 }
5145
5146 /*
5147 * Add a column to a table; this handles the AT_AddOids cases as well. The
5148 * return value is the address of the new column in the parent relation.
5149 */
5150 static ObjectAddress
ATExecAddColumn(List ** wqueue,AlteredTableInfo * tab,Relation rel,ColumnDef * colDef,bool isOid,bool recurse,bool recursing,bool if_not_exists,LOCKMODE lockmode)5151 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
5152 ColumnDef *colDef, bool isOid,
5153 bool recurse, bool recursing,
5154 bool if_not_exists, LOCKMODE lockmode)
5155 {
5156 Oid myrelid = RelationGetRelid(rel);
5157 Relation pgclass,
5158 attrdesc;
5159 HeapTuple reltup;
5160 FormData_pg_attribute attribute;
5161 int newattnum;
5162 char relkind;
5163 HeapTuple typeTuple;
5164 Oid typeOid;
5165 int32 typmod;
5166 Oid collOid;
5167 Form_pg_type tform;
5168 Expr *defval;
5169 List *children;
5170 ListCell *child;
5171 AclResult aclresult;
5172 ObjectAddress address;
5173
5174 /* At top level, permission check was done in ATPrepCmd, else do it */
5175 if (recursing)
5176 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5177
5178 if (rel->rd_rel->relispartition && !recursing)
5179 ereport(ERROR,
5180 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5181 errmsg("cannot add column to a partition")));
5182
5183 attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
5184
5185 /*
5186 * Are we adding the column to a recursion child? If so, check whether to
5187 * merge with an existing definition for the column. If we do merge, we
5188 * must not recurse. Children will already have the column, and recursing
5189 * into them would mess up attinhcount.
5190 */
5191 if (colDef->inhcount > 0)
5192 {
5193 HeapTuple tuple;
5194
5195 /* Does child already have a column by this name? */
5196 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
5197 if (HeapTupleIsValid(tuple))
5198 {
5199 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
5200 Oid ctypeId;
5201 int32 ctypmod;
5202 Oid ccollid;
5203
5204 /* Child column must match on type, typmod, and collation */
5205 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
5206 if (ctypeId != childatt->atttypid ||
5207 ctypmod != childatt->atttypmod)
5208 ereport(ERROR,
5209 (errcode(ERRCODE_DATATYPE_MISMATCH),
5210 errmsg("child table \"%s\" has different type for column \"%s\"",
5211 RelationGetRelationName(rel), colDef->colname)));
5212 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
5213 if (ccollid != childatt->attcollation)
5214 ereport(ERROR,
5215 (errcode(ERRCODE_COLLATION_MISMATCH),
5216 errmsg("child table \"%s\" has different collation for column \"%s\"",
5217 RelationGetRelationName(rel), colDef->colname),
5218 errdetail("\"%s\" versus \"%s\"",
5219 get_collation_name(ccollid),
5220 get_collation_name(childatt->attcollation))));
5221
5222 /* If it's OID, child column must actually be OID */
5223 if (isOid && childatt->attnum != ObjectIdAttributeNumber)
5224 ereport(ERROR,
5225 (errcode(ERRCODE_DATATYPE_MISMATCH),
5226 errmsg("child table \"%s\" has a conflicting \"%s\" column",
5227 RelationGetRelationName(rel), colDef->colname)));
5228
5229 /* Bump the existing child att's inhcount */
5230 childatt->attinhcount++;
5231 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
5232
5233 heap_freetuple(tuple);
5234
5235 /* Inform the user about the merge */
5236 ereport(NOTICE,
5237 (errmsg("merging definition of column \"%s\" for child \"%s\"",
5238 colDef->colname, RelationGetRelationName(rel))));
5239
5240 heap_close(attrdesc, RowExclusiveLock);
5241 return InvalidObjectAddress;
5242 }
5243 }
5244
5245 pgclass = heap_open(RelationRelationId, RowExclusiveLock);
5246
5247 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
5248 if (!HeapTupleIsValid(reltup))
5249 elog(ERROR, "cache lookup failed for relation %u", myrelid);
5250 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
5251
5252 /*
5253 * Cannot add identity column if table has children, because identity does
5254 * not inherit. (Adding column and identity separately will work.)
5255 */
5256 if (colDef->identity &&
5257 recurse &&
5258 find_inheritance_children(myrelid, NoLock) != NIL)
5259 ereport(ERROR,
5260 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5261 errmsg("cannot recursively add identity column to table that has child tables")));
5262
5263 /* skip if the name already exists and if_not_exists is true */
5264 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
5265 {
5266 heap_close(attrdesc, RowExclusiveLock);
5267 heap_freetuple(reltup);
5268 heap_close(pgclass, RowExclusiveLock);
5269 return InvalidObjectAddress;
5270 }
5271
5272 /* Determine the new attribute's number */
5273 if (isOid)
5274 newattnum = ObjectIdAttributeNumber;
5275 else
5276 {
5277 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
5278 if (newattnum > MaxHeapAttributeNumber)
5279 ereport(ERROR,
5280 (errcode(ERRCODE_TOO_MANY_COLUMNS),
5281 errmsg("tables can have at most %d columns",
5282 MaxHeapAttributeNumber)));
5283 }
5284
5285 typeTuple = typenameType(NULL, colDef->typeName, &typmod);
5286 tform = (Form_pg_type) GETSTRUCT(typeTuple);
5287 typeOid = HeapTupleGetOid(typeTuple);
5288
5289 aclresult = pg_type_aclcheck(typeOid, GetUserId(), ACL_USAGE);
5290 if (aclresult != ACLCHECK_OK)
5291 aclcheck_error_type(aclresult, typeOid);
5292
5293 collOid = GetColumnDefCollation(NULL, colDef, typeOid);
5294
5295 /* make sure datatype is legal for a column */
5296 CheckAttributeType(colDef->colname, typeOid, collOid,
5297 list_make1_oid(rel->rd_rel->reltype),
5298 false);
5299
5300 /* construct new attribute's pg_attribute entry */
5301 attribute.attrelid = myrelid;
5302 namestrcpy(&(attribute.attname), colDef->colname);
5303 attribute.atttypid = typeOid;
5304 attribute.attstattarget = (newattnum > 0) ? -1 : 0;
5305 attribute.attlen = tform->typlen;
5306 attribute.attcacheoff = -1;
5307 attribute.atttypmod = typmod;
5308 attribute.attnum = newattnum;
5309 attribute.attbyval = tform->typbyval;
5310 attribute.attndims = list_length(colDef->typeName->arrayBounds);
5311 attribute.attstorage = tform->typstorage;
5312 attribute.attalign = tform->typalign;
5313 attribute.attnotnull = colDef->is_not_null;
5314 attribute.atthasdef = false;
5315 attribute.attidentity = colDef->identity;
5316 attribute.attisdropped = false;
5317 attribute.attislocal = colDef->is_local;
5318 attribute.attinhcount = colDef->inhcount;
5319 attribute.attcollation = collOid;
5320 /* attribute.attacl is handled by InsertPgAttributeTuple */
5321
5322 ReleaseSysCache(typeTuple);
5323
5324 InsertPgAttributeTuple(attrdesc, &attribute, NULL);
5325
5326 heap_close(attrdesc, RowExclusiveLock);
5327
5328 /*
5329 * Update pg_class tuple as appropriate
5330 */
5331 if (isOid)
5332 ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true;
5333 else
5334 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
5335
5336 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
5337
5338 heap_freetuple(reltup);
5339
5340 /* Post creation hook for new attribute */
5341 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
5342
5343 heap_close(pgclass, RowExclusiveLock);
5344
5345 /* Make the attribute's catalog entry visible */
5346 CommandCounterIncrement();
5347
5348 /*
5349 * Store the DEFAULT, if any, in the catalogs
5350 */
5351 if (colDef->raw_default)
5352 {
5353 RawColumnDefault *rawEnt;
5354
5355 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
5356 rawEnt->attnum = attribute.attnum;
5357 rawEnt->raw_default = copyObject(colDef->raw_default);
5358
5359 /*
5360 * This function is intended for CREATE TABLE, so it processes a
5361 * _list_ of defaults, but we just do one.
5362 */
5363 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
5364 false, true, false);
5365
5366 /* Make the additional catalog changes visible */
5367 CommandCounterIncrement();
5368 }
5369
5370 /*
5371 * Tell Phase 3 to fill in the default expression, if there is one.
5372 *
5373 * If there is no default, Phase 3 doesn't have to do anything, because
5374 * that effectively means that the default is NULL. The heap tuple access
5375 * routines always check for attnum > # of attributes in tuple, and return
5376 * NULL if so, so without any modification of the tuple data we will get
5377 * the effect of NULL values in the new column.
5378 *
5379 * An exception occurs when the new column is of a domain type: the domain
5380 * might have a NOT NULL constraint, or a check constraint that indirectly
5381 * rejects nulls. If there are any domain constraints then we construct
5382 * an explicit NULL default value that will be passed through
5383 * CoerceToDomain processing. (This is a tad inefficient, since it causes
5384 * rewriting the table which we really don't have to do, but the present
5385 * design of domain processing doesn't offer any simple way of checking
5386 * the constraints more directly.)
5387 *
5388 * Note: we use build_column_default, and not just the cooked default
5389 * returned by AddRelationNewConstraints, so that the right thing happens
5390 * when a datatype's default applies.
5391 *
5392 * We skip this step completely for views and foreign tables. For a view,
5393 * we can only get here from CREATE OR REPLACE VIEW, which historically
5394 * doesn't set up defaults, not even for domain-typed columns. And in any
5395 * case we mustn't invoke Phase 3 on a view or foreign table, since they
5396 * have no storage.
5397 */
5398 if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE
5399 && relkind != RELKIND_FOREIGN_TABLE && attribute.attnum > 0)
5400 {
5401 /*
5402 * For an identity column, we can't use build_column_default(),
5403 * because the sequence ownership isn't set yet. So do it manually.
5404 */
5405 if (colDef->identity)
5406 {
5407 NextValueExpr *nve = makeNode(NextValueExpr);
5408
5409 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
5410 nve->typeId = typeOid;
5411
5412 defval = (Expr *) nve;
5413 }
5414 else
5415 defval = (Expr *) build_column_default(rel, attribute.attnum);
5416
5417 if (!defval && DomainHasConstraints(typeOid))
5418 {
5419 Oid baseTypeId;
5420 int32 baseTypeMod;
5421 Oid baseTypeColl;
5422
5423 baseTypeMod = typmod;
5424 baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
5425 baseTypeColl = get_typcollation(baseTypeId);
5426 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
5427 defval = (Expr *) coerce_to_target_type(NULL,
5428 (Node *) defval,
5429 baseTypeId,
5430 typeOid,
5431 typmod,
5432 COERCION_ASSIGNMENT,
5433 COERCE_IMPLICIT_CAST,
5434 -1);
5435 if (defval == NULL) /* should not happen */
5436 elog(ERROR, "failed to coerce base type to domain");
5437 }
5438
5439 if (defval)
5440 {
5441 NewColumnValue *newval;
5442
5443 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
5444 newval->attnum = attribute.attnum;
5445 newval->expr = expression_planner(defval);
5446
5447 tab->newvals = lappend(tab->newvals, newval);
5448 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
5449 }
5450
5451 /*
5452 * If the new column is NOT NULL, tell Phase 3 it needs to test that.
5453 * (Note we don't do this for an OID column. OID will be marked not
5454 * null, but since it's filled specially, there's no need to test
5455 * anything.)
5456 */
5457 tab->new_notnull |= colDef->is_not_null;
5458 }
5459
5460 /*
5461 * If we are adding an OID column, we have to tell Phase 3 to rewrite the
5462 * table to fix that.
5463 */
5464 if (isOid)
5465 tab->rewrite |= AT_REWRITE_ALTER_OID;
5466
5467 /*
5468 * Add needed dependency entries for the new column.
5469 */
5470 add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
5471 add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
5472
5473 /*
5474 * Propagate to children as appropriate. Unlike most other ALTER
5475 * routines, we have to do this one level of recursion at a time; we can't
5476 * use find_all_inheritors to do it in one pass.
5477 */
5478 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
5479
5480 /*
5481 * If we are told not to recurse, there had better not be any child
5482 * tables; else the addition would put them out of step.
5483 */
5484 if (children && !recurse)
5485 ereport(ERROR,
5486 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5487 errmsg("column must be added to child tables too")));
5488
5489 /* Children should see column as singly inherited */
5490 if (!recursing)
5491 {
5492 colDef = copyObject(colDef);
5493 colDef->inhcount = 1;
5494 colDef->is_local = false;
5495 }
5496
5497 foreach(child, children)
5498 {
5499 Oid childrelid = lfirst_oid(child);
5500 Relation childrel;
5501 AlteredTableInfo *childtab;
5502
5503 /* find_inheritance_children already got lock */
5504 childrel = heap_open(childrelid, NoLock);
5505 CheckTableNotInUse(childrel, "ALTER TABLE");
5506
5507 /* Find or create work queue entry for this table */
5508 childtab = ATGetQueueEntry(wqueue, childrel);
5509
5510 /* Recurse to child; return value is ignored */
5511 ATExecAddColumn(wqueue, childtab, childrel,
5512 colDef, isOid, recurse, true,
5513 if_not_exists, lockmode);
5514
5515 heap_close(childrel, NoLock);
5516 }
5517
5518 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
5519 return address;
5520 }
5521
5522 /*
5523 * If a new or renamed column will collide with the name of an existing
5524 * column and if_not_exists is false then error out, else do nothing.
5525 */
5526 static bool
check_for_column_name_collision(Relation rel,const char * colname,bool if_not_exists)5527 check_for_column_name_collision(Relation rel, const char *colname,
5528 bool if_not_exists)
5529 {
5530 HeapTuple attTuple;
5531 int attnum;
5532
5533 /*
5534 * this test is deliberately not attisdropped-aware, since if one tries to
5535 * add a column matching a dropped column name, it's gonna fail anyway.
5536 */
5537 attTuple = SearchSysCache2(ATTNAME,
5538 ObjectIdGetDatum(RelationGetRelid(rel)),
5539 PointerGetDatum(colname));
5540 if (!HeapTupleIsValid(attTuple))
5541 return true;
5542
5543 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
5544 ReleaseSysCache(attTuple);
5545
5546 /*
5547 * We throw a different error message for conflicts with system column
5548 * names, since they are normally not shown and the user might otherwise
5549 * be confused about the reason for the conflict.
5550 */
5551 if (attnum <= 0)
5552 ereport(ERROR,
5553 (errcode(ERRCODE_DUPLICATE_COLUMN),
5554 errmsg("column name \"%s\" conflicts with a system column name",
5555 colname)));
5556 else
5557 {
5558 if (if_not_exists)
5559 {
5560 ereport(NOTICE,
5561 (errcode(ERRCODE_DUPLICATE_COLUMN),
5562 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
5563 colname, RelationGetRelationName(rel))));
5564 return false;
5565 }
5566
5567 ereport(ERROR,
5568 (errcode(ERRCODE_DUPLICATE_COLUMN),
5569 errmsg("column \"%s\" of relation \"%s\" already exists",
5570 colname, RelationGetRelationName(rel))));
5571 }
5572
5573 return true;
5574 }
5575
5576 /*
5577 * Install a column's dependency on its datatype.
5578 */
5579 static void
add_column_datatype_dependency(Oid relid,int32 attnum,Oid typid)5580 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
5581 {
5582 ObjectAddress myself,
5583 referenced;
5584
5585 myself.classId = RelationRelationId;
5586 myself.objectId = relid;
5587 myself.objectSubId = attnum;
5588 referenced.classId = TypeRelationId;
5589 referenced.objectId = typid;
5590 referenced.objectSubId = 0;
5591 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
5592 }
5593
5594 /*
5595 * Install a column's dependency on its collation.
5596 */
5597 static void
add_column_collation_dependency(Oid relid,int32 attnum,Oid collid)5598 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
5599 {
5600 ObjectAddress myself,
5601 referenced;
5602
5603 /* We know the default collation is pinned, so don't bother recording it */
5604 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
5605 {
5606 myself.classId = RelationRelationId;
5607 myself.objectId = relid;
5608 myself.objectSubId = attnum;
5609 referenced.classId = CollationRelationId;
5610 referenced.objectId = collid;
5611 referenced.objectSubId = 0;
5612 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
5613 }
5614 }
5615
5616 /*
5617 * ALTER TABLE SET WITH OIDS
5618 *
5619 * Basically this is an ADD COLUMN for the special OID column. We have
5620 * to cons up a ColumnDef node because the ADD COLUMN code needs one.
5621 */
5622 static void
ATPrepAddOids(List ** wqueue,Relation rel,bool recurse,AlterTableCmd * cmd,LOCKMODE lockmode)5623 ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode)
5624 {
5625 /* If we're recursing to a child table, the ColumnDef is already set up */
5626 if (cmd->def == NULL)
5627 {
5628 ColumnDef *cdef = makeNode(ColumnDef);
5629
5630 cdef->colname = pstrdup("oid");
5631 cdef->typeName = makeTypeNameFromOid(OIDOID, -1);
5632 cdef->inhcount = 0;
5633 cdef->is_local = true;
5634 cdef->is_not_null = true;
5635 cdef->storage = 0;
5636 cdef->location = -1;
5637 cmd->def = (Node *) cdef;
5638 }
5639 ATPrepAddColumn(wqueue, rel, recurse, false, false, cmd, lockmode);
5640
5641 if (recurse)
5642 cmd->subtype = AT_AddOidsRecurse;
5643 }
5644
5645 /*
5646 * ALTER TABLE ALTER COLUMN DROP NOT NULL
5647 *
5648 * Return the address of the modified column. If the column was already
5649 * nullable, InvalidObjectAddress is returned.
5650 */
5651
5652 static void
ATPrepDropNotNull(Relation rel,bool recurse,bool recursing)5653 ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
5654 {
5655 /*
5656 * If the parent is a partitioned table, like check constraints, we do not
5657 * support removing the NOT NULL while partitions exist.
5658 */
5659 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5660 {
5661 PartitionDesc partdesc = RelationGetPartitionDesc(rel);
5662
5663 Assert(partdesc != NULL);
5664 if (partdesc->nparts > 0 && !recurse && !recursing)
5665 ereport(ERROR,
5666 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5667 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
5668 errhint("Do not specify the ONLY keyword.")));
5669 }
5670 }
5671 static ObjectAddress
ATExecDropNotNull(Relation rel,const char * colName,LOCKMODE lockmode)5672 ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
5673 {
5674 HeapTuple tuple;
5675 AttrNumber attnum;
5676 Relation attr_rel;
5677 List *indexoidlist;
5678 ListCell *indexoidscan;
5679 ObjectAddress address;
5680
5681 /*
5682 * lookup the attribute
5683 */
5684 attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
5685
5686 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
5687
5688 if (!HeapTupleIsValid(tuple))
5689 ereport(ERROR,
5690 (errcode(ERRCODE_UNDEFINED_COLUMN),
5691 errmsg("column \"%s\" of relation \"%s\" does not exist",
5692 colName, RelationGetRelationName(rel))));
5693
5694 attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
5695
5696 /* Prevent them from altering a system attribute */
5697 if (attnum <= 0)
5698 ereport(ERROR,
5699 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5700 errmsg("cannot alter system column \"%s\"",
5701 colName)));
5702
5703 if (get_attidentity(RelationGetRelid(rel), attnum))
5704 ereport(ERROR,
5705 (errcode(ERRCODE_SYNTAX_ERROR),
5706 errmsg("column \"%s\" of relation \"%s\" is an identity column",
5707 colName, RelationGetRelationName(rel))));
5708
5709 /*
5710 * Check that the attribute is not in a primary key
5711 *
5712 * Note: we'll throw error even if the pkey index is not valid.
5713 */
5714
5715 /* Loop over all indexes on the relation */
5716 indexoidlist = RelationGetIndexList(rel);
5717
5718 foreach(indexoidscan, indexoidlist)
5719 {
5720 Oid indexoid = lfirst_oid(indexoidscan);
5721 HeapTuple indexTuple;
5722 Form_pg_index indexStruct;
5723 int i;
5724
5725 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
5726 if (!HeapTupleIsValid(indexTuple))
5727 elog(ERROR, "cache lookup failed for index %u", indexoid);
5728 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
5729
5730 /* If the index is not a primary key, skip the check */
5731 if (indexStruct->indisprimary)
5732 {
5733 /*
5734 * Loop over each attribute in the primary key and see if it
5735 * matches the to-be-altered attribute
5736 */
5737 for (i = 0; i < indexStruct->indnatts; i++)
5738 {
5739 if (indexStruct->indkey.values[i] == attnum)
5740 ereport(ERROR,
5741 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5742 errmsg("column \"%s\" is in a primary key",
5743 colName)));
5744 }
5745 }
5746
5747 ReleaseSysCache(indexTuple);
5748 }
5749
5750 list_free(indexoidlist);
5751
5752 /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
5753 if (rel->rd_rel->relispartition)
5754 {
5755 Oid parentId = get_partition_parent(RelationGetRelid(rel));
5756 Relation parent = heap_open(parentId, AccessShareLock);
5757 TupleDesc tupDesc = RelationGetDescr(parent);
5758 AttrNumber parent_attnum;
5759
5760 parent_attnum = get_attnum(parentId, colName);
5761 if (tupDesc->attrs[parent_attnum - 1]->attnotnull)
5762 ereport(ERROR,
5763 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5764 errmsg("column \"%s\" is marked NOT NULL in parent table",
5765 colName)));
5766 heap_close(parent, AccessShareLock);
5767 }
5768
5769 /*
5770 * Okay, actually perform the catalog change ... if needed
5771 */
5772 if (((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
5773 {
5774 ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE;
5775
5776 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
5777
5778 ObjectAddressSubSet(address, RelationRelationId,
5779 RelationGetRelid(rel), attnum);
5780 }
5781 else
5782 address = InvalidObjectAddress;
5783
5784 InvokeObjectPostAlterHook(RelationRelationId,
5785 RelationGetRelid(rel), attnum);
5786
5787 heap_close(attr_rel, RowExclusiveLock);
5788
5789 return address;
5790 }
5791
5792 /*
5793 * ALTER TABLE ALTER COLUMN SET NOT NULL
5794 *
5795 * Return the address of the modified column. If the column was already NOT
5796 * NULL, InvalidObjectAddress is returned.
5797 */
5798
5799 static void
ATPrepSetNotNull(Relation rel,bool recurse,bool recursing)5800 ATPrepSetNotNull(Relation rel, bool recurse, bool recursing)
5801 {
5802 /*
5803 * If the parent is a partitioned table, like check constraints, NOT NULL
5804 * constraints must be added to the child tables. Complain if requested
5805 * otherwise and partitions exist.
5806 */
5807 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5808 {
5809 PartitionDesc partdesc = RelationGetPartitionDesc(rel);
5810
5811 if (partdesc && partdesc->nparts > 0 && !recurse && !recursing)
5812 ereport(ERROR,
5813 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5814 errmsg("cannot add constraint to only the partitioned table when partitions exist"),
5815 errhint("Do not specify the ONLY keyword.")));
5816 }
5817 }
5818
5819 static ObjectAddress
ATExecSetNotNull(AlteredTableInfo * tab,Relation rel,const char * colName,LOCKMODE lockmode)5820 ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
5821 const char *colName, LOCKMODE lockmode)
5822 {
5823 HeapTuple tuple;
5824 AttrNumber attnum;
5825 Relation attr_rel;
5826 ObjectAddress address;
5827
5828 /*
5829 * lookup the attribute
5830 */
5831 attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
5832
5833 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
5834
5835 if (!HeapTupleIsValid(tuple))
5836 ereport(ERROR,
5837 (errcode(ERRCODE_UNDEFINED_COLUMN),
5838 errmsg("column \"%s\" of relation \"%s\" does not exist",
5839 colName, RelationGetRelationName(rel))));
5840
5841 attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
5842
5843 /* Prevent them from altering a system attribute */
5844 if (attnum <= 0)
5845 ereport(ERROR,
5846 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5847 errmsg("cannot alter system column \"%s\"",
5848 colName)));
5849
5850 /*
5851 * Okay, actually perform the catalog change ... if needed
5852 */
5853 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
5854 {
5855 ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
5856
5857 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
5858
5859 /* Tell Phase 3 it needs to test the constraint */
5860 tab->new_notnull = true;
5861
5862 ObjectAddressSubSet(address, RelationRelationId,
5863 RelationGetRelid(rel), attnum);
5864 }
5865 else
5866 address = InvalidObjectAddress;
5867
5868 InvokeObjectPostAlterHook(RelationRelationId,
5869 RelationGetRelid(rel), attnum);
5870
5871 heap_close(attr_rel, RowExclusiveLock);
5872
5873 return address;
5874 }
5875
5876 /*
5877 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
5878 *
5879 * Return the address of the affected column.
5880 */
5881 static ObjectAddress
ATExecColumnDefault(Relation rel,const char * colName,Node * newDefault,LOCKMODE lockmode)5882 ATExecColumnDefault(Relation rel, const char *colName,
5883 Node *newDefault, LOCKMODE lockmode)
5884 {
5885 AttrNumber attnum;
5886 ObjectAddress address;
5887
5888 /*
5889 * get the number of the attribute
5890 */
5891 attnum = get_attnum(RelationGetRelid(rel), colName);
5892 if (attnum == InvalidAttrNumber)
5893 ereport(ERROR,
5894 (errcode(ERRCODE_UNDEFINED_COLUMN),
5895 errmsg("column \"%s\" of relation \"%s\" does not exist",
5896 colName, RelationGetRelationName(rel))));
5897
5898 /* Prevent them from altering a system attribute */
5899 if (attnum <= 0)
5900 ereport(ERROR,
5901 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5902 errmsg("cannot alter system column \"%s\"",
5903 colName)));
5904
5905 if (get_attidentity(RelationGetRelid(rel), attnum))
5906 ereport(ERROR,
5907 (errcode(ERRCODE_SYNTAX_ERROR),
5908 errmsg("column \"%s\" of relation \"%s\" is an identity column",
5909 colName, RelationGetRelationName(rel)),
5910 newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
5911
5912 /*
5913 * Remove any old default for the column. We use RESTRICT here for
5914 * safety, but at present we do not expect anything to depend on the
5915 * default.
5916 *
5917 * We treat removing the existing default as an internal operation when it
5918 * is preparatory to adding a new default, but as a user-initiated
5919 * operation when the user asked for a drop.
5920 */
5921 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
5922 newDefault == NULL ? false : true);
5923
5924 if (newDefault)
5925 {
5926 /* SET DEFAULT */
5927 RawColumnDefault *rawEnt;
5928
5929 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
5930 rawEnt->attnum = attnum;
5931 rawEnt->raw_default = newDefault;
5932
5933 /*
5934 * This function is intended for CREATE TABLE, so it processes a
5935 * _list_ of defaults, but we just do one.
5936 */
5937 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
5938 false, true, false);
5939 }
5940
5941 ObjectAddressSubSet(address, RelationRelationId,
5942 RelationGetRelid(rel), attnum);
5943 return address;
5944 }
5945
5946 /*
5947 * ALTER TABLE ALTER COLUMN ADD IDENTITY
5948 *
5949 * Return the address of the affected column.
5950 */
5951 static ObjectAddress
ATExecAddIdentity(Relation rel,const char * colName,Node * def,LOCKMODE lockmode)5952 ATExecAddIdentity(Relation rel, const char *colName,
5953 Node *def, LOCKMODE lockmode)
5954 {
5955 Relation attrelation;
5956 HeapTuple tuple;
5957 Form_pg_attribute attTup;
5958 AttrNumber attnum;
5959 ObjectAddress address;
5960 ColumnDef *cdef = castNode(ColumnDef, def);
5961
5962 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
5963
5964 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
5965 if (!HeapTupleIsValid(tuple))
5966 ereport(ERROR,
5967 (errcode(ERRCODE_UNDEFINED_COLUMN),
5968 errmsg("column \"%s\" of relation \"%s\" does not exist",
5969 colName, RelationGetRelationName(rel))));
5970 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
5971 attnum = attTup->attnum;
5972
5973 /* Can't alter a system attribute */
5974 if (attnum <= 0)
5975 ereport(ERROR,
5976 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5977 errmsg("cannot alter system column \"%s\"",
5978 colName)));
5979
5980 /*
5981 * Creating a column as identity implies NOT NULL, so adding the identity
5982 * to an existing column that is not NOT NULL would create a state that
5983 * cannot be reproduced without contortions.
5984 */
5985 if (!attTup->attnotnull)
5986 ereport(ERROR,
5987 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
5988 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
5989 colName, RelationGetRelationName(rel))));
5990
5991 if (attTup->attidentity)
5992 ereport(ERROR,
5993 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
5994 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
5995 colName, RelationGetRelationName(rel))));
5996
5997 if (attTup->atthasdef)
5998 ereport(ERROR,
5999 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6000 errmsg("column \"%s\" of relation \"%s\" already has a default value",
6001 colName, RelationGetRelationName(rel))));
6002
6003 attTup->attidentity = cdef->identity;
6004 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6005
6006 InvokeObjectPostAlterHook(RelationRelationId,
6007 RelationGetRelid(rel),
6008 attTup->attnum);
6009 ObjectAddressSubSet(address, RelationRelationId,
6010 RelationGetRelid(rel), attnum);
6011 heap_freetuple(tuple);
6012
6013 heap_close(attrelation, RowExclusiveLock);
6014
6015 return address;
6016 }
6017
6018 /*
6019 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
6020 *
6021 * Return the address of the affected column.
6022 */
6023 static ObjectAddress
ATExecSetIdentity(Relation rel,const char * colName,Node * def,LOCKMODE lockmode)6024 ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
6025 {
6026 ListCell *option;
6027 DefElem *generatedEl = NULL;
6028 HeapTuple tuple;
6029 Form_pg_attribute attTup;
6030 AttrNumber attnum;
6031 Relation attrelation;
6032 ObjectAddress address;
6033
6034 foreach(option, castNode(List, def))
6035 {
6036 DefElem *defel = lfirst_node(DefElem, option);
6037
6038 if (strcmp(defel->defname, "generated") == 0)
6039 {
6040 if (generatedEl)
6041 ereport(ERROR,
6042 (errcode(ERRCODE_SYNTAX_ERROR),
6043 errmsg("conflicting or redundant options")));
6044 generatedEl = defel;
6045 }
6046 else
6047 elog(ERROR, "option \"%s\" not recognized",
6048 defel->defname);
6049 }
6050
6051 /*
6052 * Even if there is nothing to change here, we run all the checks. There
6053 * will be a subsequent ALTER SEQUENCE that relies on everything being
6054 * there.
6055 */
6056
6057 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6058 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6059 if (!HeapTupleIsValid(tuple))
6060 ereport(ERROR,
6061 (errcode(ERRCODE_UNDEFINED_COLUMN),
6062 errmsg("column \"%s\" of relation \"%s\" does not exist",
6063 colName, RelationGetRelationName(rel))));
6064
6065 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6066 attnum = attTup->attnum;
6067
6068 if (attnum <= 0)
6069 ereport(ERROR,
6070 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6071 errmsg("cannot alter system column \"%s\"",
6072 colName)));
6073
6074 if (!attTup->attidentity)
6075 ereport(ERROR,
6076 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6077 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
6078 colName, RelationGetRelationName(rel))));
6079
6080 if (generatedEl)
6081 {
6082 attTup->attidentity = defGetInt32(generatedEl);
6083 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6084
6085 InvokeObjectPostAlterHook(RelationRelationId,
6086 RelationGetRelid(rel),
6087 attTup->attnum);
6088 ObjectAddressSubSet(address, RelationRelationId,
6089 RelationGetRelid(rel), attnum);
6090 }
6091 else
6092 address = InvalidObjectAddress;
6093
6094 heap_freetuple(tuple);
6095 heap_close(attrelation, RowExclusiveLock);
6096
6097 return address;
6098 }
6099
6100 /*
6101 * ALTER TABLE ALTER COLUMN DROP IDENTITY
6102 *
6103 * Return the address of the affected column.
6104 */
6105 static ObjectAddress
ATExecDropIdentity(Relation rel,const char * colName,bool missing_ok,LOCKMODE lockmode)6106 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
6107 {
6108 HeapTuple tuple;
6109 Form_pg_attribute attTup;
6110 AttrNumber attnum;
6111 Relation attrelation;
6112 ObjectAddress address;
6113 Oid seqid;
6114 ObjectAddress seqaddress;
6115
6116 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6117 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6118 if (!HeapTupleIsValid(tuple))
6119 ereport(ERROR,
6120 (errcode(ERRCODE_UNDEFINED_COLUMN),
6121 errmsg("column \"%s\" of relation \"%s\" does not exist",
6122 colName, RelationGetRelationName(rel))));
6123
6124 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6125 attnum = attTup->attnum;
6126
6127 if (attnum <= 0)
6128 ereport(ERROR,
6129 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6130 errmsg("cannot alter system column \"%s\"",
6131 colName)));
6132
6133 if (!attTup->attidentity)
6134 {
6135 if (!missing_ok)
6136 ereport(ERROR,
6137 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6138 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
6139 colName, RelationGetRelationName(rel))));
6140 else
6141 {
6142 ereport(NOTICE,
6143 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
6144 colName, RelationGetRelationName(rel))));
6145 heap_freetuple(tuple);
6146 heap_close(attrelation, RowExclusiveLock);
6147 return InvalidObjectAddress;
6148 }
6149 }
6150
6151 attTup->attidentity = '\0';
6152 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6153
6154 InvokeObjectPostAlterHook(RelationRelationId,
6155 RelationGetRelid(rel),
6156 attTup->attnum);
6157 ObjectAddressSubSet(address, RelationRelationId,
6158 RelationGetRelid(rel), attnum);
6159 heap_freetuple(tuple);
6160
6161 heap_close(attrelation, RowExclusiveLock);
6162
6163 /* drop the internal sequence */
6164 seqid = getOwnedSequence(RelationGetRelid(rel), attnum);
6165 deleteDependencyRecordsForClass(RelationRelationId, seqid,
6166 RelationRelationId, DEPENDENCY_INTERNAL);
6167 CommandCounterIncrement();
6168 seqaddress.classId = RelationRelationId;
6169 seqaddress.objectId = seqid;
6170 seqaddress.objectSubId = 0;
6171 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
6172
6173 return address;
6174 }
6175
6176 /*
6177 * ALTER TABLE ALTER COLUMN SET STATISTICS
6178 */
6179 static void
ATPrepSetStatistics(Relation rel,const char * colName,Node * newValue,LOCKMODE lockmode)6180 ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6181 {
6182 /*
6183 * We do our own permission checking because (a) we want to allow SET
6184 * STATISTICS on indexes (for expressional index columns), and (b) we want
6185 * to allow SET STATISTICS on system catalogs without requiring
6186 * allowSystemTableMods to be turned on.
6187 */
6188 if (rel->rd_rel->relkind != RELKIND_RELATION &&
6189 rel->rd_rel->relkind != RELKIND_MATVIEW &&
6190 rel->rd_rel->relkind != RELKIND_INDEX &&
6191 rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
6192 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
6193 ereport(ERROR,
6194 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6195 errmsg("\"%s\" is not a table, materialized view, index, or foreign table",
6196 RelationGetRelationName(rel))));
6197
6198 /* Permissions checks */
6199 if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
6200 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
6201 RelationGetRelationName(rel));
6202 }
6203
6204 /*
6205 * Return value is the address of the modified column
6206 */
6207 static ObjectAddress
ATExecSetStatistics(Relation rel,const char * colName,Node * newValue,LOCKMODE lockmode)6208 ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6209 {
6210 int newtarget;
6211 Relation attrelation;
6212 HeapTuple tuple;
6213 Form_pg_attribute attrtuple;
6214 AttrNumber attnum;
6215 ObjectAddress address;
6216
6217 Assert(IsA(newValue, Integer));
6218 newtarget = intVal(newValue);
6219
6220 /*
6221 * Limit target to a sane range
6222 */
6223 if (newtarget < -1)
6224 {
6225 ereport(ERROR,
6226 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6227 errmsg("statistics target %d is too low",
6228 newtarget)));
6229 }
6230 else if (newtarget > 10000)
6231 {
6232 newtarget = 10000;
6233 ereport(WARNING,
6234 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6235 errmsg("lowering statistics target to %d",
6236 newtarget)));
6237 }
6238
6239 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6240
6241 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6242
6243 if (!HeapTupleIsValid(tuple))
6244 ereport(ERROR,
6245 (errcode(ERRCODE_UNDEFINED_COLUMN),
6246 errmsg("column \"%s\" of relation \"%s\" does not exist",
6247 colName, RelationGetRelationName(rel))));
6248 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6249
6250 attnum = attrtuple->attnum;
6251 if (attnum <= 0)
6252 ereport(ERROR,
6253 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6254 errmsg("cannot alter system column \"%s\"",
6255 colName)));
6256
6257 attrtuple->attstattarget = newtarget;
6258
6259 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6260
6261 InvokeObjectPostAlterHook(RelationRelationId,
6262 RelationGetRelid(rel),
6263 attrtuple->attnum);
6264 ObjectAddressSubSet(address, RelationRelationId,
6265 RelationGetRelid(rel), attnum);
6266 heap_freetuple(tuple);
6267
6268 heap_close(attrelation, RowExclusiveLock);
6269
6270 return address;
6271 }
6272
6273 /*
6274 * Return value is the address of the modified column
6275 */
6276 static ObjectAddress
ATExecSetOptions(Relation rel,const char * colName,Node * options,bool isReset,LOCKMODE lockmode)6277 ATExecSetOptions(Relation rel, const char *colName, Node *options,
6278 bool isReset, LOCKMODE lockmode)
6279 {
6280 Relation attrelation;
6281 HeapTuple tuple,
6282 newtuple;
6283 Form_pg_attribute attrtuple;
6284 AttrNumber attnum;
6285 Datum datum,
6286 newOptions;
6287 bool isnull;
6288 ObjectAddress address;
6289 Datum repl_val[Natts_pg_attribute];
6290 bool repl_null[Natts_pg_attribute];
6291 bool repl_repl[Natts_pg_attribute];
6292
6293 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6294
6295 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
6296
6297 if (!HeapTupleIsValid(tuple))
6298 ereport(ERROR,
6299 (errcode(ERRCODE_UNDEFINED_COLUMN),
6300 errmsg("column \"%s\" of relation \"%s\" does not exist",
6301 colName, RelationGetRelationName(rel))));
6302 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6303
6304 attnum = attrtuple->attnum;
6305 if (attnum <= 0)
6306 ereport(ERROR,
6307 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6308 errmsg("cannot alter system column \"%s\"",
6309 colName)));
6310
6311 /* Generate new proposed attoptions (text array) */
6312 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
6313 &isnull);
6314 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
6315 castNode(List, options), NULL, NULL,
6316 false, isReset);
6317 /* Validate new options */
6318 (void) attribute_reloptions(newOptions, true);
6319
6320 /* Build new tuple. */
6321 memset(repl_null, false, sizeof(repl_null));
6322 memset(repl_repl, false, sizeof(repl_repl));
6323 if (newOptions != (Datum) 0)
6324 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
6325 else
6326 repl_null[Anum_pg_attribute_attoptions - 1] = true;
6327 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
6328 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
6329 repl_val, repl_null, repl_repl);
6330
6331 /* Update system catalog. */
6332 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
6333
6334 InvokeObjectPostAlterHook(RelationRelationId,
6335 RelationGetRelid(rel),
6336 attrtuple->attnum);
6337 ObjectAddressSubSet(address, RelationRelationId,
6338 RelationGetRelid(rel), attnum);
6339
6340 heap_freetuple(newtuple);
6341
6342 ReleaseSysCache(tuple);
6343
6344 heap_close(attrelation, RowExclusiveLock);
6345
6346 return address;
6347 }
6348
6349 /*
6350 * ALTER TABLE ALTER COLUMN SET STORAGE
6351 *
6352 * Return value is the address of the modified column
6353 */
6354 static ObjectAddress
ATExecSetStorage(Relation rel,const char * colName,Node * newValue,LOCKMODE lockmode)6355 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6356 {
6357 char *storagemode;
6358 char newstorage;
6359 Relation attrelation;
6360 HeapTuple tuple;
6361 Form_pg_attribute attrtuple;
6362 AttrNumber attnum;
6363 ObjectAddress address;
6364
6365 Assert(IsA(newValue, String));
6366 storagemode = strVal(newValue);
6367
6368 if (pg_strcasecmp(storagemode, "plain") == 0)
6369 newstorage = 'p';
6370 else if (pg_strcasecmp(storagemode, "external") == 0)
6371 newstorage = 'e';
6372 else if (pg_strcasecmp(storagemode, "extended") == 0)
6373 newstorage = 'x';
6374 else if (pg_strcasecmp(storagemode, "main") == 0)
6375 newstorage = 'm';
6376 else
6377 {
6378 ereport(ERROR,
6379 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6380 errmsg("invalid storage type \"%s\"",
6381 storagemode)));
6382 newstorage = 0; /* keep compiler quiet */
6383 }
6384
6385 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6386
6387 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6388
6389 if (!HeapTupleIsValid(tuple))
6390 ereport(ERROR,
6391 (errcode(ERRCODE_UNDEFINED_COLUMN),
6392 errmsg("column \"%s\" of relation \"%s\" does not exist",
6393 colName, RelationGetRelationName(rel))));
6394 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6395
6396 attnum = attrtuple->attnum;
6397 if (attnum <= 0)
6398 ereport(ERROR,
6399 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6400 errmsg("cannot alter system column \"%s\"",
6401 colName)));
6402
6403 /*
6404 * safety check: do not allow toasted storage modes unless column datatype
6405 * is TOAST-aware.
6406 */
6407 if (newstorage == 'p' || TypeIsToastable(attrtuple->atttypid))
6408 attrtuple->attstorage = newstorage;
6409 else
6410 ereport(ERROR,
6411 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6412 errmsg("column data type %s can only have storage PLAIN",
6413 format_type_be(attrtuple->atttypid))));
6414
6415 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6416
6417 InvokeObjectPostAlterHook(RelationRelationId,
6418 RelationGetRelid(rel),
6419 attrtuple->attnum);
6420
6421 heap_freetuple(tuple);
6422
6423 heap_close(attrelation, RowExclusiveLock);
6424
6425 ObjectAddressSubSet(address, RelationRelationId,
6426 RelationGetRelid(rel), attnum);
6427 return address;
6428 }
6429
6430
6431 /*
6432 * ALTER TABLE DROP COLUMN
6433 *
6434 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
6435 * because we have to decide at runtime whether to recurse or not depending
6436 * on whether attinhcount goes to zero or not. (We can't check this in a
6437 * static pre-pass because it won't handle multiple inheritance situations
6438 * correctly.)
6439 */
6440 static void
ATPrepDropColumn(List ** wqueue,Relation rel,bool recurse,bool recursing,AlterTableCmd * cmd,LOCKMODE lockmode)6441 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6442 AlterTableCmd *cmd, LOCKMODE lockmode)
6443 {
6444 if (rel->rd_rel->reloftype && !recursing)
6445 ereport(ERROR,
6446 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6447 errmsg("cannot drop column from typed table")));
6448
6449 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6450 ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
6451
6452 if (recurse)
6453 cmd->subtype = AT_DropColumnRecurse;
6454 }
6455
6456 /*
6457 * Checks if attnum is a partition attribute for rel
6458 *
6459 * Sets *used_in_expr if attnum is found to be referenced in some partition
6460 * key expression. It's possible for a column to be both used directly and
6461 * as part of an expression; if that happens, *used_in_expr may end up as
6462 * either true or false. That's OK for current uses of this function, because
6463 * *used_in_expr is only used to tailor the error message text.
6464 */
6465 static bool
is_partition_attr(Relation rel,AttrNumber attnum,bool * used_in_expr)6466 is_partition_attr(Relation rel, AttrNumber attnum, bool *used_in_expr)
6467 {
6468 PartitionKey key;
6469 int partnatts;
6470 List *partexprs;
6471 ListCell *partexprs_item;
6472 int i;
6473
6474 if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
6475 return false;
6476
6477 key = RelationGetPartitionKey(rel);
6478 partnatts = get_partition_natts(key);
6479 partexprs = get_partition_exprs(key);
6480
6481 partexprs_item = list_head(partexprs);
6482 for (i = 0; i < partnatts; i++)
6483 {
6484 AttrNumber partattno = get_partition_col_attnum(key, i);
6485
6486 if (partattno != 0)
6487 {
6488 if (attnum == partattno)
6489 {
6490 if (used_in_expr)
6491 *used_in_expr = false;
6492 return true;
6493 }
6494 }
6495 else
6496 {
6497 /* Arbitrary expression */
6498 Node *expr = (Node *) lfirst(partexprs_item);
6499 Bitmapset *expr_attrs = NULL;
6500
6501 /* Find all attributes referenced */
6502 pull_varattnos(expr, 1, &expr_attrs);
6503 partexprs_item = lnext(partexprs_item);
6504
6505 if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
6506 expr_attrs))
6507 {
6508 if (used_in_expr)
6509 *used_in_expr = true;
6510 return true;
6511 }
6512 }
6513 }
6514
6515 return false;
6516 }
6517
6518 /*
6519 * Return value is the address of the dropped column.
6520 */
6521 static ObjectAddress
ATExecDropColumn(List ** wqueue,Relation rel,const char * colName,DropBehavior behavior,bool recurse,bool recursing,bool missing_ok,LOCKMODE lockmode)6522 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
6523 DropBehavior behavior,
6524 bool recurse, bool recursing,
6525 bool missing_ok, LOCKMODE lockmode)
6526 {
6527 HeapTuple tuple;
6528 Form_pg_attribute targetatt;
6529 AttrNumber attnum;
6530 List *children;
6531 ObjectAddress object;
6532 bool is_expr;
6533
6534 /* At top level, permission check was done in ATPrepCmd, else do it */
6535 if (recursing)
6536 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6537
6538 /*
6539 * get the number of the attribute
6540 */
6541 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
6542 if (!HeapTupleIsValid(tuple))
6543 {
6544 if (!missing_ok)
6545 {
6546 ereport(ERROR,
6547 (errcode(ERRCODE_UNDEFINED_COLUMN),
6548 errmsg("column \"%s\" of relation \"%s\" does not exist",
6549 colName, RelationGetRelationName(rel))));
6550 }
6551 else
6552 {
6553 ereport(NOTICE,
6554 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
6555 colName, RelationGetRelationName(rel))));
6556 return InvalidObjectAddress;
6557 }
6558 }
6559 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
6560
6561 attnum = targetatt->attnum;
6562
6563 /* Can't drop a system attribute, except OID */
6564 if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
6565 ereport(ERROR,
6566 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6567 errmsg("cannot drop system column \"%s\"",
6568 colName)));
6569
6570 /*
6571 * Don't drop inherited columns, unless recursing (presumably from a drop
6572 * of the parent column)
6573 */
6574 if (targetatt->attinhcount > 0 && !recursing)
6575 ereport(ERROR,
6576 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6577 errmsg("cannot drop inherited column \"%s\"",
6578 colName)));
6579
6580 /*
6581 * Don't drop columns used in the partition key, either. (If we let this
6582 * go through, the key column's dependencies would cause a cascaded drop
6583 * of the whole table, which is surely not what the user expected.)
6584 */
6585 if (is_partition_attr(rel, attnum, &is_expr))
6586 ereport(ERROR,
6587 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6588 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
6589 colName, RelationGetRelationName(rel))));
6590
6591 ReleaseSysCache(tuple);
6592
6593 /*
6594 * Propagate to children as appropriate. Unlike most other ALTER
6595 * routines, we have to do this one level of recursion at a time; we can't
6596 * use find_all_inheritors to do it in one pass.
6597 */
6598 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
6599
6600 if (children)
6601 {
6602 Relation attr_rel;
6603 ListCell *child;
6604
6605 /*
6606 * In case of a partitioned table, the column must be dropped from the
6607 * partitions as well.
6608 */
6609 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
6610 ereport(ERROR,
6611 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6612 errmsg("cannot drop column from only the partitioned table when partitions exist"),
6613 errhint("Do not specify the ONLY keyword.")));
6614
6615 attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
6616 foreach(child, children)
6617 {
6618 Oid childrelid = lfirst_oid(child);
6619 Relation childrel;
6620 Form_pg_attribute childatt;
6621
6622 /* find_inheritance_children already got lock */
6623 childrel = heap_open(childrelid, NoLock);
6624 CheckTableNotInUse(childrel, "ALTER TABLE");
6625
6626 tuple = SearchSysCacheCopyAttName(childrelid, colName);
6627 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
6628 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
6629 colName, childrelid);
6630 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
6631
6632 if (childatt->attinhcount <= 0) /* shouldn't happen */
6633 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
6634 childrelid, colName);
6635
6636 if (recurse)
6637 {
6638 /*
6639 * If the child column has other definition sources, just
6640 * decrement its inheritance count; if not, recurse to delete
6641 * it.
6642 */
6643 if (childatt->attinhcount == 1 && !childatt->attislocal)
6644 {
6645 /* Time to delete this child column, too */
6646 ATExecDropColumn(wqueue, childrel, colName,
6647 behavior, true, true,
6648 false, lockmode);
6649 }
6650 else
6651 {
6652 /* Child column must survive my deletion */
6653 childatt->attinhcount--;
6654
6655 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
6656
6657 /* Make update visible */
6658 CommandCounterIncrement();
6659 }
6660 }
6661 else
6662 {
6663 /*
6664 * If we were told to drop ONLY in this table (no recursion),
6665 * we need to mark the inheritors' attributes as locally
6666 * defined rather than inherited.
6667 */
6668 childatt->attinhcount--;
6669 childatt->attislocal = true;
6670
6671 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
6672
6673 /* Make update visible */
6674 CommandCounterIncrement();
6675 }
6676
6677 heap_freetuple(tuple);
6678
6679 heap_close(childrel, NoLock);
6680 }
6681 heap_close(attr_rel, RowExclusiveLock);
6682 }
6683
6684 /*
6685 * Perform the actual column deletion
6686 */
6687 object.classId = RelationRelationId;
6688 object.objectId = RelationGetRelid(rel);
6689 object.objectSubId = attnum;
6690
6691 performDeletion(&object, behavior, 0);
6692
6693 /*
6694 * If we dropped the OID column, must adjust pg_class.relhasoids and tell
6695 * Phase 3 to physically get rid of the column. We formerly left the
6696 * column in place physically, but this caused subtle problems. See
6697 * http://archives.postgresql.org/pgsql-hackers/2009-02/msg00363.php
6698 */
6699 if (attnum == ObjectIdAttributeNumber)
6700 {
6701 Relation class_rel;
6702 Form_pg_class tuple_class;
6703 AlteredTableInfo *tab;
6704
6705 class_rel = heap_open(RelationRelationId, RowExclusiveLock);
6706
6707 tuple = SearchSysCacheCopy1(RELOID,
6708 ObjectIdGetDatum(RelationGetRelid(rel)));
6709 if (!HeapTupleIsValid(tuple))
6710 elog(ERROR, "cache lookup failed for relation %u",
6711 RelationGetRelid(rel));
6712 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
6713
6714 tuple_class->relhasoids = false;
6715 CatalogTupleUpdate(class_rel, &tuple->t_self, tuple);
6716
6717 heap_close(class_rel, RowExclusiveLock);
6718
6719 /* Find or create work queue entry for this table */
6720 tab = ATGetQueueEntry(wqueue, rel);
6721
6722 /* Tell Phase 3 to physically remove the OID column */
6723 tab->rewrite |= AT_REWRITE_ALTER_OID;
6724 }
6725
6726 return object;
6727 }
6728
6729 /*
6730 * ALTER TABLE ADD INDEX
6731 *
6732 * There is no such command in the grammar, but parse_utilcmd.c converts
6733 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
6734 * us schedule creation of the index at the appropriate time during ALTER.
6735 *
6736 * Return value is the address of the new index.
6737 */
6738 static ObjectAddress
ATExecAddIndex(AlteredTableInfo * tab,Relation rel,IndexStmt * stmt,bool is_rebuild,LOCKMODE lockmode)6739 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
6740 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
6741 {
6742 bool check_rights;
6743 bool skip_build;
6744 bool quiet;
6745 ObjectAddress address;
6746
6747 Assert(IsA(stmt, IndexStmt));
6748 Assert(!stmt->concurrent);
6749
6750 /* The IndexStmt has already been through transformIndexStmt */
6751 Assert(stmt->transformed);
6752
6753 /* suppress schema rights check when rebuilding existing index */
6754 check_rights = !is_rebuild;
6755 /* skip index build if phase 3 will do it or we're reusing an old one */
6756 skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode);
6757 /* suppress notices when rebuilding existing index */
6758 quiet = is_rebuild;
6759
6760 address = DefineIndex(RelationGetRelid(rel),
6761 stmt,
6762 InvalidOid, /* no predefined OID */
6763 true, /* is_alter_table */
6764 check_rights,
6765 false, /* check_not_in_use - we did it already */
6766 skip_build,
6767 quiet);
6768
6769 /*
6770 * If TryReuseIndex() stashed a relfilenode for us, we used it for the new
6771 * index instead of building from scratch. The DROP of the old edition of
6772 * this index will have scheduled the storage for deletion at commit, so
6773 * cancel that pending deletion.
6774 */
6775 if (OidIsValid(stmt->oldNode))
6776 {
6777 Relation irel = index_open(address.objectId, NoLock);
6778
6779 RelationPreserveStorage(irel->rd_node, true);
6780 index_close(irel, NoLock);
6781 }
6782
6783 return address;
6784 }
6785
6786 /*
6787 * ALTER TABLE ADD CONSTRAINT USING INDEX
6788 *
6789 * Returns the address of the new constraint.
6790 */
6791 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo * tab,Relation rel,IndexStmt * stmt,LOCKMODE lockmode)6792 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
6793 IndexStmt *stmt, LOCKMODE lockmode)
6794 {
6795 Oid index_oid = stmt->indexOid;
6796 Relation indexRel;
6797 char *indexName;
6798 IndexInfo *indexInfo;
6799 char *constraintName;
6800 char constraintType;
6801 ObjectAddress address;
6802
6803 Assert(IsA(stmt, IndexStmt));
6804 Assert(OidIsValid(index_oid));
6805 Assert(stmt->isconstraint);
6806
6807 indexRel = index_open(index_oid, AccessShareLock);
6808
6809 indexName = pstrdup(RelationGetRelationName(indexRel));
6810
6811 indexInfo = BuildIndexInfo(indexRel);
6812
6813 /* this should have been checked at parse time */
6814 if (!indexInfo->ii_Unique)
6815 elog(ERROR, "index \"%s\" is not unique", indexName);
6816
6817 /*
6818 * Determine name to assign to constraint. We require a constraint to
6819 * have the same name as the underlying index; therefore, use the index's
6820 * existing name as the default constraint name, and if the user
6821 * explicitly gives some other name for the constraint, rename the index
6822 * to match.
6823 */
6824 constraintName = stmt->idxname;
6825 if (constraintName == NULL)
6826 constraintName = indexName;
6827 else if (strcmp(constraintName, indexName) != 0)
6828 {
6829 ereport(NOTICE,
6830 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
6831 indexName, constraintName)));
6832 RenameRelationInternal(index_oid, constraintName, false);
6833 }
6834
6835 /* Extra checks needed if making primary key */
6836 if (stmt->primary)
6837 index_check_primary_key(rel, indexInfo, true, stmt);
6838
6839 /* Note we currently don't support EXCLUSION constraints here */
6840 if (stmt->primary)
6841 constraintType = CONSTRAINT_PRIMARY;
6842 else
6843 constraintType = CONSTRAINT_UNIQUE;
6844
6845 /* Create the catalog entries for the constraint */
6846 address = index_constraint_create(rel,
6847 index_oid,
6848 indexInfo,
6849 constraintName,
6850 constraintType,
6851 stmt->deferrable,
6852 stmt->initdeferred,
6853 stmt->primary,
6854 true, /* update pg_index */
6855 true, /* remove old dependencies */
6856 allowSystemTableMods,
6857 false); /* is_internal */
6858
6859 index_close(indexRel, NoLock);
6860
6861 return address;
6862 }
6863
6864 /*
6865 * ALTER TABLE ADD CONSTRAINT
6866 *
6867 * Return value is the address of the new constraint; if no constraint was
6868 * added, InvalidObjectAddress is returned.
6869 */
6870 static ObjectAddress
ATExecAddConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * newConstraint,bool recurse,bool is_readd,LOCKMODE lockmode)6871 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
6872 Constraint *newConstraint, bool recurse, bool is_readd,
6873 LOCKMODE lockmode)
6874 {
6875 ObjectAddress address = InvalidObjectAddress;
6876
6877 Assert(IsA(newConstraint, Constraint));
6878
6879 /*
6880 * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
6881 * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
6882 * switch anyway to make it easier to add more code later.
6883 */
6884 switch (newConstraint->contype)
6885 {
6886 case CONSTR_CHECK:
6887 address =
6888 ATAddCheckConstraint(wqueue, tab, rel,
6889 newConstraint, recurse, false, is_readd,
6890 lockmode);
6891 break;
6892
6893 case CONSTR_FOREIGN:
6894
6895 /*
6896 * Note that we currently never recurse for FK constraints, so the
6897 * "recurse" flag is silently ignored.
6898 *
6899 * Assign or validate constraint name
6900 */
6901 if (newConstraint->conname)
6902 {
6903 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
6904 RelationGetRelid(rel),
6905 RelationGetNamespace(rel),
6906 newConstraint->conname))
6907 ereport(ERROR,
6908 (errcode(ERRCODE_DUPLICATE_OBJECT),
6909 errmsg("constraint \"%s\" for relation \"%s\" already exists",
6910 newConstraint->conname,
6911 RelationGetRelationName(rel))));
6912 }
6913 else
6914 newConstraint->conname =
6915 ChooseConstraintName(RelationGetRelationName(rel),
6916 strVal(linitial(newConstraint->fk_attrs)),
6917 "fkey",
6918 RelationGetNamespace(rel),
6919 NIL);
6920
6921 address = ATAddForeignKeyConstraint(tab, rel, newConstraint,
6922 lockmode);
6923 break;
6924
6925 default:
6926 elog(ERROR, "unrecognized constraint type: %d",
6927 (int) newConstraint->contype);
6928 }
6929
6930 return address;
6931 }
6932
6933 /*
6934 * Add a check constraint to a single table and its children. Returns the
6935 * address of the constraint added to the parent relation, if one gets added,
6936 * or InvalidObjectAddress otherwise.
6937 *
6938 * Subroutine for ATExecAddConstraint.
6939 *
6940 * We must recurse to child tables during execution, rather than using
6941 * ALTER TABLE's normal prep-time recursion. The reason is that all the
6942 * constraints *must* be given the same name, else they won't be seen as
6943 * related later. If the user didn't explicitly specify a name, then
6944 * AddRelationNewConstraints would normally assign different names to the
6945 * child constraints. To fix that, we must capture the name assigned at
6946 * the parent table and pass that down.
6947 */
6948 static ObjectAddress
ATAddCheckConstraint(List ** wqueue,AlteredTableInfo * tab,Relation rel,Constraint * constr,bool recurse,bool recursing,bool is_readd,LOCKMODE lockmode)6949 ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
6950 Constraint *constr, bool recurse, bool recursing,
6951 bool is_readd, LOCKMODE lockmode)
6952 {
6953 List *newcons;
6954 ListCell *lcon;
6955 List *children;
6956 ListCell *child;
6957 ObjectAddress address = InvalidObjectAddress;
6958
6959 /* At top level, permission check was done in ATPrepCmd, else do it */
6960 if (recursing)
6961 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6962
6963 /*
6964 * Call AddRelationNewConstraints to do the work, making sure it works on
6965 * a copy of the Constraint so transformExpr can't modify the original. It
6966 * returns a list of cooked constraints.
6967 *
6968 * If the constraint ends up getting merged with a pre-existing one, it's
6969 * omitted from the returned list, which is what we want: we do not need
6970 * to do any validation work. That can only happen at child tables,
6971 * though, since we disallow merging at the top level.
6972 */
6973 newcons = AddRelationNewConstraints(rel, NIL,
6974 list_make1(copyObject(constr)),
6975 recursing | is_readd, /* allow_merge */
6976 !recursing, /* is_local */
6977 is_readd); /* is_internal */
6978
6979 /* we don't expect more than one constraint here */
6980 Assert(list_length(newcons) <= 1);
6981
6982 /* Add each to-be-validated constraint to Phase 3's queue */
6983 foreach(lcon, newcons)
6984 {
6985 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
6986
6987 if (!ccon->skip_validation)
6988 {
6989 NewConstraint *newcon;
6990
6991 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
6992 newcon->name = ccon->name;
6993 newcon->contype = ccon->contype;
6994 newcon->qual = ccon->expr;
6995
6996 tab->constraints = lappend(tab->constraints, newcon);
6997 }
6998
6999 /* Save the actually assigned name if it was defaulted */
7000 if (constr->conname == NULL)
7001 constr->conname = ccon->name;
7002
7003 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7004 }
7005
7006 /* At this point we must have a locked-down name to use */
7007 Assert(constr->conname != NULL);
7008
7009 /* Advance command counter in case same table is visited multiple times */
7010 CommandCounterIncrement();
7011
7012 /*
7013 * If the constraint got merged with an existing constraint, we're done.
7014 * We mustn't recurse to child tables in this case, because they've
7015 * already got the constraint, and visiting them again would lead to an
7016 * incorrect value for coninhcount.
7017 */
7018 if (newcons == NIL)
7019 return address;
7020
7021 /*
7022 * If adding a NO INHERIT constraint, no need to find our children.
7023 */
7024 if (constr->is_no_inherit)
7025 return address;
7026
7027 /*
7028 * Propagate to children as appropriate. Unlike most other ALTER
7029 * routines, we have to do this one level of recursion at a time; we can't
7030 * use find_all_inheritors to do it in one pass.
7031 */
7032 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7033
7034 /*
7035 * Check if ONLY was specified with ALTER TABLE. If so, allow the
7036 * constraint creation only if there are no children currently. Error out
7037 * otherwise.
7038 */
7039 if (!recurse && children != NIL)
7040 ereport(ERROR,
7041 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7042 errmsg("constraint must be added to child tables too")));
7043
7044 foreach(child, children)
7045 {
7046 Oid childrelid = lfirst_oid(child);
7047 Relation childrel;
7048 AlteredTableInfo *childtab;
7049
7050 /* find_inheritance_children already got lock */
7051 childrel = heap_open(childrelid, NoLock);
7052 CheckTableNotInUse(childrel, "ALTER TABLE");
7053
7054 /* Find or create work queue entry for this table */
7055 childtab = ATGetQueueEntry(wqueue, childrel);
7056
7057 /* Recurse to child */
7058 ATAddCheckConstraint(wqueue, childtab, childrel,
7059 constr, recurse, true, is_readd, lockmode);
7060
7061 heap_close(childrel, NoLock);
7062 }
7063
7064 return address;
7065 }
7066
7067 /*
7068 * Add a foreign-key constraint to a single table; return the new constraint's
7069 * address.
7070 *
7071 * Subroutine for ATExecAddConstraint. Must already hold exclusive
7072 * lock on the rel, and have done appropriate validity checks for it.
7073 * We do permissions checks here, however.
7074 */
7075 static ObjectAddress
ATAddForeignKeyConstraint(AlteredTableInfo * tab,Relation rel,Constraint * fkconstraint,LOCKMODE lockmode)7076 ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
7077 Constraint *fkconstraint, LOCKMODE lockmode)
7078 {
7079 Relation pkrel;
7080 int16 pkattnum[INDEX_MAX_KEYS];
7081 int16 fkattnum[INDEX_MAX_KEYS];
7082 Oid pktypoid[INDEX_MAX_KEYS];
7083 Oid fktypoid[INDEX_MAX_KEYS];
7084 Oid opclasses[INDEX_MAX_KEYS];
7085 Oid pfeqoperators[INDEX_MAX_KEYS];
7086 Oid ppeqoperators[INDEX_MAX_KEYS];
7087 Oid ffeqoperators[INDEX_MAX_KEYS];
7088 int i;
7089 int numfks,
7090 numpks;
7091 Oid indexOid;
7092 Oid constrOid;
7093 bool old_check_ok;
7094 ObjectAddress address;
7095 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
7096
7097 /*
7098 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
7099 * delete rows out from under us.
7100 */
7101 if (OidIsValid(fkconstraint->old_pktable_oid))
7102 pkrel = heap_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
7103 else
7104 pkrel = heap_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
7105
7106 /*
7107 * Validity checks (permission checks wait till we have the column
7108 * numbers)
7109 */
7110 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7111 ereport(ERROR,
7112 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7113 errmsg("cannot reference partitioned table \"%s\"",
7114 RelationGetRelationName(pkrel))));
7115
7116 if (pkrel->rd_rel->relkind != RELKIND_RELATION)
7117 ereport(ERROR,
7118 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7119 errmsg("referenced relation \"%s\" is not a table",
7120 RelationGetRelationName(pkrel))));
7121
7122 if (!allowSystemTableMods && IsSystemRelation(pkrel))
7123 ereport(ERROR,
7124 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
7125 errmsg("permission denied: \"%s\" is a system catalog",
7126 RelationGetRelationName(pkrel))));
7127
7128 /*
7129 * References from permanent or unlogged tables to temp tables, and from
7130 * permanent tables to unlogged tables, are disallowed because the
7131 * referenced data can vanish out from under us. References from temp
7132 * tables to any other table type are also disallowed, because other
7133 * backends might need to run the RI triggers on the perm table, but they
7134 * can't reliably see tuples in the local buffers of other backends.
7135 */
7136 switch (rel->rd_rel->relpersistence)
7137 {
7138 case RELPERSISTENCE_PERMANENT:
7139 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
7140 ereport(ERROR,
7141 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7142 errmsg("constraints on permanent tables may reference only permanent tables")));
7143 break;
7144 case RELPERSISTENCE_UNLOGGED:
7145 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT
7146 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
7147 ereport(ERROR,
7148 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7149 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
7150 break;
7151 case RELPERSISTENCE_TEMP:
7152 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
7153 ereport(ERROR,
7154 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7155 errmsg("constraints on temporary tables may reference only temporary tables")));
7156 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
7157 ereport(ERROR,
7158 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7159 errmsg("constraints on temporary tables must involve temporary tables of this session")));
7160 break;
7161 }
7162
7163 /*
7164 * Look up the referencing attributes to make sure they exist, and record
7165 * their attnums and type OIDs.
7166 */
7167 MemSet(pkattnum, 0, sizeof(pkattnum));
7168 MemSet(fkattnum, 0, sizeof(fkattnum));
7169 MemSet(pktypoid, 0, sizeof(pktypoid));
7170 MemSet(fktypoid, 0, sizeof(fktypoid));
7171 MemSet(opclasses, 0, sizeof(opclasses));
7172 MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
7173 MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
7174 MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
7175
7176 numfks = transformColumnNameList(RelationGetRelid(rel),
7177 fkconstraint->fk_attrs,
7178 fkattnum, fktypoid);
7179
7180 /*
7181 * If the attribute list for the referenced table was omitted, lookup the
7182 * definition of the primary key and use it. Otherwise, validate the
7183 * supplied attribute list. In either case, discover the index OID and
7184 * index opclasses, and the attnums and type OIDs of the attributes.
7185 */
7186 if (fkconstraint->pk_attrs == NIL)
7187 {
7188 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
7189 &fkconstraint->pk_attrs,
7190 pkattnum, pktypoid,
7191 opclasses);
7192 }
7193 else
7194 {
7195 numpks = transformColumnNameList(RelationGetRelid(pkrel),
7196 fkconstraint->pk_attrs,
7197 pkattnum, pktypoid);
7198 /* Look for an index matching the column list */
7199 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
7200 opclasses);
7201 }
7202
7203 /*
7204 * Now we can check permissions.
7205 */
7206 checkFkeyPermissions(pkrel, pkattnum, numpks);
7207
7208 /*
7209 * Look up the equality operators to use in the constraint.
7210 *
7211 * Note that we have to be careful about the difference between the actual
7212 * PK column type and the opclass' declared input type, which might be
7213 * only binary-compatible with it. The declared opcintype is the right
7214 * thing to probe pg_amop with.
7215 */
7216 if (numfks != numpks)
7217 ereport(ERROR,
7218 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
7219 errmsg("number of referencing and referenced columns for foreign key disagree")));
7220
7221 /*
7222 * On the strength of a previous constraint, we might avoid scanning
7223 * tables to validate this one. See below.
7224 */
7225 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
7226 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
7227
7228 for (i = 0; i < numpks; i++)
7229 {
7230 Oid pktype = pktypoid[i];
7231 Oid fktype = fktypoid[i];
7232 Oid fktyped;
7233 HeapTuple cla_ht;
7234 Form_pg_opclass cla_tup;
7235 Oid amid;
7236 Oid opfamily;
7237 Oid opcintype;
7238 Oid pfeqop;
7239 Oid ppeqop;
7240 Oid ffeqop;
7241 int16 eqstrategy;
7242 Oid pfeqop_right;
7243
7244 /* We need several fields out of the pg_opclass entry */
7245 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
7246 if (!HeapTupleIsValid(cla_ht))
7247 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
7248 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
7249 amid = cla_tup->opcmethod;
7250 opfamily = cla_tup->opcfamily;
7251 opcintype = cla_tup->opcintype;
7252 ReleaseSysCache(cla_ht);
7253
7254 /*
7255 * Check it's a btree; currently this can never fail since no other
7256 * index AMs support unique indexes. If we ever did have other types
7257 * of unique indexes, we'd need a way to determine which operator
7258 * strategy number is equality. (Is it reasonable to insist that
7259 * every such index AM use btree's number for equality?)
7260 */
7261 if (amid != BTREE_AM_OID)
7262 elog(ERROR, "only b-tree indexes are supported for foreign keys");
7263 eqstrategy = BTEqualStrategyNumber;
7264
7265 /*
7266 * There had better be a primary equality operator for the index.
7267 * We'll use it for PK = PK comparisons.
7268 */
7269 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
7270 eqstrategy);
7271
7272 if (!OidIsValid(ppeqop))
7273 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
7274 eqstrategy, opcintype, opcintype, opfamily);
7275
7276 /*
7277 * Are there equality operators that take exactly the FK type? Assume
7278 * we should look through any domain here.
7279 */
7280 fktyped = getBaseType(fktype);
7281
7282 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
7283 eqstrategy);
7284 if (OidIsValid(pfeqop))
7285 {
7286 pfeqop_right = fktyped;
7287 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
7288 eqstrategy);
7289 }
7290 else
7291 {
7292 /* keep compiler quiet */
7293 pfeqop_right = InvalidOid;
7294 ffeqop = InvalidOid;
7295 }
7296
7297 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
7298 {
7299 /*
7300 * Otherwise, look for an implicit cast from the FK type to the
7301 * opcintype, and if found, use the primary equality operator.
7302 * This is a bit tricky because opcintype might be a polymorphic
7303 * type such as ANYARRAY or ANYENUM; so what we have to test is
7304 * whether the two actual column types can be concurrently cast to
7305 * that type. (Otherwise, we'd fail to reject combinations such
7306 * as int[] and point[].)
7307 */
7308 Oid input_typeids[2];
7309 Oid target_typeids[2];
7310
7311 input_typeids[0] = pktype;
7312 input_typeids[1] = fktype;
7313 target_typeids[0] = opcintype;
7314 target_typeids[1] = opcintype;
7315 if (can_coerce_type(2, input_typeids, target_typeids,
7316 COERCION_IMPLICIT))
7317 {
7318 pfeqop = ffeqop = ppeqop;
7319 pfeqop_right = opcintype;
7320 }
7321 }
7322
7323 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
7324 ereport(ERROR,
7325 (errcode(ERRCODE_DATATYPE_MISMATCH),
7326 errmsg("foreign key constraint \"%s\" "
7327 "cannot be implemented",
7328 fkconstraint->conname),
7329 errdetail("Key columns \"%s\" and \"%s\" "
7330 "are of incompatible types: %s and %s.",
7331 strVal(list_nth(fkconstraint->fk_attrs, i)),
7332 strVal(list_nth(fkconstraint->pk_attrs, i)),
7333 format_type_be(fktype),
7334 format_type_be(pktype))));
7335
7336 if (old_check_ok)
7337 {
7338 /*
7339 * When a pfeqop changes, revalidate the constraint. We could
7340 * permit intra-opfamily changes, but that adds subtle complexity
7341 * without any concrete benefit for core types. We need not
7342 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
7343 */
7344 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
7345 old_pfeqop_item = lnext(old_pfeqop_item);
7346 }
7347 if (old_check_ok)
7348 {
7349 Oid old_fktype;
7350 Oid new_fktype;
7351 CoercionPathType old_pathtype;
7352 CoercionPathType new_pathtype;
7353 Oid old_castfunc;
7354 Oid new_castfunc;
7355
7356 /*
7357 * Identify coercion pathways from each of the old and new FK-side
7358 * column types to the right (foreign) operand type of the pfeqop.
7359 * We may assume that pg_constraint.conkey is not changing.
7360 */
7361 old_fktype = tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid;
7362 new_fktype = fktype;
7363 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
7364 &old_castfunc);
7365 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
7366 &new_castfunc);
7367
7368 /*
7369 * Upon a change to the cast from the FK column to its pfeqop
7370 * operand, revalidate the constraint. For this evaluation, a
7371 * binary coercion cast is equivalent to no cast at all. While
7372 * type implementors should design implicit casts with an eye
7373 * toward consistency of operations like equality, we cannot
7374 * assume here that they have done so.
7375 *
7376 * A function with a polymorphic argument could change behavior
7377 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
7378 * when the cast destination is polymorphic, we only avoid
7379 * revalidation if the input type has not changed at all. Given
7380 * just the core data types and operator classes, this requirement
7381 * prevents no would-be optimizations.
7382 *
7383 * If the cast converts from a base type to a domain thereon, then
7384 * that domain type must be the opcintype of the unique index.
7385 * Necessarily, the primary key column must then be of the domain
7386 * type. Since the constraint was previously valid, all values on
7387 * the foreign side necessarily exist on the primary side and in
7388 * turn conform to the domain. Consequently, we need not treat
7389 * domains specially here.
7390 *
7391 * Since we require that all collations share the same notion of
7392 * equality (which they do, because texteq reduces to bitwise
7393 * equality), we don't compare collation here.
7394 *
7395 * We need not directly consider the PK type. It's necessarily
7396 * binary coercible to the opcintype of the unique index column,
7397 * and ri_triggers.c will only deal with PK datums in terms of
7398 * that opcintype. Changing the opcintype also changes pfeqop.
7399 */
7400 old_check_ok = (new_pathtype == old_pathtype &&
7401 new_castfunc == old_castfunc &&
7402 (!IsPolymorphicType(pfeqop_right) ||
7403 new_fktype == old_fktype));
7404 }
7405
7406 pfeqoperators[i] = pfeqop;
7407 ppeqoperators[i] = ppeqop;
7408 ffeqoperators[i] = ffeqop;
7409 }
7410
7411 /*
7412 * Record the FK constraint in pg_constraint.
7413 */
7414 constrOid = CreateConstraintEntry(fkconstraint->conname,
7415 RelationGetNamespace(rel),
7416 CONSTRAINT_FOREIGN,
7417 fkconstraint->deferrable,
7418 fkconstraint->initdeferred,
7419 fkconstraint->initially_valid,
7420 RelationGetRelid(rel),
7421 fkattnum,
7422 numfks,
7423 InvalidOid, /* not a domain constraint */
7424 indexOid,
7425 RelationGetRelid(pkrel),
7426 pkattnum,
7427 pfeqoperators,
7428 ppeqoperators,
7429 ffeqoperators,
7430 numpks,
7431 fkconstraint->fk_upd_action,
7432 fkconstraint->fk_del_action,
7433 fkconstraint->fk_matchtype,
7434 NULL, /* no exclusion constraint */
7435 NULL, /* no check constraint */
7436 NULL,
7437 NULL,
7438 true, /* islocal */
7439 0, /* inhcount */
7440 true, /* isnoinherit */
7441 false); /* is_internal */
7442 ObjectAddressSet(address, ConstraintRelationId, constrOid);
7443
7444 /*
7445 * Create the triggers that will enforce the constraint.
7446 */
7447 createForeignKeyTriggers(rel, RelationGetRelid(pkrel), fkconstraint,
7448 constrOid, indexOid);
7449
7450 /*
7451 * Tell Phase 3 to check that the constraint is satisfied by existing
7452 * rows. We can skip this during table creation, when requested explicitly
7453 * by specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
7454 * recreating a constraint following a SET DATA TYPE operation that did
7455 * not impugn its validity.
7456 */
7457 if (!old_check_ok && !fkconstraint->skip_validation)
7458 {
7459 NewConstraint *newcon;
7460
7461 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
7462 newcon->name = fkconstraint->conname;
7463 newcon->contype = CONSTR_FOREIGN;
7464 newcon->refrelid = RelationGetRelid(pkrel);
7465 newcon->refindid = indexOid;
7466 newcon->conid = constrOid;
7467 newcon->qual = (Node *) fkconstraint;
7468
7469 tab->constraints = lappend(tab->constraints, newcon);
7470 }
7471
7472 /*
7473 * Close pk table, but keep lock until we've committed.
7474 */
7475 heap_close(pkrel, NoLock);
7476
7477 return address;
7478 }
7479
7480 /*
7481 * ALTER TABLE ALTER CONSTRAINT
7482 *
7483 * Update the attributes of a constraint.
7484 *
7485 * Currently only works for Foreign Key constraints.
7486 * Foreign keys do not inherit, so we purposely ignore the
7487 * recursion bit here, but we keep the API the same for when
7488 * other constraint types are supported.
7489 *
7490 * If the constraint is modified, returns its address; otherwise, return
7491 * InvalidObjectAddress.
7492 */
7493 static ObjectAddress
ATExecAlterConstraint(Relation rel,AlterTableCmd * cmd,bool recurse,bool recursing,LOCKMODE lockmode)7494 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
7495 bool recurse, bool recursing, LOCKMODE lockmode)
7496 {
7497 Constraint *cmdcon;
7498 Relation conrel;
7499 SysScanDesc scan;
7500 ScanKeyData key;
7501 HeapTuple contuple;
7502 Form_pg_constraint currcon = NULL;
7503 bool found = false;
7504 ObjectAddress address;
7505
7506 cmdcon = castNode(Constraint, cmd->def);
7507
7508 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
7509
7510 /*
7511 * Find and check the target constraint
7512 */
7513 ScanKeyInit(&key,
7514 Anum_pg_constraint_conrelid,
7515 BTEqualStrategyNumber, F_OIDEQ,
7516 ObjectIdGetDatum(RelationGetRelid(rel)));
7517 scan = systable_beginscan(conrel, ConstraintRelidIndexId,
7518 true, NULL, 1, &key);
7519
7520 while (HeapTupleIsValid(contuple = systable_getnext(scan)))
7521 {
7522 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
7523 if (strcmp(NameStr(currcon->conname), cmdcon->conname) == 0)
7524 {
7525 found = true;
7526 break;
7527 }
7528 }
7529
7530 if (!found)
7531 ereport(ERROR,
7532 (errcode(ERRCODE_UNDEFINED_OBJECT),
7533 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
7534 cmdcon->conname, RelationGetRelationName(rel))));
7535
7536 if (currcon->contype != CONSTRAINT_FOREIGN)
7537 ereport(ERROR,
7538 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7539 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
7540 cmdcon->conname, RelationGetRelationName(rel))));
7541
7542 if (currcon->condeferrable != cmdcon->deferrable ||
7543 currcon->condeferred != cmdcon->initdeferred)
7544 {
7545 HeapTuple copyTuple;
7546 HeapTuple tgtuple;
7547 Form_pg_constraint copy_con;
7548 List *otherrelids = NIL;
7549 ScanKeyData tgkey;
7550 SysScanDesc tgscan;
7551 Relation tgrel;
7552 ListCell *lc;
7553
7554 /*
7555 * Now update the catalog, while we have the door open.
7556 */
7557 copyTuple = heap_copytuple(contuple);
7558 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
7559 copy_con->condeferrable = cmdcon->deferrable;
7560 copy_con->condeferred = cmdcon->initdeferred;
7561 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
7562
7563 InvokeObjectPostAlterHook(ConstraintRelationId,
7564 HeapTupleGetOid(contuple), 0);
7565
7566 heap_freetuple(copyTuple);
7567
7568 /*
7569 * Now we need to update the multiple entries in pg_trigger that
7570 * implement the constraint.
7571 */
7572 tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
7573
7574 ScanKeyInit(&tgkey,
7575 Anum_pg_trigger_tgconstraint,
7576 BTEqualStrategyNumber, F_OIDEQ,
7577 ObjectIdGetDatum(HeapTupleGetOid(contuple)));
7578
7579 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
7580 NULL, 1, &tgkey);
7581
7582 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
7583 {
7584 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
7585 Form_pg_trigger copy_tg;
7586
7587 /*
7588 * Remember OIDs of other relation(s) involved in FK constraint.
7589 * (Note: it's likely that we could skip forcing a relcache inval
7590 * for other rels that don't have a trigger whose properties
7591 * change, but let's be conservative.)
7592 */
7593 if (tgform->tgrelid != RelationGetRelid(rel))
7594 otherrelids = list_append_unique_oid(otherrelids,
7595 tgform->tgrelid);
7596
7597 /*
7598 * Update deferrability of RI_FKey_noaction_del,
7599 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
7600 * triggers, but not others; see createForeignKeyTriggers and
7601 * CreateFKCheckTrigger.
7602 */
7603 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
7604 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
7605 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
7606 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
7607 continue;
7608
7609 copyTuple = heap_copytuple(tgtuple);
7610 copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
7611
7612 copy_tg->tgdeferrable = cmdcon->deferrable;
7613 copy_tg->tginitdeferred = cmdcon->initdeferred;
7614 CatalogTupleUpdate(tgrel, ©Tuple->t_self, copyTuple);
7615
7616 InvokeObjectPostAlterHook(TriggerRelationId,
7617 HeapTupleGetOid(tgtuple), 0);
7618
7619 heap_freetuple(copyTuple);
7620 }
7621
7622 systable_endscan(tgscan);
7623
7624 heap_close(tgrel, RowExclusiveLock);
7625
7626 /*
7627 * Invalidate relcache so that others see the new attributes. We must
7628 * inval both the named rel and any others having relevant triggers.
7629 * (At present there should always be exactly one other rel, but
7630 * there's no need to hard-wire such an assumption here.)
7631 */
7632 CacheInvalidateRelcache(rel);
7633 foreach(lc, otherrelids)
7634 {
7635 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
7636 }
7637
7638 ObjectAddressSet(address, ConstraintRelationId,
7639 HeapTupleGetOid(contuple));
7640 }
7641 else
7642 address = InvalidObjectAddress;
7643
7644 systable_endscan(scan);
7645
7646 heap_close(conrel, RowExclusiveLock);
7647
7648 return address;
7649 }
7650
7651 /*
7652 * ALTER TABLE VALIDATE CONSTRAINT
7653 *
7654 * XXX The reason we handle recursion here rather than at Phase 1 is because
7655 * there's no good way to skip recursing when handling foreign keys: there is
7656 * no need to lock children in that case, yet we wouldn't be able to avoid
7657 * doing so at that level.
7658 *
7659 * Return value is the address of the validated constraint. If the constraint
7660 * was already validated, InvalidObjectAddress is returned.
7661 */
7662 static ObjectAddress
ATExecValidateConstraint(List ** wqueue,Relation rel,char * constrName,bool recurse,bool recursing,LOCKMODE lockmode)7663 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
7664 bool recurse, bool recursing, LOCKMODE lockmode)
7665 {
7666 Relation conrel;
7667 SysScanDesc scan;
7668 ScanKeyData key;
7669 HeapTuple tuple;
7670 Form_pg_constraint con = NULL;
7671 bool found = false;
7672 ObjectAddress address;
7673
7674 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
7675
7676 /*
7677 * Find and check the target constraint
7678 */
7679 ScanKeyInit(&key,
7680 Anum_pg_constraint_conrelid,
7681 BTEqualStrategyNumber, F_OIDEQ,
7682 ObjectIdGetDatum(RelationGetRelid(rel)));
7683 scan = systable_beginscan(conrel, ConstraintRelidIndexId,
7684 true, NULL, 1, &key);
7685
7686 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
7687 {
7688 con = (Form_pg_constraint) GETSTRUCT(tuple);
7689 if (strcmp(NameStr(con->conname), constrName) == 0)
7690 {
7691 found = true;
7692 break;
7693 }
7694 }
7695
7696 if (!found)
7697 ereport(ERROR,
7698 (errcode(ERRCODE_UNDEFINED_OBJECT),
7699 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
7700 constrName, RelationGetRelationName(rel))));
7701
7702 if (con->contype != CONSTRAINT_FOREIGN &&
7703 con->contype != CONSTRAINT_CHECK)
7704 ereport(ERROR,
7705 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7706 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
7707 constrName, RelationGetRelationName(rel))));
7708
7709 if (!con->convalidated)
7710 {
7711 AlteredTableInfo *tab;
7712 HeapTuple copyTuple;
7713 Form_pg_constraint copy_con;
7714
7715 if (con->contype == CONSTRAINT_FOREIGN)
7716 {
7717 NewConstraint *newcon;
7718 Constraint *fkconstraint;
7719
7720 /* Queue validation for phase 3 */
7721 fkconstraint = makeNode(Constraint);
7722 /* for now this is all we need */
7723 fkconstraint->conname = constrName;
7724
7725 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
7726 newcon->name = constrName;
7727 newcon->contype = CONSTR_FOREIGN;
7728 newcon->refrelid = con->confrelid;
7729 newcon->refindid = con->conindid;
7730 newcon->conid = HeapTupleGetOid(tuple);
7731 newcon->qual = (Node *) fkconstraint;
7732
7733 /* Find or create work queue entry for this table */
7734 tab = ATGetQueueEntry(wqueue, rel);
7735 tab->constraints = lappend(tab->constraints, newcon);
7736
7737 /*
7738 * Foreign keys do not inherit, so we purposely ignore the
7739 * recursion bit here
7740 */
7741 }
7742 else if (con->contype == CONSTRAINT_CHECK)
7743 {
7744 List *children = NIL;
7745 ListCell *child;
7746 NewConstraint *newcon;
7747 bool isnull;
7748 Datum val;
7749 char *conbin;
7750
7751 /*
7752 * If we're recursing, the parent has already done this, so skip
7753 * it. Also, if the constraint is a NO INHERIT constraint, we
7754 * shouldn't try to look for it in the children.
7755 */
7756 if (!recursing && !con->connoinherit)
7757 children = find_all_inheritors(RelationGetRelid(rel),
7758 lockmode, NULL);
7759
7760 /*
7761 * For CHECK constraints, we must ensure that we only mark the
7762 * constraint as validated on the parent if it's already validated
7763 * on the children.
7764 *
7765 * We recurse before validating on the parent, to reduce risk of
7766 * deadlocks.
7767 */
7768 foreach(child, children)
7769 {
7770 Oid childoid = lfirst_oid(child);
7771 Relation childrel;
7772
7773 if (childoid == RelationGetRelid(rel))
7774 continue;
7775
7776 /*
7777 * If we are told not to recurse, there had better not be any
7778 * child tables, because we can't mark the constraint on the
7779 * parent valid unless it is valid for all child tables.
7780 */
7781 if (!recurse)
7782 ereport(ERROR,
7783 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7784 errmsg("constraint must be validated on child tables too")));
7785
7786 /* find_all_inheritors already got lock */
7787 childrel = heap_open(childoid, NoLock);
7788
7789 ATExecValidateConstraint(wqueue, childrel, constrName, false,
7790 true, lockmode);
7791 heap_close(childrel, NoLock);
7792 }
7793
7794 /* Queue validation for phase 3 */
7795 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
7796 newcon->name = constrName;
7797 newcon->contype = CONSTR_CHECK;
7798 newcon->refrelid = InvalidOid;
7799 newcon->refindid = InvalidOid;
7800 newcon->conid = HeapTupleGetOid(tuple);
7801
7802 val = SysCacheGetAttr(CONSTROID, tuple,
7803 Anum_pg_constraint_conbin, &isnull);
7804 if (isnull)
7805 elog(ERROR, "null conbin for constraint %u",
7806 HeapTupleGetOid(tuple));
7807
7808 conbin = TextDatumGetCString(val);
7809 newcon->qual = (Node *) stringToNode(conbin);
7810
7811 /* Find or create work queue entry for this table */
7812 tab = ATGetQueueEntry(wqueue, rel);
7813 tab->constraints = lappend(tab->constraints, newcon);
7814
7815 /*
7816 * Invalidate relcache so that others see the new validated
7817 * constraint.
7818 */
7819 CacheInvalidateRelcache(rel);
7820 }
7821
7822 /*
7823 * Now update the catalog, while we have the door open.
7824 */
7825 copyTuple = heap_copytuple(tuple);
7826 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
7827 copy_con->convalidated = true;
7828 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
7829
7830 InvokeObjectPostAlterHook(ConstraintRelationId,
7831 HeapTupleGetOid(tuple), 0);
7832
7833 heap_freetuple(copyTuple);
7834
7835 ObjectAddressSet(address, ConstraintRelationId,
7836 HeapTupleGetOid(tuple));
7837 }
7838 else
7839 address = InvalidObjectAddress; /* already validated */
7840
7841 systable_endscan(scan);
7842
7843 heap_close(conrel, RowExclusiveLock);
7844
7845 return address;
7846 }
7847
7848
7849 /*
7850 * transformColumnNameList - transform list of column names
7851 *
7852 * Lookup each name and return its attnum and type OID
7853 */
7854 static int
transformColumnNameList(Oid relId,List * colList,int16 * attnums,Oid * atttypids)7855 transformColumnNameList(Oid relId, List *colList,
7856 int16 *attnums, Oid *atttypids)
7857 {
7858 ListCell *l;
7859 int attnum;
7860
7861 attnum = 0;
7862 foreach(l, colList)
7863 {
7864 char *attname = strVal(lfirst(l));
7865 HeapTuple atttuple;
7866
7867 atttuple = SearchSysCacheAttName(relId, attname);
7868 if (!HeapTupleIsValid(atttuple))
7869 ereport(ERROR,
7870 (errcode(ERRCODE_UNDEFINED_COLUMN),
7871 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
7872 attname)));
7873 if (attnum >= INDEX_MAX_KEYS)
7874 ereport(ERROR,
7875 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7876 errmsg("cannot have more than %d keys in a foreign key",
7877 INDEX_MAX_KEYS)));
7878 attnums[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->attnum;
7879 atttypids[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
7880 ReleaseSysCache(atttuple);
7881 attnum++;
7882 }
7883
7884 return attnum;
7885 }
7886
7887 /*
7888 * transformFkeyGetPrimaryKey -
7889 *
7890 * Look up the names, attnums, and types of the primary key attributes
7891 * for the pkrel. Also return the index OID and index opclasses of the
7892 * index supporting the primary key.
7893 *
7894 * All parameters except pkrel are output parameters. Also, the function
7895 * return value is the number of attributes in the primary key.
7896 *
7897 * Used when the column list in the REFERENCES specification is omitted.
7898 */
7899 static int
transformFkeyGetPrimaryKey(Relation pkrel,Oid * indexOid,List ** attnamelist,int16 * attnums,Oid * atttypids,Oid * opclasses)7900 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
7901 List **attnamelist,
7902 int16 *attnums, Oid *atttypids,
7903 Oid *opclasses)
7904 {
7905 List *indexoidlist;
7906 ListCell *indexoidscan;
7907 HeapTuple indexTuple = NULL;
7908 Form_pg_index indexStruct = NULL;
7909 Datum indclassDatum;
7910 bool isnull;
7911 oidvector *indclass;
7912 int i;
7913
7914 /*
7915 * Get the list of index OIDs for the table from the relcache, and look up
7916 * each one in the pg_index syscache until we find one marked primary key
7917 * (hopefully there isn't more than one such). Insist it's valid, too.
7918 */
7919 *indexOid = InvalidOid;
7920
7921 indexoidlist = RelationGetIndexList(pkrel);
7922
7923 foreach(indexoidscan, indexoidlist)
7924 {
7925 Oid indexoid = lfirst_oid(indexoidscan);
7926
7927 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7928 if (!HeapTupleIsValid(indexTuple))
7929 elog(ERROR, "cache lookup failed for index %u", indexoid);
7930 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
7931 if (indexStruct->indisprimary && IndexIsValid(indexStruct))
7932 {
7933 /*
7934 * Refuse to use a deferrable primary key. This is per SQL spec,
7935 * and there would be a lot of interesting semantic problems if we
7936 * tried to allow it.
7937 */
7938 if (!indexStruct->indimmediate)
7939 ereport(ERROR,
7940 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7941 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
7942 RelationGetRelationName(pkrel))));
7943
7944 *indexOid = indexoid;
7945 break;
7946 }
7947 ReleaseSysCache(indexTuple);
7948 }
7949
7950 list_free(indexoidlist);
7951
7952 /*
7953 * Check that we found it
7954 */
7955 if (!OidIsValid(*indexOid))
7956 ereport(ERROR,
7957 (errcode(ERRCODE_UNDEFINED_OBJECT),
7958 errmsg("there is no primary key for referenced table \"%s\"",
7959 RelationGetRelationName(pkrel))));
7960
7961 /* Must get indclass the hard way */
7962 indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
7963 Anum_pg_index_indclass, &isnull);
7964 Assert(!isnull);
7965 indclass = (oidvector *) DatumGetPointer(indclassDatum);
7966
7967 /*
7968 * Now build the list of PK attributes from the indkey definition (we
7969 * assume a primary key cannot have expressional elements)
7970 */
7971 *attnamelist = NIL;
7972 for (i = 0; i < indexStruct->indnatts; i++)
7973 {
7974 int pkattno = indexStruct->indkey.values[i];
7975
7976 attnums[i] = pkattno;
7977 atttypids[i] = attnumTypeId(pkrel, pkattno);
7978 opclasses[i] = indclass->values[i];
7979 *attnamelist = lappend(*attnamelist,
7980 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
7981 }
7982
7983 ReleaseSysCache(indexTuple);
7984
7985 return i;
7986 }
7987
7988 /*
7989 * transformFkeyCheckAttrs -
7990 *
7991 * Make sure that the attributes of a referenced table belong to a unique
7992 * (or primary key) constraint. Return the OID of the index supporting
7993 * the constraint, as well as the opclasses associated with the index
7994 * columns.
7995 */
7996 static Oid
transformFkeyCheckAttrs(Relation pkrel,int numattrs,int16 * attnums,Oid * opclasses)7997 transformFkeyCheckAttrs(Relation pkrel,
7998 int numattrs, int16 *attnums,
7999 Oid *opclasses) /* output parameter */
8000 {
8001 Oid indexoid = InvalidOid;
8002 bool found = false;
8003 bool found_deferrable = false;
8004 List *indexoidlist;
8005 ListCell *indexoidscan;
8006 int i,
8007 j;
8008
8009 /*
8010 * Reject duplicate appearances of columns in the referenced-columns list.
8011 * Such a case is forbidden by the SQL standard, and even if we thought it
8012 * useful to allow it, there would be ambiguity about how to match the
8013 * list to unique indexes (in particular, it'd be unclear which index
8014 * opclass goes with which FK column).
8015 */
8016 for (i = 0; i < numattrs; i++)
8017 {
8018 for (j = i + 1; j < numattrs; j++)
8019 {
8020 if (attnums[i] == attnums[j])
8021 ereport(ERROR,
8022 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
8023 errmsg("foreign key referenced-columns list must not contain duplicates")));
8024 }
8025 }
8026
8027 /*
8028 * Get the list of index OIDs for the table from the relcache, and look up
8029 * each one in the pg_index syscache, and match unique indexes to the list
8030 * of attnums we are given.
8031 */
8032 indexoidlist = RelationGetIndexList(pkrel);
8033
8034 foreach(indexoidscan, indexoidlist)
8035 {
8036 HeapTuple indexTuple;
8037 Form_pg_index indexStruct;
8038
8039 indexoid = lfirst_oid(indexoidscan);
8040 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
8041 if (!HeapTupleIsValid(indexTuple))
8042 elog(ERROR, "cache lookup failed for index %u", indexoid);
8043 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
8044
8045 /*
8046 * Must have the right number of columns; must be unique and not a
8047 * partial index; forget it if there are any expressions, too. Invalid
8048 * indexes are out as well.
8049 */
8050 if (indexStruct->indnatts == numattrs &&
8051 indexStruct->indisunique &&
8052 IndexIsValid(indexStruct) &&
8053 heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
8054 heap_attisnull(indexTuple, Anum_pg_index_indexprs))
8055 {
8056 Datum indclassDatum;
8057 bool isnull;
8058 oidvector *indclass;
8059
8060 /* Must get indclass the hard way */
8061 indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
8062 Anum_pg_index_indclass, &isnull);
8063 Assert(!isnull);
8064 indclass = (oidvector *) DatumGetPointer(indclassDatum);
8065
8066 /*
8067 * The given attnum list may match the index columns in any order.
8068 * Check for a match, and extract the appropriate opclasses while
8069 * we're at it.
8070 *
8071 * We know that attnums[] is duplicate-free per the test at the
8072 * start of this function, and we checked above that the number of
8073 * index columns agrees, so if we find a match for each attnums[]
8074 * entry then we must have a one-to-one match in some order.
8075 */
8076 for (i = 0; i < numattrs; i++)
8077 {
8078 found = false;
8079 for (j = 0; j < numattrs; j++)
8080 {
8081 if (attnums[i] == indexStruct->indkey.values[j])
8082 {
8083 opclasses[i] = indclass->values[j];
8084 found = true;
8085 break;
8086 }
8087 }
8088 if (!found)
8089 break;
8090 }
8091
8092 /*
8093 * Refuse to use a deferrable unique/primary key. This is per SQL
8094 * spec, and there would be a lot of interesting semantic problems
8095 * if we tried to allow it.
8096 */
8097 if (found && !indexStruct->indimmediate)
8098 {
8099 /*
8100 * Remember that we found an otherwise matching index, so that
8101 * we can generate a more appropriate error message.
8102 */
8103 found_deferrable = true;
8104 found = false;
8105 }
8106 }
8107 ReleaseSysCache(indexTuple);
8108 if (found)
8109 break;
8110 }
8111
8112 if (!found)
8113 {
8114 if (found_deferrable)
8115 ereport(ERROR,
8116 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8117 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
8118 RelationGetRelationName(pkrel))));
8119 else
8120 ereport(ERROR,
8121 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
8122 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
8123 RelationGetRelationName(pkrel))));
8124 }
8125
8126 list_free(indexoidlist);
8127
8128 return indexoid;
8129 }
8130
8131 /*
8132 * findFkeyCast -
8133 *
8134 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
8135 * Caller has equal regard for binary coercibility and for an exact match.
8136 */
8137 static CoercionPathType
findFkeyCast(Oid targetTypeId,Oid sourceTypeId,Oid * funcid)8138 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
8139 {
8140 CoercionPathType ret;
8141
8142 if (targetTypeId == sourceTypeId)
8143 {
8144 ret = COERCION_PATH_RELABELTYPE;
8145 *funcid = InvalidOid;
8146 }
8147 else
8148 {
8149 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
8150 COERCION_IMPLICIT, funcid);
8151 if (ret == COERCION_PATH_NONE)
8152 /* A previously-relied-upon cast is now gone. */
8153 elog(ERROR, "could not find cast from %u to %u",
8154 sourceTypeId, targetTypeId);
8155 }
8156
8157 return ret;
8158 }
8159
8160 /*
8161 * Permissions checks on the referenced table for ADD FOREIGN KEY
8162 *
8163 * Note: we have already checked that the user owns the referencing table,
8164 * else we'd have failed much earlier; no additional checks are needed for it.
8165 */
8166 static void
checkFkeyPermissions(Relation rel,int16 * attnums,int natts)8167 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
8168 {
8169 Oid roleid = GetUserId();
8170 AclResult aclresult;
8171 int i;
8172
8173 /* Okay if we have relation-level REFERENCES permission */
8174 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
8175 ACL_REFERENCES);
8176 if (aclresult == ACLCHECK_OK)
8177 return;
8178 /* Else we must have REFERENCES on each column */
8179 for (i = 0; i < natts; i++)
8180 {
8181 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
8182 roleid, ACL_REFERENCES);
8183 if (aclresult != ACLCHECK_OK)
8184 aclcheck_error(aclresult, ACL_KIND_CLASS,
8185 RelationGetRelationName(rel));
8186 }
8187 }
8188
8189 /*
8190 * Scan the existing rows in a table to verify they meet a proposed FK
8191 * constraint.
8192 *
8193 * Caller must have opened and locked both relations appropriately.
8194 */
8195 static void
validateForeignKeyConstraint(char * conname,Relation rel,Relation pkrel,Oid pkindOid,Oid constraintOid)8196 validateForeignKeyConstraint(char *conname,
8197 Relation rel,
8198 Relation pkrel,
8199 Oid pkindOid,
8200 Oid constraintOid)
8201 {
8202 HeapScanDesc scan;
8203 HeapTuple tuple;
8204 Trigger trig;
8205 Snapshot snapshot;
8206
8207 ereport(DEBUG1,
8208 (errmsg("validating foreign key constraint \"%s\"", conname)));
8209
8210 /*
8211 * Build a trigger call structure; we'll need it either way.
8212 */
8213 MemSet(&trig, 0, sizeof(trig));
8214 trig.tgoid = InvalidOid;
8215 trig.tgname = conname;
8216 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
8217 trig.tgisinternal = TRUE;
8218 trig.tgconstrrelid = RelationGetRelid(pkrel);
8219 trig.tgconstrindid = pkindOid;
8220 trig.tgconstraint = constraintOid;
8221 trig.tgdeferrable = FALSE;
8222 trig.tginitdeferred = FALSE;
8223 /* we needn't fill in remaining fields */
8224
8225 /*
8226 * See if we can do it with a single LEFT JOIN query. A FALSE result
8227 * indicates we must proceed with the fire-the-trigger method.
8228 */
8229 if (RI_Initial_Check(&trig, rel, pkrel))
8230 return;
8231
8232 /*
8233 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
8234 * if that tuple had just been inserted. If any of those fail, it should
8235 * ereport(ERROR) and that's that.
8236 */
8237 snapshot = RegisterSnapshot(GetLatestSnapshot());
8238 scan = heap_beginscan(rel, snapshot, 0, NULL);
8239
8240 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
8241 {
8242 FunctionCallInfoData fcinfo;
8243 TriggerData trigdata;
8244
8245 /*
8246 * Make a call to the trigger function
8247 *
8248 * No parameters are passed, but we do set a context
8249 */
8250 MemSet(&fcinfo, 0, sizeof(fcinfo));
8251
8252 /*
8253 * We assume RI_FKey_check_ins won't look at flinfo...
8254 */
8255 trigdata.type = T_TriggerData;
8256 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
8257 trigdata.tg_relation = rel;
8258 trigdata.tg_trigtuple = tuple;
8259 trigdata.tg_newtuple = NULL;
8260 trigdata.tg_trigger = &trig;
8261 trigdata.tg_trigtuplebuf = scan->rs_cbuf;
8262 trigdata.tg_newtuplebuf = InvalidBuffer;
8263
8264 fcinfo.context = (Node *) &trigdata;
8265
8266 RI_FKey_check_ins(&fcinfo);
8267 }
8268
8269 heap_endscan(scan);
8270 UnregisterSnapshot(snapshot);
8271 }
8272
8273 static void
CreateFKCheckTrigger(Oid myRelOid,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid,bool on_insert)8274 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
8275 Oid constraintOid, Oid indexOid, bool on_insert)
8276 {
8277 CreateTrigStmt *fk_trigger;
8278
8279 /*
8280 * Note: for a self-referential FK (referencing and referenced tables are
8281 * the same), it is important that the ON UPDATE action fires before the
8282 * CHECK action, since both triggers will fire on the same row during an
8283 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
8284 * state of the row. Triggers fire in name order, so we ensure this by
8285 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
8286 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
8287 */
8288 fk_trigger = makeNode(CreateTrigStmt);
8289 fk_trigger->trigname = "RI_ConstraintTrigger_c";
8290 fk_trigger->relation = NULL;
8291 fk_trigger->row = true;
8292 fk_trigger->timing = TRIGGER_TYPE_AFTER;
8293
8294 /* Either ON INSERT or ON UPDATE */
8295 if (on_insert)
8296 {
8297 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
8298 fk_trigger->events = TRIGGER_TYPE_INSERT;
8299 }
8300 else
8301 {
8302 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
8303 fk_trigger->events = TRIGGER_TYPE_UPDATE;
8304 }
8305
8306 fk_trigger->columns = NIL;
8307 fk_trigger->transitionRels = NIL;
8308 fk_trigger->whenClause = NULL;
8309 fk_trigger->isconstraint = true;
8310 fk_trigger->deferrable = fkconstraint->deferrable;
8311 fk_trigger->initdeferred = fkconstraint->initdeferred;
8312 fk_trigger->constrrel = NULL;
8313 fk_trigger->args = NIL;
8314
8315 (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid,
8316 indexOid, true);
8317
8318 /* Make changes-so-far visible */
8319 CommandCounterIncrement();
8320 }
8321
8322 /*
8323 * Create the triggers that implement an FK constraint.
8324 *
8325 * NB: if you change any trigger properties here, see also
8326 * ATExecAlterConstraint.
8327 */
8328 static void
createForeignKeyTriggers(Relation rel,Oid refRelOid,Constraint * fkconstraint,Oid constraintOid,Oid indexOid)8329 createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
8330 Oid constraintOid, Oid indexOid)
8331 {
8332 Oid myRelOid;
8333 CreateTrigStmt *fk_trigger;
8334
8335 myRelOid = RelationGetRelid(rel);
8336
8337 /* Make changes-so-far visible */
8338 CommandCounterIncrement();
8339
8340 /*
8341 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
8342 * DELETE action on the referenced table.
8343 */
8344 fk_trigger = makeNode(CreateTrigStmt);
8345 fk_trigger->trigname = "RI_ConstraintTrigger_a";
8346 fk_trigger->relation = NULL;
8347 fk_trigger->row = true;
8348 fk_trigger->timing = TRIGGER_TYPE_AFTER;
8349 fk_trigger->events = TRIGGER_TYPE_DELETE;
8350 fk_trigger->columns = NIL;
8351 fk_trigger->transitionRels = NIL;
8352 fk_trigger->whenClause = NULL;
8353 fk_trigger->isconstraint = true;
8354 fk_trigger->constrrel = NULL;
8355 switch (fkconstraint->fk_del_action)
8356 {
8357 case FKCONSTR_ACTION_NOACTION:
8358 fk_trigger->deferrable = fkconstraint->deferrable;
8359 fk_trigger->initdeferred = fkconstraint->initdeferred;
8360 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
8361 break;
8362 case FKCONSTR_ACTION_RESTRICT:
8363 fk_trigger->deferrable = false;
8364 fk_trigger->initdeferred = false;
8365 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
8366 break;
8367 case FKCONSTR_ACTION_CASCADE:
8368 fk_trigger->deferrable = false;
8369 fk_trigger->initdeferred = false;
8370 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
8371 break;
8372 case FKCONSTR_ACTION_SETNULL:
8373 fk_trigger->deferrable = false;
8374 fk_trigger->initdeferred = false;
8375 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
8376 break;
8377 case FKCONSTR_ACTION_SETDEFAULT:
8378 fk_trigger->deferrable = false;
8379 fk_trigger->initdeferred = false;
8380 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
8381 break;
8382 default:
8383 elog(ERROR, "unrecognized FK action type: %d",
8384 (int) fkconstraint->fk_del_action);
8385 break;
8386 }
8387 fk_trigger->args = NIL;
8388
8389 (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid,
8390 indexOid, true);
8391
8392 /* Make changes-so-far visible */
8393 CommandCounterIncrement();
8394
8395 /*
8396 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
8397 * UPDATE action on the referenced table.
8398 */
8399 fk_trigger = makeNode(CreateTrigStmt);
8400 fk_trigger->trigname = "RI_ConstraintTrigger_a";
8401 fk_trigger->relation = NULL;
8402 fk_trigger->row = true;
8403 fk_trigger->timing = TRIGGER_TYPE_AFTER;
8404 fk_trigger->events = TRIGGER_TYPE_UPDATE;
8405 fk_trigger->columns = NIL;
8406 fk_trigger->transitionRels = NIL;
8407 fk_trigger->whenClause = NULL;
8408 fk_trigger->isconstraint = true;
8409 fk_trigger->constrrel = NULL;
8410 switch (fkconstraint->fk_upd_action)
8411 {
8412 case FKCONSTR_ACTION_NOACTION:
8413 fk_trigger->deferrable = fkconstraint->deferrable;
8414 fk_trigger->initdeferred = fkconstraint->initdeferred;
8415 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
8416 break;
8417 case FKCONSTR_ACTION_RESTRICT:
8418 fk_trigger->deferrable = false;
8419 fk_trigger->initdeferred = false;
8420 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
8421 break;
8422 case FKCONSTR_ACTION_CASCADE:
8423 fk_trigger->deferrable = false;
8424 fk_trigger->initdeferred = false;
8425 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
8426 break;
8427 case FKCONSTR_ACTION_SETNULL:
8428 fk_trigger->deferrable = false;
8429 fk_trigger->initdeferred = false;
8430 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
8431 break;
8432 case FKCONSTR_ACTION_SETDEFAULT:
8433 fk_trigger->deferrable = false;
8434 fk_trigger->initdeferred = false;
8435 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
8436 break;
8437 default:
8438 elog(ERROR, "unrecognized FK action type: %d",
8439 (int) fkconstraint->fk_upd_action);
8440 break;
8441 }
8442 fk_trigger->args = NIL;
8443
8444 (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid,
8445 indexOid, true);
8446
8447 /* Make changes-so-far visible */
8448 CommandCounterIncrement();
8449
8450 /*
8451 * Build and execute CREATE CONSTRAINT TRIGGER statements for the CHECK
8452 * action for both INSERTs and UPDATEs on the referencing table.
8453 */
8454 CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
8455 indexOid, true);
8456 CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
8457 indexOid, false);
8458 }
8459
8460 /*
8461 * ALTER TABLE DROP CONSTRAINT
8462 *
8463 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
8464 */
8465 static void
ATExecDropConstraint(Relation rel,const char * constrName,DropBehavior behavior,bool recurse,bool recursing,bool missing_ok,LOCKMODE lockmode)8466 ATExecDropConstraint(Relation rel, const char *constrName,
8467 DropBehavior behavior,
8468 bool recurse, bool recursing,
8469 bool missing_ok, LOCKMODE lockmode)
8470 {
8471 List *children;
8472 ListCell *child;
8473 Relation conrel;
8474 Form_pg_constraint con;
8475 SysScanDesc scan;
8476 ScanKeyData key;
8477 HeapTuple tuple;
8478 bool found = false;
8479 bool is_no_inherit_constraint = false;
8480
8481 /* At top level, permission check was done in ATPrepCmd, else do it */
8482 if (recursing)
8483 ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8484
8485 conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
8486
8487 /*
8488 * Find and drop the target constraint
8489 */
8490 ScanKeyInit(&key,
8491 Anum_pg_constraint_conrelid,
8492 BTEqualStrategyNumber, F_OIDEQ,
8493 ObjectIdGetDatum(RelationGetRelid(rel)));
8494 scan = systable_beginscan(conrel, ConstraintRelidIndexId,
8495 true, NULL, 1, &key);
8496
8497 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
8498 {
8499 ObjectAddress conobj;
8500
8501 con = (Form_pg_constraint) GETSTRUCT(tuple);
8502
8503 if (strcmp(NameStr(con->conname), constrName) != 0)
8504 continue;
8505
8506 /* Don't drop inherited constraints */
8507 if (con->coninhcount > 0 && !recursing)
8508 ereport(ERROR,
8509 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8510 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
8511 constrName, RelationGetRelationName(rel))));
8512
8513 is_no_inherit_constraint = con->connoinherit;
8514
8515 /*
8516 * If it's a foreign-key constraint, we'd better lock the referenced
8517 * table and check that that's not in use, just as we've already done
8518 * for the constrained table (else we might, eg, be dropping a trigger
8519 * that has unfired events). But we can/must skip that in the
8520 * self-referential case.
8521 */
8522 if (con->contype == CONSTRAINT_FOREIGN &&
8523 con->confrelid != RelationGetRelid(rel))
8524 {
8525 Relation frel;
8526
8527 /* Must match lock taken by RemoveTriggerById: */
8528 frel = heap_open(con->confrelid, AccessExclusiveLock);
8529 CheckTableNotInUse(frel, "ALTER TABLE");
8530 heap_close(frel, NoLock);
8531 }
8532
8533 /*
8534 * Perform the actual constraint deletion
8535 */
8536 conobj.classId = ConstraintRelationId;
8537 conobj.objectId = HeapTupleGetOid(tuple);
8538 conobj.objectSubId = 0;
8539
8540 performDeletion(&conobj, behavior, 0);
8541
8542 found = true;
8543
8544 /* constraint found and dropped -- no need to keep looping */
8545 break;
8546 }
8547
8548 systable_endscan(scan);
8549
8550 if (!found)
8551 {
8552 if (!missing_ok)
8553 {
8554 ereport(ERROR,
8555 (errcode(ERRCODE_UNDEFINED_OBJECT),
8556 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
8557 constrName, RelationGetRelationName(rel))));
8558 }
8559 else
8560 {
8561 ereport(NOTICE,
8562 (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
8563 constrName, RelationGetRelationName(rel))));
8564 heap_close(conrel, RowExclusiveLock);
8565 return;
8566 }
8567 }
8568
8569 /*
8570 * Propagate to children as appropriate. Unlike most other ALTER
8571 * routines, we have to do this one level of recursion at a time; we can't
8572 * use find_all_inheritors to do it in one pass.
8573 */
8574 if (!is_no_inherit_constraint)
8575 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8576 else
8577 children = NIL;
8578
8579 /*
8580 * For a partitioned table, if partitions exist and we are told not to
8581 * recurse, it's a user error. It doesn't make sense to have a constraint
8582 * be defined only on the parent, especially if it's a partitioned table.
8583 */
8584 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
8585 children != NIL && !recurse)
8586 ereport(ERROR,
8587 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8588 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
8589 errhint("Do not specify the ONLY keyword.")));
8590
8591 foreach(child, children)
8592 {
8593 Oid childrelid = lfirst_oid(child);
8594 Relation childrel;
8595 HeapTuple copy_tuple;
8596
8597 /* find_inheritance_children already got lock */
8598 childrel = heap_open(childrelid, NoLock);
8599 CheckTableNotInUse(childrel, "ALTER TABLE");
8600
8601 ScanKeyInit(&key,
8602 Anum_pg_constraint_conrelid,
8603 BTEqualStrategyNumber, F_OIDEQ,
8604 ObjectIdGetDatum(childrelid));
8605 scan = systable_beginscan(conrel, ConstraintRelidIndexId,
8606 true, NULL, 1, &key);
8607
8608 /* scan for matching tuple - there should only be one */
8609 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
8610 {
8611 con = (Form_pg_constraint) GETSTRUCT(tuple);
8612
8613 /* Right now only CHECK constraints can be inherited */
8614 if (con->contype != CONSTRAINT_CHECK)
8615 continue;
8616
8617 if (strcmp(NameStr(con->conname), constrName) == 0)
8618 break;
8619 }
8620
8621 if (!HeapTupleIsValid(tuple))
8622 ereport(ERROR,
8623 (errcode(ERRCODE_UNDEFINED_OBJECT),
8624 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
8625 constrName,
8626 RelationGetRelationName(childrel))));
8627
8628 copy_tuple = heap_copytuple(tuple);
8629
8630 systable_endscan(scan);
8631
8632 con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
8633
8634 if (con->coninhcount <= 0) /* shouldn't happen */
8635 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
8636 childrelid, constrName);
8637
8638 if (recurse)
8639 {
8640 /*
8641 * If the child constraint has other definition sources, just
8642 * decrement its inheritance count; if not, recurse to delete it.
8643 */
8644 if (con->coninhcount == 1 && !con->conislocal)
8645 {
8646 /* Time to delete this child constraint, too */
8647 ATExecDropConstraint(childrel, constrName, behavior,
8648 true, true,
8649 false, lockmode);
8650 }
8651 else
8652 {
8653 /* Child constraint must survive my deletion */
8654 con->coninhcount--;
8655 CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
8656
8657 /* Make update visible */
8658 CommandCounterIncrement();
8659 }
8660 }
8661 else
8662 {
8663 /*
8664 * If we were told to drop ONLY in this table (no recursion), we
8665 * need to mark the inheritors' constraints as locally defined
8666 * rather than inherited.
8667 */
8668 con->coninhcount--;
8669 con->conislocal = true;
8670
8671 CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
8672
8673 /* Make update visible */
8674 CommandCounterIncrement();
8675 }
8676
8677 heap_freetuple(copy_tuple);
8678
8679 heap_close(childrel, NoLock);
8680 }
8681
8682 heap_close(conrel, RowExclusiveLock);
8683 }
8684
8685 /*
8686 * ALTER COLUMN TYPE
8687 */
8688 static void
ATPrepAlterColumnType(List ** wqueue,AlteredTableInfo * tab,Relation rel,bool recurse,bool recursing,AlterTableCmd * cmd,LOCKMODE lockmode)8689 ATPrepAlterColumnType(List **wqueue,
8690 AlteredTableInfo *tab, Relation rel,
8691 bool recurse, bool recursing,
8692 AlterTableCmd *cmd, LOCKMODE lockmode)
8693 {
8694 char *colName = cmd->name;
8695 ColumnDef *def = (ColumnDef *) cmd->def;
8696 TypeName *typeName = def->typeName;
8697 Node *transform = def->cooked_default;
8698 HeapTuple tuple;
8699 Form_pg_attribute attTup;
8700 AttrNumber attnum;
8701 Oid targettype;
8702 int32 targettypmod;
8703 Oid targetcollid;
8704 NewColumnValue *newval;
8705 ParseState *pstate = make_parsestate(NULL);
8706 AclResult aclresult;
8707 bool is_expr;
8708
8709 if (rel->rd_rel->reloftype && !recursing)
8710 ereport(ERROR,
8711 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8712 errmsg("cannot alter column type of typed table")));
8713
8714 /* lookup the attribute so we can check inheritance status */
8715 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8716 if (!HeapTupleIsValid(tuple))
8717 ereport(ERROR,
8718 (errcode(ERRCODE_UNDEFINED_COLUMN),
8719 errmsg("column \"%s\" of relation \"%s\" does not exist",
8720 colName, RelationGetRelationName(rel))));
8721 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8722 attnum = attTup->attnum;
8723
8724 /* Can't alter a system attribute */
8725 if (attnum <= 0)
8726 ereport(ERROR,
8727 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8728 errmsg("cannot alter system column \"%s\"",
8729 colName)));
8730
8731 /*
8732 * Don't alter inherited columns. At outer level, there had better not be
8733 * any inherited definition; when recursing, we assume this was checked at
8734 * the parent level (see below).
8735 */
8736 if (attTup->attinhcount > 0 && !recursing)
8737 ereport(ERROR,
8738 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8739 errmsg("cannot alter inherited column \"%s\"",
8740 colName)));
8741
8742 /* Don't alter columns used in the partition key */
8743 if (is_partition_attr(rel, attnum, &is_expr))
8744 ereport(ERROR,
8745 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8746 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
8747 colName, RelationGetRelationName(rel))));
8748
8749 /* Look up the target type */
8750 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
8751
8752 aclresult = pg_type_aclcheck(targettype, GetUserId(), ACL_USAGE);
8753 if (aclresult != ACLCHECK_OK)
8754 aclcheck_error_type(aclresult, targettype);
8755
8756 /* And the collation */
8757 targetcollid = GetColumnDefCollation(NULL, def, targettype);
8758
8759 /* make sure datatype is legal for a column */
8760 CheckAttributeType(colName, targettype, targetcollid,
8761 list_make1_oid(rel->rd_rel->reltype),
8762 false);
8763
8764 if (tab->relkind == RELKIND_RELATION ||
8765 tab->relkind == RELKIND_PARTITIONED_TABLE)
8766 {
8767 /*
8768 * Set up an expression to transform the old data value to the new
8769 * type. If a USING option was given, use the expression as
8770 * transformed by transformAlterTableStmt, else just take the old
8771 * value and try to coerce it. We do this first so that type
8772 * incompatibility can be detected before we waste effort, and because
8773 * we need the expression to be parsed against the original table row
8774 * type.
8775 */
8776 if (!transform)
8777 {
8778 transform = (Node *) makeVar(1, attnum,
8779 attTup->atttypid, attTup->atttypmod,
8780 attTup->attcollation,
8781 0);
8782 }
8783
8784 transform = coerce_to_target_type(pstate,
8785 transform, exprType(transform),
8786 targettype, targettypmod,
8787 COERCION_ASSIGNMENT,
8788 COERCE_IMPLICIT_CAST,
8789 -1);
8790 if (transform == NULL)
8791 {
8792 /* error text depends on whether USING was specified or not */
8793 if (def->cooked_default != NULL)
8794 ereport(ERROR,
8795 (errcode(ERRCODE_DATATYPE_MISMATCH),
8796 errmsg("result of USING clause for column \"%s\""
8797 " cannot be cast automatically to type %s",
8798 colName, format_type_be(targettype)),
8799 errhint("You might need to add an explicit cast.")));
8800 else
8801 ereport(ERROR,
8802 (errcode(ERRCODE_DATATYPE_MISMATCH),
8803 errmsg("column \"%s\" cannot be cast automatically to type %s",
8804 colName, format_type_be(targettype)),
8805 /* translator: USING is SQL, don't translate it */
8806 errhint("You might need to specify \"USING %s::%s\".",
8807 quote_identifier(colName),
8808 format_type_with_typemod(targettype,
8809 targettypmod))));
8810 }
8811
8812 /* Fix collations after all else */
8813 assign_expr_collations(pstate, transform);
8814
8815 /* Plan the expr now so we can accurately assess the need to rewrite. */
8816 transform = (Node *) expression_planner((Expr *) transform);
8817
8818 /*
8819 * Add a work queue item to make ATRewriteTable update the column
8820 * contents.
8821 */
8822 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8823 newval->attnum = attnum;
8824 newval->expr = (Expr *) transform;
8825
8826 tab->newvals = lappend(tab->newvals, newval);
8827 if (ATColumnChangeRequiresRewrite(transform, attnum))
8828 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
8829 }
8830 else if (transform)
8831 ereport(ERROR,
8832 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8833 errmsg("\"%s\" is not a table",
8834 RelationGetRelationName(rel))));
8835
8836 if (tab->relkind == RELKIND_COMPOSITE_TYPE ||
8837 tab->relkind == RELKIND_FOREIGN_TABLE)
8838 {
8839 /*
8840 * For composite types, do this check now. Tables will check it later
8841 * when the table is being rewritten.
8842 */
8843 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
8844 }
8845
8846 ReleaseSysCache(tuple);
8847
8848 /*
8849 * Recurse manually by queueing a new command for each child, if
8850 * necessary. We cannot apply ATSimpleRecursion here because we need to
8851 * remap attribute numbers in the USING expression, if any.
8852 *
8853 * If we are told not to recurse, there had better not be any child
8854 * tables; else the alter would put them out of step.
8855 */
8856 if (recurse)
8857 {
8858 Oid relid = RelationGetRelid(rel);
8859 List *child_oids,
8860 *child_numparents;
8861 ListCell *lo,
8862 *li;
8863
8864 child_oids = find_all_inheritors(relid, lockmode,
8865 &child_numparents);
8866
8867 /*
8868 * find_all_inheritors does the recursive search of the inheritance
8869 * hierarchy, so all we have to do is process all of the relids in the
8870 * list that it returns.
8871 */
8872 forboth(lo, child_oids, li, child_numparents)
8873 {
8874 Oid childrelid = lfirst_oid(lo);
8875 int numparents = lfirst_int(li);
8876 Relation childrel;
8877 HeapTuple childtuple;
8878 Form_pg_attribute childattTup;
8879
8880 if (childrelid == relid)
8881 continue;
8882
8883 /* find_all_inheritors already got lock */
8884 childrel = relation_open(childrelid, NoLock);
8885 CheckTableNotInUse(childrel, "ALTER TABLE");
8886
8887 /*
8888 * Verify that the child doesn't have any inherited definitions of
8889 * this column that came from outside this inheritance hierarchy.
8890 * (renameatt makes a similar test, though in a different way
8891 * because of its different recursion mechanism.)
8892 */
8893 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
8894 colName);
8895 if (!HeapTupleIsValid(childtuple))
8896 ereport(ERROR,
8897 (errcode(ERRCODE_UNDEFINED_COLUMN),
8898 errmsg("column \"%s\" of relation \"%s\" does not exist",
8899 colName, RelationGetRelationName(childrel))));
8900 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
8901
8902 if (childattTup->attinhcount > numparents)
8903 ereport(ERROR,
8904 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8905 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
8906 colName, RelationGetRelationName(childrel))));
8907
8908 ReleaseSysCache(childtuple);
8909
8910 /*
8911 * Remap the attribute numbers. If no USING expression was
8912 * specified, there is no need for this step.
8913 */
8914 if (def->cooked_default)
8915 {
8916 AttrNumber *attmap;
8917 bool found_whole_row;
8918
8919 /* create a copy to scribble on */
8920 cmd = copyObject(cmd);
8921
8922 attmap = convert_tuples_by_name_map(RelationGetDescr(childrel),
8923 RelationGetDescr(rel),
8924 gettext_noop("could not convert row type"));
8925 ((ColumnDef *) cmd->def)->cooked_default =
8926 map_variable_attnos(def->cooked_default,
8927 1, 0,
8928 attmap, RelationGetDescr(rel)->natts,
8929 InvalidOid, &found_whole_row);
8930 if (found_whole_row)
8931 ereport(ERROR,
8932 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8933 errmsg("cannot convert whole-row table reference"),
8934 errdetail("USING expression contains a whole-row table reference.")));
8935 pfree(attmap);
8936 }
8937 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
8938 relation_close(childrel, NoLock);
8939 }
8940 }
8941 else if (!recursing &&
8942 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
8943 ereport(ERROR,
8944 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8945 errmsg("type of inherited column \"%s\" must be changed in child tables too",
8946 colName)));
8947
8948 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
8949 ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
8950 }
8951
8952 /*
8953 * When the data type of a column is changed, a rewrite might not be required
8954 * if the new type is sufficiently identical to the old one, and the USING
8955 * clause isn't trying to insert some other value. It's safe to skip the
8956 * rewrite if the old type is binary coercible to the new type, or if the
8957 * new type is an unconstrained domain over the old type. In the case of a
8958 * constrained domain, we could get by with scanning the table and checking
8959 * the constraint rather than actually rewriting it, but we don't currently
8960 * try to do that.
8961 */
8962 static bool
ATColumnChangeRequiresRewrite(Node * expr,AttrNumber varattno)8963 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
8964 {
8965 Assert(expr != NULL);
8966
8967 for (;;)
8968 {
8969 /* only one varno, so no need to check that */
8970 if (IsA(expr, Var) &&((Var *) expr)->varattno == varattno)
8971 return false;
8972 else if (IsA(expr, RelabelType))
8973 expr = (Node *) ((RelabelType *) expr)->arg;
8974 else if (IsA(expr, CoerceToDomain))
8975 {
8976 CoerceToDomain *d = (CoerceToDomain *) expr;
8977
8978 if (DomainHasConstraints(d->resulttype))
8979 return true;
8980 expr = (Node *) d->arg;
8981 }
8982 else
8983 return true;
8984 }
8985 }
8986
8987 /*
8988 * ALTER COLUMN .. SET DATA TYPE
8989 *
8990 * Return the address of the modified column.
8991 */
8992 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo * tab,Relation rel,AlterTableCmd * cmd,LOCKMODE lockmode)8993 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
8994 AlterTableCmd *cmd, LOCKMODE lockmode)
8995 {
8996 char *colName = cmd->name;
8997 ColumnDef *def = (ColumnDef *) cmd->def;
8998 TypeName *typeName = def->typeName;
8999 HeapTuple heapTup;
9000 Form_pg_attribute attTup;
9001 AttrNumber attnum;
9002 HeapTuple typeTuple;
9003 Form_pg_type tform;
9004 Oid targettype;
9005 int32 targettypmod;
9006 Oid targetcollid;
9007 Node *defaultexpr;
9008 Relation attrelation;
9009 Relation depRel;
9010 ScanKeyData key[3];
9011 SysScanDesc scan;
9012 HeapTuple depTup;
9013 ObjectAddress address;
9014
9015 attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
9016
9017 /* Look up the target column */
9018 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9019 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
9020 ereport(ERROR,
9021 (errcode(ERRCODE_UNDEFINED_COLUMN),
9022 errmsg("column \"%s\" of relation \"%s\" does not exist",
9023 colName, RelationGetRelationName(rel))));
9024 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
9025 attnum = attTup->attnum;
9026
9027 /* Check for multiple ALTER TYPE on same column --- can't cope */
9028 if (attTup->atttypid != tab->oldDesc->attrs[attnum - 1]->atttypid ||
9029 attTup->atttypmod != tab->oldDesc->attrs[attnum - 1]->atttypmod)
9030 ereport(ERROR,
9031 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9032 errmsg("cannot alter type of column \"%s\" twice",
9033 colName)));
9034
9035 /* Look up the target type (should not fail, since prep found it) */
9036 typeTuple = typenameType(NULL, typeName, &targettypmod);
9037 tform = (Form_pg_type) GETSTRUCT(typeTuple);
9038 targettype = HeapTupleGetOid(typeTuple);
9039 /* And the collation */
9040 targetcollid = GetColumnDefCollation(NULL, def, targettype);
9041
9042 /*
9043 * If there is a default expression for the column, get it and ensure we
9044 * can coerce it to the new datatype. (We must do this before changing
9045 * the column type, because build_column_default itself will try to
9046 * coerce, and will not issue the error message we want if it fails.)
9047 *
9048 * We remove any implicit coercion steps at the top level of the old
9049 * default expression; this has been agreed to satisfy the principle of
9050 * least surprise. (The conversion to the new column type should act like
9051 * it started from what the user sees as the stored expression, and the
9052 * implicit coercions aren't going to be shown.)
9053 */
9054 if (attTup->atthasdef)
9055 {
9056 defaultexpr = build_column_default(rel, attnum);
9057 Assert(defaultexpr);
9058 defaultexpr = strip_implicit_coercions(defaultexpr);
9059 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
9060 defaultexpr, exprType(defaultexpr),
9061 targettype, targettypmod,
9062 COERCION_ASSIGNMENT,
9063 COERCE_IMPLICIT_CAST,
9064 -1);
9065 if (defaultexpr == NULL)
9066 ereport(ERROR,
9067 (errcode(ERRCODE_DATATYPE_MISMATCH),
9068 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
9069 colName, format_type_be(targettype))));
9070 }
9071 else
9072 defaultexpr = NULL;
9073
9074 /*
9075 * Find everything that depends on the column (constraints, indexes, etc),
9076 * and record enough information to let us recreate the objects.
9077 *
9078 * The actual recreation does not happen here, but only after we have
9079 * performed all the individual ALTER TYPE operations. We have to save
9080 * the info before executing ALTER TYPE, though, else the deparser will
9081 * get confused.
9082 */
9083 depRel = heap_open(DependRelationId, RowExclusiveLock);
9084
9085 ScanKeyInit(&key[0],
9086 Anum_pg_depend_refclassid,
9087 BTEqualStrategyNumber, F_OIDEQ,
9088 ObjectIdGetDatum(RelationRelationId));
9089 ScanKeyInit(&key[1],
9090 Anum_pg_depend_refobjid,
9091 BTEqualStrategyNumber, F_OIDEQ,
9092 ObjectIdGetDatum(RelationGetRelid(rel)));
9093 ScanKeyInit(&key[2],
9094 Anum_pg_depend_refobjsubid,
9095 BTEqualStrategyNumber, F_INT4EQ,
9096 Int32GetDatum((int32) attnum));
9097
9098 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
9099 NULL, 3, key);
9100
9101 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
9102 {
9103 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
9104 ObjectAddress foundObject;
9105
9106 /* We don't expect any PIN dependencies on columns */
9107 if (foundDep->deptype == DEPENDENCY_PIN)
9108 elog(ERROR, "cannot alter type of a pinned column");
9109
9110 foundObject.classId = foundDep->classid;
9111 foundObject.objectId = foundDep->objid;
9112 foundObject.objectSubId = foundDep->objsubid;
9113
9114 switch (getObjectClass(&foundObject))
9115 {
9116 case OCLASS_CLASS:
9117 {
9118 char relKind = get_rel_relkind(foundObject.objectId);
9119
9120 if (relKind == RELKIND_INDEX)
9121 {
9122 Assert(foundObject.objectSubId == 0);
9123 RememberIndexForRebuilding(foundObject.objectId, tab);
9124 }
9125 else if (relKind == RELKIND_SEQUENCE)
9126 {
9127 /*
9128 * This must be a SERIAL column's sequence. We need
9129 * not do anything to it.
9130 */
9131 Assert(foundObject.objectSubId == 0);
9132 }
9133 else
9134 {
9135 /* Not expecting any other direct dependencies... */
9136 elog(ERROR, "unexpected object depending on column: %s",
9137 getObjectDescription(&foundObject));
9138 }
9139 break;
9140 }
9141
9142 case OCLASS_CONSTRAINT:
9143 Assert(foundObject.objectSubId == 0);
9144 RememberConstraintForRebuilding(foundObject.objectId, tab,
9145 foundDep->deptype);
9146 break;
9147
9148 case OCLASS_REWRITE:
9149 /* XXX someday see if we can cope with revising views */
9150 ereport(ERROR,
9151 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9152 errmsg("cannot alter type of a column used by a view or rule"),
9153 errdetail("%s depends on column \"%s\"",
9154 getObjectDescription(&foundObject),
9155 colName)));
9156 break;
9157
9158 case OCLASS_TRIGGER:
9159
9160 /*
9161 * A trigger can depend on a column because the column is
9162 * specified as an update target, or because the column is
9163 * used in the trigger's WHEN condition. The first case would
9164 * not require any extra work, but the second case would
9165 * require updating the WHEN expression, which will take a
9166 * significant amount of new code. Since we can't easily tell
9167 * which case applies, we punt for both. FIXME someday.
9168 */
9169 ereport(ERROR,
9170 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9171 errmsg("cannot alter type of a column used in a trigger definition"),
9172 errdetail("%s depends on column \"%s\"",
9173 getObjectDescription(&foundObject),
9174 colName)));
9175 break;
9176
9177 case OCLASS_POLICY:
9178
9179 /*
9180 * A policy can depend on a column because the column is
9181 * specified in the policy's USING or WITH CHECK qual
9182 * expressions. It might be possible to rewrite and recheck
9183 * the policy expression, but punt for now. It's certainly
9184 * easy enough to remove and recreate the policy; still, FIXME
9185 * someday.
9186 */
9187 ereport(ERROR,
9188 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9189 errmsg("cannot alter type of a column used in a policy definition"),
9190 errdetail("%s depends on column \"%s\"",
9191 getObjectDescription(&foundObject),
9192 colName)));
9193 break;
9194
9195 case OCLASS_DEFAULT:
9196
9197 /*
9198 * Ignore the column's default expression, since we will fix
9199 * it below.
9200 */
9201 Assert(defaultexpr);
9202 break;
9203
9204 case OCLASS_STATISTIC_EXT:
9205
9206 /*
9207 * Give the extended-stats machinery a chance to fix anything
9208 * that this column type change would break.
9209 */
9210 UpdateStatisticsForTypeChange(foundObject.objectId,
9211 RelationGetRelid(rel), attnum,
9212 attTup->atttypid, targettype);
9213 break;
9214
9215 case OCLASS_PROC:
9216 case OCLASS_TYPE:
9217 case OCLASS_CAST:
9218 case OCLASS_COLLATION:
9219 case OCLASS_CONVERSION:
9220 case OCLASS_LANGUAGE:
9221 case OCLASS_LARGEOBJECT:
9222 case OCLASS_OPERATOR:
9223 case OCLASS_OPCLASS:
9224 case OCLASS_OPFAMILY:
9225 case OCLASS_AM:
9226 case OCLASS_AMOP:
9227 case OCLASS_AMPROC:
9228 case OCLASS_SCHEMA:
9229 case OCLASS_TSPARSER:
9230 case OCLASS_TSDICT:
9231 case OCLASS_TSTEMPLATE:
9232 case OCLASS_TSCONFIG:
9233 case OCLASS_ROLE:
9234 case OCLASS_DATABASE:
9235 case OCLASS_TBLSPACE:
9236 case OCLASS_FDW:
9237 case OCLASS_FOREIGN_SERVER:
9238 case OCLASS_USER_MAPPING:
9239 case OCLASS_DEFACL:
9240 case OCLASS_EXTENSION:
9241 case OCLASS_EVENT_TRIGGER:
9242 case OCLASS_PUBLICATION:
9243 case OCLASS_PUBLICATION_REL:
9244 case OCLASS_SUBSCRIPTION:
9245 case OCLASS_TRANSFORM:
9246
9247 /*
9248 * We don't expect any of these sorts of objects to depend on
9249 * a column.
9250 */
9251 elog(ERROR, "unexpected object depending on column: %s",
9252 getObjectDescription(&foundObject));
9253 break;
9254
9255 /*
9256 * There's intentionally no default: case here; we want the
9257 * compiler to warn if a new OCLASS hasn't been handled above.
9258 */
9259 }
9260 }
9261
9262 systable_endscan(scan);
9263
9264 /*
9265 * Now scan for dependencies of this column on other things. The only
9266 * thing we should find is the dependency on the column datatype, which we
9267 * want to remove, and possibly a collation dependency.
9268 */
9269 ScanKeyInit(&key[0],
9270 Anum_pg_depend_classid,
9271 BTEqualStrategyNumber, F_OIDEQ,
9272 ObjectIdGetDatum(RelationRelationId));
9273 ScanKeyInit(&key[1],
9274 Anum_pg_depend_objid,
9275 BTEqualStrategyNumber, F_OIDEQ,
9276 ObjectIdGetDatum(RelationGetRelid(rel)));
9277 ScanKeyInit(&key[2],
9278 Anum_pg_depend_objsubid,
9279 BTEqualStrategyNumber, F_INT4EQ,
9280 Int32GetDatum((int32) attnum));
9281
9282 scan = systable_beginscan(depRel, DependDependerIndexId, true,
9283 NULL, 3, key);
9284
9285 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
9286 {
9287 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
9288
9289 if (foundDep->deptype != DEPENDENCY_NORMAL)
9290 elog(ERROR, "found unexpected dependency type '%c'",
9291 foundDep->deptype);
9292 if (!(foundDep->refclassid == TypeRelationId &&
9293 foundDep->refobjid == attTup->atttypid) &&
9294 !(foundDep->refclassid == CollationRelationId &&
9295 foundDep->refobjid == attTup->attcollation))
9296 elog(ERROR, "found unexpected dependency for column");
9297
9298 CatalogTupleDelete(depRel, &depTup->t_self);
9299 }
9300
9301 systable_endscan(scan);
9302
9303 heap_close(depRel, RowExclusiveLock);
9304
9305 /*
9306 * Here we go --- change the recorded column type and collation. (Note
9307 * heapTup is a copy of the syscache entry, so okay to scribble on.)
9308 */
9309 attTup->atttypid = targettype;
9310 attTup->atttypmod = targettypmod;
9311 attTup->attcollation = targetcollid;
9312 attTup->attndims = list_length(typeName->arrayBounds);
9313 attTup->attlen = tform->typlen;
9314 attTup->attbyval = tform->typbyval;
9315 attTup->attalign = tform->typalign;
9316 attTup->attstorage = tform->typstorage;
9317
9318 ReleaseSysCache(typeTuple);
9319
9320 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
9321
9322 heap_close(attrelation, RowExclusiveLock);
9323
9324 /* Install dependencies on new datatype and collation */
9325 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
9326 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
9327
9328 /*
9329 * Drop any pg_statistic entry for the column, since it's now wrong type
9330 */
9331 RemoveStatistics(RelationGetRelid(rel), attnum);
9332
9333 InvokeObjectPostAlterHook(RelationRelationId,
9334 RelationGetRelid(rel), attnum);
9335
9336 /*
9337 * Update the default, if present, by brute force --- remove and re-add
9338 * the default. Probably unsafe to take shortcuts, since the new version
9339 * may well have additional dependencies. (It's okay to do this now,
9340 * rather than after other ALTER TYPE commands, since the default won't
9341 * depend on other column types.)
9342 */
9343 if (defaultexpr)
9344 {
9345 /* Must make new row visible since it will be updated again */
9346 CommandCounterIncrement();
9347
9348 /*
9349 * We use RESTRICT here for safety, but at present we do not expect
9350 * anything to depend on the default.
9351 */
9352 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
9353 true);
9354
9355 StoreAttrDefault(rel, attnum, defaultexpr, true);
9356 }
9357
9358 ObjectAddressSubSet(address, RelationRelationId,
9359 RelationGetRelid(rel), attnum);
9360
9361 /* Cleanup */
9362 heap_freetuple(heapTup);
9363
9364 return address;
9365 }
9366
9367 /*
9368 * Subroutine for ATExecAlterColumnType: remember that a replica identity
9369 * needs to be reset.
9370 */
9371 static void
RememberReplicaIdentityForRebuilding(Oid indoid,AlteredTableInfo * tab)9372 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
9373 {
9374 if (!get_index_isreplident(indoid))
9375 return;
9376
9377 if (tab->replicaIdentityIndex)
9378 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
9379
9380 tab->replicaIdentityIndex = get_rel_name(indoid);
9381 }
9382
9383 /*
9384 * Subroutine for ATExecAlterColumnType: remember any clustered index.
9385 */
9386 static void
RememberClusterOnForRebuilding(Oid indoid,AlteredTableInfo * tab)9387 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
9388 {
9389 if (!get_index_isclustered(indoid))
9390 return;
9391
9392 if (tab->clusterOnIndex)
9393 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
9394
9395 tab->clusterOnIndex = get_rel_name(indoid);
9396 }
9397
9398 /*
9399 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
9400 * to be rebuilt (which we might already know).
9401 */
9402 static void
RememberConstraintForRebuilding(Oid conoid,AlteredTableInfo * tab,DependencyType deptype)9403 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab,
9404 DependencyType deptype)
9405 {
9406 /*
9407 * This de-duplication check is critical for two independent reasons: we
9408 * mustn't try to recreate the same constraint twice, and if a constraint
9409 * depends on more than one column whose type is to be altered, we must
9410 * capture its definition string before applying any of the column type
9411 * changes. ruleutils.c will get confused if we ask again later.
9412 */
9413 if (!list_member_oid(tab->changedConstraintOids, conoid))
9414 {
9415 /* OK, capture the constraint's existing definition string */
9416 char *defstring = pg_get_constraintdef_command(conoid);
9417 Oid indoid;
9418
9419 /*
9420 * Put NORMAL dependencies at the front of the list and AUTO
9421 * dependencies at the back. This makes sure that foreign-key
9422 * constraints depending on this column will be dropped before unique
9423 * or primary-key constraints of the column; which we must have
9424 * because the FK constraints depend on the indexes belonging to the
9425 * unique constraints.
9426 */
9427 if (deptype == DEPENDENCY_NORMAL)
9428 {
9429 tab->changedConstraintOids = lcons_oid(conoid,
9430 tab->changedConstraintOids);
9431 tab->changedConstraintDefs = lcons(defstring,
9432 tab->changedConstraintDefs);
9433 }
9434 else
9435 {
9436 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
9437 conoid);
9438 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
9439 defstring);
9440 }
9441
9442 /*
9443 * For the index of a constraint, if any, remember if it is used for
9444 * the table's replica identity or if it is a clustered index, so that
9445 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
9446 * those properties.
9447 */
9448 indoid = get_constraint_index(conoid);
9449 if (OidIsValid(indoid))
9450 {
9451 RememberReplicaIdentityForRebuilding(indoid, tab);
9452 RememberClusterOnForRebuilding(indoid, tab);
9453 }
9454 }
9455 }
9456
9457 /*
9458 * Subroutine for ATExecAlterColumnType: remember that an index needs
9459 * to be rebuilt (which we might already know).
9460 */
9461 static void
RememberIndexForRebuilding(Oid indoid,AlteredTableInfo * tab)9462 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
9463 {
9464 /*
9465 * This de-duplication check is critical for two independent reasons: we
9466 * mustn't try to recreate the same index twice, and if an index depends
9467 * on more than one column whose type is to be altered, we must capture
9468 * its definition string before applying any of the column type changes.
9469 * ruleutils.c will get confused if we ask again later.
9470 */
9471 if (!list_member_oid(tab->changedIndexOids, indoid))
9472 {
9473 /*
9474 * Before adding it as an index-to-rebuild, we'd better see if it
9475 * belongs to a constraint, and if so rebuild the constraint instead.
9476 * Typically this check fails, because constraint indexes normally
9477 * have only dependencies on their constraint. But it's possible for
9478 * such an index to also have direct dependencies on table columns,
9479 * for example with a partial exclusion constraint.
9480 */
9481 Oid conoid = get_index_constraint(indoid);
9482
9483 if (OidIsValid(conoid))
9484 {
9485 /* index dependencies on columns should generally be AUTO */
9486 RememberConstraintForRebuilding(conoid, tab, DEPENDENCY_AUTO);
9487 }
9488 else
9489 {
9490 /* OK, capture the index's existing definition string */
9491 char *defstring = pg_get_indexdef_string(indoid);
9492
9493 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
9494 indoid);
9495 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
9496 defstring);
9497
9498 /*
9499 * Remember if this index is used for the table's replica identity
9500 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
9501 * can queue up commands necessary to restore those properties.
9502 */
9503 RememberReplicaIdentityForRebuilding(indoid, tab);
9504 RememberClusterOnForRebuilding(indoid, tab);
9505 }
9506 }
9507 }
9508
9509 /*
9510 * Returns the address of the modified column
9511 */
9512 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel,const char * colName,List * options,LOCKMODE lockmode)9513 ATExecAlterColumnGenericOptions(Relation rel,
9514 const char *colName,
9515 List *options,
9516 LOCKMODE lockmode)
9517 {
9518 Relation ftrel;
9519 Relation attrel;
9520 ForeignServer *server;
9521 ForeignDataWrapper *fdw;
9522 HeapTuple tuple;
9523 HeapTuple newtuple;
9524 bool isnull;
9525 Datum repl_val[Natts_pg_attribute];
9526 bool repl_null[Natts_pg_attribute];
9527 bool repl_repl[Natts_pg_attribute];
9528 Datum datum;
9529 Form_pg_foreign_table fttableform;
9530 Form_pg_attribute atttableform;
9531 AttrNumber attnum;
9532 ObjectAddress address;
9533
9534 if (options == NIL)
9535 return InvalidObjectAddress;
9536
9537 /* First, determine FDW validator associated to the foreign table. */
9538 ftrel = heap_open(ForeignTableRelationId, AccessShareLock);
9539 tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
9540 if (!HeapTupleIsValid(tuple))
9541 ereport(ERROR,
9542 (errcode(ERRCODE_UNDEFINED_OBJECT),
9543 errmsg("foreign table \"%s\" does not exist",
9544 RelationGetRelationName(rel))));
9545 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
9546 server = GetForeignServer(fttableform->ftserver);
9547 fdw = GetForeignDataWrapper(server->fdwid);
9548
9549 heap_close(ftrel, AccessShareLock);
9550 ReleaseSysCache(tuple);
9551
9552 attrel = heap_open(AttributeRelationId, RowExclusiveLock);
9553 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9554 if (!HeapTupleIsValid(tuple))
9555 ereport(ERROR,
9556 (errcode(ERRCODE_UNDEFINED_COLUMN),
9557 errmsg("column \"%s\" of relation \"%s\" does not exist",
9558 colName, RelationGetRelationName(rel))));
9559
9560 /* Prevent them from altering a system attribute */
9561 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
9562 attnum = atttableform->attnum;
9563 if (attnum <= 0)
9564 ereport(ERROR,
9565 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9566 errmsg("cannot alter system column \"%s\"", colName)));
9567
9568
9569 /* Initialize buffers for new tuple values */
9570 memset(repl_val, 0, sizeof(repl_val));
9571 memset(repl_null, false, sizeof(repl_null));
9572 memset(repl_repl, false, sizeof(repl_repl));
9573
9574 /* Extract the current options */
9575 datum = SysCacheGetAttr(ATTNAME,
9576 tuple,
9577 Anum_pg_attribute_attfdwoptions,
9578 &isnull);
9579 if (isnull)
9580 datum = PointerGetDatum(NULL);
9581
9582 /* Transform the options */
9583 datum = transformGenericOptions(AttributeRelationId,
9584 datum,
9585 options,
9586 fdw->fdwvalidator);
9587
9588 if (PointerIsValid(DatumGetPointer(datum)))
9589 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
9590 else
9591 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
9592
9593 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
9594
9595 /* Everything looks good - update the tuple */
9596
9597 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
9598 repl_val, repl_null, repl_repl);
9599
9600 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
9601
9602 InvokeObjectPostAlterHook(RelationRelationId,
9603 RelationGetRelid(rel),
9604 atttableform->attnum);
9605 ObjectAddressSubSet(address, RelationRelationId,
9606 RelationGetRelid(rel), attnum);
9607
9608 ReleaseSysCache(tuple);
9609
9610 heap_close(attrel, RowExclusiveLock);
9611
9612 heap_freetuple(newtuple);
9613
9614 return address;
9615 }
9616
9617 /*
9618 * Cleanup after we've finished all the ALTER TYPE operations for a
9619 * particular relation. We have to drop and recreate all the indexes
9620 * and constraints that depend on the altered columns.
9621 */
9622 static void
ATPostAlterTypeCleanup(List ** wqueue,AlteredTableInfo * tab,LOCKMODE lockmode)9623 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
9624 {
9625 ObjectAddress obj;
9626 ListCell *def_item;
9627 ListCell *oid_item;
9628
9629 /*
9630 * Re-parse the index and constraint definitions, and attach them to the
9631 * appropriate work queue entries. We do this before dropping because in
9632 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
9633 * lock on the table the constraint is attached to, and we need to get
9634 * that before reparsing/dropping.
9635 *
9636 * We can't rely on the output of deparsing to tell us which relation to
9637 * operate on, because concurrent activity might have made the name
9638 * resolve differently. Instead, we've got to use the OID of the
9639 * constraint or index we're processing to figure out which relation to
9640 * operate on.
9641 */
9642 forboth(oid_item, tab->changedConstraintOids,
9643 def_item, tab->changedConstraintDefs)
9644 {
9645 Oid oldId = lfirst_oid(oid_item);
9646 HeapTuple tup;
9647 Form_pg_constraint con;
9648 Oid relid;
9649 Oid confrelid;
9650 char contype;
9651 bool conislocal;
9652
9653 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
9654 if (!HeapTupleIsValid(tup)) /* should not happen */
9655 elog(ERROR, "cache lookup failed for constraint %u", oldId);
9656 con = (Form_pg_constraint) GETSTRUCT(tup);
9657 relid = con->conrelid;
9658 confrelid = con->confrelid;
9659 contype = con->contype;
9660 conislocal = con->conislocal;
9661 ReleaseSysCache(tup);
9662
9663 /*
9664 * If the constraint is inherited (only), we don't want to inject a
9665 * new definition here; it'll get recreated when ATAddCheckConstraint
9666 * recurses from adding the parent table's constraint. But we had to
9667 * carry the info this far so that we can drop the constraint below.
9668 */
9669 if (!conislocal)
9670 continue;
9671
9672 /*
9673 * When rebuilding an FK constraint that references the table we're
9674 * modifying, we might not yet have any lock on the FK's table, so get
9675 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
9676 * step, so there's no value in asking for anything weaker.
9677 */
9678 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
9679 LockRelationOid(relid, AccessExclusiveLock);
9680
9681 ATPostAlterTypeParse(oldId, relid, confrelid,
9682 (char *) lfirst(def_item),
9683 wqueue, lockmode, tab->rewrite);
9684 }
9685 forboth(oid_item, tab->changedIndexOids,
9686 def_item, tab->changedIndexDefs)
9687 {
9688 Oid oldId = lfirst_oid(oid_item);
9689 Oid relid;
9690
9691 relid = IndexGetRelation(oldId, false);
9692 ATPostAlterTypeParse(oldId, relid, InvalidOid,
9693 (char *) lfirst(def_item),
9694 wqueue, lockmode, tab->rewrite);
9695 }
9696
9697 /*
9698 * Queue up command to restore replica identity index marking
9699 */
9700 if (tab->replicaIdentityIndex)
9701 {
9702 AlterTableCmd *cmd = makeNode(AlterTableCmd);
9703 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
9704
9705 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
9706 subcmd->name = tab->replicaIdentityIndex;
9707 cmd->subtype = AT_ReplicaIdentity;
9708 cmd->def = (Node *) subcmd;
9709
9710 /* do it after indexes and constraints */
9711 tab->subcmds[AT_PASS_OLD_CONSTR] =
9712 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
9713 }
9714
9715 /*
9716 * Queue up command to restore marking of index used for cluster.
9717 */
9718 if (tab->clusterOnIndex)
9719 {
9720 AlterTableCmd *cmd = makeNode(AlterTableCmd);
9721
9722 cmd->subtype = AT_ClusterOn;
9723 cmd->name = tab->clusterOnIndex;
9724
9725 /* do it after indexes and constraints */
9726 tab->subcmds[AT_PASS_OLD_CONSTR] =
9727 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
9728 }
9729
9730 /*
9731 * Now we can drop the existing constraints and indexes --- constraints
9732 * first, since some of them might depend on the indexes. In fact, we
9733 * have to delete FOREIGN KEY constraints before UNIQUE constraints, but
9734 * we already ordered the constraint list to ensure that would happen. It
9735 * should be okay to use DROP_RESTRICT here, since nothing else should be
9736 * depending on these objects.
9737 */
9738 foreach(oid_item, tab->changedConstraintOids)
9739 {
9740 obj.classId = ConstraintRelationId;
9741 obj.objectId = lfirst_oid(oid_item);
9742 obj.objectSubId = 0;
9743 performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
9744 }
9745
9746 foreach(oid_item, tab->changedIndexOids)
9747 {
9748 obj.classId = RelationRelationId;
9749 obj.objectId = lfirst_oid(oid_item);
9750 obj.objectSubId = 0;
9751 performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
9752 }
9753
9754 /*
9755 * The objects will get recreated during subsequent passes over the work
9756 * queue.
9757 */
9758 }
9759
9760 static void
ATPostAlterTypeParse(Oid oldId,Oid oldRelId,Oid refRelId,char * cmd,List ** wqueue,LOCKMODE lockmode,bool rewrite)9761 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
9762 List **wqueue, LOCKMODE lockmode, bool rewrite)
9763 {
9764 List *raw_parsetree_list;
9765 List *querytree_list;
9766 ListCell *list_item;
9767 Relation rel;
9768
9769 /*
9770 * We expect that we will get only ALTER TABLE and CREATE INDEX
9771 * statements. Hence, there is no need to pass them through
9772 * parse_analyze() or the rewriter, but instead we need to pass them
9773 * through parse_utilcmd.c to make them ready for execution.
9774 */
9775 raw_parsetree_list = raw_parser(cmd);
9776 querytree_list = NIL;
9777 foreach(list_item, raw_parsetree_list)
9778 {
9779 RawStmt *rs = lfirst_node(RawStmt, list_item);
9780 Node *stmt = rs->stmt;
9781
9782 if (IsA(stmt, IndexStmt))
9783 querytree_list = lappend(querytree_list,
9784 transformIndexStmt(oldRelId,
9785 (IndexStmt *) stmt,
9786 cmd));
9787 else if (IsA(stmt, AlterTableStmt))
9788 querytree_list = list_concat(querytree_list,
9789 transformAlterTableStmt(oldRelId,
9790 (AlterTableStmt *) stmt,
9791 cmd));
9792 else
9793 querytree_list = lappend(querytree_list, stmt);
9794 }
9795
9796 /* Caller should already have acquired whatever lock we need. */
9797 rel = relation_open(oldRelId, NoLock);
9798
9799 /*
9800 * Attach each generated command to the proper place in the work queue.
9801 * Note this could result in creation of entirely new work-queue entries.
9802 *
9803 * Also note that we have to tweak the command subtypes, because it turns
9804 * out that re-creation of indexes and constraints has to act a bit
9805 * differently from initial creation.
9806 */
9807 foreach(list_item, querytree_list)
9808 {
9809 Node *stm = (Node *) lfirst(list_item);
9810 AlteredTableInfo *tab;
9811
9812 tab = ATGetQueueEntry(wqueue, rel);
9813
9814 if (IsA(stm, IndexStmt))
9815 {
9816 IndexStmt *stmt = (IndexStmt *) stm;
9817 AlterTableCmd *newcmd;
9818
9819 if (!rewrite)
9820 TryReuseIndex(oldId, stmt);
9821 /* keep the index's comment */
9822 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
9823
9824 newcmd = makeNode(AlterTableCmd);
9825 newcmd->subtype = AT_ReAddIndex;
9826 newcmd->def = (Node *) stmt;
9827 tab->subcmds[AT_PASS_OLD_INDEX] =
9828 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
9829 }
9830 else if (IsA(stm, AlterTableStmt))
9831 {
9832 AlterTableStmt *stmt = (AlterTableStmt *) stm;
9833 ListCell *lcmd;
9834
9835 foreach(lcmd, stmt->cmds)
9836 {
9837 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
9838
9839 if (cmd->subtype == AT_AddIndex)
9840 {
9841 IndexStmt *indstmt;
9842 Oid indoid;
9843
9844 indstmt = castNode(IndexStmt, cmd->def);
9845 indoid = get_constraint_index(oldId);
9846
9847 if (!rewrite)
9848 TryReuseIndex(indoid, indstmt);
9849 /* keep any comment on the index */
9850 indstmt->idxcomment = GetComment(indoid,
9851 RelationRelationId, 0);
9852
9853 cmd->subtype = AT_ReAddIndex;
9854 tab->subcmds[AT_PASS_OLD_INDEX] =
9855 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
9856
9857 /* recreate any comment on the constraint */
9858 RebuildConstraintComment(tab,
9859 AT_PASS_OLD_INDEX,
9860 oldId,
9861 rel, indstmt->idxname);
9862 }
9863 else if (cmd->subtype == AT_AddConstraint)
9864 {
9865 Constraint *con;
9866
9867 con = castNode(Constraint, cmd->def);
9868 con->old_pktable_oid = refRelId;
9869 /* rewriting neither side of a FK */
9870 if (con->contype == CONSTR_FOREIGN &&
9871 !rewrite && tab->rewrite == 0)
9872 TryReuseForeignKey(oldId, con);
9873 cmd->subtype = AT_ReAddConstraint;
9874 tab->subcmds[AT_PASS_OLD_CONSTR] =
9875 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
9876
9877 /* recreate any comment on the constraint */
9878 RebuildConstraintComment(tab,
9879 AT_PASS_OLD_CONSTR,
9880 oldId,
9881 rel, con->conname);
9882 }
9883 else
9884 elog(ERROR, "unexpected statement subtype: %d",
9885 (int) cmd->subtype);
9886 }
9887 }
9888 else
9889 elog(ERROR, "unexpected statement type: %d",
9890 (int) nodeTag(stm));
9891 }
9892
9893 relation_close(rel, NoLock);
9894 }
9895
9896 /*
9897 * Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
9898 * a constraint that is being re-added.
9899 */
9900 static void
RebuildConstraintComment(AlteredTableInfo * tab,int pass,Oid objid,Relation rel,char * conname)9901 RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
9902 Relation rel, char *conname)
9903 {
9904 CommentStmt *cmd;
9905 char *comment_str;
9906 AlterTableCmd *newcmd;
9907
9908 /* Look for comment for object wanted, and leave if none */
9909 comment_str = GetComment(objid, ConstraintRelationId, 0);
9910 if (comment_str == NULL)
9911 return;
9912
9913 /* Build node CommentStmt */
9914 cmd = makeNode(CommentStmt);
9915 cmd->objtype = OBJECT_TABCONSTRAINT;
9916 cmd->object = (Node *) list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
9917 makeString(pstrdup(RelationGetRelationName(rel))),
9918 makeString(pstrdup(conname)));
9919 cmd->comment = comment_str;
9920
9921 /* Append it to list of commands */
9922 newcmd = makeNode(AlterTableCmd);
9923 newcmd->subtype = AT_ReAddComment;
9924 newcmd->def = (Node *) cmd;
9925 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
9926 }
9927
9928 /*
9929 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
9930 * for the real analysis, then mutates the IndexStmt based on that verdict.
9931 */
9932 static void
TryReuseIndex(Oid oldId,IndexStmt * stmt)9933 TryReuseIndex(Oid oldId, IndexStmt *stmt)
9934 {
9935 if (CheckIndexCompatible(oldId,
9936 stmt->accessMethod,
9937 stmt->indexParams,
9938 stmt->excludeOpNames))
9939 {
9940 Relation irel = index_open(oldId, NoLock);
9941
9942 stmt->oldNode = irel->rd_node.relNode;
9943 index_close(irel, NoLock);
9944 }
9945 }
9946
9947 /*
9948 * Subroutine for ATPostAlterTypeParse().
9949 *
9950 * Stash the old P-F equality operator into the Constraint node, for possible
9951 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
9952 * this constraint can be skipped.
9953 */
9954 static void
TryReuseForeignKey(Oid oldId,Constraint * con)9955 TryReuseForeignKey(Oid oldId, Constraint *con)
9956 {
9957 HeapTuple tup;
9958 Datum adatum;
9959 bool isNull;
9960 ArrayType *arr;
9961 Oid *rawarr;
9962 int numkeys;
9963 int i;
9964
9965 Assert(con->contype == CONSTR_FOREIGN);
9966 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
9967
9968 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
9969 if (!HeapTupleIsValid(tup)) /* should not happen */
9970 elog(ERROR, "cache lookup failed for constraint %u", oldId);
9971
9972 adatum = SysCacheGetAttr(CONSTROID, tup,
9973 Anum_pg_constraint_conpfeqop, &isNull);
9974 if (isNull)
9975 elog(ERROR, "null conpfeqop for constraint %u", oldId);
9976 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
9977 numkeys = ARR_DIMS(arr)[0];
9978 /* test follows the one in ri_FetchConstraintInfo() */
9979 if (ARR_NDIM(arr) != 1 ||
9980 ARR_HASNULL(arr) ||
9981 ARR_ELEMTYPE(arr) != OIDOID)
9982 elog(ERROR, "conpfeqop is not a 1-D Oid array");
9983 rawarr = (Oid *) ARR_DATA_PTR(arr);
9984
9985 /* stash a List of the operator Oids in our Constraint node */
9986 for (i = 0; i < numkeys; i++)
9987 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
9988
9989 ReleaseSysCache(tup);
9990 }
9991
9992 /*
9993 * ALTER TABLE OWNER
9994 *
9995 * recursing is true if we are recursing from a table to its indexes,
9996 * sequences, or toast table. We don't allow the ownership of those things to
9997 * be changed separately from the parent table. Also, we can skip permission
9998 * checks (this is necessary not just an optimization, else we'd fail to
9999 * handle toast tables properly).
10000 *
10001 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
10002 * free-standing composite type.
10003 */
10004 void
ATExecChangeOwner(Oid relationOid,Oid newOwnerId,bool recursing,LOCKMODE lockmode)10005 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
10006 {
10007 Relation target_rel;
10008 Relation class_rel;
10009 HeapTuple tuple;
10010 Form_pg_class tuple_class;
10011
10012 /*
10013 * Get exclusive lock till end of transaction on the target table. Use
10014 * relation_open so that we can work on indexes and sequences.
10015 */
10016 target_rel = relation_open(relationOid, lockmode);
10017
10018 /* Get its pg_class tuple, too */
10019 class_rel = heap_open(RelationRelationId, RowExclusiveLock);
10020
10021 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
10022 if (!HeapTupleIsValid(tuple))
10023 elog(ERROR, "cache lookup failed for relation %u", relationOid);
10024 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
10025
10026 /* Can we change the ownership of this tuple? */
10027 switch (tuple_class->relkind)
10028 {
10029 case RELKIND_RELATION:
10030 case RELKIND_VIEW:
10031 case RELKIND_MATVIEW:
10032 case RELKIND_FOREIGN_TABLE:
10033 case RELKIND_PARTITIONED_TABLE:
10034 /* ok to change owner */
10035 break;
10036 case RELKIND_INDEX:
10037 if (!recursing)
10038 {
10039 /*
10040 * Because ALTER INDEX OWNER used to be allowed, and in fact
10041 * is generated by old versions of pg_dump, we give a warning
10042 * and do nothing rather than erroring out. Also, to avoid
10043 * unnecessary chatter while restoring those old dumps, say
10044 * nothing at all if the command would be a no-op anyway.
10045 */
10046 if (tuple_class->relowner != newOwnerId)
10047 ereport(WARNING,
10048 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10049 errmsg("cannot change owner of index \"%s\"",
10050 NameStr(tuple_class->relname)),
10051 errhint("Change the ownership of the index's table, instead.")));
10052 /* quick hack to exit via the no-op path */
10053 newOwnerId = tuple_class->relowner;
10054 }
10055 break;
10056 case RELKIND_SEQUENCE:
10057 if (!recursing &&
10058 tuple_class->relowner != newOwnerId)
10059 {
10060 /* if it's an owned sequence, disallow changing it by itself */
10061 Oid tableId;
10062 int32 colId;
10063
10064 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
10065 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
10066 ereport(ERROR,
10067 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10068 errmsg("cannot change owner of sequence \"%s\"",
10069 NameStr(tuple_class->relname)),
10070 errdetail("Sequence \"%s\" is linked to table \"%s\".",
10071 NameStr(tuple_class->relname),
10072 get_rel_name(tableId))));
10073 }
10074 break;
10075 case RELKIND_COMPOSITE_TYPE:
10076 if (recursing)
10077 break;
10078 ereport(ERROR,
10079 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10080 errmsg("\"%s\" is a composite type",
10081 NameStr(tuple_class->relname)),
10082 errhint("Use ALTER TYPE instead.")));
10083 break;
10084 case RELKIND_TOASTVALUE:
10085 if (recursing)
10086 break;
10087 /* FALL THRU */
10088 default:
10089 ereport(ERROR,
10090 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10091 errmsg("\"%s\" is not a table, view, sequence, or foreign table",
10092 NameStr(tuple_class->relname))));
10093 }
10094
10095 /*
10096 * If the new owner is the same as the existing owner, consider the
10097 * command to have succeeded. This is for dump restoration purposes.
10098 */
10099 if (tuple_class->relowner != newOwnerId)
10100 {
10101 Datum repl_val[Natts_pg_class];
10102 bool repl_null[Natts_pg_class];
10103 bool repl_repl[Natts_pg_class];
10104 Acl *newAcl;
10105 Datum aclDatum;
10106 bool isNull;
10107 HeapTuple newtuple;
10108
10109 /* skip permission checks when recursing to index or toast table */
10110 if (!recursing)
10111 {
10112 /* Superusers can always do it */
10113 if (!superuser())
10114 {
10115 Oid namespaceOid = tuple_class->relnamespace;
10116 AclResult aclresult;
10117
10118 /* Otherwise, must be owner of the existing object */
10119 if (!pg_class_ownercheck(relationOid, GetUserId()))
10120 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
10121 RelationGetRelationName(target_rel));
10122
10123 /* Must be able to become new owner */
10124 check_is_member_of_role(GetUserId(), newOwnerId);
10125
10126 /* New owner must have CREATE privilege on namespace */
10127 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
10128 ACL_CREATE);
10129 if (aclresult != ACLCHECK_OK)
10130 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
10131 get_namespace_name(namespaceOid));
10132 }
10133 }
10134
10135 memset(repl_null, false, sizeof(repl_null));
10136 memset(repl_repl, false, sizeof(repl_repl));
10137
10138 repl_repl[Anum_pg_class_relowner - 1] = true;
10139 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
10140
10141 /*
10142 * Determine the modified ACL for the new owner. This is only
10143 * necessary when the ACL is non-null.
10144 */
10145 aclDatum = SysCacheGetAttr(RELOID, tuple,
10146 Anum_pg_class_relacl,
10147 &isNull);
10148 if (!isNull)
10149 {
10150 newAcl = aclnewowner(DatumGetAclP(aclDatum),
10151 tuple_class->relowner, newOwnerId);
10152 repl_repl[Anum_pg_class_relacl - 1] = true;
10153 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
10154 }
10155
10156 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
10157
10158 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
10159
10160 heap_freetuple(newtuple);
10161
10162 /*
10163 * We must similarly update any per-column ACLs to reflect the new
10164 * owner; for neatness reasons that's split out as a subroutine.
10165 */
10166 change_owner_fix_column_acls(relationOid,
10167 tuple_class->relowner,
10168 newOwnerId);
10169
10170 /*
10171 * Update owner dependency reference, if any. A composite type has
10172 * none, because it's tracked for the pg_type entry instead of here;
10173 * indexes and TOAST tables don't have their own entries either.
10174 */
10175 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
10176 tuple_class->relkind != RELKIND_INDEX &&
10177 tuple_class->relkind != RELKIND_TOASTVALUE)
10178 changeDependencyOnOwner(RelationRelationId, relationOid,
10179 newOwnerId);
10180
10181 /*
10182 * Also change the ownership of the table's row type, if it has one
10183 */
10184 if (tuple_class->relkind != RELKIND_INDEX)
10185 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
10186
10187 /*
10188 * If we are operating on a table or materialized view, also change
10189 * the ownership of any indexes and sequences that belong to the
10190 * relation, as well as its toast table (if it has one).
10191 */
10192 if (tuple_class->relkind == RELKIND_RELATION ||
10193 tuple_class->relkind == RELKIND_MATVIEW ||
10194 tuple_class->relkind == RELKIND_TOASTVALUE)
10195 {
10196 List *index_oid_list;
10197 ListCell *i;
10198
10199 /* Find all the indexes belonging to this relation */
10200 index_oid_list = RelationGetIndexList(target_rel);
10201
10202 /* For each index, recursively change its ownership */
10203 foreach(i, index_oid_list)
10204 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
10205
10206 list_free(index_oid_list);
10207 }
10208
10209 /* If it has a toast table, recurse to change its ownership */
10210 if (tuple_class->reltoastrelid != InvalidOid)
10211 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
10212 true, lockmode);
10213
10214 /* If it has dependent sequences, recurse to change them too */
10215 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
10216 }
10217
10218 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
10219
10220 ReleaseSysCache(tuple);
10221 heap_close(class_rel, RowExclusiveLock);
10222 relation_close(target_rel, NoLock);
10223 }
10224
10225 /*
10226 * change_owner_fix_column_acls
10227 *
10228 * Helper function for ATExecChangeOwner. Scan the columns of the table
10229 * and fix any non-null column ACLs to reflect the new owner.
10230 */
10231 static void
change_owner_fix_column_acls(Oid relationOid,Oid oldOwnerId,Oid newOwnerId)10232 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
10233 {
10234 Relation attRelation;
10235 SysScanDesc scan;
10236 ScanKeyData key[1];
10237 HeapTuple attributeTuple;
10238
10239 attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
10240 ScanKeyInit(&key[0],
10241 Anum_pg_attribute_attrelid,
10242 BTEqualStrategyNumber, F_OIDEQ,
10243 ObjectIdGetDatum(relationOid));
10244 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
10245 true, NULL, 1, key);
10246 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
10247 {
10248 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
10249 Datum repl_val[Natts_pg_attribute];
10250 bool repl_null[Natts_pg_attribute];
10251 bool repl_repl[Natts_pg_attribute];
10252 Acl *newAcl;
10253 Datum aclDatum;
10254 bool isNull;
10255 HeapTuple newtuple;
10256
10257 /* Ignore dropped columns */
10258 if (att->attisdropped)
10259 continue;
10260
10261 aclDatum = heap_getattr(attributeTuple,
10262 Anum_pg_attribute_attacl,
10263 RelationGetDescr(attRelation),
10264 &isNull);
10265 /* Null ACLs do not require changes */
10266 if (isNull)
10267 continue;
10268
10269 memset(repl_null, false, sizeof(repl_null));
10270 memset(repl_repl, false, sizeof(repl_repl));
10271
10272 newAcl = aclnewowner(DatumGetAclP(aclDatum),
10273 oldOwnerId, newOwnerId);
10274 repl_repl[Anum_pg_attribute_attacl - 1] = true;
10275 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
10276
10277 newtuple = heap_modify_tuple(attributeTuple,
10278 RelationGetDescr(attRelation),
10279 repl_val, repl_null, repl_repl);
10280
10281 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
10282
10283 heap_freetuple(newtuple);
10284 }
10285 systable_endscan(scan);
10286 heap_close(attRelation, RowExclusiveLock);
10287 }
10288
10289 /*
10290 * change_owner_recurse_to_sequences
10291 *
10292 * Helper function for ATExecChangeOwner. Examines pg_depend searching
10293 * for sequences that are dependent on serial columns, and changes their
10294 * ownership.
10295 */
10296 static void
change_owner_recurse_to_sequences(Oid relationOid,Oid newOwnerId,LOCKMODE lockmode)10297 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
10298 {
10299 Relation depRel;
10300 SysScanDesc scan;
10301 ScanKeyData key[2];
10302 HeapTuple tup;
10303
10304 /*
10305 * SERIAL sequences are those having an auto dependency on one of the
10306 * table's columns (we don't care *which* column, exactly).
10307 */
10308 depRel = heap_open(DependRelationId, AccessShareLock);
10309
10310 ScanKeyInit(&key[0],
10311 Anum_pg_depend_refclassid,
10312 BTEqualStrategyNumber, F_OIDEQ,
10313 ObjectIdGetDatum(RelationRelationId));
10314 ScanKeyInit(&key[1],
10315 Anum_pg_depend_refobjid,
10316 BTEqualStrategyNumber, F_OIDEQ,
10317 ObjectIdGetDatum(relationOid));
10318 /* we leave refobjsubid unspecified */
10319
10320 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
10321 NULL, 2, key);
10322
10323 while (HeapTupleIsValid(tup = systable_getnext(scan)))
10324 {
10325 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
10326 Relation seqRel;
10327
10328 /* skip dependencies other than auto dependencies on columns */
10329 if (depForm->refobjsubid == 0 ||
10330 depForm->classid != RelationRelationId ||
10331 depForm->objsubid != 0 ||
10332 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
10333 continue;
10334
10335 /* Use relation_open just in case it's an index */
10336 seqRel = relation_open(depForm->objid, lockmode);
10337
10338 /* skip non-sequence relations */
10339 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
10340 {
10341 /* No need to keep the lock */
10342 relation_close(seqRel, lockmode);
10343 continue;
10344 }
10345
10346 /* We don't need to close the sequence while we alter it. */
10347 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
10348
10349 /* Now we can close it. Keep the lock till end of transaction. */
10350 relation_close(seqRel, NoLock);
10351 }
10352
10353 systable_endscan(scan);
10354
10355 relation_close(depRel, AccessShareLock);
10356 }
10357
10358 /*
10359 * ALTER TABLE CLUSTER ON
10360 *
10361 * The only thing we have to do is to change the indisclustered bits.
10362 *
10363 * Return the address of the new clustering index.
10364 */
10365 static ObjectAddress
ATExecClusterOn(Relation rel,const char * indexName,LOCKMODE lockmode)10366 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
10367 {
10368 Oid indexOid;
10369 ObjectAddress address;
10370
10371 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
10372
10373 if (!OidIsValid(indexOid))
10374 ereport(ERROR,
10375 (errcode(ERRCODE_UNDEFINED_OBJECT),
10376 errmsg("index \"%s\" for table \"%s\" does not exist",
10377 indexName, RelationGetRelationName(rel))));
10378
10379 /* Check index is valid to cluster on */
10380 check_index_is_clusterable(rel, indexOid, false, lockmode);
10381
10382 /* And do the work */
10383 mark_index_clustered(rel, indexOid, false);
10384
10385 ObjectAddressSet(address,
10386 RelationRelationId, indexOid);
10387
10388 return address;
10389 }
10390
10391 /*
10392 * ALTER TABLE SET WITHOUT CLUSTER
10393 *
10394 * We have to find any indexes on the table that have indisclustered bit
10395 * set and turn it off.
10396 */
10397 static void
ATExecDropCluster(Relation rel,LOCKMODE lockmode)10398 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
10399 {
10400 mark_index_clustered(rel, InvalidOid, false);
10401 }
10402
10403 /*
10404 * ALTER TABLE SET TABLESPACE
10405 */
10406 static void
ATPrepSetTableSpace(AlteredTableInfo * tab,Relation rel,char * tablespacename,LOCKMODE lockmode)10407 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
10408 {
10409 Oid tablespaceId;
10410
10411 /* Check that the tablespace exists */
10412 tablespaceId = get_tablespace_oid(tablespacename, false);
10413
10414 /* Check permissions except when moving to database's default */
10415 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
10416 {
10417 AclResult aclresult;
10418
10419 aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
10420 if (aclresult != ACLCHECK_OK)
10421 aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
10422 }
10423
10424 /* Save info for Phase 3 to do the real work */
10425 if (OidIsValid(tab->newTableSpace))
10426 ereport(ERROR,
10427 (errcode(ERRCODE_SYNTAX_ERROR),
10428 errmsg("cannot have multiple SET TABLESPACE subcommands")));
10429
10430 tab->newTableSpace = tablespaceId;
10431 }
10432
10433 /*
10434 * Set, reset, or replace reloptions.
10435 */
10436 static void
ATExecSetRelOptions(Relation rel,List * defList,AlterTableType operation,LOCKMODE lockmode)10437 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
10438 LOCKMODE lockmode)
10439 {
10440 Oid relid;
10441 Relation pgclass;
10442 HeapTuple tuple;
10443 HeapTuple newtuple;
10444 Datum datum;
10445 bool isnull;
10446 Datum newOptions;
10447 Datum repl_val[Natts_pg_class];
10448 bool repl_null[Natts_pg_class];
10449 bool repl_repl[Natts_pg_class];
10450 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
10451
10452 if (defList == NIL && operation != AT_ReplaceRelOptions)
10453 return; /* nothing to do */
10454
10455 pgclass = heap_open(RelationRelationId, RowExclusiveLock);
10456
10457 /* Fetch heap tuple */
10458 relid = RelationGetRelid(rel);
10459 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
10460 if (!HeapTupleIsValid(tuple))
10461 elog(ERROR, "cache lookup failed for relation %u", relid);
10462
10463 if (operation == AT_ReplaceRelOptions)
10464 {
10465 /*
10466 * If we're supposed to replace the reloptions list, we just pretend
10467 * there were none before.
10468 */
10469 datum = (Datum) 0;
10470 isnull = true;
10471 }
10472 else
10473 {
10474 /* Get the old reloptions */
10475 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
10476 &isnull);
10477 }
10478
10479 /* Generate new proposed reloptions (text array) */
10480 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
10481 defList, NULL, validnsps, false,
10482 operation == AT_ResetRelOptions);
10483
10484 /* Validate */
10485 switch (rel->rd_rel->relkind)
10486 {
10487 case RELKIND_RELATION:
10488 case RELKIND_TOASTVALUE:
10489 case RELKIND_MATVIEW:
10490 case RELKIND_PARTITIONED_TABLE:
10491 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
10492 break;
10493 case RELKIND_VIEW:
10494 (void) view_reloptions(newOptions, true);
10495 break;
10496 case RELKIND_INDEX:
10497 (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
10498 break;
10499 default:
10500 ereport(ERROR,
10501 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10502 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
10503 RelationGetRelationName(rel))));
10504 break;
10505 }
10506
10507 /* Special-case validation of view options */
10508 if (rel->rd_rel->relkind == RELKIND_VIEW)
10509 {
10510 Query *view_query = get_view_query(rel);
10511 List *view_options = untransformRelOptions(newOptions);
10512 ListCell *cell;
10513 bool check_option = false;
10514
10515 foreach(cell, view_options)
10516 {
10517 DefElem *defel = (DefElem *) lfirst(cell);
10518
10519 if (pg_strcasecmp(defel->defname, "check_option") == 0)
10520 check_option = true;
10521 }
10522
10523 /*
10524 * If the check option is specified, look to see if the view is
10525 * actually auto-updatable or not.
10526 */
10527 if (check_option)
10528 {
10529 const char *view_updatable_error =
10530 view_query_is_auto_updatable(view_query, true);
10531
10532 if (view_updatable_error)
10533 ereport(ERROR,
10534 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10535 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
10536 errhint("%s", _(view_updatable_error))));
10537 }
10538 }
10539
10540 /*
10541 * All we need do here is update the pg_class row; the new options will be
10542 * propagated into relcaches during post-commit cache inval.
10543 */
10544 memset(repl_val, 0, sizeof(repl_val));
10545 memset(repl_null, false, sizeof(repl_null));
10546 memset(repl_repl, false, sizeof(repl_repl));
10547
10548 if (newOptions != (Datum) 0)
10549 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
10550 else
10551 repl_null[Anum_pg_class_reloptions - 1] = true;
10552
10553 repl_repl[Anum_pg_class_reloptions - 1] = true;
10554
10555 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
10556 repl_val, repl_null, repl_repl);
10557
10558 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
10559
10560 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
10561
10562 heap_freetuple(newtuple);
10563
10564 ReleaseSysCache(tuple);
10565
10566 /* repeat the whole exercise for the toast table, if there's one */
10567 if (OidIsValid(rel->rd_rel->reltoastrelid))
10568 {
10569 Relation toastrel;
10570 Oid toastid = rel->rd_rel->reltoastrelid;
10571
10572 toastrel = heap_open(toastid, lockmode);
10573
10574 /* Fetch heap tuple */
10575 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
10576 if (!HeapTupleIsValid(tuple))
10577 elog(ERROR, "cache lookup failed for relation %u", toastid);
10578
10579 if (operation == AT_ReplaceRelOptions)
10580 {
10581 /*
10582 * If we're supposed to replace the reloptions list, we just
10583 * pretend there were none before.
10584 */
10585 datum = (Datum) 0;
10586 isnull = true;
10587 }
10588 else
10589 {
10590 /* Get the old reloptions */
10591 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
10592 &isnull);
10593 }
10594
10595 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
10596 defList, "toast", validnsps, false,
10597 operation == AT_ResetRelOptions);
10598
10599 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
10600
10601 memset(repl_val, 0, sizeof(repl_val));
10602 memset(repl_null, false, sizeof(repl_null));
10603 memset(repl_repl, false, sizeof(repl_repl));
10604
10605 if (newOptions != (Datum) 0)
10606 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
10607 else
10608 repl_null[Anum_pg_class_reloptions - 1] = true;
10609
10610 repl_repl[Anum_pg_class_reloptions - 1] = true;
10611
10612 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
10613 repl_val, repl_null, repl_repl);
10614
10615 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
10616
10617 InvokeObjectPostAlterHookArg(RelationRelationId,
10618 RelationGetRelid(toastrel), 0,
10619 InvalidOid, true);
10620
10621 heap_freetuple(newtuple);
10622
10623 ReleaseSysCache(tuple);
10624
10625 heap_close(toastrel, NoLock);
10626 }
10627
10628 heap_close(pgclass, RowExclusiveLock);
10629 }
10630
10631 /*
10632 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
10633 * rewriting to be done, so we just want to copy the data as fast as possible.
10634 */
10635 static void
ATExecSetTableSpace(Oid tableOid,Oid newTableSpace,LOCKMODE lockmode)10636 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
10637 {
10638 Relation rel;
10639 Oid oldTableSpace;
10640 Oid reltoastrelid;
10641 Oid newrelfilenode;
10642 RelFileNode newrnode;
10643 SMgrRelation dstrel;
10644 Relation pg_class;
10645 HeapTuple tuple;
10646 Form_pg_class rd_rel;
10647 ForkNumber forkNum;
10648 List *reltoastidxids = NIL;
10649 ListCell *lc;
10650
10651 /*
10652 * Need lock here in case we are recursing to toast table or index
10653 */
10654 rel = relation_open(tableOid, lockmode);
10655
10656 /*
10657 * No work if no change in tablespace.
10658 */
10659 oldTableSpace = rel->rd_rel->reltablespace;
10660 if (newTableSpace == oldTableSpace ||
10661 (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
10662 {
10663 InvokeObjectPostAlterHook(RelationRelationId,
10664 RelationGetRelid(rel), 0);
10665
10666 relation_close(rel, NoLock);
10667 return;
10668 }
10669
10670 /*
10671 * We cannot support moving mapped relations into different tablespaces.
10672 * (In particular this eliminates all shared catalogs.)
10673 */
10674 if (RelationIsMapped(rel))
10675 ereport(ERROR,
10676 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10677 errmsg("cannot move system relation \"%s\"",
10678 RelationGetRelationName(rel))));
10679
10680 /* Can't move a non-shared relation into pg_global */
10681 if (newTableSpace == GLOBALTABLESPACE_OID)
10682 ereport(ERROR,
10683 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
10684 errmsg("only shared relations can be placed in pg_global tablespace")));
10685
10686 /*
10687 * Don't allow moving temp tables of other backends ... their local buffer
10688 * manager is not going to cope.
10689 */
10690 if (RELATION_IS_OTHER_TEMP(rel))
10691 ereport(ERROR,
10692 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10693 errmsg("cannot move temporary tables of other sessions")));
10694
10695 reltoastrelid = rel->rd_rel->reltoastrelid;
10696 /* Fetch the list of indexes on toast relation if necessary */
10697 if (OidIsValid(reltoastrelid))
10698 {
10699 Relation toastRel = relation_open(reltoastrelid, lockmode);
10700
10701 reltoastidxids = RelationGetIndexList(toastRel);
10702 relation_close(toastRel, lockmode);
10703 }
10704
10705 /* Get a modifiable copy of the relation's pg_class row */
10706 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
10707
10708 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tableOid));
10709 if (!HeapTupleIsValid(tuple))
10710 elog(ERROR, "cache lookup failed for relation %u", tableOid);
10711 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
10712
10713 /*
10714 * Since we copy the file directly without looking at the shared buffers,
10715 * we'd better first flush out any pages of the source relation that are
10716 * in shared buffers. We assume no new changes will be made while we are
10717 * holding exclusive lock on the rel.
10718 */
10719 FlushRelationBuffers(rel);
10720
10721 /*
10722 * Relfilenodes are not unique in databases across tablespaces, so we need
10723 * to allocate a new one in the new tablespace.
10724 */
10725 newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
10726 rel->rd_rel->relpersistence);
10727
10728 /* Open old and new relation */
10729 newrnode = rel->rd_node;
10730 newrnode.relNode = newrelfilenode;
10731 newrnode.spcNode = newTableSpace;
10732 dstrel = smgropen(newrnode, rel->rd_backend);
10733
10734 RelationOpenSmgr(rel);
10735
10736 /*
10737 * Create and copy all forks of the relation, and schedule unlinking of
10738 * old physical files.
10739 *
10740 * NOTE: any conflict in relfilenode value will be caught in
10741 * RelationCreateStorage().
10742 */
10743 RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
10744
10745 /* copy main fork */
10746 copy_relation_data(rel->rd_smgr, dstrel, MAIN_FORKNUM,
10747 rel->rd_rel->relpersistence);
10748
10749 /* copy those extra forks that exist */
10750 for (forkNum = MAIN_FORKNUM + 1; forkNum <= MAX_FORKNUM; forkNum++)
10751 {
10752 if (smgrexists(rel->rd_smgr, forkNum))
10753 {
10754 smgrcreate(dstrel, forkNum, false);
10755
10756 /*
10757 * WAL log creation if the relation is persistent, or this is the
10758 * init fork of an unlogged relation.
10759 */
10760 if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
10761 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
10762 forkNum == INIT_FORKNUM))
10763 log_smgrcreate(&newrnode, forkNum);
10764 copy_relation_data(rel->rd_smgr, dstrel, forkNum,
10765 rel->rd_rel->relpersistence);
10766 }
10767 }
10768
10769 /* drop old relation, and close new one */
10770 RelationDropStorage(rel);
10771 smgrclose(dstrel);
10772
10773 /* update the pg_class row */
10774 rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
10775 rd_rel->relfilenode = newrelfilenode;
10776 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
10777
10778 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
10779
10780 heap_freetuple(tuple);
10781
10782 heap_close(pg_class, RowExclusiveLock);
10783
10784 relation_close(rel, NoLock);
10785
10786 /* Make sure the reltablespace change is visible */
10787 CommandCounterIncrement();
10788
10789 /* Move associated toast relation and/or indexes, too */
10790 if (OidIsValid(reltoastrelid))
10791 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
10792 foreach(lc, reltoastidxids)
10793 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
10794
10795 /* Clean up */
10796 list_free(reltoastidxids);
10797 }
10798
10799 /*
10800 * Alter Table ALL ... SET TABLESPACE
10801 *
10802 * Allows a user to move all objects of some type in a given tablespace in the
10803 * current database to another tablespace. Objects can be chosen based on the
10804 * owner of the object also, to allow users to move only their objects.
10805 * The user must have CREATE rights on the new tablespace, as usual. The main
10806 * permissions handling is done by the lower-level table move function.
10807 *
10808 * All to-be-moved objects are locked first. If NOWAIT is specified and the
10809 * lock can't be acquired then we ereport(ERROR).
10810 */
10811 Oid
AlterTableMoveAll(AlterTableMoveAllStmt * stmt)10812 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
10813 {
10814 List *relations = NIL;
10815 ListCell *l;
10816 ScanKeyData key[1];
10817 Relation rel;
10818 HeapScanDesc scan;
10819 HeapTuple tuple;
10820 Oid orig_tablespaceoid;
10821 Oid new_tablespaceoid;
10822 List *role_oids = roleSpecsToIds(stmt->roles);
10823
10824 /* Ensure we were not asked to move something we can't */
10825 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
10826 stmt->objtype != OBJECT_MATVIEW)
10827 ereport(ERROR,
10828 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
10829 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
10830
10831 /* Get the orig and new tablespace OIDs */
10832 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
10833 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
10834
10835 /* Can't move shared relations in to or out of pg_global */
10836 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
10837 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
10838 new_tablespaceoid == GLOBALTABLESPACE_OID)
10839 ereport(ERROR,
10840 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
10841 errmsg("cannot move relations in to or out of pg_global tablespace")));
10842
10843 /*
10844 * Must have CREATE rights on the new tablespace, unless it is the
10845 * database default tablespace (which all users implicitly have CREATE
10846 * rights on).
10847 */
10848 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
10849 {
10850 AclResult aclresult;
10851
10852 aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(),
10853 ACL_CREATE);
10854 if (aclresult != ACLCHECK_OK)
10855 aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
10856 get_tablespace_name(new_tablespaceoid));
10857 }
10858
10859 /*
10860 * Now that the checks are done, check if we should set either to
10861 * InvalidOid because it is our database's default tablespace.
10862 */
10863 if (orig_tablespaceoid == MyDatabaseTableSpace)
10864 orig_tablespaceoid = InvalidOid;
10865
10866 if (new_tablespaceoid == MyDatabaseTableSpace)
10867 new_tablespaceoid = InvalidOid;
10868
10869 /* no-op */
10870 if (orig_tablespaceoid == new_tablespaceoid)
10871 return new_tablespaceoid;
10872
10873 /*
10874 * Walk the list of objects in the tablespace and move them. This will
10875 * only find objects in our database, of course.
10876 */
10877 ScanKeyInit(&key[0],
10878 Anum_pg_class_reltablespace,
10879 BTEqualStrategyNumber, F_OIDEQ,
10880 ObjectIdGetDatum(orig_tablespaceoid));
10881
10882 rel = heap_open(RelationRelationId, AccessShareLock);
10883 scan = heap_beginscan_catalog(rel, 1, key);
10884 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
10885 {
10886 Oid relOid = HeapTupleGetOid(tuple);
10887 Form_pg_class relForm;
10888
10889 relForm = (Form_pg_class) GETSTRUCT(tuple);
10890
10891 /*
10892 * Do not move objects in pg_catalog as part of this, if an admin
10893 * really wishes to do so, they can issue the individual ALTER
10894 * commands directly.
10895 *
10896 * Also, explicitly avoid any shared tables, temp tables, or TOAST
10897 * (TOAST will be moved with the main table).
10898 */
10899 if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared ||
10900 isAnyTempNamespace(relForm->relnamespace) ||
10901 relForm->relnamespace == PG_TOAST_NAMESPACE)
10902 continue;
10903
10904 /* Only move the object type requested */
10905 if ((stmt->objtype == OBJECT_TABLE &&
10906 relForm->relkind != RELKIND_RELATION &&
10907 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
10908 (stmt->objtype == OBJECT_INDEX &&
10909 relForm->relkind != RELKIND_INDEX) ||
10910 (stmt->objtype == OBJECT_MATVIEW &&
10911 relForm->relkind != RELKIND_MATVIEW))
10912 continue;
10913
10914 /* Check if we are only moving objects owned by certain roles */
10915 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
10916 continue;
10917
10918 /*
10919 * Handle permissions-checking here since we are locking the tables
10920 * and also to avoid doing a bunch of work only to fail part-way. Note
10921 * that permissions will also be checked by AlterTableInternal().
10922 *
10923 * Caller must be considered an owner on the table to move it.
10924 */
10925 if (!pg_class_ownercheck(relOid, GetUserId()))
10926 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
10927 NameStr(relForm->relname));
10928
10929 if (stmt->nowait &&
10930 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
10931 ereport(ERROR,
10932 (errcode(ERRCODE_OBJECT_IN_USE),
10933 errmsg("aborting because lock on relation \"%s.%s\" is not available",
10934 get_namespace_name(relForm->relnamespace),
10935 NameStr(relForm->relname))));
10936 else
10937 LockRelationOid(relOid, AccessExclusiveLock);
10938
10939 /* Add to our list of objects to move */
10940 relations = lappend_oid(relations, relOid);
10941 }
10942
10943 heap_endscan(scan);
10944 heap_close(rel, AccessShareLock);
10945
10946 if (relations == NIL)
10947 ereport(NOTICE,
10948 (errcode(ERRCODE_NO_DATA_FOUND),
10949 errmsg("no matching relations in tablespace \"%s\" found",
10950 orig_tablespaceoid == InvalidOid ? "(database default)" :
10951 get_tablespace_name(orig_tablespaceoid))));
10952
10953 /* Everything is locked, loop through and move all of the relations. */
10954 foreach(l, relations)
10955 {
10956 List *cmds = NIL;
10957 AlterTableCmd *cmd = makeNode(AlterTableCmd);
10958
10959 cmd->subtype = AT_SetTableSpace;
10960 cmd->name = stmt->new_tablespacename;
10961
10962 cmds = lappend(cmds, cmd);
10963
10964 EventTriggerAlterTableStart((Node *) stmt);
10965 /* OID is set by AlterTableInternal */
10966 AlterTableInternal(lfirst_oid(l), cmds, false);
10967 EventTriggerAlterTableEnd();
10968 }
10969
10970 return new_tablespaceoid;
10971 }
10972
10973 /*
10974 * Copy data, block by block
10975 */
10976 static void
copy_relation_data(SMgrRelation src,SMgrRelation dst,ForkNumber forkNum,char relpersistence)10977 copy_relation_data(SMgrRelation src, SMgrRelation dst,
10978 ForkNumber forkNum, char relpersistence)
10979 {
10980 PGAlignedBlock buf;
10981 Page page;
10982 bool use_wal;
10983 bool copying_initfork;
10984 BlockNumber nblocks;
10985 BlockNumber blkno;
10986
10987 page = (Page) buf.data;
10988
10989 /*
10990 * The init fork for an unlogged relation in many respects has to be
10991 * treated the same as normal relation, changes need to be WAL logged and
10992 * it needs to be synced to disk.
10993 */
10994 copying_initfork = relpersistence == RELPERSISTENCE_UNLOGGED &&
10995 forkNum == INIT_FORKNUM;
10996
10997 /*
10998 * We need to log the copied data in WAL iff WAL archiving/streaming is
10999 * enabled AND it's a permanent relation.
11000 */
11001 use_wal = XLogIsNeeded() &&
11002 (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork);
11003
11004 nblocks = smgrnblocks(src, forkNum);
11005
11006 for (blkno = 0; blkno < nblocks; blkno++)
11007 {
11008 /* If we got a cancel signal during the copy of the data, quit */
11009 CHECK_FOR_INTERRUPTS();
11010
11011 smgrread(src, forkNum, blkno, buf.data);
11012
11013 if (!PageIsVerified(page, blkno))
11014 ereport(ERROR,
11015 (errcode(ERRCODE_DATA_CORRUPTED),
11016 errmsg("invalid page in block %u of relation %s",
11017 blkno,
11018 relpathbackend(src->smgr_rnode.node,
11019 src->smgr_rnode.backend,
11020 forkNum))));
11021
11022 /*
11023 * WAL-log the copied page. Unfortunately we don't know what kind of a
11024 * page this is, so we have to log the full page including any unused
11025 * space.
11026 */
11027 if (use_wal)
11028 log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
11029
11030 PageSetChecksumInplace(page, blkno);
11031
11032 /*
11033 * Now write the page. We say isTemp = true even if it's not a temp
11034 * rel, because there's no need for smgr to schedule an fsync for this
11035 * write; we'll do it ourselves below.
11036 */
11037 smgrextend(dst, forkNum, blkno, buf.data, true);
11038 }
11039
11040 /*
11041 * If the rel is WAL-logged, must fsync before commit. We use heap_sync
11042 * to ensure that the toast table gets fsync'd too. (For a temp or
11043 * unlogged rel we don't care since the data will be gone after a crash
11044 * anyway.)
11045 *
11046 * It's obvious that we must do this when not WAL-logging the copy. It's
11047 * less obvious that we have to do it even if we did WAL-log the copied
11048 * pages. The reason is that since we're copying outside shared buffers, a
11049 * CHECKPOINT occurring during the copy has no way to flush the previously
11050 * written data to disk (indeed it won't know the new rel even exists). A
11051 * crash later on would replay WAL from the checkpoint, therefore it
11052 * wouldn't replay our earlier WAL entries. If we do not fsync those pages
11053 * here, they might still not be on disk when the crash occurs.
11054 */
11055 if (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork)
11056 smgrimmedsync(dst, forkNum);
11057 }
11058
11059 /*
11060 * ALTER TABLE ENABLE/DISABLE TRIGGER
11061 *
11062 * We just pass this off to trigger.c.
11063 */
11064 static void
ATExecEnableDisableTrigger(Relation rel,char * trigname,char fires_when,bool skip_system,LOCKMODE lockmode)11065 ATExecEnableDisableTrigger(Relation rel, char *trigname,
11066 char fires_when, bool skip_system, LOCKMODE lockmode)
11067 {
11068 EnableDisableTrigger(rel, trigname, fires_when, skip_system);
11069 }
11070
11071 /*
11072 * ALTER TABLE ENABLE/DISABLE RULE
11073 *
11074 * We just pass this off to rewriteDefine.c.
11075 */
11076 static void
ATExecEnableDisableRule(Relation rel,char * rulename,char fires_when,LOCKMODE lockmode)11077 ATExecEnableDisableRule(Relation rel, char *rulename,
11078 char fires_when, LOCKMODE lockmode)
11079 {
11080 EnableDisableRule(rel, rulename, fires_when);
11081 }
11082
11083 /*
11084 * ALTER TABLE INHERIT
11085 *
11086 * Add a parent to the child's parents. This verifies that all the columns and
11087 * check constraints of the parent appear in the child and that they have the
11088 * same data types and expressions.
11089 */
11090 static void
ATPrepAddInherit(Relation child_rel)11091 ATPrepAddInherit(Relation child_rel)
11092 {
11093 if (child_rel->rd_rel->reloftype)
11094 ereport(ERROR,
11095 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11096 errmsg("cannot change inheritance of typed table")));
11097
11098 if (child_rel->rd_rel->relispartition)
11099 ereport(ERROR,
11100 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11101 errmsg("cannot change inheritance of a partition")));
11102
11103 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11104 ereport(ERROR,
11105 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11106 errmsg("cannot change inheritance of partitioned table")));
11107 }
11108
11109 /*
11110 * Return the address of the new parent relation.
11111 */
11112 static ObjectAddress
ATExecAddInherit(Relation child_rel,RangeVar * parent,LOCKMODE lockmode)11113 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
11114 {
11115 Relation parent_rel;
11116 List *children;
11117 ObjectAddress address;
11118 const char *trigger_name;
11119
11120 /*
11121 * A self-exclusive lock is needed here. See the similar case in
11122 * MergeAttributes() for a full explanation.
11123 */
11124 parent_rel = heap_openrv(parent, ShareUpdateExclusiveLock);
11125
11126 /*
11127 * Must be owner of both parent and child -- child was checked by
11128 * ATSimplePermissions call in ATPrepCmd
11129 */
11130 ATSimplePermissions(parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
11131
11132 /* Permanent rels cannot inherit from temporary ones */
11133 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
11134 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
11135 ereport(ERROR,
11136 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11137 errmsg("cannot inherit from temporary relation \"%s\"",
11138 RelationGetRelationName(parent_rel))));
11139
11140 /* If parent rel is temp, it must belong to this session */
11141 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
11142 !parent_rel->rd_islocaltemp)
11143 ereport(ERROR,
11144 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11145 errmsg("cannot inherit from temporary relation of another session")));
11146
11147 /* Ditto for the child */
11148 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
11149 !child_rel->rd_islocaltemp)
11150 ereport(ERROR,
11151 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11152 errmsg("cannot inherit to temporary relation of another session")));
11153
11154 /* Prevent partitioned tables from becoming inheritance parents */
11155 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11156 ereport(ERROR,
11157 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11158 errmsg("cannot inherit from partitioned table \"%s\"",
11159 parent->relname)));
11160
11161 /* Likewise for partitions */
11162 if (parent_rel->rd_rel->relispartition)
11163 ereport(ERROR,
11164 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11165 errmsg("cannot inherit from a partition")));
11166
11167 /*
11168 * Prevent circularity by seeing if proposed parent inherits from child.
11169 * (In particular, this disallows making a rel inherit from itself.)
11170 *
11171 * This is not completely bulletproof because of race conditions: in
11172 * multi-level inheritance trees, someone else could concurrently be
11173 * making another inheritance link that closes the loop but does not join
11174 * either of the rels we have locked. Preventing that seems to require
11175 * exclusive locks on the entire inheritance tree, which is a cure worse
11176 * than the disease. find_all_inheritors() will cope with circularity
11177 * anyway, so don't sweat it too much.
11178 *
11179 * We use weakest lock we can on child's children, namely AccessShareLock.
11180 */
11181 children = find_all_inheritors(RelationGetRelid(child_rel),
11182 AccessShareLock, NULL);
11183
11184 if (list_member_oid(children, RelationGetRelid(parent_rel)))
11185 ereport(ERROR,
11186 (errcode(ERRCODE_DUPLICATE_TABLE),
11187 errmsg("circular inheritance not allowed"),
11188 errdetail("\"%s\" is already a child of \"%s\".",
11189 parent->relname,
11190 RelationGetRelationName(child_rel))));
11191
11192 /* If parent has OIDs then child must have OIDs */
11193 if (parent_rel->rd_rel->relhasoids && !child_rel->rd_rel->relhasoids)
11194 ereport(ERROR,
11195 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11196 errmsg("table \"%s\" without OIDs cannot inherit from table \"%s\" with OIDs",
11197 RelationGetRelationName(child_rel),
11198 RelationGetRelationName(parent_rel))));
11199
11200 /*
11201 * If child_rel has row-level triggers with transition tables, we
11202 * currently don't allow it to become an inheritance child. See also
11203 * prohibitions in ATExecAttachPartition() and CreateTrigger().
11204 */
11205 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
11206 if (trigger_name != NULL)
11207 ereport(ERROR,
11208 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11209 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
11210 trigger_name, RelationGetRelationName(child_rel)),
11211 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies")));
11212
11213 /* OK to create inheritance */
11214 CreateInheritance(child_rel, parent_rel);
11215
11216 ObjectAddressSet(address, RelationRelationId,
11217 RelationGetRelid(parent_rel));
11218
11219 /* keep our lock on the parent relation until commit */
11220 heap_close(parent_rel, NoLock);
11221
11222 return address;
11223 }
11224
11225 /*
11226 * CreateInheritance
11227 * Catalog manipulation portion of creating inheritance between a child
11228 * table and a parent table.
11229 *
11230 * Common to ATExecAddInherit() and ATExecAttachPartition().
11231 */
11232 static void
CreateInheritance(Relation child_rel,Relation parent_rel)11233 CreateInheritance(Relation child_rel, Relation parent_rel)
11234 {
11235 Relation catalogRelation;
11236 SysScanDesc scan;
11237 ScanKeyData key;
11238 HeapTuple inheritsTuple;
11239 int32 inhseqno;
11240
11241 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
11242 catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
11243
11244 /*
11245 * Check for duplicates in the list of parents, and determine the highest
11246 * inhseqno already present; we'll use the next one for the new parent.
11247 * Also, if proposed child is a partition, it cannot already be
11248 * inheriting.
11249 *
11250 * Note: we do not reject the case where the child already inherits from
11251 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
11252 */
11253 ScanKeyInit(&key,
11254 Anum_pg_inherits_inhrelid,
11255 BTEqualStrategyNumber, F_OIDEQ,
11256 ObjectIdGetDatum(RelationGetRelid(child_rel)));
11257 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
11258 true, NULL, 1, &key);
11259
11260 /* inhseqno sequences start at 1 */
11261 inhseqno = 0;
11262 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
11263 {
11264 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
11265
11266 if (inh->inhparent == RelationGetRelid(parent_rel))
11267 ereport(ERROR,
11268 (errcode(ERRCODE_DUPLICATE_TABLE),
11269 errmsg("relation \"%s\" would be inherited from more than once",
11270 RelationGetRelationName(parent_rel))));
11271
11272 if (inh->inhseqno > inhseqno)
11273 inhseqno = inh->inhseqno;
11274 }
11275 systable_endscan(scan);
11276
11277 /* Match up the columns and bump attinhcount as needed */
11278 MergeAttributesIntoExisting(child_rel, parent_rel);
11279
11280 /* Match up the constraints and bump coninhcount as needed */
11281 MergeConstraintsIntoExisting(child_rel, parent_rel);
11282
11283 /*
11284 * OK, it looks valid. Make the catalog entries that show inheritance.
11285 */
11286 StoreCatalogInheritance1(RelationGetRelid(child_rel),
11287 RelationGetRelid(parent_rel),
11288 inhseqno + 1,
11289 catalogRelation,
11290 parent_rel->rd_rel->relkind ==
11291 RELKIND_PARTITIONED_TABLE);
11292
11293 /* Now we're done with pg_inherits */
11294 heap_close(catalogRelation, RowExclusiveLock);
11295 }
11296
11297 /*
11298 * Obtain the source-text form of the constraint expression for a check
11299 * constraint, given its pg_constraint tuple
11300 */
11301 static char *
decompile_conbin(HeapTuple contup,TupleDesc tupdesc)11302 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
11303 {
11304 Form_pg_constraint con;
11305 bool isnull;
11306 Datum attr;
11307 Datum expr;
11308
11309 con = (Form_pg_constraint) GETSTRUCT(contup);
11310 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
11311 if (isnull)
11312 elog(ERROR, "null conbin for constraint %u", HeapTupleGetOid(contup));
11313
11314 expr = DirectFunctionCall2(pg_get_expr, attr,
11315 ObjectIdGetDatum(con->conrelid));
11316 return TextDatumGetCString(expr);
11317 }
11318
11319 /*
11320 * Determine whether two check constraints are functionally equivalent
11321 *
11322 * The test we apply is to see whether they reverse-compile to the same
11323 * source string. This insulates us from issues like whether attributes
11324 * have the same physical column numbers in parent and child relations.
11325 */
11326 static bool
constraints_equivalent(HeapTuple a,HeapTuple b,TupleDesc tupleDesc)11327 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
11328 {
11329 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
11330 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
11331
11332 if (acon->condeferrable != bcon->condeferrable ||
11333 acon->condeferred != bcon->condeferred ||
11334 strcmp(decompile_conbin(a, tupleDesc),
11335 decompile_conbin(b, tupleDesc)) != 0)
11336 return false;
11337 else
11338 return true;
11339 }
11340
11341 /*
11342 * Check columns in child table match up with columns in parent, and increment
11343 * their attinhcount.
11344 *
11345 * Called by CreateInheritance
11346 *
11347 * Currently all parent columns must be found in child. Missing columns are an
11348 * error. One day we might consider creating new columns like CREATE TABLE
11349 * does. However, that is widely unpopular --- in the common use case of
11350 * partitioned tables it's a foot-gun.
11351 *
11352 * The data type must match exactly. If the parent column is NOT NULL then
11353 * the child must be as well. Defaults are not compared, however.
11354 */
11355 static void
MergeAttributesIntoExisting(Relation child_rel,Relation parent_rel)11356 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
11357 {
11358 Relation attrrel;
11359 AttrNumber parent_attno;
11360 int parent_natts;
11361 TupleDesc tupleDesc;
11362 HeapTuple tuple;
11363 bool child_is_partition = false;
11364
11365 attrrel = heap_open(AttributeRelationId, RowExclusiveLock);
11366
11367 tupleDesc = RelationGetDescr(parent_rel);
11368 parent_natts = tupleDesc->natts;
11369
11370 /* If parent_rel is a partitioned table, child_rel must be a partition */
11371 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11372 child_is_partition = true;
11373
11374 for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
11375 {
11376 Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
11377 char *attributeName = NameStr(attribute->attname);
11378
11379 /* Ignore dropped columns in the parent. */
11380 if (attribute->attisdropped)
11381 continue;
11382
11383 /* Find same column in child (matching on column name). */
11384 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
11385 attributeName);
11386 if (HeapTupleIsValid(tuple))
11387 {
11388 /* Check they are same type, typmod, and collation */
11389 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
11390
11391 if (attribute->atttypid != childatt->atttypid ||
11392 attribute->atttypmod != childatt->atttypmod)
11393 ereport(ERROR,
11394 (errcode(ERRCODE_DATATYPE_MISMATCH),
11395 errmsg("child table \"%s\" has different type for column \"%s\"",
11396 RelationGetRelationName(child_rel),
11397 attributeName)));
11398
11399 if (attribute->attcollation != childatt->attcollation)
11400 ereport(ERROR,
11401 (errcode(ERRCODE_COLLATION_MISMATCH),
11402 errmsg("child table \"%s\" has different collation for column \"%s\"",
11403 RelationGetRelationName(child_rel),
11404 attributeName)));
11405
11406 /*
11407 * Check child doesn't discard NOT NULL property. (Other
11408 * constraints are checked elsewhere.)
11409 */
11410 if (attribute->attnotnull && !childatt->attnotnull)
11411 ereport(ERROR,
11412 (errcode(ERRCODE_DATATYPE_MISMATCH),
11413 errmsg("column \"%s\" in child table must be marked NOT NULL",
11414 attributeName)));
11415
11416 /*
11417 * OK, bump the child column's inheritance count. (If we fail
11418 * later on, this change will just roll back.)
11419 */
11420 childatt->attinhcount++;
11421
11422 /*
11423 * In case of partitions, we must enforce that value of attislocal
11424 * is same in all partitions. (Note: there are only inherited
11425 * attributes in partitions)
11426 */
11427 if (child_is_partition)
11428 {
11429 Assert(childatt->attinhcount == 1);
11430 childatt->attislocal = false;
11431 }
11432
11433 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
11434 heap_freetuple(tuple);
11435 }
11436 else
11437 {
11438 ereport(ERROR,
11439 (errcode(ERRCODE_DATATYPE_MISMATCH),
11440 errmsg("child table is missing column \"%s\"",
11441 attributeName)));
11442 }
11443 }
11444
11445 /*
11446 * If the parent has an OID column, so must the child, and we'd better
11447 * update the child's attinhcount and attislocal the same as for normal
11448 * columns. We needn't check data type or not-nullness though.
11449 */
11450 if (tupleDesc->tdhasoid)
11451 {
11452 /*
11453 * Here we match by column number not name; the match *must* be the
11454 * system column, not some random column named "oid".
11455 */
11456 tuple = SearchSysCacheCopy2(ATTNUM,
11457 ObjectIdGetDatum(RelationGetRelid(child_rel)),
11458 Int16GetDatum(ObjectIdAttributeNumber));
11459 if (HeapTupleIsValid(tuple))
11460 {
11461 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
11462
11463 /* See comments above; these changes should be the same */
11464 childatt->attinhcount++;
11465
11466 if (child_is_partition)
11467 {
11468 Assert(childatt->attinhcount == 1);
11469 childatt->attislocal = false;
11470 }
11471
11472 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
11473 heap_freetuple(tuple);
11474 }
11475 else
11476 {
11477 ereport(ERROR,
11478 (errcode(ERRCODE_DATATYPE_MISMATCH),
11479 errmsg("child table is missing column \"%s\"",
11480 "oid")));
11481 }
11482 }
11483
11484 heap_close(attrrel, RowExclusiveLock);
11485 }
11486
11487 /*
11488 * Check constraints in child table match up with constraints in parent,
11489 * and increment their coninhcount.
11490 *
11491 * Constraints that are marked ONLY in the parent are ignored.
11492 *
11493 * Called by CreateInheritance
11494 *
11495 * Currently all constraints in parent must be present in the child. One day we
11496 * may consider adding new constraints like CREATE TABLE does.
11497 *
11498 * XXX This is O(N^2) which may be an issue with tables with hundreds of
11499 * constraints. As long as tables have more like 10 constraints it shouldn't be
11500 * a problem though. Even 100 constraints ought not be the end of the world.
11501 *
11502 * XXX See MergeWithExistingConstraint too if you change this code.
11503 */
11504 static void
MergeConstraintsIntoExisting(Relation child_rel,Relation parent_rel)11505 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
11506 {
11507 Relation catalog_relation;
11508 TupleDesc tuple_desc;
11509 SysScanDesc parent_scan;
11510 ScanKeyData parent_key;
11511 HeapTuple parent_tuple;
11512 bool child_is_partition = false;
11513
11514 catalog_relation = heap_open(ConstraintRelationId, RowExclusiveLock);
11515 tuple_desc = RelationGetDescr(catalog_relation);
11516
11517 /* If parent_rel is a partitioned table, child_rel must be a partition */
11518 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11519 child_is_partition = true;
11520
11521 /* Outer loop scans through the parent's constraint definitions */
11522 ScanKeyInit(&parent_key,
11523 Anum_pg_constraint_conrelid,
11524 BTEqualStrategyNumber, F_OIDEQ,
11525 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
11526 parent_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
11527 true, NULL, 1, &parent_key);
11528
11529 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
11530 {
11531 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
11532 SysScanDesc child_scan;
11533 ScanKeyData child_key;
11534 HeapTuple child_tuple;
11535 bool found = false;
11536
11537 if (parent_con->contype != CONSTRAINT_CHECK)
11538 continue;
11539
11540 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
11541 if (parent_con->connoinherit)
11542 continue;
11543
11544 /* Search for a child constraint matching this one */
11545 ScanKeyInit(&child_key,
11546 Anum_pg_constraint_conrelid,
11547 BTEqualStrategyNumber, F_OIDEQ,
11548 ObjectIdGetDatum(RelationGetRelid(child_rel)));
11549 child_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
11550 true, NULL, 1, &child_key);
11551
11552 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
11553 {
11554 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
11555 HeapTuple child_copy;
11556
11557 if (child_con->contype != CONSTRAINT_CHECK)
11558 continue;
11559
11560 if (strcmp(NameStr(parent_con->conname),
11561 NameStr(child_con->conname)) != 0)
11562 continue;
11563
11564 if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
11565 ereport(ERROR,
11566 (errcode(ERRCODE_DATATYPE_MISMATCH),
11567 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
11568 RelationGetRelationName(child_rel),
11569 NameStr(parent_con->conname))));
11570
11571 /* If the child constraint is "no inherit" then cannot merge */
11572 if (child_con->connoinherit)
11573 ereport(ERROR,
11574 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11575 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
11576 NameStr(child_con->conname),
11577 RelationGetRelationName(child_rel))));
11578
11579 /*
11580 * If the child constraint is "not valid" then cannot merge with a
11581 * valid parent constraint
11582 */
11583 if (parent_con->convalidated && !child_con->convalidated)
11584 ereport(ERROR,
11585 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11586 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
11587 NameStr(child_con->conname),
11588 RelationGetRelationName(child_rel))));
11589
11590 /*
11591 * OK, bump the child constraint's inheritance count. (If we fail
11592 * later on, this change will just roll back.)
11593 */
11594 child_copy = heap_copytuple(child_tuple);
11595 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
11596 child_con->coninhcount++;
11597
11598 /*
11599 * In case of partitions, an inherited constraint must be
11600 * inherited only once since it cannot have multiple parents and
11601 * it is never considered local.
11602 */
11603 if (child_is_partition)
11604 {
11605 Assert(child_con->coninhcount == 1);
11606 child_con->conislocal = false;
11607 }
11608
11609 CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
11610 heap_freetuple(child_copy);
11611
11612 found = true;
11613 break;
11614 }
11615
11616 systable_endscan(child_scan);
11617
11618 if (!found)
11619 ereport(ERROR,
11620 (errcode(ERRCODE_DATATYPE_MISMATCH),
11621 errmsg("child table is missing constraint \"%s\"",
11622 NameStr(parent_con->conname))));
11623 }
11624
11625 systable_endscan(parent_scan);
11626 heap_close(catalog_relation, RowExclusiveLock);
11627 }
11628
11629 /*
11630 * ALTER TABLE NO INHERIT
11631 *
11632 * Return value is the address of the relation that is no longer parent.
11633 */
11634 static ObjectAddress
ATExecDropInherit(Relation rel,RangeVar * parent,LOCKMODE lockmode)11635 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
11636 {
11637 ObjectAddress address;
11638 Relation parent_rel;
11639
11640 if (rel->rd_rel->relispartition)
11641 ereport(ERROR,
11642 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11643 errmsg("cannot change inheritance of a partition")));
11644
11645 /*
11646 * AccessShareLock on the parent is probably enough, seeing that DROP
11647 * TABLE doesn't lock parent tables at all. We need some lock since we'll
11648 * be inspecting the parent's schema.
11649 */
11650 parent_rel = heap_openrv(parent, AccessShareLock);
11651
11652 /*
11653 * We don't bother to check ownership of the parent table --- ownership of
11654 * the child is presumed enough rights.
11655 */
11656
11657 /* Off to RemoveInheritance() where most of the work happens */
11658 RemoveInheritance(rel, parent_rel);
11659
11660 ObjectAddressSet(address, RelationRelationId,
11661 RelationGetRelid(parent_rel));
11662
11663 /* keep our lock on the parent relation until commit */
11664 heap_close(parent_rel, NoLock);
11665
11666 return address;
11667 }
11668
11669 /*
11670 * RemoveInheritance
11671 *
11672 * Drop a parent from the child's parents. This just adjusts the attinhcount
11673 * and attislocal of the columns and removes the pg_inherit and pg_depend
11674 * entries.
11675 *
11676 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
11677 * up attislocal stays true, which means if a child is ever removed from a
11678 * parent then its columns will never be automatically dropped which may
11679 * surprise. But at least we'll never surprise by dropping columns someone
11680 * isn't expecting to be dropped which would actually mean data loss.
11681 *
11682 * coninhcount and conislocal for inherited constraints are adjusted in
11683 * exactly the same way.
11684 *
11685 * Common to ATExecDropInherit() and ATExecDetachPartition().
11686 */
11687 static void
RemoveInheritance(Relation child_rel,Relation parent_rel)11688 RemoveInheritance(Relation child_rel, Relation parent_rel)
11689 {
11690 Relation catalogRelation;
11691 SysScanDesc scan;
11692 ScanKeyData key[3];
11693 HeapTuple inheritsTuple,
11694 attributeTuple,
11695 constraintTuple;
11696 List *connames;
11697 bool found = false;
11698 bool child_is_partition = false;
11699
11700 /* If parent_rel is a partitioned table, child_rel must be a partition */
11701 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11702 child_is_partition = true;
11703
11704 /*
11705 * Find and destroy the pg_inherits entry linking the two, or error out if
11706 * there is none.
11707 */
11708 catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
11709 ScanKeyInit(&key[0],
11710 Anum_pg_inherits_inhrelid,
11711 BTEqualStrategyNumber, F_OIDEQ,
11712 ObjectIdGetDatum(RelationGetRelid(child_rel)));
11713 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
11714 true, NULL, 1, key);
11715
11716 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
11717 {
11718 Oid inhparent;
11719
11720 inhparent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
11721 if (inhparent == RelationGetRelid(parent_rel))
11722 {
11723 CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
11724 found = true;
11725 break;
11726 }
11727 }
11728
11729 systable_endscan(scan);
11730 heap_close(catalogRelation, RowExclusiveLock);
11731
11732 if (!found)
11733 {
11734 if (child_is_partition)
11735 ereport(ERROR,
11736 (errcode(ERRCODE_UNDEFINED_TABLE),
11737 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
11738 RelationGetRelationName(child_rel),
11739 RelationGetRelationName(parent_rel))));
11740 else
11741 ereport(ERROR,
11742 (errcode(ERRCODE_UNDEFINED_TABLE),
11743 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
11744 RelationGetRelationName(parent_rel),
11745 RelationGetRelationName(child_rel))));
11746 }
11747
11748 /*
11749 * Search through child columns looking for ones matching parent rel
11750 */
11751 catalogRelation = heap_open(AttributeRelationId, RowExclusiveLock);
11752 ScanKeyInit(&key[0],
11753 Anum_pg_attribute_attrelid,
11754 BTEqualStrategyNumber, F_OIDEQ,
11755 ObjectIdGetDatum(RelationGetRelid(child_rel)));
11756 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
11757 true, NULL, 1, key);
11758 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
11759 {
11760 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
11761
11762 /* Ignore if dropped or not inherited */
11763 if (att->attisdropped)
11764 continue;
11765 if (att->attinhcount <= 0)
11766 continue;
11767
11768 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
11769 NameStr(att->attname)))
11770 {
11771 /* Decrement inhcount and possibly set islocal to true */
11772 HeapTuple copyTuple = heap_copytuple(attributeTuple);
11773 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
11774
11775 copy_att->attinhcount--;
11776 if (copy_att->attinhcount == 0)
11777 copy_att->attislocal = true;
11778
11779 CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
11780 heap_freetuple(copyTuple);
11781 }
11782 }
11783 systable_endscan(scan);
11784 heap_close(catalogRelation, RowExclusiveLock);
11785
11786 /*
11787 * Likewise, find inherited check constraints and disinherit them. To do
11788 * this, we first need a list of the names of the parent's check
11789 * constraints. (We cheat a bit by only checking for name matches,
11790 * assuming that the expressions will match.)
11791 */
11792 catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
11793 ScanKeyInit(&key[0],
11794 Anum_pg_constraint_conrelid,
11795 BTEqualStrategyNumber, F_OIDEQ,
11796 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
11797 scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
11798 true, NULL, 1, key);
11799
11800 connames = NIL;
11801
11802 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
11803 {
11804 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
11805
11806 if (con->contype == CONSTRAINT_CHECK)
11807 connames = lappend(connames, pstrdup(NameStr(con->conname)));
11808 }
11809
11810 systable_endscan(scan);
11811
11812 /* Now scan the child's constraints */
11813 ScanKeyInit(&key[0],
11814 Anum_pg_constraint_conrelid,
11815 BTEqualStrategyNumber, F_OIDEQ,
11816 ObjectIdGetDatum(RelationGetRelid(child_rel)));
11817 scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
11818 true, NULL, 1, key);
11819
11820 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
11821 {
11822 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
11823 bool match;
11824 ListCell *lc;
11825
11826 if (con->contype != CONSTRAINT_CHECK)
11827 continue;
11828
11829 match = false;
11830 foreach(lc, connames)
11831 {
11832 if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
11833 {
11834 match = true;
11835 break;
11836 }
11837 }
11838
11839 if (match)
11840 {
11841 /* Decrement inhcount and possibly set islocal to true */
11842 HeapTuple copyTuple = heap_copytuple(constraintTuple);
11843 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11844
11845 if (copy_con->coninhcount <= 0) /* shouldn't happen */
11846 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
11847 RelationGetRelid(child_rel), NameStr(copy_con->conname));
11848
11849 copy_con->coninhcount--;
11850 if (copy_con->coninhcount == 0)
11851 copy_con->conislocal = true;
11852
11853 CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
11854 heap_freetuple(copyTuple);
11855 }
11856 }
11857
11858 systable_endscan(scan);
11859 heap_close(catalogRelation, RowExclusiveLock);
11860
11861 drop_parent_dependency(RelationGetRelid(child_rel),
11862 RelationRelationId,
11863 RelationGetRelid(parent_rel),
11864 child_dependency_type(child_is_partition));
11865
11866 /*
11867 * Post alter hook of this inherits. Since object_access_hook doesn't take
11868 * multiple object identifiers, we relay oid of parent relation using
11869 * auxiliary_id argument.
11870 */
11871 InvokeObjectPostAlterHookArg(InheritsRelationId,
11872 RelationGetRelid(child_rel), 0,
11873 RelationGetRelid(parent_rel), false);
11874 }
11875
11876 /*
11877 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
11878 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
11879 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
11880 * be TypeRelationId). There's no convenient way to do this, so go trawling
11881 * through pg_depend.
11882 */
11883 static void
drop_parent_dependency(Oid relid,Oid refclassid,Oid refobjid,DependencyType deptype)11884 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
11885 DependencyType deptype)
11886 {
11887 Relation catalogRelation;
11888 SysScanDesc scan;
11889 ScanKeyData key[3];
11890 HeapTuple depTuple;
11891
11892 catalogRelation = heap_open(DependRelationId, RowExclusiveLock);
11893
11894 ScanKeyInit(&key[0],
11895 Anum_pg_depend_classid,
11896 BTEqualStrategyNumber, F_OIDEQ,
11897 ObjectIdGetDatum(RelationRelationId));
11898 ScanKeyInit(&key[1],
11899 Anum_pg_depend_objid,
11900 BTEqualStrategyNumber, F_OIDEQ,
11901 ObjectIdGetDatum(relid));
11902 ScanKeyInit(&key[2],
11903 Anum_pg_depend_objsubid,
11904 BTEqualStrategyNumber, F_INT4EQ,
11905 Int32GetDatum(0));
11906
11907 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
11908 NULL, 3, key);
11909
11910 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
11911 {
11912 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
11913
11914 if (dep->refclassid == refclassid &&
11915 dep->refobjid == refobjid &&
11916 dep->refobjsubid == 0 &&
11917 dep->deptype == deptype)
11918 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
11919 }
11920
11921 systable_endscan(scan);
11922 heap_close(catalogRelation, RowExclusiveLock);
11923 }
11924
11925 /*
11926 * ALTER TABLE OF
11927 *
11928 * Attach a table to a composite type, as though it had been created with CREATE
11929 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
11930 * subject table must not have inheritance parents. These restrictions ensure
11931 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
11932 *
11933 * The address of the type is returned.
11934 */
11935 static ObjectAddress
ATExecAddOf(Relation rel,const TypeName * ofTypename,LOCKMODE lockmode)11936 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
11937 {
11938 Oid relid = RelationGetRelid(rel);
11939 Type typetuple;
11940 Oid typeid;
11941 Relation inheritsRelation,
11942 relationRelation;
11943 SysScanDesc scan;
11944 ScanKeyData key;
11945 AttrNumber table_attno,
11946 type_attno;
11947 TupleDesc typeTupleDesc,
11948 tableTupleDesc;
11949 ObjectAddress tableobj,
11950 typeobj;
11951 HeapTuple classtuple;
11952
11953 /* Validate the type. */
11954 typetuple = typenameType(NULL, ofTypename, NULL);
11955 check_of_type(typetuple);
11956 typeid = HeapTupleGetOid(typetuple);
11957
11958 /* Fail if the table has any inheritance parents. */
11959 inheritsRelation = heap_open(InheritsRelationId, AccessShareLock);
11960 ScanKeyInit(&key,
11961 Anum_pg_inherits_inhrelid,
11962 BTEqualStrategyNumber, F_OIDEQ,
11963 ObjectIdGetDatum(relid));
11964 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
11965 true, NULL, 1, &key);
11966 if (HeapTupleIsValid(systable_getnext(scan)))
11967 ereport(ERROR,
11968 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11969 errmsg("typed tables cannot inherit")));
11970 systable_endscan(scan);
11971 heap_close(inheritsRelation, AccessShareLock);
11972
11973 /*
11974 * Check the tuple descriptors for compatibility. Unlike inheritance, we
11975 * require that the order also match. However, attnotnull need not match.
11976 * Also unlike inheritance, we do not require matching relhasoids.
11977 */
11978 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
11979 tableTupleDesc = RelationGetDescr(rel);
11980 table_attno = 1;
11981 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
11982 {
11983 Form_pg_attribute type_attr,
11984 table_attr;
11985 const char *type_attname,
11986 *table_attname;
11987
11988 /* Get the next non-dropped type attribute. */
11989 type_attr = typeTupleDesc->attrs[type_attno - 1];
11990 if (type_attr->attisdropped)
11991 continue;
11992 type_attname = NameStr(type_attr->attname);
11993
11994 /* Get the next non-dropped table attribute. */
11995 do
11996 {
11997 if (table_attno > tableTupleDesc->natts)
11998 ereport(ERROR,
11999 (errcode(ERRCODE_DATATYPE_MISMATCH),
12000 errmsg("table is missing column \"%s\"",
12001 type_attname)));
12002 table_attr = tableTupleDesc->attrs[table_attno++ - 1];
12003 } while (table_attr->attisdropped);
12004 table_attname = NameStr(table_attr->attname);
12005
12006 /* Compare name. */
12007 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
12008 ereport(ERROR,
12009 (errcode(ERRCODE_DATATYPE_MISMATCH),
12010 errmsg("table has column \"%s\" where type requires \"%s\"",
12011 table_attname, type_attname)));
12012
12013 /* Compare type. */
12014 if (table_attr->atttypid != type_attr->atttypid ||
12015 table_attr->atttypmod != type_attr->atttypmod ||
12016 table_attr->attcollation != type_attr->attcollation)
12017 ereport(ERROR,
12018 (errcode(ERRCODE_DATATYPE_MISMATCH),
12019 errmsg("table \"%s\" has different type for column \"%s\"",
12020 RelationGetRelationName(rel), type_attname)));
12021 }
12022 DecrTupleDescRefCount(typeTupleDesc);
12023
12024 /* Any remaining columns at the end of the table had better be dropped. */
12025 for (; table_attno <= tableTupleDesc->natts; table_attno++)
12026 {
12027 Form_pg_attribute table_attr = tableTupleDesc->attrs[table_attno - 1];
12028
12029 if (!table_attr->attisdropped)
12030 ereport(ERROR,
12031 (errcode(ERRCODE_DATATYPE_MISMATCH),
12032 errmsg("table has extra column \"%s\"",
12033 NameStr(table_attr->attname))));
12034 }
12035
12036 /* If the table was already typed, drop the existing dependency. */
12037 if (rel->rd_rel->reloftype)
12038 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
12039 DEPENDENCY_NORMAL);
12040
12041 /* Record a dependency on the new type. */
12042 tableobj.classId = RelationRelationId;
12043 tableobj.objectId = relid;
12044 tableobj.objectSubId = 0;
12045 typeobj.classId = TypeRelationId;
12046 typeobj.objectId = typeid;
12047 typeobj.objectSubId = 0;
12048 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
12049
12050 /* Update pg_class.reloftype */
12051 relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
12052 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
12053 if (!HeapTupleIsValid(classtuple))
12054 elog(ERROR, "cache lookup failed for relation %u", relid);
12055 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
12056 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
12057
12058 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
12059
12060 heap_freetuple(classtuple);
12061 heap_close(relationRelation, RowExclusiveLock);
12062
12063 ReleaseSysCache(typetuple);
12064
12065 return typeobj;
12066 }
12067
12068 /*
12069 * ALTER TABLE NOT OF
12070 *
12071 * Detach a typed table from its originating type. Just clear reloftype and
12072 * remove the dependency.
12073 */
12074 static void
ATExecDropOf(Relation rel,LOCKMODE lockmode)12075 ATExecDropOf(Relation rel, LOCKMODE lockmode)
12076 {
12077 Oid relid = RelationGetRelid(rel);
12078 Relation relationRelation;
12079 HeapTuple tuple;
12080
12081 if (!OidIsValid(rel->rd_rel->reloftype))
12082 ereport(ERROR,
12083 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12084 errmsg("\"%s\" is not a typed table",
12085 RelationGetRelationName(rel))));
12086
12087 /*
12088 * We don't bother to check ownership of the type --- ownership of the
12089 * table is presumed enough rights. No lock required on the type, either.
12090 */
12091
12092 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
12093 DEPENDENCY_NORMAL);
12094
12095 /* Clear pg_class.reloftype */
12096 relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
12097 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
12098 if (!HeapTupleIsValid(tuple))
12099 elog(ERROR, "cache lookup failed for relation %u", relid);
12100 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
12101 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
12102
12103 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
12104
12105 heap_freetuple(tuple);
12106 heap_close(relationRelation, RowExclusiveLock);
12107 }
12108
12109 /*
12110 * relation_mark_replica_identity: Update a table's replica identity
12111 *
12112 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
12113 * index. Otherwise, it should be InvalidOid.
12114 */
12115 static void
relation_mark_replica_identity(Relation rel,char ri_type,Oid indexOid,bool is_internal)12116 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
12117 bool is_internal)
12118 {
12119 Relation pg_index;
12120 Relation pg_class;
12121 HeapTuple pg_class_tuple;
12122 HeapTuple pg_index_tuple;
12123 Form_pg_class pg_class_form;
12124 Form_pg_index pg_index_form;
12125
12126 ListCell *index;
12127
12128 /*
12129 * Check whether relreplident has changed, and update it if so.
12130 */
12131 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
12132 pg_class_tuple = SearchSysCacheCopy1(RELOID,
12133 ObjectIdGetDatum(RelationGetRelid(rel)));
12134 if (!HeapTupleIsValid(pg_class_tuple))
12135 elog(ERROR, "cache lookup failed for relation \"%s\"",
12136 RelationGetRelationName(rel));
12137 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
12138 if (pg_class_form->relreplident != ri_type)
12139 {
12140 pg_class_form->relreplident = ri_type;
12141 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
12142 }
12143 heap_close(pg_class, RowExclusiveLock);
12144 heap_freetuple(pg_class_tuple);
12145
12146 /*
12147 * Check whether the correct index is marked indisreplident; if so, we're
12148 * done.
12149 */
12150 if (OidIsValid(indexOid))
12151 {
12152 Assert(ri_type == REPLICA_IDENTITY_INDEX);
12153
12154 pg_index_tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
12155 if (!HeapTupleIsValid(pg_index_tuple))
12156 elog(ERROR, "cache lookup failed for index %u", indexOid);
12157 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
12158
12159 if (pg_index_form->indisreplident)
12160 {
12161 ReleaseSysCache(pg_index_tuple);
12162 return;
12163 }
12164 ReleaseSysCache(pg_index_tuple);
12165 }
12166
12167 /*
12168 * Clear the indisreplident flag from any index that had it previously,
12169 * and set it for any index that should have it now.
12170 */
12171 pg_index = heap_open(IndexRelationId, RowExclusiveLock);
12172 foreach(index, RelationGetIndexList(rel))
12173 {
12174 Oid thisIndexOid = lfirst_oid(index);
12175 bool dirty = false;
12176
12177 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
12178 ObjectIdGetDatum(thisIndexOid));
12179 if (!HeapTupleIsValid(pg_index_tuple))
12180 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
12181 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
12182
12183 /*
12184 * Unset the bit if set. We know it's wrong because we checked this
12185 * earlier.
12186 */
12187 if (pg_index_form->indisreplident)
12188 {
12189 dirty = true;
12190 pg_index_form->indisreplident = false;
12191 }
12192 else if (thisIndexOid == indexOid)
12193 {
12194 dirty = true;
12195 pg_index_form->indisreplident = true;
12196 }
12197
12198 if (dirty)
12199 {
12200 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
12201 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
12202 InvalidOid, is_internal);
12203 }
12204 heap_freetuple(pg_index_tuple);
12205 }
12206
12207 heap_close(pg_index, RowExclusiveLock);
12208 }
12209
12210 /*
12211 * ALTER TABLE <name> REPLICA IDENTITY ...
12212 */
12213 static void
ATExecReplicaIdentity(Relation rel,ReplicaIdentityStmt * stmt,LOCKMODE lockmode)12214 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
12215 {
12216 Oid indexOid;
12217 Relation indexRel;
12218 int key;
12219
12220 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
12221 {
12222 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
12223 return;
12224 }
12225 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
12226 {
12227 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
12228 return;
12229 }
12230 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
12231 {
12232 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
12233 return;
12234 }
12235 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
12236 {
12237 /* fallthrough */ ;
12238 }
12239 else
12240 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
12241
12242
12243 /* Check that the index exists */
12244 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
12245 if (!OidIsValid(indexOid))
12246 ereport(ERROR,
12247 (errcode(ERRCODE_UNDEFINED_OBJECT),
12248 errmsg("index \"%s\" for table \"%s\" does not exist",
12249 stmt->name, RelationGetRelationName(rel))));
12250
12251 indexRel = index_open(indexOid, ShareLock);
12252
12253 /* Check that the index is on the relation we're altering. */
12254 if (indexRel->rd_index == NULL ||
12255 indexRel->rd_index->indrelid != RelationGetRelid(rel))
12256 ereport(ERROR,
12257 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12258 errmsg("\"%s\" is not an index for table \"%s\"",
12259 RelationGetRelationName(indexRel),
12260 RelationGetRelationName(rel))));
12261 /* The AM must support uniqueness, and the index must in fact be unique. */
12262 if (!indexRel->rd_amroutine->amcanunique ||
12263 !indexRel->rd_index->indisunique)
12264 ereport(ERROR,
12265 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12266 errmsg("cannot use non-unique index \"%s\" as replica identity",
12267 RelationGetRelationName(indexRel))));
12268 /* Deferred indexes are not guaranteed to be always unique. */
12269 if (!indexRel->rd_index->indimmediate)
12270 ereport(ERROR,
12271 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12272 errmsg("cannot use non-immediate index \"%s\" as replica identity",
12273 RelationGetRelationName(indexRel))));
12274 /* Expression indexes aren't supported. */
12275 if (RelationGetIndexExpressions(indexRel) != NIL)
12276 ereport(ERROR,
12277 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12278 errmsg("cannot use expression index \"%s\" as replica identity",
12279 RelationGetRelationName(indexRel))));
12280 /* Predicate indexes aren't supported. */
12281 if (RelationGetIndexPredicate(indexRel) != NIL)
12282 ereport(ERROR,
12283 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12284 errmsg("cannot use partial index \"%s\" as replica identity",
12285 RelationGetRelationName(indexRel))));
12286 /* And neither are invalid indexes. */
12287 if (!IndexIsValid(indexRel->rd_index))
12288 ereport(ERROR,
12289 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12290 errmsg("cannot use invalid index \"%s\" as replica identity",
12291 RelationGetRelationName(indexRel))));
12292
12293 /* Check index for nullable columns. */
12294 for (key = 0; key < indexRel->rd_index->indnatts; key++)
12295 {
12296 int16 attno = indexRel->rd_index->indkey.values[key];
12297 Form_pg_attribute attr;
12298
12299 /* Allow OID column to be indexed; it's certainly not nullable */
12300 if (attno == ObjectIdAttributeNumber)
12301 continue;
12302
12303 /*
12304 * Reject any other system columns. (Going forward, we'll disallow
12305 * indexes containing such columns in the first place, but they might
12306 * exist in older branches.)
12307 */
12308 if (attno <= 0)
12309 ereport(ERROR,
12310 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
12311 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
12312 RelationGetRelationName(indexRel), attno)));
12313
12314 attr = rel->rd_att->attrs[attno - 1];
12315 if (!attr->attnotnull)
12316 ereport(ERROR,
12317 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12318 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
12319 RelationGetRelationName(indexRel),
12320 NameStr(attr->attname))));
12321 }
12322
12323 /* This index is suitable for use as a replica identity. Mark it. */
12324 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
12325
12326 index_close(indexRel, NoLock);
12327 }
12328
12329 /*
12330 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
12331 */
12332 static void
ATExecEnableRowSecurity(Relation rel)12333 ATExecEnableRowSecurity(Relation rel)
12334 {
12335 Relation pg_class;
12336 Oid relid;
12337 HeapTuple tuple;
12338
12339 relid = RelationGetRelid(rel);
12340
12341 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
12342
12343 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
12344
12345 if (!HeapTupleIsValid(tuple))
12346 elog(ERROR, "cache lookup failed for relation %u", relid);
12347
12348 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = true;
12349 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
12350
12351 heap_close(pg_class, RowExclusiveLock);
12352 heap_freetuple(tuple);
12353 }
12354
12355 static void
ATExecDisableRowSecurity(Relation rel)12356 ATExecDisableRowSecurity(Relation rel)
12357 {
12358 Relation pg_class;
12359 Oid relid;
12360 HeapTuple tuple;
12361
12362 relid = RelationGetRelid(rel);
12363
12364 /* Pull the record for this relation and update it */
12365 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
12366
12367 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
12368
12369 if (!HeapTupleIsValid(tuple))
12370 elog(ERROR, "cache lookup failed for relation %u", relid);
12371
12372 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = false;
12373 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
12374
12375 heap_close(pg_class, RowExclusiveLock);
12376 heap_freetuple(tuple);
12377 }
12378
12379 /*
12380 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
12381 */
12382 static void
ATExecForceNoForceRowSecurity(Relation rel,bool force_rls)12383 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
12384 {
12385 Relation pg_class;
12386 Oid relid;
12387 HeapTuple tuple;
12388
12389 relid = RelationGetRelid(rel);
12390
12391 pg_class = heap_open(RelationRelationId, RowExclusiveLock);
12392
12393 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
12394
12395 if (!HeapTupleIsValid(tuple))
12396 elog(ERROR, "cache lookup failed for relation %u", relid);
12397
12398 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
12399 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
12400
12401 heap_close(pg_class, RowExclusiveLock);
12402 heap_freetuple(tuple);
12403 }
12404
12405 /*
12406 * ALTER FOREIGN TABLE <name> OPTIONS (...)
12407 */
12408 static void
ATExecGenericOptions(Relation rel,List * options)12409 ATExecGenericOptions(Relation rel, List *options)
12410 {
12411 Relation ftrel;
12412 ForeignServer *server;
12413 ForeignDataWrapper *fdw;
12414 HeapTuple tuple;
12415 bool isnull;
12416 Datum repl_val[Natts_pg_foreign_table];
12417 bool repl_null[Natts_pg_foreign_table];
12418 bool repl_repl[Natts_pg_foreign_table];
12419 Datum datum;
12420 Form_pg_foreign_table tableform;
12421
12422 if (options == NIL)
12423 return;
12424
12425 ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
12426
12427 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
12428 if (!HeapTupleIsValid(tuple))
12429 ereport(ERROR,
12430 (errcode(ERRCODE_UNDEFINED_OBJECT),
12431 errmsg("foreign table \"%s\" does not exist",
12432 RelationGetRelationName(rel))));
12433 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
12434 server = GetForeignServer(tableform->ftserver);
12435 fdw = GetForeignDataWrapper(server->fdwid);
12436
12437 memset(repl_val, 0, sizeof(repl_val));
12438 memset(repl_null, false, sizeof(repl_null));
12439 memset(repl_repl, false, sizeof(repl_repl));
12440
12441 /* Extract the current options */
12442 datum = SysCacheGetAttr(FOREIGNTABLEREL,
12443 tuple,
12444 Anum_pg_foreign_table_ftoptions,
12445 &isnull);
12446 if (isnull)
12447 datum = PointerGetDatum(NULL);
12448
12449 /* Transform the options */
12450 datum = transformGenericOptions(ForeignTableRelationId,
12451 datum,
12452 options,
12453 fdw->fdwvalidator);
12454
12455 if (PointerIsValid(DatumGetPointer(datum)))
12456 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
12457 else
12458 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
12459
12460 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
12461
12462 /* Everything looks good - update the tuple */
12463
12464 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
12465 repl_val, repl_null, repl_repl);
12466
12467 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
12468
12469 /*
12470 * Invalidate relcache so that all sessions will refresh any cached plans
12471 * that might depend on the old options.
12472 */
12473 CacheInvalidateRelcache(rel);
12474
12475 InvokeObjectPostAlterHook(ForeignTableRelationId,
12476 RelationGetRelid(rel), 0);
12477
12478 heap_close(ftrel, RowExclusiveLock);
12479
12480 heap_freetuple(tuple);
12481 }
12482
12483 /*
12484 * Preparation phase for SET LOGGED/UNLOGGED
12485 *
12486 * This verifies that we're not trying to change a temp table. Also,
12487 * existing foreign key constraints are checked to avoid ending up with
12488 * permanent tables referencing unlogged tables.
12489 *
12490 * Return value is false if the operation is a no-op (in which case the
12491 * checks are skipped), otherwise true.
12492 */
12493 static bool
ATPrepChangePersistence(Relation rel,bool toLogged)12494 ATPrepChangePersistence(Relation rel, bool toLogged)
12495 {
12496 Relation pg_constraint;
12497 HeapTuple tuple;
12498 SysScanDesc scan;
12499 ScanKeyData skey[1];
12500
12501 /*
12502 * Disallow changing status for a temp table. Also verify whether we can
12503 * get away with doing nothing; in such cases we don't need to run the
12504 * checks below, either.
12505 */
12506 switch (rel->rd_rel->relpersistence)
12507 {
12508 case RELPERSISTENCE_TEMP:
12509 ereport(ERROR,
12510 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12511 errmsg("cannot change logged status of table \"%s\" because it is temporary",
12512 RelationGetRelationName(rel)),
12513 errtable(rel)));
12514 break;
12515 case RELPERSISTENCE_PERMANENT:
12516 if (toLogged)
12517 /* nothing to do */
12518 return false;
12519 break;
12520 case RELPERSISTENCE_UNLOGGED:
12521 if (!toLogged)
12522 /* nothing to do */
12523 return false;
12524 break;
12525 }
12526
12527 /*
12528 * Check that the table is not part any publication when changing to
12529 * UNLOGGED as UNLOGGED tables can't be published.
12530 */
12531 if (!toLogged &&
12532 list_length(GetRelationPublications(RelationGetRelid(rel))) > 0)
12533 ereport(ERROR,
12534 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12535 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
12536 RelationGetRelationName(rel)),
12537 errdetail("Unlogged relations cannot be replicated.")));
12538
12539 /*
12540 * Check existing foreign key constraints to preserve the invariant that
12541 * permanent tables cannot reference unlogged ones. Self-referencing
12542 * foreign keys can safely be ignored.
12543 */
12544 pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
12545
12546 /*
12547 * Scan conrelid if changing to permanent, else confrelid. This also
12548 * determines whether a useful index exists.
12549 */
12550 ScanKeyInit(&skey[0],
12551 toLogged ? Anum_pg_constraint_conrelid :
12552 Anum_pg_constraint_confrelid,
12553 BTEqualStrategyNumber, F_OIDEQ,
12554 ObjectIdGetDatum(RelationGetRelid(rel)));
12555 scan = systable_beginscan(pg_constraint,
12556 toLogged ? ConstraintRelidIndexId : InvalidOid,
12557 true, NULL, 1, skey);
12558
12559 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
12560 {
12561 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
12562
12563 if (con->contype == CONSTRAINT_FOREIGN)
12564 {
12565 Oid foreignrelid;
12566 Relation foreignrel;
12567
12568 /* the opposite end of what we used as scankey */
12569 foreignrelid = toLogged ? con->confrelid : con->conrelid;
12570
12571 /* ignore if self-referencing */
12572 if (RelationGetRelid(rel) == foreignrelid)
12573 continue;
12574
12575 foreignrel = relation_open(foreignrelid, AccessShareLock);
12576
12577 if (toLogged)
12578 {
12579 if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
12580 ereport(ERROR,
12581 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12582 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
12583 RelationGetRelationName(rel),
12584 RelationGetRelationName(foreignrel)),
12585 errtableconstraint(rel, NameStr(con->conname))));
12586 }
12587 else
12588 {
12589 if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
12590 ereport(ERROR,
12591 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12592 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
12593 RelationGetRelationName(rel),
12594 RelationGetRelationName(foreignrel)),
12595 errtableconstraint(rel, NameStr(con->conname))));
12596 }
12597
12598 relation_close(foreignrel, AccessShareLock);
12599 }
12600 }
12601
12602 systable_endscan(scan);
12603
12604 heap_close(pg_constraint, AccessShareLock);
12605
12606 return true;
12607 }
12608
12609 /*
12610 * Execute ALTER TABLE SET SCHEMA
12611 */
12612 ObjectAddress
AlterTableNamespace(AlterObjectSchemaStmt * stmt,Oid * oldschema)12613 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
12614 {
12615 Relation rel;
12616 Oid relid;
12617 Oid oldNspOid;
12618 Oid nspOid;
12619 RangeVar *newrv;
12620 ObjectAddresses *objsMoved;
12621 ObjectAddress myself;
12622
12623 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
12624 stmt->missing_ok, false,
12625 RangeVarCallbackForAlterRelation,
12626 (void *) stmt);
12627
12628 if (!OidIsValid(relid))
12629 {
12630 ereport(NOTICE,
12631 (errmsg("relation \"%s\" does not exist, skipping",
12632 stmt->relation->relname)));
12633 return InvalidObjectAddress;
12634 }
12635
12636 rel = relation_open(relid, NoLock);
12637
12638 oldNspOid = RelationGetNamespace(rel);
12639
12640 /* If it's an owned sequence, disallow moving it by itself. */
12641 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
12642 {
12643 Oid tableId;
12644 int32 colId;
12645
12646 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
12647 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
12648 ereport(ERROR,
12649 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12650 errmsg("cannot move an owned sequence into another schema"),
12651 errdetail("Sequence \"%s\" is linked to table \"%s\".",
12652 RelationGetRelationName(rel),
12653 get_rel_name(tableId))));
12654 }
12655
12656 /* Get and lock schema OID and check its permissions. */
12657 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
12658 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
12659
12660 /* common checks on switching namespaces */
12661 CheckSetNamespace(oldNspOid, nspOid);
12662
12663 objsMoved = new_object_addresses();
12664 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
12665 free_object_addresses(objsMoved);
12666
12667 ObjectAddressSet(myself, RelationRelationId, relid);
12668
12669 if (oldschema)
12670 *oldschema = oldNspOid;
12671
12672 /* close rel, but keep lock until commit */
12673 relation_close(rel, NoLock);
12674
12675 return myself;
12676 }
12677
12678 /*
12679 * The guts of relocating a table or materialized view to another namespace:
12680 * besides moving the relation itself, its dependent objects are relocated to
12681 * the new schema.
12682 */
12683 void
AlterTableNamespaceInternal(Relation rel,Oid oldNspOid,Oid nspOid,ObjectAddresses * objsMoved)12684 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
12685 ObjectAddresses *objsMoved)
12686 {
12687 Relation classRel;
12688
12689 Assert(objsMoved != NULL);
12690
12691 /* OK, modify the pg_class row and pg_depend entry */
12692 classRel = heap_open(RelationRelationId, RowExclusiveLock);
12693
12694 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
12695 nspOid, true, objsMoved);
12696
12697 /* Fix the table's row type too */
12698 AlterTypeNamespaceInternal(rel->rd_rel->reltype,
12699 nspOid, false, false, objsMoved);
12700
12701 /* Fix other dependent stuff */
12702 if (rel->rd_rel->relkind == RELKIND_RELATION ||
12703 rel->rd_rel->relkind == RELKIND_MATVIEW ||
12704 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12705 {
12706 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
12707 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
12708 objsMoved, AccessExclusiveLock);
12709 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
12710 false, objsMoved);
12711 }
12712
12713 heap_close(classRel, RowExclusiveLock);
12714 }
12715
12716 /*
12717 * The guts of relocating a relation to another namespace: fix the pg_class
12718 * entry, and the pg_depend entry if any. Caller must already have
12719 * opened and write-locked pg_class.
12720 */
12721 void
AlterRelationNamespaceInternal(Relation classRel,Oid relOid,Oid oldNspOid,Oid newNspOid,bool hasDependEntry,ObjectAddresses * objsMoved)12722 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
12723 Oid oldNspOid, Oid newNspOid,
12724 bool hasDependEntry,
12725 ObjectAddresses *objsMoved)
12726 {
12727 HeapTuple classTup;
12728 Form_pg_class classForm;
12729 ObjectAddress thisobj;
12730 bool already_done = false;
12731
12732 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
12733 if (!HeapTupleIsValid(classTup))
12734 elog(ERROR, "cache lookup failed for relation %u", relOid);
12735 classForm = (Form_pg_class) GETSTRUCT(classTup);
12736
12737 Assert(classForm->relnamespace == oldNspOid);
12738
12739 thisobj.classId = RelationRelationId;
12740 thisobj.objectId = relOid;
12741 thisobj.objectSubId = 0;
12742
12743 /*
12744 * If the object has already been moved, don't move it again. If it's
12745 * already in the right place, don't move it, but still fire the object
12746 * access hook.
12747 */
12748 already_done = object_address_present(&thisobj, objsMoved);
12749 if (!already_done && oldNspOid != newNspOid)
12750 {
12751 /* check for duplicate name (more friendly than unique-index failure) */
12752 if (get_relname_relid(NameStr(classForm->relname),
12753 newNspOid) != InvalidOid)
12754 ereport(ERROR,
12755 (errcode(ERRCODE_DUPLICATE_TABLE),
12756 errmsg("relation \"%s\" already exists in schema \"%s\"",
12757 NameStr(classForm->relname),
12758 get_namespace_name(newNspOid))));
12759
12760 /* classTup is a copy, so OK to scribble on */
12761 classForm->relnamespace = newNspOid;
12762
12763 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
12764
12765 /* Update dependency on schema if caller said so */
12766 if (hasDependEntry &&
12767 changeDependencyFor(RelationRelationId,
12768 relOid,
12769 NamespaceRelationId,
12770 oldNspOid,
12771 newNspOid) != 1)
12772 elog(ERROR, "failed to change schema dependency for relation \"%s\"",
12773 NameStr(classForm->relname));
12774 }
12775 if (!already_done)
12776 {
12777 add_exact_object_address(&thisobj, objsMoved);
12778
12779 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
12780 }
12781
12782 heap_freetuple(classTup);
12783 }
12784
12785 /*
12786 * Move all indexes for the specified relation to another namespace.
12787 *
12788 * Note: we assume adequate permission checking was done by the caller,
12789 * and that the caller has a suitable lock on the owning relation.
12790 */
12791 static void
AlterIndexNamespaces(Relation classRel,Relation rel,Oid oldNspOid,Oid newNspOid,ObjectAddresses * objsMoved)12792 AlterIndexNamespaces(Relation classRel, Relation rel,
12793 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
12794 {
12795 List *indexList;
12796 ListCell *l;
12797
12798 indexList = RelationGetIndexList(rel);
12799
12800 foreach(l, indexList)
12801 {
12802 Oid indexOid = lfirst_oid(l);
12803 ObjectAddress thisobj;
12804
12805 thisobj.classId = RelationRelationId;
12806 thisobj.objectId = indexOid;
12807 thisobj.objectSubId = 0;
12808
12809 /*
12810 * Note: currently, the index will not have its own dependency on the
12811 * namespace, so we don't need to do changeDependencyFor(). There's no
12812 * row type in pg_type, either.
12813 *
12814 * XXX this objsMoved test may be pointless -- surely we have a single
12815 * dependency link from a relation to each index?
12816 */
12817 if (!object_address_present(&thisobj, objsMoved))
12818 {
12819 AlterRelationNamespaceInternal(classRel, indexOid,
12820 oldNspOid, newNspOid,
12821 false, objsMoved);
12822 add_exact_object_address(&thisobj, objsMoved);
12823 }
12824 }
12825
12826 list_free(indexList);
12827 }
12828
12829 /*
12830 * Move all identity and SERIAL-column sequences of the specified relation to another
12831 * namespace.
12832 *
12833 * Note: we assume adequate permission checking was done by the caller,
12834 * and that the caller has a suitable lock on the owning relation.
12835 */
12836 static void
AlterSeqNamespaces(Relation classRel,Relation rel,Oid oldNspOid,Oid newNspOid,ObjectAddresses * objsMoved,LOCKMODE lockmode)12837 AlterSeqNamespaces(Relation classRel, Relation rel,
12838 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
12839 LOCKMODE lockmode)
12840 {
12841 Relation depRel;
12842 SysScanDesc scan;
12843 ScanKeyData key[2];
12844 HeapTuple tup;
12845
12846 /*
12847 * SERIAL sequences are those having an auto dependency on one of the
12848 * table's columns (we don't care *which* column, exactly).
12849 */
12850 depRel = heap_open(DependRelationId, AccessShareLock);
12851
12852 ScanKeyInit(&key[0],
12853 Anum_pg_depend_refclassid,
12854 BTEqualStrategyNumber, F_OIDEQ,
12855 ObjectIdGetDatum(RelationRelationId));
12856 ScanKeyInit(&key[1],
12857 Anum_pg_depend_refobjid,
12858 BTEqualStrategyNumber, F_OIDEQ,
12859 ObjectIdGetDatum(RelationGetRelid(rel)));
12860 /* we leave refobjsubid unspecified */
12861
12862 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
12863 NULL, 2, key);
12864
12865 while (HeapTupleIsValid(tup = systable_getnext(scan)))
12866 {
12867 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
12868 Relation seqRel;
12869
12870 /* skip dependencies other than auto dependencies on columns */
12871 if (depForm->refobjsubid == 0 ||
12872 depForm->classid != RelationRelationId ||
12873 depForm->objsubid != 0 ||
12874 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
12875 continue;
12876
12877 /* Use relation_open just in case it's an index */
12878 seqRel = relation_open(depForm->objid, lockmode);
12879
12880 /* skip non-sequence relations */
12881 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
12882 {
12883 /* No need to keep the lock */
12884 relation_close(seqRel, lockmode);
12885 continue;
12886 }
12887
12888 /* Fix the pg_class and pg_depend entries */
12889 AlterRelationNamespaceInternal(classRel, depForm->objid,
12890 oldNspOid, newNspOid,
12891 true, objsMoved);
12892
12893 /*
12894 * Sequences have entries in pg_type. We need to be careful to move
12895 * them to the new namespace, too.
12896 */
12897 AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype,
12898 newNspOid, false, false, objsMoved);
12899
12900 /* Now we can close it. Keep the lock till end of transaction. */
12901 relation_close(seqRel, NoLock);
12902 }
12903
12904 systable_endscan(scan);
12905
12906 relation_close(depRel, AccessShareLock);
12907 }
12908
12909
12910 /*
12911 * This code supports
12912 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
12913 *
12914 * Because we only support this for TEMP tables, it's sufficient to remember
12915 * the state in a backend-local data structure.
12916 */
12917
12918 /*
12919 * Register a newly-created relation's ON COMMIT action.
12920 */
12921 void
register_on_commit_action(Oid relid,OnCommitAction action)12922 register_on_commit_action(Oid relid, OnCommitAction action)
12923 {
12924 OnCommitItem *oc;
12925 MemoryContext oldcxt;
12926
12927 /*
12928 * We needn't bother registering the relation unless there is an ON COMMIT
12929 * action we need to take.
12930 */
12931 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
12932 return;
12933
12934 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
12935
12936 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
12937 oc->relid = relid;
12938 oc->oncommit = action;
12939 oc->creating_subid = GetCurrentSubTransactionId();
12940 oc->deleting_subid = InvalidSubTransactionId;
12941
12942 on_commits = lcons(oc, on_commits);
12943
12944 MemoryContextSwitchTo(oldcxt);
12945 }
12946
12947 /*
12948 * Unregister any ON COMMIT action when a relation is deleted.
12949 *
12950 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
12951 */
12952 void
remove_on_commit_action(Oid relid)12953 remove_on_commit_action(Oid relid)
12954 {
12955 ListCell *l;
12956
12957 foreach(l, on_commits)
12958 {
12959 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
12960
12961 if (oc->relid == relid)
12962 {
12963 oc->deleting_subid = GetCurrentSubTransactionId();
12964 break;
12965 }
12966 }
12967 }
12968
12969 /*
12970 * Perform ON COMMIT actions.
12971 *
12972 * This is invoked just before actually committing, since it's possible
12973 * to encounter errors.
12974 */
12975 void
PreCommit_on_commit_actions(void)12976 PreCommit_on_commit_actions(void)
12977 {
12978 ListCell *l;
12979 List *oids_to_truncate = NIL;
12980 List *oids_to_drop = NIL;
12981
12982 foreach(l, on_commits)
12983 {
12984 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
12985
12986 /* Ignore entry if already dropped in this xact */
12987 if (oc->deleting_subid != InvalidSubTransactionId)
12988 continue;
12989
12990 switch (oc->oncommit)
12991 {
12992 case ONCOMMIT_NOOP:
12993 case ONCOMMIT_PRESERVE_ROWS:
12994 /* Do nothing (there shouldn't be such entries, actually) */
12995 break;
12996 case ONCOMMIT_DELETE_ROWS:
12997
12998 /*
12999 * If this transaction hasn't accessed any temporary
13000 * relations, we can skip truncating ON COMMIT DELETE ROWS
13001 * tables, as they must still be empty.
13002 */
13003 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPREL))
13004 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
13005 break;
13006 case ONCOMMIT_DROP:
13007 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
13008 break;
13009 }
13010 }
13011
13012 /*
13013 * Truncate relations before dropping so that all dependencies between
13014 * relations are removed after they are worked on. Doing it like this
13015 * might be a waste as it is possible that a relation being truncated will
13016 * be dropped anyway due to its parent being dropped, but this makes the
13017 * code more robust because of not having to re-check that the relation
13018 * exists at truncation time.
13019 */
13020 if (oids_to_truncate != NIL)
13021 {
13022 heap_truncate(oids_to_truncate);
13023 CommandCounterIncrement(); /* XXX needed? */
13024 }
13025 if (oids_to_drop != NIL)
13026 {
13027 ObjectAddresses *targetObjects = new_object_addresses();
13028 ListCell *l;
13029
13030 foreach(l, oids_to_drop)
13031 {
13032 ObjectAddress object;
13033
13034 object.classId = RelationRelationId;
13035 object.objectId = lfirst_oid(l);
13036 object.objectSubId = 0;
13037
13038 Assert(!object_address_present(&object, targetObjects));
13039
13040 add_exact_object_address(&object, targetObjects);
13041 }
13042
13043 /*
13044 * Since this is an automatic drop, rather than one directly initiated
13045 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
13046 */
13047 performMultipleDeletions(targetObjects, DROP_CASCADE,
13048 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
13049
13050 #ifdef USE_ASSERT_CHECKING
13051
13052 /*
13053 * Note that table deletion will call remove_on_commit_action, so the
13054 * entry should get marked as deleted.
13055 */
13056 foreach(l, on_commits)
13057 {
13058 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
13059
13060 if (oc->oncommit != ONCOMMIT_DROP)
13061 continue;
13062
13063 Assert(oc->deleting_subid != InvalidSubTransactionId);
13064 }
13065 #endif
13066 }
13067 }
13068
13069 /*
13070 * Post-commit or post-abort cleanup for ON COMMIT management.
13071 *
13072 * All we do here is remove no-longer-needed OnCommitItem entries.
13073 *
13074 * During commit, remove entries that were deleted during this transaction;
13075 * during abort, remove those created during this transaction.
13076 */
13077 void
AtEOXact_on_commit_actions(bool isCommit)13078 AtEOXact_on_commit_actions(bool isCommit)
13079 {
13080 ListCell *cur_item;
13081 ListCell *prev_item;
13082
13083 prev_item = NULL;
13084 cur_item = list_head(on_commits);
13085
13086 while (cur_item != NULL)
13087 {
13088 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
13089
13090 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
13091 oc->creating_subid != InvalidSubTransactionId)
13092 {
13093 /* cur_item must be removed */
13094 on_commits = list_delete_cell(on_commits, cur_item, prev_item);
13095 pfree(oc);
13096 if (prev_item)
13097 cur_item = lnext(prev_item);
13098 else
13099 cur_item = list_head(on_commits);
13100 }
13101 else
13102 {
13103 /* cur_item must be preserved */
13104 oc->creating_subid = InvalidSubTransactionId;
13105 oc->deleting_subid = InvalidSubTransactionId;
13106 prev_item = cur_item;
13107 cur_item = lnext(prev_item);
13108 }
13109 }
13110 }
13111
13112 /*
13113 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
13114 *
13115 * During subabort, we can immediately remove entries created during this
13116 * subtransaction. During subcommit, just relabel entries marked during
13117 * this subtransaction as being the parent's responsibility.
13118 */
13119 void
AtEOSubXact_on_commit_actions(bool isCommit,SubTransactionId mySubid,SubTransactionId parentSubid)13120 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
13121 SubTransactionId parentSubid)
13122 {
13123 ListCell *cur_item;
13124 ListCell *prev_item;
13125
13126 prev_item = NULL;
13127 cur_item = list_head(on_commits);
13128
13129 while (cur_item != NULL)
13130 {
13131 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
13132
13133 if (!isCommit && oc->creating_subid == mySubid)
13134 {
13135 /* cur_item must be removed */
13136 on_commits = list_delete_cell(on_commits, cur_item, prev_item);
13137 pfree(oc);
13138 if (prev_item)
13139 cur_item = lnext(prev_item);
13140 else
13141 cur_item = list_head(on_commits);
13142 }
13143 else
13144 {
13145 /* cur_item must be preserved */
13146 if (oc->creating_subid == mySubid)
13147 oc->creating_subid = parentSubid;
13148 if (oc->deleting_subid == mySubid)
13149 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
13150 prev_item = cur_item;
13151 cur_item = lnext(prev_item);
13152 }
13153 }
13154 }
13155
13156 /*
13157 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
13158 * the relation to be locked only if (1) it's a plain table, materialized
13159 * view, or TOAST table and (2) the current user is the owner (or the
13160 * superuser). This meets the permission-checking needs of CLUSTER, REINDEX
13161 * TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it can be
13162 * used by all.
13163 */
13164 void
RangeVarCallbackOwnsTable(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)13165 RangeVarCallbackOwnsTable(const RangeVar *relation,
13166 Oid relId, Oid oldRelId, void *arg)
13167 {
13168 char relkind;
13169
13170 /* Nothing to do if the relation was not found. */
13171 if (!OidIsValid(relId))
13172 return;
13173
13174 /*
13175 * If the relation does exist, check whether it's an index. But note that
13176 * the relation might have been dropped between the time we did the name
13177 * lookup and now. In that case, there's nothing to do.
13178 */
13179 relkind = get_rel_relkind(relId);
13180 if (!relkind)
13181 return;
13182 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
13183 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
13184 ereport(ERROR,
13185 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13186 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
13187
13188 /* Check permissions */
13189 if (!pg_class_ownercheck(relId, GetUserId()))
13190 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname);
13191 }
13192
13193 /*
13194 * Callback to RangeVarGetRelidExtended(), similar to
13195 * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
13196 */
13197 void
RangeVarCallbackOwnsRelation(const RangeVar * relation,Oid relId,Oid oldRelId,void * arg)13198 RangeVarCallbackOwnsRelation(const RangeVar *relation,
13199 Oid relId, Oid oldRelId, void *arg)
13200 {
13201 HeapTuple tuple;
13202
13203 /* Nothing to do if the relation was not found. */
13204 if (!OidIsValid(relId))
13205 return;
13206
13207 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
13208 if (!HeapTupleIsValid(tuple)) /* should not happen */
13209 elog(ERROR, "cache lookup failed for relation %u", relId);
13210
13211 if (!pg_class_ownercheck(relId, GetUserId()))
13212 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
13213 relation->relname);
13214
13215 if (!allowSystemTableMods &&
13216 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
13217 ereport(ERROR,
13218 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
13219 errmsg("permission denied: \"%s\" is a system catalog",
13220 relation->relname)));
13221
13222 ReleaseSysCache(tuple);
13223 }
13224
13225 /*
13226 * Common RangeVarGetRelid callback for rename, set schema, and alter table
13227 * processing.
13228 */
13229 static void
RangeVarCallbackForAlterRelation(const RangeVar * rv,Oid relid,Oid oldrelid,void * arg)13230 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
13231 void *arg)
13232 {
13233 Node *stmt = (Node *) arg;
13234 ObjectType reltype;
13235 HeapTuple tuple;
13236 Form_pg_class classform;
13237 AclResult aclresult;
13238 char relkind;
13239
13240 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13241 if (!HeapTupleIsValid(tuple))
13242 return; /* concurrently dropped */
13243 classform = (Form_pg_class) GETSTRUCT(tuple);
13244 relkind = classform->relkind;
13245
13246 /* Must own relation. */
13247 if (!pg_class_ownercheck(relid, GetUserId()))
13248 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname);
13249
13250 /* No system table modifications unless explicitly allowed. */
13251 if (!allowSystemTableMods && IsSystemClass(relid, classform))
13252 ereport(ERROR,
13253 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
13254 errmsg("permission denied: \"%s\" is a system catalog",
13255 rv->relname)));
13256
13257 /*
13258 * Extract the specified relation type from the statement parse tree.
13259 *
13260 * Also, for ALTER .. RENAME, check permissions: the user must (still)
13261 * have CREATE rights on the containing namespace.
13262 */
13263 if (IsA(stmt, RenameStmt))
13264 {
13265 aclresult = pg_namespace_aclcheck(classform->relnamespace,
13266 GetUserId(), ACL_CREATE);
13267 if (aclresult != ACLCHECK_OK)
13268 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
13269 get_namespace_name(classform->relnamespace));
13270 reltype = ((RenameStmt *) stmt)->renameType;
13271 }
13272 else if (IsA(stmt, AlterObjectSchemaStmt))
13273 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
13274
13275 else if (IsA(stmt, AlterTableStmt))
13276 reltype = ((AlterTableStmt *) stmt)->relkind;
13277 else
13278 {
13279 reltype = OBJECT_TABLE; /* placate compiler */
13280 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
13281 }
13282
13283 /*
13284 * For compatibility with prior releases, we allow ALTER TABLE to be used
13285 * with most other types of relations (but not composite types). We allow
13286 * similar flexibility for ALTER INDEX in the case of RENAME, but not
13287 * otherwise. Otherwise, the user must select the correct form of the
13288 * command for the relation at issue.
13289 */
13290 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
13291 ereport(ERROR,
13292 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13293 errmsg("\"%s\" is not a sequence", rv->relname)));
13294
13295 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
13296 ereport(ERROR,
13297 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13298 errmsg("\"%s\" is not a view", rv->relname)));
13299
13300 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
13301 ereport(ERROR,
13302 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13303 errmsg("\"%s\" is not a materialized view", rv->relname)));
13304
13305 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
13306 ereport(ERROR,
13307 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13308 errmsg("\"%s\" is not a foreign table", rv->relname)));
13309
13310 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
13311 ereport(ERROR,
13312 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13313 errmsg("\"%s\" is not a composite type", rv->relname)));
13314
13315 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX
13316 && !IsA(stmt, RenameStmt))
13317 ereport(ERROR,
13318 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13319 errmsg("\"%s\" is not an index", rv->relname)));
13320
13321 /*
13322 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
13323 * TYPE for that.
13324 */
13325 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
13326 ereport(ERROR,
13327 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13328 errmsg("\"%s\" is a composite type", rv->relname),
13329 errhint("Use ALTER TYPE instead.")));
13330
13331 /*
13332 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
13333 * to a different schema, such as indexes and TOAST tables.
13334 */
13335 if (IsA(stmt, AlterObjectSchemaStmt) &&
13336 relkind != RELKIND_RELATION &&
13337 relkind != RELKIND_VIEW &&
13338 relkind != RELKIND_MATVIEW &&
13339 relkind != RELKIND_SEQUENCE &&
13340 relkind != RELKIND_FOREIGN_TABLE &&
13341 relkind != RELKIND_PARTITIONED_TABLE)
13342 ereport(ERROR,
13343 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13344 errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table",
13345 rv->relname)));
13346
13347 ReleaseSysCache(tuple);
13348 }
13349
13350 /*
13351 * Transform any expressions present in the partition key
13352 *
13353 * Returns a transformed PartitionSpec, as well as the strategy code
13354 */
13355 static PartitionSpec *
transformPartitionSpec(Relation rel,PartitionSpec * partspec,char * strategy)13356 transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
13357 {
13358 PartitionSpec *newspec;
13359 ParseState *pstate;
13360 RangeTblEntry *rte;
13361 ListCell *l;
13362
13363 newspec = makeNode(PartitionSpec);
13364
13365 newspec->strategy = partspec->strategy;
13366 newspec->partParams = NIL;
13367 newspec->location = partspec->location;
13368
13369 /* Parse partitioning strategy name */
13370 if (pg_strcasecmp(partspec->strategy, "list") == 0)
13371 *strategy = PARTITION_STRATEGY_LIST;
13372 else if (pg_strcasecmp(partspec->strategy, "range") == 0)
13373 *strategy = PARTITION_STRATEGY_RANGE;
13374 else
13375 ereport(ERROR,
13376 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13377 errmsg("unrecognized partitioning strategy \"%s\"",
13378 partspec->strategy)));
13379
13380 /* Check valid number of columns for strategy */
13381 if (*strategy == PARTITION_STRATEGY_LIST &&
13382 list_length(partspec->partParams) != 1)
13383 ereport(ERROR,
13384 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13385 errmsg("cannot use \"list\" partition strategy with more than one column")));
13386
13387 /*
13388 * Create a dummy ParseState and insert the target relation as its sole
13389 * rangetable entry. We need a ParseState for transformExpr.
13390 */
13391 pstate = make_parsestate(NULL);
13392 rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
13393 addRTEtoQuery(pstate, rte, true, true, true);
13394
13395 /* take care of any partition expressions */
13396 foreach(l, partspec->partParams)
13397 {
13398 PartitionElem *pelem = castNode(PartitionElem, lfirst(l));
13399
13400 if (pelem->expr)
13401 {
13402 /* Copy, to avoid scribbling on the input */
13403 pelem = copyObject(pelem);
13404
13405 /* Now do parse transformation of the expression */
13406 pelem->expr = transformExpr(pstate, pelem->expr,
13407 EXPR_KIND_PARTITION_EXPRESSION);
13408
13409 /* we have to fix its collations too */
13410 assign_expr_collations(pstate, pelem->expr);
13411 }
13412
13413 newspec->partParams = lappend(newspec->partParams, pelem);
13414 }
13415
13416 return newspec;
13417 }
13418
13419 /*
13420 * Compute per-partition-column information from a list of PartitionElems.
13421 * Expressions in the PartitionElems must be parse-analyzed already.
13422 */
13423 static void
ComputePartitionAttrs(Relation rel,List * partParams,AttrNumber * partattrs,List ** partexprs,Oid * partopclass,Oid * partcollation)13424 ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
13425 List **partexprs, Oid *partopclass, Oid *partcollation)
13426 {
13427 int attn;
13428 ListCell *lc;
13429
13430 attn = 0;
13431 foreach(lc, partParams)
13432 {
13433 PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
13434 Oid atttype;
13435 Oid attcollation;
13436
13437 if (pelem->name != NULL)
13438 {
13439 /* Simple attribute reference */
13440 HeapTuple atttuple;
13441 Form_pg_attribute attform;
13442
13443 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
13444 pelem->name);
13445 if (!HeapTupleIsValid(atttuple))
13446 ereport(ERROR,
13447 (errcode(ERRCODE_UNDEFINED_COLUMN),
13448 errmsg("column \"%s\" named in partition key does not exist",
13449 pelem->name)));
13450 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13451
13452 if (attform->attnum <= 0)
13453 ereport(ERROR,
13454 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13455 errmsg("cannot use system column \"%s\" in partition key",
13456 pelem->name)));
13457
13458 partattrs[attn] = attform->attnum;
13459 atttype = attform->atttypid;
13460 attcollation = attform->attcollation;
13461 ReleaseSysCache(atttuple);
13462 }
13463 else
13464 {
13465 /* Expression */
13466 Node *expr = pelem->expr;
13467
13468 Assert(expr != NULL);
13469 atttype = exprType(expr);
13470 attcollation = exprCollation(expr);
13471
13472 /*
13473 * The expression must be of a storable type (e.g., not RECORD).
13474 * The test is the same as for whether a table column is of a safe
13475 * type (which is why we needn't check for the non-expression
13476 * case).
13477 */
13478 CheckAttributeType("partition key",
13479 atttype, attcollation,
13480 NIL, false);
13481
13482 /*
13483 * Strip any top-level COLLATE clause. This ensures that we treat
13484 * "x COLLATE y" and "(x COLLATE y)" alike.
13485 */
13486 while (IsA(expr, CollateExpr))
13487 expr = (Node *) ((CollateExpr *) expr)->arg;
13488
13489 if (IsA(expr, Var) &&
13490 ((Var *) expr)->varattno > 0)
13491 {
13492 /*
13493 * User wrote "(column)" or "(column COLLATE something)".
13494 * Treat it like simple attribute anyway.
13495 */
13496 partattrs[attn] = ((Var *) expr)->varattno;
13497 }
13498 else
13499 {
13500 Bitmapset *expr_attrs = NULL;
13501 int i;
13502
13503 partattrs[attn] = 0; /* marks the column as expression */
13504 *partexprs = lappend(*partexprs, expr);
13505
13506 /*
13507 * Try to simplify the expression before checking for
13508 * mutability. The main practical value of doing it in this
13509 * order is that an inline-able SQL-language function will be
13510 * accepted if its expansion is immutable, whether or not the
13511 * function itself is marked immutable.
13512 *
13513 * Note that expression_planner does not change the passed in
13514 * expression destructively and we have already saved the
13515 * expression to be stored into the catalog above.
13516 */
13517 expr = (Node *) expression_planner((Expr *) expr);
13518
13519 /*
13520 * Partition expression cannot contain mutable functions,
13521 * because a given row must always map to the same partition
13522 * as long as there is no change in the partition boundary
13523 * structure.
13524 */
13525 if (contain_mutable_functions(expr))
13526 ereport(ERROR,
13527 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13528 errmsg("functions in partition key expression must be marked IMMUTABLE")));
13529
13530 /*
13531 * transformPartitionSpec() should have already rejected
13532 * subqueries, aggregates, window functions, and SRFs, based
13533 * on the EXPR_KIND_ for partition expressions.
13534 */
13535
13536 /*
13537 * Cannot have expressions containing whole-row references or
13538 * system column references.
13539 */
13540 pull_varattnos(expr, 1, &expr_attrs);
13541 if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
13542 expr_attrs))
13543 ereport(ERROR,
13544 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13545 errmsg("partition key expressions cannot contain whole-row references")));
13546 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
13547 {
13548 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
13549 expr_attrs))
13550 ereport(ERROR,
13551 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13552 errmsg("partition key expressions cannot contain system column references")));
13553 }
13554
13555 /*
13556 * While it is not exactly *wrong* for a partition expression
13557 * to be a constant, it seems better to reject such keys.
13558 */
13559 if (IsA(expr, Const))
13560 ereport(ERROR,
13561 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13562 errmsg("cannot use constant expression as partition key")));
13563 }
13564 }
13565
13566 /*
13567 * Apply collation override if any
13568 */
13569 if (pelem->collation)
13570 attcollation = get_collation_oid(pelem->collation, false);
13571
13572 /*
13573 * Check we have a collation iff it's a collatable type. The only
13574 * expected failures here are (1) COLLATE applied to a noncollatable
13575 * type, or (2) partition expression had an unresolved collation. But
13576 * we might as well code this to be a complete consistency check.
13577 */
13578 if (type_is_collatable(atttype))
13579 {
13580 if (!OidIsValid(attcollation))
13581 ereport(ERROR,
13582 (errcode(ERRCODE_INDETERMINATE_COLLATION),
13583 errmsg("could not determine which collation to use for partition expression"),
13584 errhint("Use the COLLATE clause to set the collation explicitly.")));
13585 }
13586 else
13587 {
13588 if (OidIsValid(attcollation))
13589 ereport(ERROR,
13590 (errcode(ERRCODE_DATATYPE_MISMATCH),
13591 errmsg("collations are not supported by type %s",
13592 format_type_be(atttype))));
13593 }
13594
13595 partcollation[attn] = attcollation;
13596
13597 /*
13598 * Identify a btree opclass to use. Currently, we use only btree
13599 * operators, which seems enough for list and range partitioning.
13600 */
13601 if (!pelem->opclass)
13602 {
13603 partopclass[attn] = GetDefaultOpClass(atttype, BTREE_AM_OID);
13604
13605 if (!OidIsValid(partopclass[attn]))
13606 ereport(ERROR,
13607 (errcode(ERRCODE_UNDEFINED_OBJECT),
13608 errmsg("data type %s has no default btree operator class",
13609 format_type_be(atttype)),
13610 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
13611 }
13612 else
13613 partopclass[attn] = ResolveOpClass(pelem->opclass,
13614 atttype,
13615 "btree",
13616 BTREE_AM_OID);
13617
13618 attn++;
13619 }
13620 }
13621
13622 /*
13623 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
13624 *
13625 * Return the address of the newly attached partition.
13626 */
13627 static ObjectAddress
ATExecAttachPartition(List ** wqueue,Relation rel,PartitionCmd * cmd)13628 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
13629 {
13630 Relation attachrel,
13631 catalog;
13632 List *attachrel_children;
13633 TupleConstr *attachrel_constr;
13634 List *partConstraint,
13635 *existConstraint;
13636 SysScanDesc scan;
13637 ScanKeyData skey;
13638 AttrNumber attno;
13639 int natts;
13640 TupleDesc tupleDesc;
13641 bool skip_validate = false;
13642 ObjectAddress address;
13643 const char *trigger_name;
13644 bool found_whole_row;
13645
13646 attachrel = heap_openrv(cmd->name, AccessExclusiveLock);
13647
13648 /*
13649 * Must be owner of both parent and source table -- parent was checked by
13650 * ATSimplePermissions call in ATPrepCmd
13651 */
13652 ATSimplePermissions(attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
13653
13654 /* A partition can only have one parent */
13655 if (attachrel->rd_rel->relispartition)
13656 ereport(ERROR,
13657 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13658 errmsg("\"%s\" is already a partition",
13659 RelationGetRelationName(attachrel))));
13660
13661 if (OidIsValid(attachrel->rd_rel->reloftype))
13662 ereport(ERROR,
13663 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13664 errmsg("cannot attach a typed table as partition")));
13665
13666 /*
13667 * Table being attached should not already be part of inheritance; either
13668 * as a child table...
13669 */
13670 catalog = heap_open(InheritsRelationId, AccessShareLock);
13671 ScanKeyInit(&skey,
13672 Anum_pg_inherits_inhrelid,
13673 BTEqualStrategyNumber, F_OIDEQ,
13674 ObjectIdGetDatum(RelationGetRelid(attachrel)));
13675 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
13676 NULL, 1, &skey);
13677 if (HeapTupleIsValid(systable_getnext(scan)))
13678 ereport(ERROR,
13679 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13680 errmsg("cannot attach inheritance child as partition")));
13681 systable_endscan(scan);
13682
13683 /* ...or as a parent table (except the case when it is partitioned) */
13684 ScanKeyInit(&skey,
13685 Anum_pg_inherits_inhparent,
13686 BTEqualStrategyNumber, F_OIDEQ,
13687 ObjectIdGetDatum(RelationGetRelid(attachrel)));
13688 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
13689 1, &skey);
13690 if (HeapTupleIsValid(systable_getnext(scan)) &&
13691 attachrel->rd_rel->relkind == RELKIND_RELATION)
13692 ereport(ERROR,
13693 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13694 errmsg("cannot attach inheritance parent as partition")));
13695 systable_endscan(scan);
13696 heap_close(catalog, AccessShareLock);
13697
13698 /*
13699 * Prevent circularity by seeing if rel is a partition of attachrel. (In
13700 * particular, this disallows making a rel a partition of itself.)
13701 *
13702 * We do that by checking if rel is a member of the list of attachrel's
13703 * partitions provided the latter is partitioned at all. We want to avoid
13704 * having to construct this list again, so we request the strongest lock
13705 * on all partitions. We need the strongest lock, because we may decide
13706 * to scan them if we find out that the table being attached (or its leaf
13707 * partitions) may contain rows that violate the partition constraint. If
13708 * the table has a constraint that would prevent such rows, which by
13709 * definition is present in all the partitions, we need not scan the
13710 * table, nor its partitions. But we cannot risk a deadlock by taking a
13711 * weaker lock now and the stronger one only when needed.
13712 */
13713 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
13714 AccessExclusiveLock, NULL);
13715 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
13716 ereport(ERROR,
13717 (errcode(ERRCODE_DUPLICATE_TABLE),
13718 errmsg("circular inheritance not allowed"),
13719 errdetail("\"%s\" is already a child of \"%s\".",
13720 RelationGetRelationName(rel),
13721 RelationGetRelationName(attachrel))));
13722
13723 /* If the parent is permanent, so must be all of its partitions. */
13724 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
13725 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
13726 ereport(ERROR,
13727 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13728 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
13729 RelationGetRelationName(rel))));
13730
13731 /* Temp parent cannot have a partition that is itself not a temp */
13732 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13733 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
13734 ereport(ERROR,
13735 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13736 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
13737 RelationGetRelationName(rel))));
13738
13739 /* If the parent is temp, it must belong to this session */
13740 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13741 !rel->rd_islocaltemp)
13742 ereport(ERROR,
13743 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13744 errmsg("cannot attach as partition of temporary relation of another session")));
13745
13746 /* Ditto for the partition */
13747 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13748 !attachrel->rd_islocaltemp)
13749 ereport(ERROR,
13750 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13751 errmsg("cannot attach temporary relation of another session as partition")));
13752
13753 /* If parent has OIDs then child must have OIDs */
13754 if (rel->rd_rel->relhasoids && !attachrel->rd_rel->relhasoids)
13755 ereport(ERROR,
13756 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13757 errmsg("cannot attach table \"%s\" without OIDs as partition of"
13758 " table \"%s\" with OIDs", RelationGetRelationName(attachrel),
13759 RelationGetRelationName(rel))));
13760
13761 /* OTOH, if parent doesn't have them, do not allow in attachrel either */
13762 if (attachrel->rd_rel->relhasoids && !rel->rd_rel->relhasoids)
13763 ereport(ERROR,
13764 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13765 errmsg("cannot attach table \"%s\" with OIDs as partition of table"
13766 " \"%s\" without OIDs", RelationGetRelationName(attachrel),
13767 RelationGetRelationName(rel))));
13768
13769 /* Check if there are any columns in attachrel that aren't in the parent */
13770 tupleDesc = RelationGetDescr(attachrel);
13771 natts = tupleDesc->natts;
13772 for (attno = 1; attno <= natts; attno++)
13773 {
13774 Form_pg_attribute attribute = tupleDesc->attrs[attno - 1];
13775 char *attributeName = NameStr(attribute->attname);
13776
13777 /* Ignore dropped */
13778 if (attribute->attisdropped)
13779 continue;
13780
13781 /* Try to find the column in parent (matching on column name) */
13782 if (!SearchSysCacheExists2(ATTNAME,
13783 ObjectIdGetDatum(RelationGetRelid(rel)),
13784 CStringGetDatum(attributeName)))
13785 ereport(ERROR,
13786 (errcode(ERRCODE_DATATYPE_MISMATCH),
13787 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
13788 RelationGetRelationName(attachrel), attributeName,
13789 RelationGetRelationName(rel)),
13790 errdetail("The new partition may contain only the columns present in parent.")));
13791 }
13792
13793 /*
13794 * If child_rel has row-level triggers with transition tables, we
13795 * currently don't allow it to become a partition. See also prohibitions
13796 * in ATExecAddInherit() and CreateTrigger().
13797 */
13798 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
13799 if (trigger_name != NULL)
13800 ereport(ERROR,
13801 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13802 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
13803 trigger_name, RelationGetRelationName(attachrel)),
13804 errdetail("ROW triggers with transition tables are not supported on partitions")));
13805
13806 /* OK to create inheritance. Rest of the checks performed there */
13807 CreateInheritance(attachrel, rel);
13808
13809 /*
13810 * Check that the new partition's bound is valid and does not overlap any
13811 * of existing partitions of the parent - note that it does not return on
13812 * error.
13813 */
13814 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
13815 cmd->bound);
13816
13817 /* Update the pg_class entry. */
13818 StorePartitionBound(attachrel, rel, cmd->bound);
13819
13820 /*
13821 * Generate partition constraint from the partition bound specification.
13822 * If the parent itself is a partition, make sure to include its
13823 * constraint as well.
13824 */
13825 partConstraint = list_concat(get_qual_from_partbound(attachrel, rel,
13826 cmd->bound),
13827 RelationGetPartitionQual(rel));
13828
13829 /*
13830 * Run the partition quals through const-simplification similar to check
13831 * constraints. We skip canonicalize_qual, though, because partition
13832 * quals should be in canonical form already; also, since the qual is in
13833 * implicit-AND format, we'd have to explicitly convert it to explicit-AND
13834 * format and back again.
13835 */
13836 partConstraint = (List *) eval_const_expressions(NULL,
13837 (Node *) partConstraint);
13838
13839 partConstraint = list_make1(make_ands_explicit(partConstraint));
13840
13841 /*
13842 * Adjust the generated constraint to match this partition's attribute
13843 * numbers.
13844 */
13845 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
13846 rel, &found_whole_row);
13847 /* There can never be a whole-row reference here */
13848 if (found_whole_row)
13849 elog(ERROR, "unexpected whole-row reference found in partition key");
13850
13851 /*
13852 * Check if we can do away with having to scan the table being attached to
13853 * validate the partition constraint, by *proving* that the existing
13854 * constraints of the table *imply* the partition predicate. We include
13855 * the table's check constraints and NOT NULL constraints in the list of
13856 * clauses passed to predicate_implied_by().
13857 *
13858 * There is a case in which we cannot rely on just the result of the
13859 * proof.
13860 */
13861 attachrel_constr = tupleDesc->constr;
13862 existConstraint = NIL;
13863 if (attachrel_constr != NULL)
13864 {
13865 int num_check = attachrel_constr->num_check;
13866 int i;
13867
13868 if (attachrel_constr->has_not_null)
13869 {
13870 int natts = attachrel->rd_att->natts;
13871
13872 for (i = 1; i <= natts; i++)
13873 {
13874 Form_pg_attribute att = attachrel->rd_att->attrs[i - 1];
13875
13876 if (att->attnotnull && !att->attisdropped)
13877 {
13878 NullTest *ntest = makeNode(NullTest);
13879
13880 ntest->arg = (Expr *) makeVar(1,
13881 i,
13882 att->atttypid,
13883 att->atttypmod,
13884 att->attcollation,
13885 0);
13886 ntest->nulltesttype = IS_NOT_NULL;
13887
13888 /*
13889 * argisrow=false is correct even for a composite column,
13890 * because attnotnull does not represent a SQL-spec IS NOT
13891 * NULL test in such a case, just IS DISTINCT FROM NULL.
13892 */
13893 ntest->argisrow = false;
13894 ntest->location = -1;
13895 existConstraint = lappend(existConstraint, ntest);
13896 }
13897 }
13898 }
13899
13900 for (i = 0; i < num_check; i++)
13901 {
13902 Node *cexpr;
13903
13904 /*
13905 * If this constraint hasn't been fully validated yet, we must
13906 * ignore it here.
13907 */
13908 if (!attachrel_constr->check[i].ccvalid)
13909 continue;
13910
13911 cexpr = stringToNode(attachrel_constr->check[i].ccbin);
13912
13913 /*
13914 * Run each expression through const-simplification and
13915 * canonicalization. It is necessary, because we will be
13916 * comparing it to similarly-processed qual clauses, and may fail
13917 * to detect valid matches without this.
13918 */
13919 cexpr = eval_const_expressions(NULL, cexpr);
13920 cexpr = (Node *) canonicalize_qual_ext((Expr *) cexpr, true);
13921
13922 existConstraint = list_concat(existConstraint,
13923 make_ands_implicit((Expr *) cexpr));
13924 }
13925
13926 /* And away we go ... */
13927 if (predicate_implied_by(partConstraint, existConstraint, true))
13928 skip_validate = true;
13929 }
13930
13931 if (skip_validate)
13932 {
13933 /* No need to scan the table after all. */
13934 ereport(INFO,
13935 (errmsg("partition constraint for table \"%s\" is implied by existing constraints",
13936 RelationGetRelationName(attachrel))));
13937 }
13938 else
13939 {
13940 /* Constraints proved insufficient, so we need to scan the table. */
13941 ListCell *lc;
13942
13943 foreach(lc, attachrel_children)
13944 {
13945 AlteredTableInfo *tab;
13946 Oid part_relid = lfirst_oid(lc);
13947 Relation part_rel;
13948 List *my_partconstr = partConstraint;
13949
13950 /* Lock already taken */
13951 if (part_relid != RelationGetRelid(attachrel))
13952 part_rel = heap_open(part_relid, NoLock);
13953 else
13954 part_rel = attachrel;
13955
13956 /*
13957 * Skip if the partition is itself a partitioned table. We can
13958 * only ever scan RELKIND_RELATION relations.
13959 */
13960 if (part_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13961 {
13962 if (part_rel != attachrel)
13963 heap_close(part_rel, NoLock);
13964 continue;
13965 }
13966
13967 if (part_rel != attachrel)
13968 {
13969 /*
13970 * Adjust the constraint that we constructed above for
13971 * attachRel so that it matches this partition's attribute
13972 * numbers.
13973 */
13974 my_partconstr = map_partition_varattnos(my_partconstr, 1,
13975 part_rel, attachrel,
13976 &found_whole_row);
13977 /* There can never be a whole-row reference here */
13978 if (found_whole_row)
13979 elog(ERROR, "unexpected whole-row reference found in partition key");
13980 }
13981
13982 /* Grab a work queue entry. */
13983 tab = ATGetQueueEntry(wqueue, part_rel);
13984 tab->partition_constraint = (Expr *) linitial(my_partconstr);
13985
13986 /* keep our lock until commit */
13987 if (part_rel != attachrel)
13988 heap_close(part_rel, NoLock);
13989 }
13990 }
13991
13992 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
13993
13994 /*
13995 * If the partition we just attached is partitioned itself, invalidate
13996 * relcache for all descendent partitions too to ensure that their
13997 * rd_partcheck expression trees are rebuilt; partitions already locked
13998 * at the beginning of this function.
13999 */
14000 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14001 {
14002 ListCell *l;
14003
14004 foreach(l, attachrel_children)
14005 {
14006 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
14007 }
14008 }
14009
14010 /* keep our lock until commit */
14011 heap_close(attachrel, NoLock);
14012
14013 return address;
14014 }
14015
14016 /*
14017 * ALTER TABLE DETACH PARTITION
14018 *
14019 * Return the address of the relation that is no longer a partition of rel.
14020 */
14021 static ObjectAddress
ATExecDetachPartition(Relation rel,RangeVar * name)14022 ATExecDetachPartition(Relation rel, RangeVar *name)
14023 {
14024 Relation partRel,
14025 classRel;
14026 HeapTuple tuple,
14027 newtuple;
14028 Datum new_val[Natts_pg_class];
14029 bool isnull,
14030 new_null[Natts_pg_class],
14031 new_repl[Natts_pg_class];
14032 ObjectAddress address;
14033
14034 partRel = heap_openrv(name, ShareUpdateExclusiveLock);
14035
14036 /* All inheritance related checks are performed within the function */
14037 RemoveInheritance(partRel, rel);
14038
14039 /* Update pg_class tuple */
14040 classRel = heap_open(RelationRelationId, RowExclusiveLock);
14041 tuple = SearchSysCacheCopy1(RELOID,
14042 ObjectIdGetDatum(RelationGetRelid(partRel)));
14043 if (!HeapTupleIsValid(tuple))
14044 elog(ERROR, "cache lookup failed for relation %u",
14045 RelationGetRelid(partRel));
14046 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
14047
14048 (void) SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relpartbound,
14049 &isnull);
14050 Assert(!isnull);
14051
14052 /* Clear relpartbound and reset relispartition */
14053 memset(new_val, 0, sizeof(new_val));
14054 memset(new_null, false, sizeof(new_null));
14055 memset(new_repl, false, sizeof(new_repl));
14056 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
14057 new_null[Anum_pg_class_relpartbound - 1] = true;
14058 new_repl[Anum_pg_class_relpartbound - 1] = true;
14059 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
14060 new_val, new_null, new_repl);
14061
14062 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
14063 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
14064 heap_freetuple(newtuple);
14065 heap_close(classRel, RowExclusiveLock);
14066
14067 /*
14068 * Invalidate the parent's relcache so that the partition is no longer
14069 * included in its partition descriptor.
14070 */
14071 CacheInvalidateRelcache(rel);
14072
14073 /*
14074 * If the partition we just detached is partitioned itself, invalidate
14075 * relcache for all descendent partitions too to ensure that their
14076 * rd_partcheck expression trees are rebuilt; must lock partitions
14077 * before doing so, using the same lockmode as what partRel has been
14078 * locked with by the caller.
14079 */
14080 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14081 {
14082 List *children;
14083 ListCell *cell;
14084
14085 children = find_all_inheritors(RelationGetRelid(partRel),
14086 AccessExclusiveLock, NULL);
14087 foreach(cell, children)
14088 {
14089 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
14090 }
14091 }
14092
14093 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
14094
14095 /* keep our lock until commit */
14096 heap_close(partRel, NoLock);
14097
14098 return address;
14099 }
14100