Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * tablecmds.c
4 : * Commands for creating and altering table structures and settings
5 : *
6 : * Portions Copyright (c) 1996-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 : } AlteredTableInfo;
177 :
178 : /* Struct describing one new constraint to check in Phase 3 scan */
179 : /* Note: new NOT NULL constraints are handled elsewhere */
180 : typedef struct NewConstraint
181 : {
182 : char *name; /* Constraint name, or NULL if none */
183 : ConstrType contype; /* CHECK or FOREIGN */
184 : Oid refrelid; /* PK rel, if FOREIGN */
185 : Oid refindid; /* OID of PK's index, if FOREIGN */
186 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
187 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
188 : ExprState *qualstate; /* Execution state for CHECK expr */
189 : } NewConstraint;
190 :
191 : /*
192 : * Struct describing one new column value that needs to be computed during
193 : * Phase 3 copy (this could be either a new column with a non-null default, or
194 : * a column that we're changing the type of). Columns without such an entry
195 : * are just copied from the old table during ATRewriteTable. Note that the
196 : * expr is an expression over *old* table values.
197 : */
198 : typedef struct NewColumnValue
199 : {
200 : AttrNumber attnum; /* which column */
201 : Expr *expr; /* expression to compute */
202 : ExprState *exprstate; /* execution state */
203 : } NewColumnValue;
204 :
205 : /*
206 : * Error-reporting support for RemoveRelations
207 : */
208 : struct dropmsgstrings
209 : {
210 : char kind;
211 : int nonexistent_code;
212 : const char *nonexistent_msg;
213 : const char *skipping_msg;
214 : const char *nota_msg;
215 : const char *drophint_msg;
216 : };
217 :
218 : static const struct dropmsgstrings dropmsgstringarray[] = {
219 : {RELKIND_RELATION,
220 : ERRCODE_UNDEFINED_TABLE,
221 : gettext_noop("table \"%s\" does not exist"),
222 : gettext_noop("table \"%s\" does not exist, skipping"),
223 : gettext_noop("\"%s\" is not a table"),
224 : gettext_noop("Use DROP TABLE to remove a table.")},
225 : {RELKIND_SEQUENCE,
226 : ERRCODE_UNDEFINED_TABLE,
227 : gettext_noop("sequence \"%s\" does not exist"),
228 : gettext_noop("sequence \"%s\" does not exist, skipping"),
229 : gettext_noop("\"%s\" is not a sequence"),
230 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
231 : {RELKIND_VIEW,
232 : ERRCODE_UNDEFINED_TABLE,
233 : gettext_noop("view \"%s\" does not exist"),
234 : gettext_noop("view \"%s\" does not exist, skipping"),
235 : gettext_noop("\"%s\" is not a view"),
236 : gettext_noop("Use DROP VIEW to remove a view.")},
237 : {RELKIND_MATVIEW,
238 : ERRCODE_UNDEFINED_TABLE,
239 : gettext_noop("materialized view \"%s\" does not exist"),
240 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
241 : gettext_noop("\"%s\" is not a materialized view"),
242 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
243 : {RELKIND_INDEX,
244 : ERRCODE_UNDEFINED_OBJECT,
245 : gettext_noop("index \"%s\" does not exist"),
246 : gettext_noop("index \"%s\" does not exist, skipping"),
247 : gettext_noop("\"%s\" is not an index"),
248 : gettext_noop("Use DROP INDEX to remove an index.")},
249 : {RELKIND_COMPOSITE_TYPE,
250 : ERRCODE_UNDEFINED_OBJECT,
251 : gettext_noop("type \"%s\" does not exist"),
252 : gettext_noop("type \"%s\" does not exist, skipping"),
253 : gettext_noop("\"%s\" is not a type"),
254 : gettext_noop("Use DROP TYPE to remove a type.")},
255 : {RELKIND_FOREIGN_TABLE,
256 : ERRCODE_UNDEFINED_OBJECT,
257 : gettext_noop("foreign table \"%s\" does not exist"),
258 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
259 : gettext_noop("\"%s\" is not a foreign table"),
260 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
261 : {RELKIND_PARTITIONED_TABLE,
262 : ERRCODE_UNDEFINED_TABLE,
263 : gettext_noop("table \"%s\" does not exist"),
264 : gettext_noop("table \"%s\" does not exist, skipping"),
265 : gettext_noop("\"%s\" is not a table"),
266 : gettext_noop("Use DROP TABLE to remove a table.")},
267 : {'\0', 0, NULL, NULL, NULL, NULL}
268 : };
269 :
270 : struct DropRelationCallbackState
271 : {
272 : char relkind;
273 : Oid heapOid;
274 : Oid partParentOid;
275 : bool concurrent;
276 : };
277 :
278 : /* Alter table target-type flags for ATSimplePermissions */
279 : #define ATT_TABLE 0x0001
280 : #define ATT_VIEW 0x0002
281 : #define ATT_MATVIEW 0x0004
282 : #define ATT_INDEX 0x0008
283 : #define ATT_COMPOSITE_TYPE 0x0010
284 : #define ATT_FOREIGN_TABLE 0x0020
285 :
286 : /*
287 : * Partition tables are expected to be dropped when the parent partitioned
288 : * table gets dropped. Hence for partitioning we use AUTO dependency.
289 : * Otherwise, for regular inheritance use NORMAL dependency.
290 : */
291 : #define child_dependency_type(child_is_partition) \
292 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
293 :
294 : static void truncate_check_rel(Relation rel);
295 : static List *MergeAttributes(List *schema, List *supers, char relpersistence,
296 : bool is_partition, List **supOids, List **supconstr,
297 : int *supOidCount);
298 : static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
299 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
300 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
301 : static void StoreCatalogInheritance(Oid relationId, List *supers,
302 : bool child_is_partition);
303 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
304 : int16 seqNumber, Relation inhRelation,
305 : bool child_is_partition);
306 : static int findAttrByName(const char *attributeName, List *schema);
307 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
308 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
309 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
310 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
311 : LOCKMODE lockmode);
312 : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
313 : bool recurse, bool recursing, LOCKMODE lockmode);
314 : static ObjectAddress ATExecValidateConstraint(Relation rel, char *constrName,
315 : bool recurse, bool recursing, LOCKMODE lockmode);
316 : static int transformColumnNameList(Oid relId, List *colList,
317 : int16 *attnums, Oid *atttypids);
318 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
319 : List **attnamelist,
320 : int16 *attnums, Oid *atttypids,
321 : Oid *opclasses);
322 : static Oid transformFkeyCheckAttrs(Relation pkrel,
323 : int numattrs, int16 *attnums,
324 : Oid *opclasses);
325 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
326 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
327 : Oid *funcid);
328 : static void validateCheckConstraint(Relation rel, HeapTuple constrtup);
329 : static void validateForeignKeyConstraint(char *conname,
330 : Relation rel, Relation pkrel,
331 : Oid pkindOid, Oid constraintOid);
332 : static void createForeignKeyTriggers(Relation rel, Oid refRelOid,
333 : Constraint *fkconstraint,
334 : Oid constraintOid, Oid indexOid);
335 : static void ATController(AlterTableStmt *parsetree,
336 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
337 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
338 : bool recurse, bool recursing, LOCKMODE lockmode);
339 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode);
340 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
341 : AlterTableCmd *cmd, LOCKMODE lockmode);
342 : static void ATRewriteTables(AlterTableStmt *parsetree,
343 : List **wqueue, LOCKMODE lockmode);
344 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
345 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
346 : static void ATSimplePermissions(Relation rel, int allowed_targets);
347 : static void ATWrongRelkindError(Relation rel, int allowed_targets);
348 : static void ATSimpleRecursion(List **wqueue, Relation rel,
349 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
350 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
351 : LOCKMODE lockmode);
352 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
353 : DropBehavior behavior);
354 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
355 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode);
356 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
357 : Relation rel, ColumnDef *colDef, bool isOid,
358 : bool recurse, bool recursing,
359 : bool if_not_exists, LOCKMODE lockmode);
360 : static bool check_for_column_name_collision(Relation rel, const char *colname,
361 : bool if_not_exists);
362 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
363 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
364 : static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
365 : AlterTableCmd *cmd, LOCKMODE lockmode);
366 : static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
367 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
368 : static void ATPrepSetNotNull(Relation rel, bool recurse, bool recursing);
369 : static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
370 : const char *colName, LOCKMODE lockmode);
371 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
372 : Node *newDefault, LOCKMODE lockmode);
373 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
374 : Node *def, LOCKMODE lockmode);
375 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
376 : Node *def, LOCKMODE lockmode);
377 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
378 : static void ATPrepSetStatistics(Relation rel, const char *colName,
379 : Node *newValue, LOCKMODE lockmode);
380 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName,
381 : Node *newValue, LOCKMODE lockmode);
382 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
383 : Node *options, bool isReset, LOCKMODE lockmode);
384 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
385 : Node *newValue, LOCKMODE lockmode);
386 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
387 : AlterTableCmd *cmd, LOCKMODE lockmode);
388 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
389 : DropBehavior behavior,
390 : bool recurse, bool recursing,
391 : bool missing_ok, LOCKMODE lockmode);
392 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
393 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
394 : static ObjectAddress ATExecAddConstraint(List **wqueue,
395 : AlteredTableInfo *tab, Relation rel,
396 : Constraint *newConstraint, bool recurse, bool is_readd,
397 : LOCKMODE lockmode);
398 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
399 : IndexStmt *stmt, LOCKMODE lockmode);
400 : static ObjectAddress ATAddCheckConstraint(List **wqueue,
401 : AlteredTableInfo *tab, Relation rel,
402 : Constraint *constr,
403 : bool recurse, bool recursing, bool is_readd,
404 : LOCKMODE lockmode);
405 : static ObjectAddress ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
406 : Constraint *fkconstraint, LOCKMODE lockmode);
407 : static void ATExecDropConstraint(Relation rel, const char *constrName,
408 : DropBehavior behavior,
409 : bool recurse, bool recursing,
410 : bool missing_ok, LOCKMODE lockmode);
411 : static void ATPrepAlterColumnType(List **wqueue,
412 : AlteredTableInfo *tab, Relation rel,
413 : bool recurse, bool recursing,
414 : AlterTableCmd *cmd, LOCKMODE lockmode);
415 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
416 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
417 : AlterTableCmd *cmd, LOCKMODE lockmode);
418 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
419 : List *options, LOCKMODE lockmode);
420 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
421 : LOCKMODE lockmode);
422 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
423 : char *cmd, List **wqueue, LOCKMODE lockmode,
424 : bool rewrite);
425 : static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
426 : Oid objid, Relation rel, char *conname);
427 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
428 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
429 : static void change_owner_fix_column_acls(Oid relationOid,
430 : Oid oldOwnerId, Oid newOwnerId);
431 : static void change_owner_recurse_to_sequences(Oid relationOid,
432 : Oid newOwnerId, LOCKMODE lockmode);
433 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
434 : LOCKMODE lockmode);
435 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
436 : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
437 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
438 : char *tablespacename, LOCKMODE lockmode);
439 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
440 : static void ATExecSetRelOptions(Relation rel, List *defList,
441 : AlterTableType operation,
442 : LOCKMODE lockmode);
443 : static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
444 : char fires_when, bool skip_system, LOCKMODE lockmode);
445 : static void ATExecEnableDisableRule(Relation rel, char *rulename,
446 : char fires_when, LOCKMODE lockmode);
447 : static void ATPrepAddInherit(Relation child_rel);
448 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
449 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
450 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
451 : DependencyType deptype);
452 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
453 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
454 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
455 : static void ATExecGenericOptions(Relation rel, List *options);
456 : static void ATExecEnableRowSecurity(Relation rel);
457 : static void ATExecDisableRowSecurity(Relation rel);
458 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
459 :
460 : static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
461 : ForkNumber forkNum, char relpersistence);
462 : static const char *storage_name(char c);
463 :
464 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
465 : Oid oldRelOid, void *arg);
466 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
467 : Oid oldrelid, void *arg);
468 : static bool is_partition_attr(Relation rel, AttrNumber attnum, bool *used_in_expr);
469 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
470 : static void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
471 : List **partexprs, Oid *partopclass, Oid *partcollation);
472 : static void CreateInheritance(Relation child_rel, Relation parent_rel);
473 : static void RemoveInheritance(Relation child_rel, Relation parent_rel);
474 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
475 : PartitionCmd *cmd);
476 : static bool PartConstraintImpliedByRelConstraint(Relation scanrel,
477 : List *partConstraint);
478 : static void ValidatePartitionConstraints(List **wqueue, Relation scanrel,
479 : List *scanrel_children,
480 : List *partConstraint);
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
503 2155 : 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 2155 : StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
530 :
531 : /*
532 : * Check consistency of arguments
533 : */
534 2155 : if (stmt->oncommit != ONCOMMIT_NOOP
535 10 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
536 2 : ereport(ERROR,
537 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
538 : errmsg("ON COMMIT can only be used on temporary tables")));
539 :
540 2153 : if (stmt->partspec != NULL)
541 : {
542 93 : if (relkind != RELKIND_RELATION)
543 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
544 :
545 93 : 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 2153 : namespaceId =
555 2153 : 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 2153 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
563 311 : && InSecurityRestrictedOperation())
564 0 : 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 2153 : if (stmt->tablespacename)
573 : {
574 7 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
575 : }
576 : else
577 : {
578 2146 : 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 2152 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
584 : {
585 : AclResult aclresult;
586 :
587 6 : aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
588 : ACL_CREATE);
589 6 : if (aclresult != ACLCHECK_OK)
590 1 : aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
591 1 : get_tablespace_name(tablespaceId));
592 : }
593 :
594 : /* In all cases disallow placing user relations in pg_global */
595 2151 : if (tablespaceId == GLOBALTABLESPACE_OID)
596 0 : 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 2151 : if (!OidIsValid(ownerId))
602 2144 : ownerId = GetUserId();
603 :
604 : /*
605 : * Parse and validate reloptions, if any.
606 : */
607 2151 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
608 : true, false);
609 :
610 2151 : if (relkind == RELKIND_VIEW)
611 385 : (void) view_reloptions(reloptions, true);
612 : else
613 1766 : (void) heap_reloptions(relkind, reloptions, true);
614 :
615 2149 : if (stmt->ofTypename)
616 : {
617 : AclResult aclresult;
618 :
619 14 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
620 :
621 14 : aclresult = pg_type_aclcheck(ofTypeId, GetUserId(), ACL_USAGE);
622 14 : if (aclresult != ACLCHECK_OK)
623 1 : aclcheck_error_type(aclresult, ofTypeId);
624 : }
625 : else
626 2135 : 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 2135 : stmt->tableElts =
634 4296 : MergeAttributes(stmt->tableElts, stmt->inhRelations,
635 2148 : stmt->relation->relpersistence,
636 2148 : 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 2135 : 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 2131 : localHasOids = interpretOidsOption(stmt->options,
656 2131 : (relkind == RELKIND_RELATION ||
657 : relkind == RELKIND_PARTITIONED_TABLE));
658 2131 : 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 2131 : if (stmt->partbound && parentOidCount == 0 && localHasOids)
665 1 : 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 2130 : rawDefaults = NIL;
682 2130 : cookedDefaults = NIL;
683 2130 : attnum = 0;
684 :
685 7929 : foreach(listptr, stmt->tableElts)
686 : {
687 5799 : ColumnDef *colDef = lfirst(listptr);
688 : Form_pg_attribute attr;
689 :
690 5799 : attnum++;
691 5799 : attr = TupleDescAttr(descriptor, attnum - 1);
692 :
693 5799 : if (colDef->raw_default != NULL)
694 : {
695 : RawColumnDefault *rawEnt;
696 :
697 131 : Assert(colDef->cooked_default == NULL);
698 :
699 131 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
700 131 : rawEnt->attnum = attnum;
701 131 : rawEnt->raw_default = colDef->raw_default;
702 131 : rawDefaults = lappend(rawDefaults, rawEnt);
703 131 : attr->atthasdef = true;
704 : }
705 5668 : else if (colDef->cooked_default != NULL)
706 : {
707 : CookedConstraint *cooked;
708 :
709 17 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
710 17 : cooked->contype = CONSTR_DEFAULT;
711 17 : cooked->conoid = InvalidOid; /* until created */
712 17 : cooked->name = NULL;
713 17 : cooked->attnum = attnum;
714 17 : cooked->expr = colDef->cooked_default;
715 17 : cooked->skip_validation = false;
716 17 : cooked->is_local = true; /* not used for defaults */
717 17 : cooked->inhcount = 0; /* ditto */
718 17 : cooked->is_no_inherit = false;
719 17 : cookedDefaults = lappend(cookedDefaults, cooked);
720 17 : attr->atthasdef = true;
721 : }
722 :
723 5799 : if (colDef->identity)
724 12 : attr->attidentity = colDef->identity;
725 : }
726 :
727 : /*
728 : * Create the relation. Inherited defaults and constraints are passed in
729 : * for immediate handling --- since they don't need parsing, they can be
730 : * stored immediately.
731 : */
732 4260 : relationId = heap_create_with_catalog(relname,
733 : namespaceId,
734 : tablespaceId,
735 : InvalidOid,
736 : InvalidOid,
737 : ofTypeId,
738 : ownerId,
739 : descriptor,
740 : list_concat(cookedDefaults,
741 : old_constraints),
742 : relkind,
743 2130 : stmt->relation->relpersistence,
744 : false,
745 : false,
746 : localHasOids,
747 : parentOidCount,
748 : stmt->oncommit,
749 : reloptions,
750 : true,
751 : allowSystemTableMods,
752 : false,
753 : typaddress);
754 :
755 : /* Store inheritance information for new rel. */
756 2125 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
757 :
758 : /*
759 : * We must bump the command counter to make the newly-created relation
760 : * tuple visible for opening.
761 : */
762 2125 : CommandCounterIncrement();
763 :
764 : /*
765 : * Open the new relation and acquire exclusive lock on it. This isn't
766 : * really necessary for locking out other backends (since they can't see
767 : * the new rel anyway until we commit), but it keeps the lock manager from
768 : * complaining about deadlock risks.
769 : */
770 2125 : rel = relation_open(relationId, AccessExclusiveLock);
771 :
772 : /* Process and store partition bound, if any. */
773 2125 : if (stmt->partbound)
774 : {
775 : PartitionBoundSpec *bound;
776 : ParseState *pstate;
777 136 : Oid parentId = linitial_oid(inheritOids);
778 : Relation parent;
779 :
780 : /* Already have strong enough lock on the parent */
781 136 : parent = heap_open(parentId, NoLock);
782 :
783 : /*
784 : * We are going to try to validate the partition bound specification
785 : * against the partition key of parentRel, so it better have one.
786 : */
787 136 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
788 1 : ereport(ERROR,
789 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
790 : errmsg("\"%s\" is not partitioned",
791 : RelationGetRelationName(parent))));
792 :
793 : /* Tranform the bound values */
794 135 : pstate = make_parsestate(NULL);
795 135 : pstate->p_sourcetext = queryString;
796 :
797 135 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
798 :
799 : /*
800 : * Check first that the new partition's bound is valid and does not
801 : * overlap with any of existing partitions of the parent - note that
802 : * it does not return on error.
803 : */
804 128 : check_new_partition_bound(relname, parent, bound);
805 :
806 : /* Update the pg_class entry. */
807 116 : StorePartitionBound(rel, parent, bound);
808 :
809 116 : heap_close(parent, NoLock);
810 :
811 : /*
812 : * The code that follows may also update the pg_class tuple to update
813 : * relnumchecks, so bump up the command counter to avoid the "already
814 : * updated by self" error.
815 : */
816 116 : CommandCounterIncrement();
817 : }
818 :
819 : /*
820 : * Process the partitioning specification (if any) and store the partition
821 : * key information into the catalog.
822 : */
823 2105 : if (stmt->partspec)
824 : {
825 : char strategy;
826 : int partnatts;
827 : AttrNumber partattrs[PARTITION_MAX_KEYS];
828 : Oid partopclass[PARTITION_MAX_KEYS];
829 : Oid partcollation[PARTITION_MAX_KEYS];
830 93 : List *partexprs = NIL;
831 :
832 93 : partnatts = list_length(stmt->partspec->partParams);
833 :
834 : /* Protect fixed-size arrays here and in executor */
835 93 : if (partnatts > PARTITION_MAX_KEYS)
836 0 : ereport(ERROR,
837 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
838 : errmsg("cannot partition using more than %d columns",
839 : PARTITION_MAX_KEYS)));
840 :
841 : /*
842 : * We need to transform the raw parsetrees corresponding to partition
843 : * expressions into executable expression trees. Like column defaults
844 : * and CHECK constraints, we could not have done the transformation
845 : * earlier.
846 : */
847 93 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
848 : &strategy);
849 :
850 86 : ComputePartitionAttrs(rel, stmt->partspec->partParams,
851 : partattrs, &partexprs, partopclass,
852 : partcollation);
853 :
854 75 : StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
855 : partopclass, partcollation);
856 : }
857 :
858 : /*
859 : * Now add any newly specified column default values and CHECK constraints
860 : * to the new relation. These are passed to us in the form of raw
861 : * parsetrees; we need to transform them to executable expression trees
862 : * before they can be added. The most convenient way to do that is to
863 : * apply the parser's transformExpr routine, but transformExpr doesn't
864 : * work unless we have a pre-existing relation. So, the transformation has
865 : * to be postponed to this final step of CREATE TABLE.
866 : */
867 2087 : if (rawDefaults || stmt->constraints)
868 144 : AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
869 : true, true, false);
870 :
871 2084 : ObjectAddressSet(address, RelationRelationId, relationId);
872 :
873 : /*
874 : * Clean up. We keep lock on new relation (although it shouldn't be
875 : * visible to anyone else anyway, until commit).
876 : */
877 2084 : relation_close(rel, NoLock);
878 :
879 2084 : return address;
880 : }
881 :
882 : /*
883 : * Emit the right error or warning message for a "DROP" command issued on a
884 : * non-existent relation
885 : */
886 : static void
887 50 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
888 : {
889 : const struct dropmsgstrings *rentry;
890 :
891 69 : if (rel->schemaname != NULL &&
892 19 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
893 : {
894 7 : if (!missing_ok)
895 : {
896 0 : ereport(ERROR,
897 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
898 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
899 : }
900 : else
901 : {
902 7 : ereport(NOTICE,
903 : (errmsg("schema \"%s\" does not exist, skipping",
904 : rel->schemaname)));
905 : }
906 35 : return;
907 : }
908 :
909 92 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
910 : {
911 92 : if (rentry->kind == rightkind)
912 : {
913 43 : if (!missing_ok)
914 : {
915 22 : ereport(ERROR,
916 : (errcode(rentry->nonexistent_code),
917 : errmsg(rentry->nonexistent_msg, rel->relname)));
918 : }
919 : else
920 : {
921 21 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
922 21 : break;
923 : }
924 : }
925 : }
926 :
927 21 : Assert(rentry->kind != '\0'); /* Should be impossible */
928 : }
929 :
930 : /*
931 : * Emit the right error message for a "DROP" command issued on a
932 : * relation of the wrong type
933 : */
934 : static void
935 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
936 : {
937 : const struct dropmsgstrings *rentry;
938 : const struct dropmsgstrings *wentry;
939 :
940 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
941 0 : if (rentry->kind == rightkind)
942 0 : break;
943 0 : Assert(rentry->kind != '\0');
944 :
945 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
946 0 : if (wentry->kind == wrongkind)
947 0 : break;
948 : /* wrongkind could be something we don't have in our table... */
949 :
950 0 : ereport(ERROR,
951 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
952 : errmsg(rentry->nota_msg, relname),
953 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
954 : }
955 :
956 : /*
957 : * RemoveRelations
958 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
959 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
960 : */
961 : void
962 819 : RemoveRelations(DropStmt *drop)
963 : {
964 : ObjectAddresses *objects;
965 : char relkind;
966 : ListCell *cell;
967 819 : int flags = 0;
968 819 : LOCKMODE lockmode = AccessExclusiveLock;
969 :
970 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
971 819 : if (drop->concurrent)
972 : {
973 8 : flags |= PERFORM_DELETION_CONCURRENTLY;
974 8 : lockmode = ShareUpdateExclusiveLock;
975 8 : Assert(drop->removeType == OBJECT_INDEX);
976 8 : if (list_length(drop->objects) != 1)
977 1 : ereport(ERROR,
978 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
979 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
980 7 : if (drop->behavior == DROP_CASCADE)
981 0 : ereport(ERROR,
982 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
983 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
984 : }
985 :
986 : /*
987 : * First we identify all the relations, then we delete them in a single
988 : * performMultipleDeletions() call. This is to avoid unwanted DROP
989 : * RESTRICT errors if one of the relations depends on another.
990 : */
991 :
992 : /* Determine required relkind */
993 818 : switch (drop->removeType)
994 : {
995 : case OBJECT_TABLE:
996 635 : relkind = RELKIND_RELATION;
997 635 : break;
998 :
999 : case OBJECT_INDEX:
1000 62 : relkind = RELKIND_INDEX;
1001 62 : break;
1002 :
1003 : case OBJECT_SEQUENCE:
1004 23 : relkind = RELKIND_SEQUENCE;
1005 23 : break;
1006 :
1007 : case OBJECT_VIEW:
1008 83 : relkind = RELKIND_VIEW;
1009 83 : break;
1010 :
1011 : case OBJECT_MATVIEW:
1012 5 : relkind = RELKIND_MATVIEW;
1013 5 : break;
1014 :
1015 : case OBJECT_FOREIGN_TABLE:
1016 10 : relkind = RELKIND_FOREIGN_TABLE;
1017 10 : break;
1018 :
1019 : default:
1020 0 : elog(ERROR, "unrecognized drop object type: %d",
1021 : (int) drop->removeType);
1022 : relkind = 0; /* keep compiler quiet */
1023 : break;
1024 : }
1025 :
1026 : /* Lock and validate each relation; build a list of object addresses */
1027 818 : objects = new_object_addresses();
1028 :
1029 1717 : foreach(cell, drop->objects)
1030 : {
1031 921 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1032 : Oid relOid;
1033 : ObjectAddress obj;
1034 : struct DropRelationCallbackState state;
1035 :
1036 : /*
1037 : * These next few steps are a great deal like relation_openrv, but we
1038 : * don't bother building a relcache entry since we don't need it.
1039 : *
1040 : * Check for shared-cache-inval messages before trying to access the
1041 : * relation. This is needed to cover the case where the name
1042 : * identifies a rel that has been dropped and recreated since the
1043 : * start of our transaction: if we don't flush the old syscache entry,
1044 : * then we'll latch onto that entry and suffer an error later.
1045 : */
1046 921 : AcceptInvalidationMessages();
1047 :
1048 : /* Look up the appropriate relation using namespace search. */
1049 921 : state.relkind = relkind;
1050 921 : state.heapOid = InvalidOid;
1051 921 : state.partParentOid = InvalidOid;
1052 921 : state.concurrent = drop->concurrent;
1053 921 : relOid = RangeVarGetRelidExtended(rel, lockmode, true,
1054 : false,
1055 : RangeVarCallbackForDropRelation,
1056 : (void *) &state);
1057 :
1058 : /* Not there? */
1059 921 : if (!OidIsValid(relOid))
1060 : {
1061 50 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1062 28 : continue;
1063 : }
1064 :
1065 : /* OK, we're ready to delete this one */
1066 871 : obj.classId = RelationRelationId;
1067 871 : obj.objectId = relOid;
1068 871 : obj.objectSubId = 0;
1069 :
1070 871 : add_exact_object_address(&obj, objects);
1071 : }
1072 :
1073 796 : performMultipleDeletions(objects, drop->behavior, flags);
1074 :
1075 787 : free_object_addresses(objects);
1076 787 : }
1077 :
1078 : /*
1079 : * Before acquiring a table lock, check whether we have sufficient rights.
1080 : * In the case of DROP INDEX, also try to lock the table before the index.
1081 : * Also, if the table to be dropped is a partition, we try to lock the parent
1082 : * first.
1083 : */
1084 : static void
1085 933 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1086 : void *arg)
1087 : {
1088 : HeapTuple tuple;
1089 : struct DropRelationCallbackState *state;
1090 : char relkind;
1091 : char expected_relkind;
1092 : bool is_partition;
1093 : Form_pg_class classform;
1094 : LOCKMODE heap_lockmode;
1095 :
1096 933 : state = (struct DropRelationCallbackState *) arg;
1097 933 : relkind = state->relkind;
1098 1866 : heap_lockmode = state->concurrent ?
1099 933 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1100 :
1101 : /*
1102 : * If we previously locked some other index's heap, and the name we're
1103 : * looking up no longer refers to that relation, release the now-useless
1104 : * lock.
1105 : */
1106 933 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1107 : {
1108 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1109 0 : state->heapOid = InvalidOid;
1110 : }
1111 :
1112 : /*
1113 : * Similarly, if we previously locked some other partition's heap, and the
1114 : * name we're looking up no longer refers to that relation, release the
1115 : * now-useless lock.
1116 : */
1117 933 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1118 : {
1119 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1120 0 : state->partParentOid = InvalidOid;
1121 : }
1122 :
1123 : /* Didn't find a relation, so no need for locking or permission checks. */
1124 933 : if (!OidIsValid(relOid))
1125 50 : return;
1126 :
1127 883 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1128 883 : if (!HeapTupleIsValid(tuple))
1129 0 : return; /* concurrently dropped, so nothing to do */
1130 883 : classform = (Form_pg_class) GETSTRUCT(tuple);
1131 883 : is_partition = classform->relispartition;
1132 :
1133 : /*
1134 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1135 : * but RemoveRelations() can only pass one relkind for a given relation.
1136 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1137 : * That means we must be careful before giving the wrong type error when
1138 : * the relation is RELKIND_PARTITIONED_TABLE.
1139 : */
1140 883 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1141 56 : expected_relkind = RELKIND_RELATION;
1142 : else
1143 827 : expected_relkind = classform->relkind;
1144 :
1145 883 : if (relkind != expected_relkind)
1146 0 : DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
1147 :
1148 : /* Allow DROP to either table owner or schema owner */
1149 883 : if (!pg_class_ownercheck(relOid, GetUserId()) &&
1150 0 : !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
1151 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
1152 0 : rel->relname);
1153 :
1154 883 : if (!allowSystemTableMods && IsSystemClass(relOid, classform))
1155 0 : ereport(ERROR,
1156 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1157 : errmsg("permission denied: \"%s\" is a system catalog",
1158 : rel->relname)));
1159 :
1160 883 : ReleaseSysCache(tuple);
1161 :
1162 : /*
1163 : * In DROP INDEX, attempt to acquire lock on the parent table before
1164 : * locking the index. index_drop() will need this anyway, and since
1165 : * regular queries lock tables before their indexes, we risk deadlock if
1166 : * we do it the other way around. No error if we don't find a pg_index
1167 : * entry, though --- the relation may have been dropped.
1168 : */
1169 883 : if (relkind == RELKIND_INDEX && relOid != oldRelOid)
1170 : {
1171 57 : state->heapOid = IndexGetRelation(relOid, true);
1172 57 : if (OidIsValid(state->heapOid))
1173 57 : LockRelationOid(state->heapOid, heap_lockmode);
1174 : }
1175 :
1176 : /*
1177 : * Similarly, if the relation is a partition, we must acquire lock on its
1178 : * parent before locking the partition. That's because queries lock the
1179 : * parent before its partitions, so we risk deadlock it we do it the other
1180 : * way around.
1181 : */
1182 883 : if (is_partition && relOid != oldRelOid)
1183 : {
1184 14 : state->partParentOid = get_partition_parent(relOid);
1185 14 : if (OidIsValid(state->partParentOid))
1186 14 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1187 : }
1188 : }
1189 :
1190 : /*
1191 : * ExecuteTruncate
1192 : * Executes a TRUNCATE command.
1193 : *
1194 : * This is a multi-relation truncate. We first open and grab exclusive
1195 : * lock on all relations involved, checking permissions and otherwise
1196 : * verifying that the relation is OK for truncation. In CASCADE mode,
1197 : * relations having FK references to the targeted relations are automatically
1198 : * added to the group; in RESTRICT mode, we check that all FK references are
1199 : * internal to the group that's being truncated. Finally all the relations
1200 : * are truncated and reindexed.
1201 : */
1202 : void
1203 69 : ExecuteTruncate(TruncateStmt *stmt)
1204 : {
1205 69 : List *rels = NIL;
1206 69 : List *relids = NIL;
1207 69 : List *seq_relids = NIL;
1208 : EState *estate;
1209 : ResultRelInfo *resultRelInfos;
1210 : ResultRelInfo *resultRelInfo;
1211 : SubTransactionId mySubid;
1212 : ListCell *cell;
1213 :
1214 : /*
1215 : * Open, exclusive-lock, and check all the explicitly-specified relations
1216 : */
1217 155 : foreach(cell, stmt->relations)
1218 : {
1219 95 : RangeVar *rv = lfirst(cell);
1220 : Relation rel;
1221 95 : bool recurse = rv->inh;
1222 : Oid myrelid;
1223 :
1224 95 : rel = heap_openrv(rv, AccessExclusiveLock);
1225 95 : myrelid = RelationGetRelid(rel);
1226 : /* don't throw error for "TRUNCATE foo, foo" */
1227 95 : if (list_member_oid(relids, myrelid))
1228 : {
1229 0 : heap_close(rel, AccessExclusiveLock);
1230 0 : continue;
1231 : }
1232 95 : truncate_check_rel(rel);
1233 90 : rels = lappend(rels, rel);
1234 90 : relids = lappend_oid(relids, myrelid);
1235 :
1236 90 : if (recurse)
1237 : {
1238 : ListCell *child;
1239 : List *children;
1240 :
1241 84 : children = find_all_inheritors(myrelid, AccessExclusiveLock, NULL);
1242 :
1243 192 : foreach(child, children)
1244 : {
1245 110 : Oid childrelid = lfirst_oid(child);
1246 :
1247 110 : if (list_member_oid(relids, childrelid))
1248 84 : continue;
1249 :
1250 : /* find_all_inheritors already got lock */
1251 26 : rel = heap_open(childrelid, NoLock);
1252 26 : truncate_check_rel(rel);
1253 24 : rels = lappend(rels, rel);
1254 24 : relids = lappend_oid(relids, childrelid);
1255 : }
1256 : }
1257 6 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1258 2 : ereport(ERROR,
1259 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1260 : errmsg("cannot truncate only a partitioned table"),
1261 : errhint("Do not specify the ONLY keyword, or use truncate only on the partitions directly.")));
1262 : }
1263 :
1264 : /*
1265 : * In CASCADE mode, suck in all referencing relations as well. This
1266 : * requires multiple iterations to find indirectly-dependent relations. At
1267 : * each phase, we need to exclusive-lock new rels before looking for their
1268 : * dependencies, else we might miss something. Also, we check each rel as
1269 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1270 : * time on a rel we have no permissions for.
1271 : */
1272 60 : if (stmt->behavior == DROP_CASCADE)
1273 : {
1274 : for (;;)
1275 : {
1276 : List *newrelids;
1277 :
1278 5 : newrelids = heap_truncate_find_FKs(relids);
1279 5 : if (newrelids == NIL)
1280 2 : break; /* nothing else to add */
1281 :
1282 9 : foreach(cell, newrelids)
1283 : {
1284 6 : Oid relid = lfirst_oid(cell);
1285 : Relation rel;
1286 :
1287 6 : rel = heap_open(relid, AccessExclusiveLock);
1288 6 : ereport(NOTICE,
1289 : (errmsg("truncate cascades to table \"%s\"",
1290 : RelationGetRelationName(rel))));
1291 6 : truncate_check_rel(rel);
1292 6 : rels = lappend(rels, rel);
1293 6 : relids = lappend_oid(relids, relid);
1294 : }
1295 3 : }
1296 : }
1297 :
1298 : /*
1299 : * Check foreign key references. In CASCADE mode, this should be
1300 : * unnecessary since we just pulled in all the references; but as a
1301 : * cross-check, do it anyway if in an Assert-enabled build.
1302 : */
1303 : #ifdef USE_ASSERT_CHECKING
1304 60 : heap_truncate_check_FKs(rels, false);
1305 : #else
1306 : if (stmt->behavior == DROP_RESTRICT)
1307 : heap_truncate_check_FKs(rels, false);
1308 : #endif
1309 :
1310 : /*
1311 : * If we are asked to restart sequences, find all the sequences, lock them
1312 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1313 : * We want to do this early since it's pointless to do all the truncation
1314 : * work only to fail on sequence permissions.
1315 : */
1316 49 : if (stmt->restart_seqs)
1317 : {
1318 6 : foreach(cell, rels)
1319 : {
1320 3 : Relation rel = (Relation) lfirst(cell);
1321 3 : List *seqlist = getOwnedSequences(RelationGetRelid(rel), 0);
1322 : ListCell *seqcell;
1323 :
1324 8 : foreach(seqcell, seqlist)
1325 : {
1326 5 : Oid seq_relid = lfirst_oid(seqcell);
1327 : Relation seq_rel;
1328 :
1329 5 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1330 :
1331 : /* This check must match AlterSequence! */
1332 5 : if (!pg_class_ownercheck(seq_relid, GetUserId()))
1333 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
1334 0 : RelationGetRelationName(seq_rel));
1335 :
1336 5 : seq_relids = lappend_oid(seq_relids, seq_relid);
1337 :
1338 5 : relation_close(seq_rel, NoLock);
1339 : }
1340 : }
1341 : }
1342 :
1343 : /* Prepare to catch AFTER triggers. */
1344 49 : AfterTriggerBeginQuery();
1345 :
1346 : /*
1347 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1348 : * each relation. We don't need to call ExecOpenIndices, though.
1349 : */
1350 49 : estate = CreateExecutorState();
1351 49 : resultRelInfos = (ResultRelInfo *)
1352 49 : palloc(list_length(rels) * sizeof(ResultRelInfo));
1353 49 : resultRelInfo = resultRelInfos;
1354 142 : foreach(cell, rels)
1355 : {
1356 93 : Relation rel = (Relation) lfirst(cell);
1357 :
1358 93 : InitResultRelInfo(resultRelInfo,
1359 : rel,
1360 : 0, /* dummy rangetable index */
1361 : NULL,
1362 : 0);
1363 93 : resultRelInfo++;
1364 : }
1365 49 : estate->es_result_relations = resultRelInfos;
1366 49 : estate->es_num_result_relations = list_length(rels);
1367 :
1368 : /*
1369 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
1370 : * truncating (this is because one of them might throw an error). Also, if
1371 : * we were to allow them to prevent statement execution, that would need
1372 : * to be handled here.
1373 : */
1374 49 : resultRelInfo = resultRelInfos;
1375 142 : foreach(cell, rels)
1376 : {
1377 93 : estate->es_result_relation_info = resultRelInfo;
1378 93 : ExecBSTruncateTriggers(estate, resultRelInfo);
1379 93 : resultRelInfo++;
1380 : }
1381 :
1382 : /*
1383 : * OK, truncate each table.
1384 : */
1385 49 : mySubid = GetCurrentSubTransactionId();
1386 :
1387 142 : foreach(cell, rels)
1388 : {
1389 93 : Relation rel = (Relation) lfirst(cell);
1390 :
1391 : /* Skip partitioned tables as there is nothing to do */
1392 93 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1393 9 : continue;
1394 :
1395 : /*
1396 : * Normally, we need a transaction-safe truncation here. However, if
1397 : * the table was either created in the current (sub)transaction or has
1398 : * a new relfilenode in the current (sub)transaction, then we can just
1399 : * truncate it in-place, because a rollback would cause the whole
1400 : * table or the current physical file to be thrown away anyway.
1401 : */
1402 168 : if (rel->rd_createSubid == mySubid ||
1403 84 : rel->rd_newRelfilenodeSubid == mySubid)
1404 : {
1405 : /* Immediate, non-rollbackable truncation is OK */
1406 0 : heap_truncate_one_rel(rel);
1407 : }
1408 : else
1409 : {
1410 : Oid heap_relid;
1411 : Oid toast_relid;
1412 : MultiXactId minmulti;
1413 :
1414 : /*
1415 : * This effectively deletes all rows in the table, and may be done
1416 : * in a serializable transaction. In that case we must record a
1417 : * rw-conflict in to this transaction from each transaction
1418 : * holding a predicate lock on the table.
1419 : */
1420 84 : CheckTableForSerializableConflictIn(rel);
1421 :
1422 84 : minmulti = GetOldestMultiXactId();
1423 :
1424 : /*
1425 : * Need the full transaction-safe pushups.
1426 : *
1427 : * Create a new empty storage file for the relation, and assign it
1428 : * as the relfilenode value. The old storage file is scheduled for
1429 : * deletion at commit.
1430 : */
1431 84 : RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence,
1432 : RecentXmin, minmulti);
1433 84 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
1434 0 : heap_create_init_fork(rel);
1435 :
1436 84 : heap_relid = RelationGetRelid(rel);
1437 84 : toast_relid = rel->rd_rel->reltoastrelid;
1438 :
1439 : /*
1440 : * The same for the toast table, if any.
1441 : */
1442 84 : if (OidIsValid(toast_relid))
1443 : {
1444 33 : rel = relation_open(toast_relid, AccessExclusiveLock);
1445 33 : RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence,
1446 : RecentXmin, minmulti);
1447 33 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
1448 0 : heap_create_init_fork(rel);
1449 33 : heap_close(rel, NoLock);
1450 : }
1451 :
1452 : /*
1453 : * Reconstruct the indexes to match, and we're done.
1454 : */
1455 84 : reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, 0);
1456 : }
1457 :
1458 84 : pgstat_count_truncate(rel);
1459 : }
1460 :
1461 : /*
1462 : * Restart owned sequences if we were asked to.
1463 : */
1464 54 : foreach(cell, seq_relids)
1465 : {
1466 5 : Oid seq_relid = lfirst_oid(cell);
1467 :
1468 5 : ResetSequence(seq_relid);
1469 : }
1470 :
1471 : /*
1472 : * Process all AFTER STATEMENT TRUNCATE triggers.
1473 : */
1474 49 : resultRelInfo = resultRelInfos;
1475 142 : foreach(cell, rels)
1476 : {
1477 93 : estate->es_result_relation_info = resultRelInfo;
1478 93 : ExecASTruncateTriggers(estate, resultRelInfo);
1479 93 : resultRelInfo++;
1480 : }
1481 :
1482 : /* Handle queued AFTER triggers */
1483 49 : AfterTriggerEndQuery(estate);
1484 :
1485 : /* We can clean up the EState now */
1486 49 : FreeExecutorState(estate);
1487 :
1488 : /* And close the rels (can't do this while EState still holds refs) */
1489 142 : foreach(cell, rels)
1490 : {
1491 93 : Relation rel = (Relation) lfirst(cell);
1492 :
1493 93 : heap_close(rel, NoLock);
1494 : }
1495 49 : }
1496 :
1497 : /*
1498 : * Check that a given rel is safe to truncate. Subroutine for ExecuteTruncate
1499 : */
1500 : static void
1501 127 : truncate_check_rel(Relation rel)
1502 : {
1503 : AclResult aclresult;
1504 :
1505 : /*
1506 : * Only allow truncate on regular tables and partitioned tables (although,
1507 : * the latter are only being included here for the following checks; no
1508 : * physical truncation will occur in their case.)
1509 : */
1510 143 : if (rel->rd_rel->relkind != RELKIND_RELATION &&
1511 16 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1512 4 : ereport(ERROR,
1513 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1514 : errmsg("\"%s\" is not a table",
1515 : RelationGetRelationName(rel))));
1516 :
1517 : /* Permissions checks */
1518 123 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
1519 : ACL_TRUNCATE);
1520 123 : if (aclresult != ACLCHECK_OK)
1521 3 : aclcheck_error(aclresult, ACL_KIND_CLASS,
1522 3 : RelationGetRelationName(rel));
1523 :
1524 120 : if (!allowSystemTableMods && IsSystemRelation(rel))
1525 0 : ereport(ERROR,
1526 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1527 : errmsg("permission denied: \"%s\" is a system catalog",
1528 : RelationGetRelationName(rel))));
1529 :
1530 : /*
1531 : * Don't allow truncate on temp tables of other backends ... their local
1532 : * buffer manager is not going to cope.
1533 : */
1534 120 : if (RELATION_IS_OTHER_TEMP(rel))
1535 0 : ereport(ERROR,
1536 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1537 : errmsg("cannot truncate temporary tables of other sessions")));
1538 :
1539 : /*
1540 : * Also check for active uses of the relation in the current transaction,
1541 : * including open scans and pending AFTER trigger events.
1542 : */
1543 120 : CheckTableNotInUse(rel, "TRUNCATE");
1544 120 : }
1545 :
1546 : /*
1547 : * storage_name
1548 : * returns the name corresponding to a typstorage/attstorage enum value
1549 : */
1550 : static const char *
1551 4 : storage_name(char c)
1552 : {
1553 4 : switch (c)
1554 : {
1555 : case 'p':
1556 0 : return "PLAIN";
1557 : case 'm':
1558 2 : return "MAIN";
1559 : case 'x':
1560 2 : return "EXTENDED";
1561 : case 'e':
1562 0 : return "EXTERNAL";
1563 : default:
1564 0 : return "???";
1565 : }
1566 : }
1567 :
1568 : /*----------
1569 : * MergeAttributes
1570 : * Returns new schema given initial schema and superclasses.
1571 : *
1572 : * Input arguments:
1573 : * 'schema' is the column/attribute definition for the table. (It's a list
1574 : * of ColumnDef's.) It is destructively changed.
1575 : * 'supers' is a list of names (as RangeVar nodes) of parent relations.
1576 : * 'relpersistence' is a persistence type of the table.
1577 : * 'is_partition' tells if the table is a partition
1578 : *
1579 : * Output arguments:
1580 : * 'supOids' receives a list of the OIDs of the parent relations.
1581 : * 'supconstr' receives a list of constraints belonging to the parents,
1582 : * updated as necessary to be valid for the child.
1583 : * 'supOidCount' is set to the number of parents that have OID columns.
1584 : *
1585 : * Return value:
1586 : * Completed schema list.
1587 : *
1588 : * Notes:
1589 : * The order in which the attributes are inherited is very important.
1590 : * Intuitively, the inherited attributes should come first. If a table
1591 : * inherits from multiple parents, the order of those attributes are
1592 : * according to the order of the parents specified in CREATE TABLE.
1593 : *
1594 : * Here's an example:
1595 : *
1596 : * create table person (name text, age int4, location point);
1597 : * create table emp (salary int4, manager text) inherits(person);
1598 : * create table student (gpa float8) inherits (person);
1599 : * create table stud_emp (percent int4) inherits (emp, student);
1600 : *
1601 : * The order of the attributes of stud_emp is:
1602 : *
1603 : * person {1:name, 2:age, 3:location}
1604 : * / \
1605 : * {6:gpa} student emp {4:salary, 5:manager}
1606 : * \ /
1607 : * stud_emp {7:percent}
1608 : *
1609 : * If the same attribute name appears multiple times, then it appears
1610 : * in the result table in the proper location for its first appearance.
1611 : *
1612 : * Constraints (including NOT NULL constraints) for the child table
1613 : * are the union of all relevant constraints, from both the child schema
1614 : * and parent tables.
1615 : *
1616 : * The default value for a child column is defined as:
1617 : * (1) If the child schema specifies a default, that value is used.
1618 : * (2) If neither the child nor any parent specifies a default, then
1619 : * the column will not have a default.
1620 : * (3) If conflicting defaults are inherited from different parents
1621 : * (and not overridden by the child), an error is raised.
1622 : * (4) Otherwise the inherited default is used.
1623 : * Rule (3) is new in Postgres 7.1; in earlier releases you got a
1624 : * rather arbitrary choice of which parent default to use.
1625 : *----------
1626 : */
1627 : static List *
1628 2148 : MergeAttributes(List *schema, List *supers, char relpersistence,
1629 : bool is_partition, List **supOids, List **supconstr,
1630 : int *supOidCount)
1631 : {
1632 : ListCell *entry;
1633 2148 : List *inhSchema = NIL;
1634 2148 : List *parentOids = NIL;
1635 2148 : List *constraints = NIL;
1636 2148 : int parentsWithOids = 0;
1637 2148 : bool have_bogus_defaults = false;
1638 : int child_attno;
1639 : static Node bogus_marker = {0}; /* marks conflicting defaults */
1640 2148 : List *saved_schema = NIL;
1641 :
1642 : /*
1643 : * Check for and reject tables with too many columns. We perform this
1644 : * check relatively early for two reasons: (a) we don't run the risk of
1645 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
1646 : * okay if we're processing <= 1600 columns, but could take minutes to
1647 : * execute if the user attempts to create a table with hundreds of
1648 : * thousands of columns.
1649 : *
1650 : * Note that we also need to check that we do not exceed this figure after
1651 : * including columns from inherited relations.
1652 : */
1653 2148 : if (list_length(schema) > MaxHeapAttributeNumber)
1654 0 : ereport(ERROR,
1655 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1656 : errmsg("tables can have at most %d columns",
1657 : MaxHeapAttributeNumber)));
1658 :
1659 : /*
1660 : * In case of a partition, there are no new column definitions, only dummy
1661 : * ColumnDefs created for column constraints. We merge them with the
1662 : * constraints inherited from the parent.
1663 : */
1664 2148 : if (is_partition)
1665 : {
1666 139 : saved_schema = schema;
1667 139 : schema = NIL;
1668 : }
1669 :
1670 : /*
1671 : * Check for duplicate names in the explicit list of attributes.
1672 : *
1673 : * Although we might consider merging such entries in the same way that we
1674 : * handle name conflicts for inherited attributes, it seems to make more
1675 : * sense to assume such conflicts are errors.
1676 : */
1677 7454 : foreach(entry, schema)
1678 : {
1679 5309 : ColumnDef *coldef = lfirst(entry);
1680 5309 : ListCell *rest = lnext(entry);
1681 5309 : ListCell *prev = entry;
1682 :
1683 5309 : if (coldef->typeName == NULL)
1684 :
1685 : /*
1686 : * Typed table column option that does not belong to a column from
1687 : * the type. This works because the columns from the type come
1688 : * first in the list.
1689 : */
1690 1 : ereport(ERROR,
1691 : (errcode(ERRCODE_UNDEFINED_COLUMN),
1692 : errmsg("column \"%s\" does not exist",
1693 : coldef->colname)));
1694 :
1695 28446 : while (rest != NULL)
1696 : {
1697 17832 : ColumnDef *restdef = lfirst(rest);
1698 17832 : ListCell *next = lnext(rest); /* need to save it in case we
1699 : * delete it */
1700 :
1701 17832 : if (strcmp(coldef->colname, restdef->colname) == 0)
1702 : {
1703 7 : if (coldef->is_from_type)
1704 : {
1705 : /*
1706 : * merge the column options into the column from the type
1707 : */
1708 5 : coldef->is_not_null = restdef->is_not_null;
1709 5 : coldef->raw_default = restdef->raw_default;
1710 5 : coldef->cooked_default = restdef->cooked_default;
1711 5 : coldef->constraints = restdef->constraints;
1712 5 : coldef->is_from_type = false;
1713 5 : list_delete_cell(schema, rest, prev);
1714 : }
1715 : else
1716 2 : ereport(ERROR,
1717 : (errcode(ERRCODE_DUPLICATE_COLUMN),
1718 : errmsg("column \"%s\" specified more than once",
1719 : coldef->colname)));
1720 : }
1721 17830 : prev = rest;
1722 17830 : rest = next;
1723 : }
1724 : }
1725 :
1726 : /*
1727 : * Scan the parents left-to-right, and merge their attributes to form a
1728 : * list of inherited attributes (inhSchema). Also check to see if we need
1729 : * to inherit an OID column.
1730 : */
1731 2145 : child_attno = 0;
1732 2448 : foreach(entry, supers)
1733 : {
1734 307 : RangeVar *parent = (RangeVar *) lfirst(entry);
1735 : Relation relation;
1736 : TupleDesc tupleDesc;
1737 : TupleConstr *constr;
1738 : AttrNumber *newattno;
1739 : AttrNumber parent_attno;
1740 :
1741 : /*
1742 : * A self-exclusive lock is needed here. If two backends attempt to
1743 : * add children to the same parent simultaneously, and that parent has
1744 : * no pre-existing children, then both will attempt to update the
1745 : * parent's relhassubclass field, leading to a "tuple concurrently
1746 : * updated" error. Also, this interlocks against a concurrent ANALYZE
1747 : * on the parent table, which might otherwise be attempting to clear
1748 : * the parent's relhassubclass field, if its previous children were
1749 : * recently dropped.
1750 : *
1751 : * If the child table is a partition, then we instead grab an
1752 : * exclusive lock on the parent because its partition descriptor will
1753 : * be changed by addition of the new partition.
1754 : */
1755 307 : if (!is_partition)
1756 168 : relation = heap_openrv(parent, ShareUpdateExclusiveLock);
1757 : else
1758 139 : relation = heap_openrv(parent, AccessExclusiveLock);
1759 :
1760 : /*
1761 : * We do not allow partitioned tables and partitions to participate in
1762 : * regular inheritance.
1763 : */
1764 307 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
1765 : !is_partition)
1766 1 : ereport(ERROR,
1767 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1768 : errmsg("cannot inherit from partitioned table \"%s\"",
1769 : parent->relname)));
1770 306 : if (relation->rd_rel->relispartition && !is_partition)
1771 1 : ereport(ERROR,
1772 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1773 : errmsg("cannot inherit from partition \"%s\"",
1774 : parent->relname)));
1775 :
1776 445 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
1777 278 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
1778 138 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1779 0 : ereport(ERROR,
1780 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1781 : errmsg("inherited relation \"%s\" is not a table or foreign table",
1782 : parent->relname)));
1783 : /* Permanent rels cannot inherit from temporary ones */
1784 595 : if (relpersistence != RELPERSISTENCE_TEMP &&
1785 290 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
1786 1 : ereport(ERROR,
1787 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1788 : errmsg(!is_partition
1789 : ? "cannot inherit from temporary relation \"%s\""
1790 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
1791 : parent->relname)));
1792 :
1793 : /* If existing rel is temp, it must belong to this session */
1794 318 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
1795 14 : !relation->rd_islocaltemp)
1796 0 : ereport(ERROR,
1797 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1798 : errmsg(!is_partition
1799 : ? "cannot inherit from temporary relation of another session"
1800 : : "cannot create as partition of temporary relation of another session")));
1801 :
1802 : /*
1803 : * We should have an UNDER permission flag for this, but for now,
1804 : * demand that creator of a child table own the parent.
1805 : */
1806 304 : if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
1807 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
1808 0 : RelationGetRelationName(relation));
1809 :
1810 : /*
1811 : * Reject duplications in the list of parents.
1812 : */
1813 304 : if (list_member_oid(parentOids, RelationGetRelid(relation)))
1814 0 : ereport(ERROR,
1815 : (errcode(ERRCODE_DUPLICATE_TABLE),
1816 : errmsg("relation \"%s\" would be inherited from more than once",
1817 : parent->relname)));
1818 :
1819 304 : parentOids = lappend_oid(parentOids, RelationGetRelid(relation));
1820 :
1821 304 : if (relation->rd_rel->relhasoids)
1822 7 : parentsWithOids++;
1823 :
1824 304 : tupleDesc = RelationGetDescr(relation);
1825 304 : constr = tupleDesc->constr;
1826 :
1827 : /*
1828 : * newattno[] will contain the child-table attribute numbers for the
1829 : * attributes of this parent table. (They are not the same for
1830 : * parents after the first one, nor if we have dropped columns.)
1831 : */
1832 304 : newattno = (AttrNumber *)
1833 304 : palloc0(tupleDesc->natts * sizeof(AttrNumber));
1834 :
1835 1179 : for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1836 571 : parent_attno++)
1837 : {
1838 572 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
1839 : parent_attno - 1);
1840 572 : char *attributeName = NameStr(attribute->attname);
1841 : int exist_attno;
1842 : ColumnDef *def;
1843 :
1844 : /*
1845 : * Ignore dropped columns in the parent.
1846 : */
1847 572 : if (attribute->attisdropped)
1848 2 : continue; /* leave newattno entry as zero */
1849 :
1850 : /*
1851 : * Does it conflict with some previously inherited column?
1852 : */
1853 570 : exist_attno = findAttrByName(attributeName, inhSchema);
1854 570 : if (exist_attno > 0)
1855 : {
1856 : Oid defTypeId;
1857 : int32 deftypmod;
1858 : Oid defCollId;
1859 :
1860 : /*
1861 : * Yes, try to merge the two column definitions. They must
1862 : * have the same type, typmod, and collation.
1863 : */
1864 25 : ereport(NOTICE,
1865 : (errmsg("merging multiple inherited definitions of column \"%s\"",
1866 : attributeName)));
1867 25 : def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
1868 25 : typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
1869 50 : if (defTypeId != attribute->atttypid ||
1870 25 : deftypmod != attribute->atttypmod)
1871 0 : ereport(ERROR,
1872 : (errcode(ERRCODE_DATATYPE_MISMATCH),
1873 : errmsg("inherited column \"%s\" has a type conflict",
1874 : attributeName),
1875 : errdetail("%s versus %s",
1876 : format_type_with_typemod(defTypeId,
1877 : deftypmod),
1878 : format_type_with_typemod(attribute->atttypid,
1879 : attribute->atttypmod))));
1880 25 : defCollId = GetColumnDefCollation(NULL, def, defTypeId);
1881 25 : if (defCollId != attribute->attcollation)
1882 0 : ereport(ERROR,
1883 : (errcode(ERRCODE_COLLATION_MISMATCH),
1884 : errmsg("inherited column \"%s\" has a collation conflict",
1885 : attributeName),
1886 : errdetail("\"%s\" versus \"%s\"",
1887 : get_collation_name(defCollId),
1888 : get_collation_name(attribute->attcollation))));
1889 :
1890 : /* Copy storage parameter */
1891 25 : if (def->storage == 0)
1892 0 : def->storage = attribute->attstorage;
1893 25 : else if (def->storage != attribute->attstorage)
1894 1 : ereport(ERROR,
1895 : (errcode(ERRCODE_DATATYPE_MISMATCH),
1896 : errmsg("inherited column \"%s\" has a storage parameter conflict",
1897 : attributeName),
1898 : errdetail("%s versus %s",
1899 : storage_name(def->storage),
1900 : storage_name(attribute->attstorage))));
1901 :
1902 24 : def->inhcount++;
1903 : /* Merge of NOT NULL constraints = OR 'em together */
1904 24 : def->is_not_null |= attribute->attnotnull;
1905 : /* Default and other constraints are handled below */
1906 24 : newattno[parent_attno - 1] = exist_attno;
1907 : }
1908 : else
1909 : {
1910 : /*
1911 : * No, create a new inherited column
1912 : */
1913 545 : def = makeNode(ColumnDef);
1914 545 : def->colname = pstrdup(attributeName);
1915 545 : def->typeName = makeTypeNameFromOid(attribute->atttypid,
1916 : attribute->atttypmod);
1917 545 : def->inhcount = 1;
1918 545 : def->is_local = false;
1919 545 : def->is_not_null = attribute->attnotnull;
1920 545 : def->is_from_type = false;
1921 545 : def->is_from_parent = true;
1922 545 : def->storage = attribute->attstorage;
1923 545 : def->raw_default = NULL;
1924 545 : def->cooked_default = NULL;
1925 545 : def->collClause = NULL;
1926 545 : def->collOid = attribute->attcollation;
1927 545 : def->constraints = NIL;
1928 545 : def->location = -1;
1929 545 : inhSchema = lappend(inhSchema, def);
1930 545 : newattno[parent_attno - 1] = ++child_attno;
1931 : }
1932 :
1933 : /*
1934 : * Copy default if any
1935 : */
1936 569 : if (attribute->atthasdef)
1937 : {
1938 24 : Node *this_default = NULL;
1939 : AttrDefault *attrdef;
1940 : int i;
1941 :
1942 : /* Find default in constraint structure */
1943 24 : Assert(constr != NULL);
1944 24 : attrdef = constr->defval;
1945 28 : for (i = 0; i < constr->num_defval; i++)
1946 : {
1947 28 : if (attrdef[i].adnum == parent_attno)
1948 : {
1949 24 : this_default = stringToNode(attrdef[i].adbin);
1950 24 : break;
1951 : }
1952 : }
1953 24 : Assert(this_default != NULL);
1954 :
1955 : /*
1956 : * If default expr could contain any vars, we'd need to fix
1957 : * 'em, but it can't; so default is ready to apply to child.
1958 : *
1959 : * If we already had a default from some prior parent, check
1960 : * to see if they are the same. If so, no problem; if not,
1961 : * mark the column as having a bogus default. Below, we will
1962 : * complain if the bogus default isn't overridden by the child
1963 : * schema.
1964 : */
1965 24 : Assert(def->raw_default == NULL);
1966 24 : if (def->cooked_default == NULL)
1967 21 : def->cooked_default = this_default;
1968 3 : else if (!equal(def->cooked_default, this_default))
1969 : {
1970 2 : def->cooked_default = &bogus_marker;
1971 2 : have_bogus_defaults = true;
1972 : }
1973 : }
1974 : }
1975 :
1976 : /*
1977 : * Now copy the CHECK constraints of this parent, adjusting attnos
1978 : * using the completed newattno[] map. Identically named constraints
1979 : * are merged if possible, else we throw error.
1980 : */
1981 303 : if (constr && constr->num_check > 0)
1982 : {
1983 34 : ConstrCheck *check = constr->check;
1984 : int i;
1985 :
1986 72 : for (i = 0; i < constr->num_check; i++)
1987 : {
1988 38 : char *name = check[i].ccname;
1989 : Node *expr;
1990 : bool found_whole_row;
1991 :
1992 : /* ignore if the constraint is non-inheritable */
1993 38 : if (check[i].ccnoinherit)
1994 7 : continue;
1995 :
1996 : /* Adjust Vars to match new table's column numbering */
1997 31 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
1998 : 1, 0,
1999 : newattno, tupleDesc->natts,
2000 : InvalidOid, &found_whole_row);
2001 :
2002 : /*
2003 : * For the moment we have to reject whole-row variables. We
2004 : * could convert them, if we knew the new table's rowtype OID,
2005 : * but that hasn't been assigned yet.
2006 : */
2007 31 : if (found_whole_row)
2008 0 : ereport(ERROR,
2009 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2010 : errmsg("cannot convert whole-row table reference"),
2011 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2012 : name,
2013 : RelationGetRelationName(relation))));
2014 :
2015 : /* check for duplicate */
2016 31 : if (!MergeCheckConstraint(constraints, name, expr))
2017 : {
2018 : /* nope, this is a new one */
2019 : CookedConstraint *cooked;
2020 :
2021 29 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
2022 29 : cooked->contype = CONSTR_CHECK;
2023 29 : cooked->conoid = InvalidOid; /* until created */
2024 29 : cooked->name = pstrdup(name);
2025 29 : cooked->attnum = 0; /* not used for constraints */
2026 29 : cooked->expr = expr;
2027 29 : cooked->skip_validation = false;
2028 29 : cooked->is_local = false;
2029 29 : cooked->inhcount = 1;
2030 29 : cooked->is_no_inherit = false;
2031 29 : constraints = lappend(constraints, cooked);
2032 : }
2033 : }
2034 : }
2035 :
2036 303 : pfree(newattno);
2037 :
2038 : /*
2039 : * Close the parent rel, but keep our lock on it until xact commit.
2040 : * That will prevent someone else from deleting or ALTERing the parent
2041 : * before the child is committed.
2042 : */
2043 303 : heap_close(relation, NoLock);
2044 : }
2045 :
2046 : /*
2047 : * If we had no inherited attributes, the result schema is just the
2048 : * explicitly declared columns. Otherwise, we need to merge the declared
2049 : * columns into the inherited schema list. Although, we never have any
2050 : * explicitly declared columns if the table is a partition.
2051 : */
2052 2141 : if (inhSchema != NIL)
2053 : {
2054 279 : int schema_attno = 0;
2055 :
2056 376 : foreach(entry, schema)
2057 : {
2058 101 : ColumnDef *newdef = lfirst(entry);
2059 101 : char *attributeName = newdef->colname;
2060 : int exist_attno;
2061 :
2062 101 : schema_attno++;
2063 :
2064 : /*
2065 : * Does it conflict with some previously inherited column?
2066 : */
2067 101 : exist_attno = findAttrByName(attributeName, inhSchema);
2068 101 : if (exist_attno > 0)
2069 : {
2070 : ColumnDef *def;
2071 : Oid defTypeId,
2072 : newTypeId;
2073 : int32 deftypmod,
2074 : newtypmod;
2075 : Oid defcollid,
2076 : newcollid;
2077 :
2078 : /*
2079 : * Partitions have only one parent and have no column
2080 : * definitions of their own, so conflict should never occur.
2081 : */
2082 24 : Assert(!is_partition);
2083 :
2084 : /*
2085 : * Yes, try to merge the two column definitions. They must
2086 : * have the same type, typmod, and collation.
2087 : */
2088 24 : if (exist_attno == schema_attno)
2089 22 : ereport(NOTICE,
2090 : (errmsg("merging column \"%s\" with inherited definition",
2091 : attributeName)));
2092 : else
2093 2 : ereport(NOTICE,
2094 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
2095 : errdetail("User-specified column moved to the position of the inherited column.")));
2096 24 : def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2097 24 : typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2098 24 : typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
2099 24 : if (defTypeId != newTypeId || deftypmod != newtypmod)
2100 2 : ereport(ERROR,
2101 : (errcode(ERRCODE_DATATYPE_MISMATCH),
2102 : errmsg("column \"%s\" has a type conflict",
2103 : attributeName),
2104 : errdetail("%s versus %s",
2105 : format_type_with_typemod(defTypeId,
2106 : deftypmod),
2107 : format_type_with_typemod(newTypeId,
2108 : newtypmod))));
2109 22 : defcollid = GetColumnDefCollation(NULL, def, defTypeId);
2110 22 : newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
2111 22 : if (defcollid != newcollid)
2112 1 : ereport(ERROR,
2113 : (errcode(ERRCODE_COLLATION_MISMATCH),
2114 : errmsg("column \"%s\" has a collation conflict",
2115 : attributeName),
2116 : errdetail("\"%s\" versus \"%s\"",
2117 : get_collation_name(defcollid),
2118 : get_collation_name(newcollid))));
2119 :
2120 : /*
2121 : * Identity is never inherited. The new column can have an
2122 : * identity definition, so we always just take that one.
2123 : */
2124 21 : def->identity = newdef->identity;
2125 :
2126 : /* Copy storage parameter */
2127 21 : if (def->storage == 0)
2128 0 : def->storage = newdef->storage;
2129 21 : else if (newdef->storage != 0 && def->storage != newdef->storage)
2130 1 : ereport(ERROR,
2131 : (errcode(ERRCODE_DATATYPE_MISMATCH),
2132 : errmsg("column \"%s\" has a storage parameter conflict",
2133 : attributeName),
2134 : errdetail("%s versus %s",
2135 : storage_name(def->storage),
2136 : storage_name(newdef->storage))));
2137 :
2138 : /* Mark the column as locally defined */
2139 20 : def->is_local = true;
2140 : /* Merge of NOT NULL constraints = OR 'em together */
2141 20 : def->is_not_null |= newdef->is_not_null;
2142 : /* If new def has a default, override previous default */
2143 20 : if (newdef->raw_default != NULL)
2144 : {
2145 1 : def->raw_default = newdef->raw_default;
2146 1 : def->cooked_default = newdef->cooked_default;
2147 : }
2148 : }
2149 : else
2150 : {
2151 : /*
2152 : * No, attach new column to result schema
2153 : */
2154 77 : inhSchema = lappend(inhSchema, newdef);
2155 : }
2156 : }
2157 :
2158 275 : schema = inhSchema;
2159 :
2160 : /*
2161 : * Check that we haven't exceeded the legal # of columns after merging
2162 : * in inherited columns.
2163 : */
2164 275 : if (list_length(schema) > MaxHeapAttributeNumber)
2165 0 : ereport(ERROR,
2166 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2167 : errmsg("tables can have at most %d columns",
2168 : MaxHeapAttributeNumber)));
2169 : }
2170 :
2171 : /*
2172 : * Now that we have the column definition list for a partition, we can
2173 : * check whether the columns referenced in the column constraint specs
2174 : * actually exist. Also, we merge the constraints into the corresponding
2175 : * column definitions.
2176 : */
2177 2137 : if (is_partition && list_length(saved_schema) > 0)
2178 : {
2179 3 : schema = list_concat(schema, saved_schema);
2180 :
2181 8 : foreach(entry, schema)
2182 : {
2183 6 : ColumnDef *coldef = lfirst(entry);
2184 6 : ListCell *rest = lnext(entry);
2185 6 : ListCell *prev = entry;
2186 :
2187 : /*
2188 : * Partition column option that does not belong to a column from
2189 : * the parent. This works because the columns from the parent
2190 : * come first in the list (see above).
2191 : */
2192 6 : if (coldef->typeName == NULL)
2193 0 : ereport(ERROR,
2194 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2195 : errmsg("column \"%s\" does not exist",
2196 : coldef->colname)));
2197 23 : while (rest != NULL)
2198 : {
2199 12 : ColumnDef *restdef = lfirst(rest);
2200 12 : ListCell *next = lnext(rest); /* need to save it in case we
2201 : * delete it */
2202 :
2203 12 : if (strcmp(coldef->colname, restdef->colname) == 0)
2204 : {
2205 : /*
2206 : * merge the column options into the column from the
2207 : * parent
2208 : */
2209 4 : if (coldef->is_from_parent)
2210 : {
2211 3 : coldef->is_not_null = restdef->is_not_null;
2212 3 : coldef->raw_default = restdef->raw_default;
2213 3 : coldef->cooked_default = restdef->cooked_default;
2214 3 : coldef->constraints = restdef->constraints;
2215 3 : coldef->is_from_parent = false;
2216 3 : list_delete_cell(schema, rest, prev);
2217 : }
2218 : else
2219 1 : ereport(ERROR,
2220 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2221 : errmsg("column \"%s\" specified more than once",
2222 : coldef->colname)));
2223 : }
2224 11 : prev = rest;
2225 11 : rest = next;
2226 : }
2227 : }
2228 : }
2229 :
2230 : /*
2231 : * If we found any conflicting parent default values, check to make sure
2232 : * they were overridden by the child.
2233 : */
2234 2136 : if (have_bogus_defaults)
2235 : {
2236 3 : foreach(entry, schema)
2237 : {
2238 2 : ColumnDef *def = lfirst(entry);
2239 :
2240 2 : if (def->cooked_default == &bogus_marker)
2241 1 : ereport(ERROR,
2242 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2243 : errmsg("column \"%s\" inherits conflicting default values",
2244 : def->colname),
2245 : errhint("To resolve the conflict, specify a default explicitly.")));
2246 : }
2247 : }
2248 :
2249 2135 : *supOids = parentOids;
2250 2135 : *supconstr = constraints;
2251 2135 : *supOidCount = parentsWithOids;
2252 2135 : return schema;
2253 : }
2254 :
2255 :
2256 : /*
2257 : * MergeCheckConstraint
2258 : * Try to merge an inherited CHECK constraint with previous ones
2259 : *
2260 : * If we inherit identically-named constraints from multiple parents, we must
2261 : * merge them, or throw an error if they don't have identical definitions.
2262 : *
2263 : * constraints is a list of CookedConstraint structs for previous constraints.
2264 : *
2265 : * Returns TRUE if merged (constraint is a duplicate), or FALSE if it's
2266 : * got a so-far-unique name, or throws error if conflict.
2267 : */
2268 : static bool
2269 31 : MergeCheckConstraint(List *constraints, char *name, Node *expr)
2270 : {
2271 : ListCell *lc;
2272 :
2273 34 : foreach(lc, constraints)
2274 : {
2275 5 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
2276 :
2277 5 : Assert(ccon->contype == CONSTR_CHECK);
2278 :
2279 : /* Non-matching names never conflict */
2280 5 : if (strcmp(ccon->name, name) != 0)
2281 3 : continue;
2282 :
2283 2 : if (equal(expr, ccon->expr))
2284 : {
2285 : /* OK to merge */
2286 2 : ccon->inhcount++;
2287 2 : return true;
2288 : }
2289 :
2290 0 : ereport(ERROR,
2291 : (errcode(ERRCODE_DUPLICATE_OBJECT),
2292 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
2293 : name)));
2294 : }
2295 :
2296 29 : return false;
2297 : }
2298 :
2299 :
2300 : /*
2301 : * StoreCatalogInheritance
2302 : * Updates the system catalogs with proper inheritance information.
2303 : *
2304 : * supers is a list of the OIDs of the new relation's direct ancestors.
2305 : */
2306 : static void
2307 2125 : StoreCatalogInheritance(Oid relationId, List *supers,
2308 : bool child_is_partition)
2309 : {
2310 : Relation relation;
2311 : int16 seqNumber;
2312 : ListCell *entry;
2313 :
2314 : /*
2315 : * sanity checks
2316 : */
2317 2125 : AssertArg(OidIsValid(relationId));
2318 :
2319 2125 : if (supers == NIL)
2320 3977 : return;
2321 :
2322 : /*
2323 : * Store INHERITS information in pg_inherits using direct ancestors only.
2324 : * Also enter dependencies on the direct ancestors, and make sure they are
2325 : * marked with relhassubclass = true.
2326 : *
2327 : * (Once upon a time, both direct and indirect ancestors were found here
2328 : * and then entered into pg_ipl. Since that catalog doesn't exist
2329 : * anymore, there's no need to look for indirect ancestors.)
2330 : */
2331 273 : relation = heap_open(InheritsRelationId, RowExclusiveLock);
2332 :
2333 273 : seqNumber = 1;
2334 567 : foreach(entry, supers)
2335 : {
2336 294 : Oid parentOid = lfirst_oid(entry);
2337 :
2338 294 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
2339 : child_is_partition);
2340 294 : seqNumber++;
2341 : }
2342 :
2343 273 : heap_close(relation, RowExclusiveLock);
2344 : }
2345 :
2346 : /*
2347 : * Make catalog entries showing relationId as being an inheritance child
2348 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
2349 : */
2350 : static void
2351 339 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
2352 : int16 seqNumber, Relation inhRelation,
2353 : bool child_is_partition)
2354 : {
2355 339 : TupleDesc desc = RelationGetDescr(inhRelation);
2356 : Datum values[Natts_pg_inherits];
2357 : bool nulls[Natts_pg_inherits];
2358 : ObjectAddress childobject,
2359 : parentobject;
2360 : HeapTuple tuple;
2361 :
2362 : /*
2363 : * Make the pg_inherits entry
2364 : */
2365 339 : values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
2366 339 : values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
2367 339 : values[Anum_pg_inherits_inhseqno - 1] = Int16GetDatum(seqNumber);
2368 :
2369 339 : memset(nulls, 0, sizeof(nulls));
2370 :
2371 339 : tuple = heap_form_tuple(desc, values, nulls);
2372 :
2373 339 : CatalogTupleInsert(inhRelation, tuple);
2374 :
2375 339 : heap_freetuple(tuple);
2376 :
2377 : /*
2378 : * Store a dependency too
2379 : */
2380 339 : parentobject.classId = RelationRelationId;
2381 339 : parentobject.objectId = parentOid;
2382 339 : parentobject.objectSubId = 0;
2383 339 : childobject.classId = RelationRelationId;
2384 339 : childobject.objectId = relationId;
2385 339 : childobject.objectSubId = 0;
2386 :
2387 339 : recordDependencyOn(&childobject, &parentobject,
2388 : child_dependency_type(child_is_partition));
2389 :
2390 : /*
2391 : * Post creation hook of this inheritance. Since object_access_hook
2392 : * doesn't take multiple object identifiers, we relay oid of parent
2393 : * relation using auxiliary_id argument.
2394 : */
2395 339 : InvokeObjectPostAlterHookArg(InheritsRelationId,
2396 : relationId, 0,
2397 : parentOid, false);
2398 :
2399 : /*
2400 : * Mark the parent as having subclasses.
2401 : */
2402 339 : SetRelationHasSubclass(parentOid, true);
2403 339 : }
2404 :
2405 : /*
2406 : * Look for an existing schema entry with the given name.
2407 : *
2408 : * Returns the index (starting with 1) if attribute already exists in schema,
2409 : * 0 if it doesn't.
2410 : */
2411 : static int
2412 671 : findAttrByName(const char *attributeName, List *schema)
2413 : {
2414 : ListCell *s;
2415 671 : int i = 1;
2416 :
2417 1246 : foreach(s, schema)
2418 : {
2419 624 : ColumnDef *def = lfirst(s);
2420 :
2421 624 : if (strcmp(attributeName, def->colname) == 0)
2422 49 : return i;
2423 :
2424 575 : i++;
2425 : }
2426 622 : return 0;
2427 : }
2428 :
2429 :
2430 : /*
2431 : * SetRelationHasSubclass
2432 : * Set the value of the relation's relhassubclass field in pg_class.
2433 : *
2434 : * NOTE: caller must be holding an appropriate lock on the relation.
2435 : * ShareUpdateExclusiveLock is sufficient.
2436 : *
2437 : * NOTE: an important side-effect of this operation is that an SI invalidation
2438 : * message is sent out to all backends --- including me --- causing plans
2439 : * referencing the relation to be rebuilt with the new list of children.
2440 : * This must happen even if we find that no change is needed in the pg_class
2441 : * row.
2442 : */
2443 : void
2444 339 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
2445 : {
2446 : Relation relationRelation;
2447 : HeapTuple tuple;
2448 : Form_pg_class classtuple;
2449 :
2450 : /*
2451 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
2452 : */
2453 339 : relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
2454 339 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
2455 339 : if (!HeapTupleIsValid(tuple))
2456 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
2457 339 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
2458 :
2459 339 : if (classtuple->relhassubclass != relhassubclass)
2460 : {
2461 197 : classtuple->relhassubclass = relhassubclass;
2462 197 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
2463 : }
2464 : else
2465 : {
2466 : /* no need to change tuple, but force relcache rebuild anyway */
2467 142 : CacheInvalidateRelcacheByTuple(tuple);
2468 : }
2469 :
2470 339 : heap_freetuple(tuple);
2471 339 : heap_close(relationRelation, RowExclusiveLock);
2472 339 : }
2473 :
2474 : /*
2475 : * renameatt_check - basic sanity checks before attribute rename
2476 : */
2477 : static void
2478 142 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
2479 : {
2480 142 : char relkind = classform->relkind;
2481 :
2482 142 : if (classform->reloftype && !recursing)
2483 1 : ereport(ERROR,
2484 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2485 : errmsg("cannot rename column of typed table")));
2486 :
2487 : /*
2488 : * Renaming the columns of sequences or toast tables doesn't actually
2489 : * break anything from the system's point of view, since internal
2490 : * references are by attnum. But it doesn't seem right to allow users to
2491 : * change names that are hardcoded into the system, hence the following
2492 : * restriction.
2493 : */
2494 141 : if (relkind != RELKIND_RELATION &&
2495 14 : relkind != RELKIND_VIEW &&
2496 14 : relkind != RELKIND_MATVIEW &&
2497 6 : relkind != RELKIND_COMPOSITE_TYPE &&
2498 6 : relkind != RELKIND_INDEX &&
2499 0 : relkind != RELKIND_FOREIGN_TABLE &&
2500 : relkind != RELKIND_PARTITIONED_TABLE)
2501 0 : ereport(ERROR,
2502 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2503 : errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table",
2504 : NameStr(classform->relname))));
2505 :
2506 : /*
2507 : * permissions checking. only the owner of a class can change its schema.
2508 : */
2509 141 : if (!pg_class_ownercheck(myrelid, GetUserId()))
2510 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
2511 0 : NameStr(classform->relname));
2512 141 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
2513 0 : ereport(ERROR,
2514 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2515 : errmsg("permission denied: \"%s\" is a system catalog",
2516 : NameStr(classform->relname))));
2517 141 : }
2518 :
2519 : /*
2520 : * renameatt_internal - workhorse for renameatt
2521 : *
2522 : * Return value is the attribute number in the 'myrelid' relation.
2523 : */
2524 : static AttrNumber
2525 83 : renameatt_internal(Oid myrelid,
2526 : const char *oldattname,
2527 : const char *newattname,
2528 : bool recurse,
2529 : bool recursing,
2530 : int expected_parents,
2531 : DropBehavior behavior)
2532 : {
2533 : Relation targetrelation;
2534 : Relation attrelation;
2535 : HeapTuple atttup;
2536 : Form_pg_attribute attform;
2537 : AttrNumber attnum;
2538 :
2539 : /*
2540 : * Grab an exclusive lock on the target table, which we will NOT release
2541 : * until end of transaction.
2542 : */
2543 83 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
2544 83 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
2545 :
2546 : /*
2547 : * if the 'recurse' flag is set then we are supposed to rename this
2548 : * attribute in all classes that inherit from 'relname' (as well as in
2549 : * 'relname').
2550 : *
2551 : * any permissions or problems with duplicate attributes will cause the
2552 : * whole transaction to abort, which is what we want -- all or nothing.
2553 : */
2554 83 : if (recurse)
2555 : {
2556 : List *child_oids,
2557 : *child_numparents;
2558 : ListCell *lo,
2559 : *li;
2560 :
2561 : /*
2562 : * we need the number of parents for each child so that the recursive
2563 : * calls to renameatt() can determine whether there are any parents
2564 : * outside the inheritance hierarchy being processed.
2565 : */
2566 33 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
2567 : &child_numparents);
2568 :
2569 : /*
2570 : * find_all_inheritors does the recursive search of the inheritance
2571 : * hierarchy, so all we have to do is process all of the relids in the
2572 : * list that it returns.
2573 : */
2574 105 : forboth(lo, child_oids, li, child_numparents)
2575 : {
2576 77 : Oid childrelid = lfirst_oid(lo);
2577 77 : int numparents = lfirst_int(li);
2578 :
2579 77 : if (childrelid == myrelid)
2580 33 : continue;
2581 : /* note we need not recurse again */
2582 44 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
2583 : }
2584 : }
2585 : else
2586 : {
2587 : /*
2588 : * If we are told not to recurse, there had better not be any child
2589 : * tables; else the rename would put them out of step.
2590 : *
2591 : * expected_parents will only be 0 if we are not already recursing.
2592 : */
2593 56 : if (expected_parents == 0 &&
2594 6 : find_inheritance_children(myrelid, NoLock) != NIL)
2595 2 : ereport(ERROR,
2596 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2597 : errmsg("inherited column \"%s\" must be renamed in child tables too",
2598 : oldattname)));
2599 : }
2600 :
2601 : /* rename attributes in typed tables of composite type */
2602 76 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
2603 : {
2604 : List *child_oids;
2605 : ListCell *lo;
2606 :
2607 4 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
2608 4 : RelationGetRelationName(targetrelation),
2609 : behavior);
2610 :
2611 4 : foreach(lo, child_oids)
2612 1 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
2613 : }
2614 :
2615 75 : attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
2616 :
2617 75 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
2618 75 : if (!HeapTupleIsValid(atttup))
2619 4 : ereport(ERROR,
2620 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2621 : errmsg("column \"%s\" does not exist",
2622 : oldattname)));
2623 71 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
2624 :
2625 71 : attnum = attform->attnum;
2626 71 : if (attnum <= 0)
2627 0 : ereport(ERROR,
2628 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2629 : errmsg("cannot rename system column \"%s\"",
2630 : oldattname)));
2631 :
2632 : /*
2633 : * if the attribute is inherited, forbid the renaming. if this is a
2634 : * top-level call to renameatt(), then expected_parents will be 0, so the
2635 : * effect of this code will be to prohibit the renaming if the attribute
2636 : * is inherited at all. if this is a recursive call to renameatt(),
2637 : * expected_parents will be the number of parents the current relation has
2638 : * within the inheritance hierarchy being processed, so we'll prohibit the
2639 : * renaming only if there are additional parents from elsewhere.
2640 : */
2641 71 : if (attform->attinhcount > expected_parents)
2642 5 : ereport(ERROR,
2643 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2644 : errmsg("cannot rename inherited column \"%s\"",
2645 : oldattname)));
2646 :
2647 : /* new name should not already exist */
2648 66 : (void) check_for_column_name_collision(targetrelation, newattname, false);
2649 :
2650 : /* apply the update */
2651 64 : namestrcpy(&(attform->attname), newattname);
2652 :
2653 64 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
2654 :
2655 64 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
2656 :
2657 64 : heap_freetuple(atttup);
2658 :
2659 64 : heap_close(attrelation, RowExclusiveLock);
2660 :
2661 64 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
2662 :
2663 64 : return attnum;
2664 : }
2665 :
2666 : /*
2667 : * Perform permissions and integrity checks before acquiring a relation lock.
2668 : */
2669 : static void
2670 54 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
2671 : void *arg)
2672 : {
2673 : HeapTuple tuple;
2674 : Form_pg_class form;
2675 :
2676 54 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
2677 54 : if (!HeapTupleIsValid(tuple))
2678 59 : return; /* concurrently dropped */
2679 48 : form = (Form_pg_class) GETSTRUCT(tuple);
2680 48 : renameatt_check(relid, form, false);
2681 47 : ReleaseSysCache(tuple);
2682 : }
2683 :
2684 : /*
2685 : * renameatt - changes the name of an attribute in a relation
2686 : *
2687 : * The returned ObjectAddress is that of the renamed column.
2688 : */
2689 : ObjectAddress
2690 44 : renameatt(RenameStmt *stmt)
2691 : {
2692 : Oid relid;
2693 : AttrNumber attnum;
2694 : ObjectAddress address;
2695 :
2696 : /* lock level taken here should match renameatt_internal */
2697 44 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
2698 44 : stmt->missing_ok, false,
2699 : RangeVarCallbackForRenameAttribute,
2700 : NULL);
2701 :
2702 42 : if (!OidIsValid(relid))
2703 : {
2704 4 : ereport(NOTICE,
2705 : (errmsg("relation \"%s\" does not exist, skipping",
2706 : stmt->relation->relname)));
2707 4 : return InvalidObjectAddress;
2708 : }
2709 :
2710 38 : attnum =
2711 76 : renameatt_internal(relid,
2712 38 : stmt->subname, /* old att name */
2713 38 : stmt->newname, /* new att name */
2714 38 : stmt->relation->inh, /* recursive? */
2715 : false, /* recursing? */
2716 : 0, /* expected inhcount */
2717 : stmt->behavior);
2718 :
2719 24 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
2720 :
2721 24 : return address;
2722 : }
2723 :
2724 : /*
2725 : * same logic as renameatt_internal
2726 : */
2727 : static ObjectAddress
2728 12 : rename_constraint_internal(Oid myrelid,
2729 : Oid mytypid,
2730 : const char *oldconname,
2731 : const char *newconname,
2732 : bool recurse,
2733 : bool recursing,
2734 : int expected_parents)
2735 : {
2736 12 : Relation targetrelation = NULL;
2737 : Oid constraintOid;
2738 : HeapTuple tuple;
2739 : Form_pg_constraint con;
2740 : ObjectAddress address;
2741 :
2742 12 : AssertArg(!myrelid || !mytypid);
2743 :
2744 12 : if (mytypid)
2745 : {
2746 1 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
2747 : }
2748 : else
2749 : {
2750 11 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
2751 :
2752 : /*
2753 : * don't tell it whether we're recursing; we allow changing typed
2754 : * tables here
2755 : */
2756 11 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
2757 :
2758 11 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
2759 : }
2760 :
2761 12 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
2762 12 : if (!HeapTupleIsValid(tuple))
2763 0 : elog(ERROR, "cache lookup failed for constraint %u",
2764 : constraintOid);
2765 12 : con = (Form_pg_constraint) GETSTRUCT(tuple);
2766 :
2767 12 : if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
2768 : {
2769 7 : if (recurse)
2770 : {
2771 : List *child_oids,
2772 : *child_numparents;
2773 : ListCell *lo,
2774 : *li;
2775 :
2776 4 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
2777 : &child_numparents);
2778 :
2779 10 : forboth(lo, child_oids, li, child_numparents)
2780 : {
2781 6 : Oid childrelid = lfirst_oid(lo);
2782 6 : int numparents = lfirst_int(li);
2783 :
2784 6 : if (childrelid == myrelid)
2785 4 : continue;
2786 :
2787 2 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
2788 : }
2789 : }
2790 : else
2791 : {
2792 4 : if (expected_parents == 0 &&
2793 1 : find_inheritance_children(myrelid, NoLock) != NIL)
2794 1 : ereport(ERROR,
2795 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2796 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
2797 : oldconname)));
2798 : }
2799 :
2800 6 : if (con->coninhcount > expected_parents)
2801 1 : ereport(ERROR,
2802 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2803 : errmsg("cannot rename inherited constraint \"%s\"",
2804 : oldconname)));
2805 : }
2806 :
2807 10 : if (con->conindid
2808 2 : && (con->contype == CONSTRAINT_PRIMARY
2809 1 : || con->contype == CONSTRAINT_UNIQUE
2810 0 : || con->contype == CONSTRAINT_EXCLUSION))
2811 : /* rename the index; this renames the constraint as well */
2812 2 : RenameRelationInternal(con->conindid, newconname, false);
2813 : else
2814 8 : RenameConstraintById(constraintOid, newconname);
2815 :
2816 10 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
2817 :
2818 10 : ReleaseSysCache(tuple);
2819 :
2820 10 : if (targetrelation)
2821 9 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
2822 :
2823 10 : return address;
2824 : }
2825 :
2826 : ObjectAddress
2827 11 : RenameConstraint(RenameStmt *stmt)
2828 : {
2829 11 : Oid relid = InvalidOid;
2830 11 : Oid typid = InvalidOid;
2831 :
2832 11 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
2833 : {
2834 : Relation rel;
2835 : HeapTuple tup;
2836 :
2837 1 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
2838 1 : rel = heap_open(TypeRelationId, RowExclusiveLock);
2839 1 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
2840 1 : if (!HeapTupleIsValid(tup))
2841 0 : elog(ERROR, "cache lookup failed for type %u", typid);
2842 1 : checkDomainOwner(tup);
2843 1 : ReleaseSysCache(tup);
2844 1 : heap_close(rel, NoLock);
2845 : }
2846 : else
2847 : {
2848 : /* lock level taken here should match rename_constraint_internal */
2849 10 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
2850 10 : stmt->missing_ok, false,
2851 : RangeVarCallbackForRenameAttribute,
2852 : NULL);
2853 10 : if (!OidIsValid(relid))
2854 : {
2855 1 : ereport(NOTICE,
2856 : (errmsg("relation \"%s\" does not exist, skipping",
2857 : stmt->relation->relname)));
2858 1 : return InvalidObjectAddress;
2859 : }
2860 : }
2861 :
2862 10 : return
2863 10 : rename_constraint_internal(relid, typid,
2864 10 : stmt->subname,
2865 10 : stmt->newname,
2866 19 : (stmt->relation &&
2867 9 : stmt->relation->inh), /* recursive? */
2868 : false, /* recursing? */
2869 : 0 /* expected inhcount */ );
2870 :
2871 : }
2872 :
2873 : /*
2874 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
2875 : * RENAME
2876 : */
2877 : ObjectAddress
2878 27 : RenameRelation(RenameStmt *stmt)
2879 : {
2880 : Oid relid;
2881 : ObjectAddress address;
2882 :
2883 : /*
2884 : * Grab an exclusive lock on the target table, index, sequence, view,
2885 : * materialized view, or foreign table, which we will NOT release until
2886 : * end of transaction.
2887 : *
2888 : * Lock level used here should match RenameRelationInternal, to avoid lock
2889 : * escalation.
2890 : */
2891 27 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
2892 27 : stmt->missing_ok, false,
2893 : RangeVarCallbackForAlterRelation,
2894 : (void *) stmt);
2895 :
2896 25 : if (!OidIsValid(relid))
2897 : {
2898 3 : ereport(NOTICE,
2899 : (errmsg("relation \"%s\" does not exist, skipping",
2900 : stmt->relation->relname)));
2901 3 : return InvalidObjectAddress;
2902 : }
2903 :
2904 : /* Do the work */
2905 22 : RenameRelationInternal(relid, stmt->newname, false);
2906 :
2907 20 : ObjectAddressSet(address, RelationRelationId, relid);
2908 :
2909 20 : return address;
2910 : }
2911 :
2912 : /*
2913 : * RenameRelationInternal - change the name of a relation
2914 : *
2915 : * XXX - When renaming sequences, we don't bother to modify the
2916 : * sequence name that is stored within the sequence itself
2917 : * (this would cause problems with MVCC). In the future,
2918 : * the sequence name should probably be removed from the
2919 : * sequence, AFAIK there's no need for it to be there.
2920 : */
2921 : void
2922 109 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
2923 : {
2924 : Relation targetrelation;
2925 : Relation relrelation; /* for RELATION relation */
2926 : HeapTuple reltup;
2927 : Form_pg_class relform;
2928 : Oid namespaceId;
2929 :
2930 : /*
2931 : * Grab an exclusive lock on the target table, index, sequence, view,
2932 : * materialized view, or foreign table, which we will NOT release until
2933 : * end of transaction.
2934 : */
2935 109 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
2936 109 : namespaceId = RelationGetNamespace(targetrelation);
2937 :
2938 : /*
2939 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
2940 : */
2941 109 : relrelation = heap_open(RelationRelationId, RowExclusiveLock);
2942 :
2943 109 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
2944 109 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
2945 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
2946 109 : relform = (Form_pg_class) GETSTRUCT(reltup);
2947 :
2948 109 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
2949 2 : ereport(ERROR,
2950 : (errcode(ERRCODE_DUPLICATE_TABLE),
2951 : errmsg("relation \"%s\" already exists",
2952 : newrelname)));
2953 :
2954 : /*
2955 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
2956 : * because it's a copy...)
2957 : */
2958 107 : namestrcpy(&(relform->relname), newrelname);
2959 :
2960 107 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
2961 :
2962 107 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
2963 : InvalidOid, is_internal);
2964 :
2965 107 : heap_freetuple(reltup);
2966 107 : heap_close(relrelation, RowExclusiveLock);
2967 :
2968 : /*
2969 : * Also rename the associated type, if any.
2970 : */
2971 107 : if (OidIsValid(targetrelation->rd_rel->reltype))
2972 59 : RenameTypeInternal(targetrelation->rd_rel->reltype,
2973 : newrelname, namespaceId);
2974 :
2975 : /*
2976 : * Also rename the associated constraint, if any.
2977 : */
2978 107 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX)
2979 : {
2980 48 : Oid constraintId = get_index_constraint(myrelid);
2981 :
2982 48 : if (OidIsValid(constraintId))
2983 3 : RenameConstraintById(constraintId, newrelname);
2984 : }
2985 :
2986 : /*
2987 : * Close rel, but keep exclusive lock!
2988 : */
2989 107 : relation_close(targetrelation, NoLock);
2990 107 : }
2991 :
2992 : /*
2993 : * Disallow ALTER TABLE (and similar commands) when the current backend has
2994 : * any open reference to the target table besides the one just acquired by
2995 : * the calling command; this implies there's an open cursor or active plan.
2996 : * We need this check because our lock doesn't protect us against stomping
2997 : * on our own foot, only other people's feet!
2998 : *
2999 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
3000 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
3001 : * possibly be relaxed to only error out for certain types of alterations.
3002 : * But the use-case for allowing any of these things is not obvious, so we
3003 : * won't work hard at it for now.
3004 : *
3005 : * We also reject these commands if there are any pending AFTER trigger events
3006 : * for the rel. This is certainly necessary for the rewriting variants of
3007 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
3008 : * events would try to fetch the wrong tuples. It might be overly cautious
3009 : * in other cases, but again it seems better to err on the side of paranoia.
3010 : *
3011 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
3012 : * we are worried about active indexscans on the index. The trigger-event
3013 : * check can be skipped, since we are doing no damage to the parent table.
3014 : *
3015 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
3016 : */
3017 : void
3018 5101 : CheckTableNotInUse(Relation rel, const char *stmt)
3019 : {
3020 : int expected_refcnt;
3021 :
3022 5101 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
3023 5101 : if (rel->rd_refcnt != expected_refcnt)
3024 2 : ereport(ERROR,
3025 : (errcode(ERRCODE_OBJECT_IN_USE),
3026 : /* translator: first %s is a SQL command, eg ALTER TABLE */
3027 : errmsg("cannot %s \"%s\" because "
3028 : "it is being used by active queries in this session",
3029 : stmt, RelationGetRelationName(rel))));
3030 :
3031 9119 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
3032 4020 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
3033 2 : ereport(ERROR,
3034 : (errcode(ERRCODE_OBJECT_IN_USE),
3035 : /* translator: first %s is a SQL command, eg ALTER TABLE */
3036 : errmsg("cannot %s \"%s\" because "
3037 : "it has pending trigger events",
3038 : stmt, RelationGetRelationName(rel))));
3039 5097 : }
3040 :
3041 : /*
3042 : * AlterTableLookupRelation
3043 : * Look up, and lock, the OID for the relation named by an alter table
3044 : * statement.
3045 : */
3046 : Oid
3047 1013 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
3048 : {
3049 1013 : return RangeVarGetRelidExtended(stmt->relation, lockmode, stmt->missing_ok, false,
3050 : RangeVarCallbackForAlterRelation,
3051 : (void *) stmt);
3052 : }
3053 :
3054 : /*
3055 : * AlterTable
3056 : * Execute ALTER TABLE, which can be a list of subcommands
3057 : *
3058 : * ALTER TABLE is performed in three phases:
3059 : * 1. Examine subcommands and perform pre-transformation checking.
3060 : * 2. Update system catalogs.
3061 : * 3. Scan table(s) to check new constraints, and optionally recopy
3062 : * the data into new table(s).
3063 : * Phase 3 is not performed unless one or more of the subcommands requires
3064 : * it. The intention of this design is to allow multiple independent
3065 : * updates of the table schema to be performed with only one pass over the
3066 : * data.
3067 : *
3068 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
3069 : * each table to be affected (there may be multiple affected tables if the
3070 : * commands traverse a table inheritance hierarchy). Also we do preliminary
3071 : * validation of the subcommands, including parse transformation of those
3072 : * expressions that need to be evaluated with respect to the old table
3073 : * schema.
3074 : *
3075 : * ATRewriteCatalogs performs phase 2 for each affected table. (Note that
3076 : * phases 2 and 3 normally do no explicit recursion, since phase 1 already
3077 : * did it --- although some subcommands have to recurse in phase 2 instead.)
3078 : * Certain subcommands need to be performed before others to avoid
3079 : * unnecessary conflicts; for example, DROP COLUMN should come before
3080 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
3081 : * lists, one for each logical "pass" of phase 2.
3082 : *
3083 : * ATRewriteTables performs phase 3 for those tables that need it.
3084 : *
3085 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
3086 : * the whole operation; we don't have to do anything special to clean up.
3087 : *
3088 : * The caller must lock the relation, with an appropriate lock level
3089 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
3090 : * or higher. We pass the lock level down
3091 : * so that we can apply it recursively to inherited tables. Note that the
3092 : * lock level we want as we recurse might well be higher than required for
3093 : * that specific subcommand. So we pass down the overall lock requirement,
3094 : * rather than reassess it at lower levels.
3095 : */
3096 : void
3097 973 : AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt)
3098 : {
3099 : Relation rel;
3100 :
3101 : /* Caller is required to provide an adequate lock. */
3102 973 : rel = relation_open(relid, NoLock);
3103 :
3104 973 : CheckTableNotInUse(rel, "ALTER TABLE");
3105 :
3106 971 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode);
3107 709 : }
3108 :
3109 : /*
3110 : * AlterTableInternal
3111 : *
3112 : * ALTER TABLE with target specified by OID
3113 : *
3114 : * We do not reject if the relation is already open, because it's quite
3115 : * likely that one or more layers of caller have it open. That means it
3116 : * is unsafe to use this entry point for alterations that could break
3117 : * existing query plans. On the assumption it's not used for such, we
3118 : * don't have to reject pending AFTER triggers, either.
3119 : */
3120 : void
3121 43 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
3122 : {
3123 : Relation rel;
3124 43 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
3125 :
3126 43 : rel = relation_open(relid, lockmode);
3127 :
3128 43 : EventTriggerAlterTableRelid(relid);
3129 :
3130 43 : ATController(NULL, rel, cmds, recurse, lockmode);
3131 42 : }
3132 :
3133 : /*
3134 : * AlterTableGetLockLevel
3135 : *
3136 : * Sets the overall lock level required for the supplied list of subcommands.
3137 : * Policy for doing this set according to needs of AlterTable(), see
3138 : * comments there for overall explanation.
3139 : *
3140 : * Function is called before and after parsing, so it must give same
3141 : * answer each time it is called. Some subcommands are transformed
3142 : * into other subcommand types, so the transform must never be made to a
3143 : * lower lock level than previously assigned. All transforms are noted below.
3144 : *
3145 : * Since this is called before we lock the table we cannot use table metadata
3146 : * to influence the type of lock we acquire.
3147 : *
3148 : * There should be no lockmodes hardcoded into the subcommand functions. All
3149 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
3150 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
3151 : * and does not travel through this section of code and cannot be combined with
3152 : * any of the subcommands given here.
3153 : *
3154 : * Note that Hot Standby only knows about AccessExclusiveLocks on the master
3155 : * so any changes that might affect SELECTs running on standbys need to use
3156 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
3157 : * have a solution for that also.
3158 : *
3159 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
3160 : * that takes a lock less than AccessExclusiveLock can change object definitions
3161 : * while pg_dump is running. Be careful to check that the appropriate data is
3162 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
3163 : * otherwise we might end up with an inconsistent dump that can't restore.
3164 : */
3165 : LOCKMODE
3166 1056 : AlterTableGetLockLevel(List *cmds)
3167 : {
3168 : /*
3169 : * This only works if we read catalog tables using MVCC snapshots.
3170 : */
3171 : ListCell *lcmd;
3172 1056 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
3173 :
3174 2138 : foreach(lcmd, cmds)
3175 : {
3176 1082 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3177 1082 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
3178 :
3179 1082 : switch (cmd->subtype)
3180 : {
3181 : /*
3182 : * These subcommands rewrite the heap, so require full locks.
3183 : */
3184 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
3185 : * to SELECT */
3186 : case AT_SetTableSpace: /* must rewrite heap */
3187 : case AT_AlterColumnType: /* must rewrite heap */
3188 : case AT_AddOids: /* must rewrite heap */
3189 245 : cmd_lockmode = AccessExclusiveLock;
3190 245 : break;
3191 :
3192 : /*
3193 : * These subcommands may require addition of toast tables. If
3194 : * we add a toast table to a table currently being scanned, we
3195 : * might miss data added to the new toast table by concurrent
3196 : * insert transactions.
3197 : */
3198 : case AT_SetStorage: /* may add toast tables, see
3199 : * ATRewriteCatalogs() */
3200 14 : cmd_lockmode = AccessExclusiveLock;
3201 14 : break;
3202 :
3203 : /*
3204 : * Removing constraints can affect SELECTs that have been
3205 : * optimised assuming the constraint holds true.
3206 : */
3207 : case AT_DropConstraint: /* as DROP INDEX */
3208 : case AT_DropNotNull: /* may change some SQL plans */
3209 67 : cmd_lockmode = AccessExclusiveLock;
3210 67 : break;
3211 :
3212 : /*
3213 : * Subcommands that may be visible to concurrent SELECTs
3214 : */
3215 : case AT_DropColumn: /* change visible to SELECT */
3216 : case AT_AddColumnToView: /* CREATE VIEW */
3217 : case AT_DropOids: /* calls AT_DropColumn */
3218 : case AT_EnableAlwaysRule: /* may change SELECT rules */
3219 : case AT_EnableReplicaRule: /* may change SELECT rules */
3220 : case AT_EnableRule: /* may change SELECT rules */
3221 : case AT_DisableRule: /* may change SELECT rules */
3222 120 : cmd_lockmode = AccessExclusiveLock;
3223 120 : break;
3224 :
3225 : /*
3226 : * Changing owner may remove implicit SELECT privileges
3227 : */
3228 : case AT_ChangeOwner: /* change visible to SELECT */
3229 18 : cmd_lockmode = AccessExclusiveLock;
3230 18 : break;
3231 :
3232 : /*
3233 : * Changing foreign table options may affect optimization.
3234 : */
3235 : case AT_GenericOptions:
3236 : case AT_AlterColumnGenericOptions:
3237 11 : cmd_lockmode = AccessExclusiveLock;
3238 11 : break;
3239 :
3240 : /*
3241 : * These subcommands affect write operations only.
3242 : */
3243 : case AT_EnableTrig:
3244 : case AT_EnableAlwaysTrig:
3245 : case AT_EnableReplicaTrig:
3246 : case AT_EnableTrigAll:
3247 : case AT_EnableTrigUser:
3248 : case AT_DisableTrig:
3249 : case AT_DisableTrigAll:
3250 : case AT_DisableTrigUser:
3251 6 : cmd_lockmode = ShareRowExclusiveLock;
3252 6 : break;
3253 :
3254 : /*
3255 : * These subcommands affect write operations only. XXX
3256 : * Theoretically, these could be ShareRowExclusiveLock.
3257 : */
3258 : case AT_ColumnDefault:
3259 : case AT_AlterConstraint:
3260 : case AT_AddIndex: /* from ADD CONSTRAINT */
3261 : case AT_AddIndexConstraint:
3262 : case AT_ReplicaIdentity:
3263 : case AT_SetNotNull:
3264 : case AT_EnableRowSecurity:
3265 : case AT_DisableRowSecurity:
3266 : case AT_ForceRowSecurity:
3267 : case AT_NoForceRowSecurity:
3268 : case AT_AddIdentity:
3269 : case AT_DropIdentity:
3270 : case AT_SetIdentity:
3271 164 : cmd_lockmode = AccessExclusiveLock;
3272 164 : break;
3273 :
3274 : case AT_AddConstraint:
3275 : case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
3276 : case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
3277 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
3278 243 : if (IsA(cmd->def, Constraint))
3279 : {
3280 243 : Constraint *con = (Constraint *) cmd->def;
3281 :
3282 243 : switch (con->contype)
3283 : {
3284 : case CONSTR_EXCLUSION:
3285 : case CONSTR_PRIMARY:
3286 : case CONSTR_UNIQUE:
3287 :
3288 : /*
3289 : * Cases essentially the same as CREATE INDEX. We
3290 : * could reduce the lock strength to ShareLock if
3291 : * we can work out how to allow concurrent catalog
3292 : * updates. XXX Might be set down to
3293 : * ShareRowExclusiveLock but requires further
3294 : * analysis.
3295 : */
3296 41 : cmd_lockmode = AccessExclusiveLock;
3297 41 : break;
3298 : case CONSTR_FOREIGN:
3299 :
3300 : /*
3301 : * We add triggers to both tables when we add a
3302 : * Foreign Key, so the lock level must be at least
3303 : * as strong as CREATE TRIGGER.
3304 : */
3305 135 : cmd_lockmode = ShareRowExclusiveLock;
3306 135 : break;
3307 :
3308 : default:
3309 67 : cmd_lockmode = AccessExclusiveLock;
3310 : }
3311 : }
3312 243 : break;
3313 :
3314 : /*
3315 : * These subcommands affect inheritance behaviour. Queries
3316 : * started before us will continue to see the old inheritance
3317 : * behaviour, while queries started after we commit will see
3318 : * new behaviour. No need to prevent reads or writes to the
3319 : * subtable while we hook it up though. Changing the TupDesc
3320 : * may be a problem, so keep highest lock.
3321 : */
3322 : case AT_AddInherit:
3323 : case AT_DropInherit:
3324 30 : cmd_lockmode = AccessExclusiveLock;
3325 30 : break;
3326 :
3327 : /*
3328 : * These subcommands affect implicit row type conversion. They
3329 : * have affects similar to CREATE/DROP CAST on queries. don't
3330 : * provide for invalidating parse trees as a result of such
3331 : * changes, so we keep these at AccessExclusiveLock.
3332 : */
3333 : case AT_AddOf:
3334 : case AT_DropOf:
3335 10 : cmd_lockmode = AccessExclusiveLock;
3336 10 : break;
3337 :
3338 : /*
3339 : * Only used by CREATE OR REPLACE VIEW which must conflict
3340 : * with an SELECTs currently using the view.
3341 : */
3342 : case AT_ReplaceRelOptions:
3343 20 : cmd_lockmode = AccessExclusiveLock;
3344 20 : break;
3345 :
3346 : /*
3347 : * These subcommands affect general strategies for performance
3348 : * and maintenance, though don't change the semantic results
3349 : * from normal data reads and writes. Delaying an ALTER TABLE
3350 : * behind currently active writes only delays the point where
3351 : * the new strategy begins to take effect, so there is no
3352 : * benefit in waiting. In this case the minimum restriction
3353 : * applies: we don't currently allow concurrent catalog
3354 : * updates.
3355 : */
3356 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
3357 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
3358 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
3359 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
3360 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
3361 17 : cmd_lockmode = ShareUpdateExclusiveLock;
3362 17 : break;
3363 :
3364 : case AT_SetLogged:
3365 : case AT_SetUnLogged:
3366 9 : cmd_lockmode = AccessExclusiveLock;
3367 9 : break;
3368 :
3369 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
3370 15 : cmd_lockmode = ShareUpdateExclusiveLock;
3371 15 : break;
3372 :
3373 : /*
3374 : * Rel options are more complex than first appears. Options
3375 : * are set here for tables, views and indexes; for historical
3376 : * reasons these can all be used with ALTER TABLE, so we can't
3377 : * decide between them using the basic grammar.
3378 : */
3379 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
3380 : * getTables() */
3381 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
3382 : * getTables() */
3383 24 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
3384 24 : break;
3385 :
3386 : case AT_AttachPartition:
3387 : case AT_DetachPartition:
3388 69 : cmd_lockmode = AccessExclusiveLock;
3389 69 : break;
3390 :
3391 : default: /* oops */
3392 0 : elog(ERROR, "unrecognized alter table type: %d",
3393 : (int) cmd->subtype);
3394 : break;
3395 : }
3396 :
3397 : /*
3398 : * Take the greatest lockmode from any subcommand
3399 : */
3400 1082 : if (cmd_lockmode > lockmode)
3401 1012 : lockmode = cmd_lockmode;
3402 : }
3403 :
3404 1056 : return lockmode;
3405 : }
3406 :
3407 : /*
3408 : * ATController provides top level control over the phases.
3409 : *
3410 : * parsetree is passed in to allow it to be passed to event triggers
3411 : * when requested.
3412 : */
3413 : static void
3414 1014 : ATController(AlterTableStmt *parsetree,
3415 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
3416 : {
3417 1014 : List *wqueue = NIL;
3418 : ListCell *lcmd;
3419 :
3420 : /* Phase 1: preliminary examination of commands, create work queue */
3421 2026 : foreach(lcmd, cmds)
3422 : {
3423 1045 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3424 :
3425 1045 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode);
3426 : }
3427 :
3428 : /* Close the relation, but keep lock until commit */
3429 981 : relation_close(rel, NoLock);
3430 :
3431 : /* Phase 2: update system catalogs */
3432 981 : ATRewriteCatalogs(&wqueue, lockmode);
3433 :
3434 : /* Phase 3: scan/rewrite tables as needed */
3435 775 : ATRewriteTables(parsetree, &wqueue, lockmode);
3436 751 : }
3437 :
3438 : /*
3439 : * ATPrepCmd
3440 : *
3441 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
3442 : * recursion and permission checks.
3443 : *
3444 : * Caller must have acquired appropriate lock type on relation already.
3445 : * This lock should be held until commit.
3446 : */
3447 : static void
3448 1113 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
3449 : bool recurse, bool recursing, LOCKMODE lockmode)
3450 : {
3451 : AlteredTableInfo *tab;
3452 1113 : int pass = AT_PASS_UNSET;
3453 :
3454 : /* Find or create work queue entry for this table */
3455 1113 : tab = ATGetQueueEntry(wqueue, rel);
3456 :
3457 : /*
3458 : * Copy the original subcommand for each table. This avoids conflicts
3459 : * when different child tables need to make different parse
3460 : * transformations (for example, the same column may have different column
3461 : * numbers in different children).
3462 : */
3463 1113 : cmd = copyObject(cmd);
3464 :
3465 : /*
3466 : * Do permissions checking, recursion to child tables if needed, and any
3467 : * additional phase-1 processing needed.
3468 : */
3469 1113 : switch (cmd->subtype)
3470 : {
3471 : case AT_AddColumn: /* ADD COLUMN */
3472 149 : ATSimplePermissions(rel,
3473 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3474 149 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
3475 : lockmode);
3476 : /* Recursion occurs during execution phase */
3477 147 : pass = AT_PASS_ADD_COL;
3478 147 : break;
3479 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
3480 2 : ATSimplePermissions(rel, ATT_VIEW);
3481 2 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
3482 : lockmode);
3483 : /* Recursion occurs during execution phase */
3484 2 : pass = AT_PASS_ADD_COL;
3485 2 : break;
3486 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
3487 :
3488 : /*
3489 : * We allow defaults on views so that INSERT into a view can have
3490 : * default-ish behavior. This works because the rewriter
3491 : * substitutes default values into INSERTs before it expands
3492 : * rules.
3493 : */
3494 36 : ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3495 36 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3496 : /* No command-specific prep needed */
3497 36 : pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
3498 36 : break;
3499 : case AT_AddIdentity:
3500 6 : ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3501 6 : pass = AT_PASS_ADD_CONSTR;
3502 6 : break;
3503 : case AT_DropIdentity:
3504 4 : ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3505 4 : pass = AT_PASS_DROP;
3506 4 : break;
3507 : case AT_SetIdentity:
3508 4 : ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
3509 4 : pass = AT_PASS_COL_ATTRS;
3510 4 : break;
3511 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
3512 20 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3513 19 : ATPrepDropNotNull(rel, recurse, recursing);
3514 18 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3515 : /* No command-specific prep needed */
3516 18 : pass = AT_PASS_DROP;
3517 18 : break;
3518 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
3519 55 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3520 54 : ATPrepSetNotNull(rel, recurse, recursing);
3521 53 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3522 : /* No command-specific prep needed */
3523 53 : pass = AT_PASS_ADD_CONSTR;
3524 53 : break;
3525 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
3526 15 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3527 : /* Performs own permission checks */
3528 15 : ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode);
3529 15 : pass = AT_PASS_MISC;
3530 15 : break;
3531 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
3532 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
3533 3 : ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE);
3534 : /* This command never recurses */
3535 3 : pass = AT_PASS_MISC;
3536 3 : break;
3537 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
3538 17 : ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
3539 17 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
3540 : /* No command-specific prep needed */
3541 17 : pass = AT_PASS_MISC;
3542 17 : break;
3543 : case AT_DropColumn: /* DROP COLUMN */
3544 115 : ATSimplePermissions(rel,
3545 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3546 114 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
3547 : /* Recursion occurs during execution phase */
3548 112 : pass = AT_PASS_DROP;
3549 112 : break;
3550 : case AT_AddIndex: /* ADD INDEX */
3551 34 : ATSimplePermissions(rel, ATT_TABLE);
3552 : /* This command never recurses */
3553 : /* No command-specific prep needed */
3554 34 : pass = AT_PASS_ADD_INDEX;
3555 34 : break;
3556 : case AT_AddConstraint: /* ADD CONSTRAINT */
3557 204 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3558 : /* Recursion occurs during execution phase */
3559 : /* No command-specific prep needed except saving recurse flag */
3560 204 : if (recurse)
3561 202 : cmd->subtype = AT_AddConstraintRecurse;
3562 204 : pass = AT_PASS_ADD_CONSTR;
3563 204 : break;
3564 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
3565 2 : ATSimplePermissions(rel, ATT_TABLE);
3566 : /* This command never recurses */
3567 : /* No command-specific prep needed */
3568 2 : pass = AT_PASS_ADD_CONSTR;
3569 2 : break;
3570 : case AT_DropConstraint: /* DROP CONSTRAINT */
3571 45 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3572 : /* Recursion occurs during execution phase */
3573 : /* No command-specific prep needed except saving recurse flag */
3574 45 : if (recurse)
3575 41 : cmd->subtype = AT_DropConstraintRecurse;
3576 45 : pass = AT_PASS_DROP;
3577 45 : break;
3578 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
3579 90 : ATSimplePermissions(rel,
3580 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
3581 : /* Performs own recursion */
3582 90 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
3583 70 : pass = AT_PASS_ALTER_TYPE;
3584 70 : break;
3585 : case AT_AlterColumnGenericOptions:
3586 7 : ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
3587 : /* This command never recurses */
3588 : /* No command-specific prep needed */
3589 7 : pass = AT_PASS_MISC;
3590 7 : break;
3591 : case AT_ChangeOwner: /* ALTER OWNER */
3592 : /* This command never recurses */
3593 : /* No command-specific prep needed */
3594 17 : pass = AT_PASS_MISC;
3595 17 : break;
3596 : case AT_ClusterOn: /* CLUSTER ON */
3597 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
3598 5 : ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
3599 : /* These commands never recurse */
3600 : /* No command-specific prep needed */
3601 5 : pass = AT_PASS_MISC;
3602 5 : break;
3603 : case AT_SetLogged: /* SET LOGGED */
3604 4 : ATSimplePermissions(rel, ATT_TABLE);
3605 4 : tab->chgPersistence = ATPrepChangePersistence(rel, true);
3606 : /* force rewrite if necessary; see comment in ATRewriteTables */
3607 3 : if (tab->chgPersistence)
3608 : {
3609 2 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3610 2 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
3611 : }
3612 3 : pass = AT_PASS_MISC;
3613 3 : break;
3614 : case AT_SetUnLogged: /* SET UNLOGGED */
3615 5 : ATSimplePermissions(rel, ATT_TABLE);
3616 5 : tab->chgPersistence = ATPrepChangePersistence(rel, false);
3617 : /* force rewrite if necessary; see comment in ATRewriteTables */
3618 4 : if (tab->chgPersistence)
3619 : {
3620 3 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3621 3 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
3622 : }
3623 4 : pass = AT_PASS_MISC;
3624 4 : break;
3625 : case AT_AddOids: /* SET WITH OIDS */
3626 9 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3627 9 : if (!rel->rd_rel->relhasoids || recursing)
3628 9 : ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
3629 : /* Recursion occurs during execution phase */
3630 9 : pass = AT_PASS_ADD_COL;
3631 9 : break;
3632 : case AT_DropOids: /* SET WITHOUT OIDS */
3633 10 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3634 : /* Performs own recursion */
3635 10 : if (rel->rd_rel->relhasoids)
3636 : {
3637 10 : AlterTableCmd *dropCmd = makeNode(AlterTableCmd);
3638 :
3639 10 : dropCmd->subtype = AT_DropColumn;
3640 10 : dropCmd->name = pstrdup("oid");
3641 10 : dropCmd->behavior = cmd->behavior;
3642 10 : ATPrepCmd(wqueue, rel, dropCmd, recurse, false, lockmode);
3643 : }
3644 10 : pass = AT_PASS_DROP;
3645 10 : break;
3646 : case AT_SetTableSpace: /* SET TABLESPACE */
3647 9 : ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX);
3648 : /* This command never recurses */
3649 9 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
3650 9 : pass = AT_PASS_MISC; /* doesn't actually matter */
3651 9 : break;
3652 : case AT_SetRelOptions: /* SET (...) */
3653 : case AT_ResetRelOptions: /* RESET (...) */
3654 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
3655 44 : ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
3656 : /* This command never recurses */
3657 : /* No command-specific prep needed */
3658 44 : pass = AT_PASS_MISC;
3659 44 : break;
3660 : case AT_AddInherit: /* INHERIT */
3661 25 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3662 : /* This command never recurses */
3663 25 : ATPrepAddInherit(rel);
3664 22 : pass = AT_PASS_MISC;
3665 22 : break;
3666 : case AT_DropInherit: /* NO INHERIT */
3667 5 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3668 : /* This command never recurses */
3669 : /* No command-specific prep needed */
3670 5 : pass = AT_PASS_MISC;
3671 5 : break;
3672 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
3673 6 : ATSimplePermissions(rel, ATT_TABLE);
3674 5 : pass = AT_PASS_MISC;
3675 5 : break;
3676 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
3677 15 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3678 : /* Recursion occurs during execution phase */
3679 : /* No command-specific prep needed except saving recurse flag */
3680 15 : if (recurse)
3681 15 : cmd->subtype = AT_ValidateConstraintRecurse;
3682 15 : pass = AT_PASS_MISC;
3683 15 : break;
3684 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
3685 15 : ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
3686 15 : pass = AT_PASS_MISC;
3687 : /* This command never recurses */
3688 : /* No command-specific prep needed */
3689 15 : break;
3690 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
3691 : case AT_EnableAlwaysTrig:
3692 : case AT_EnableReplicaTrig:
3693 : case AT_EnableTrigAll:
3694 : case AT_EnableTrigUser:
3695 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
3696 : case AT_DisableTrigAll:
3697 : case AT_DisableTrigUser:
3698 6 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
3699 6 : pass = AT_PASS_MISC;
3700 6 : break;
3701 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
3702 : case AT_EnableAlwaysRule:
3703 : case AT_EnableReplicaRule:
3704 : case AT_DisableRule:
3705 : case AT_AddOf: /* OF */
3706 : case AT_DropOf: /* NOT OF */
3707 : case AT_EnableRowSecurity:
3708 : case AT_DisableRowSecurity:
3709 : case AT_ForceRowSecurity:
3710 : case AT_NoForceRowSecurity:
3711 63 : ATSimplePermissions(rel, ATT_TABLE);
3712 : /* These commands never recurse */
3713 : /* No command-specific prep needed */
3714 63 : pass = AT_PASS_MISC;
3715 63 : break;
3716 : case AT_GenericOptions:
3717 1 : ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
3718 : /* No command-specific prep needed */
3719 1 : pass = AT_PASS_MISC;
3720 1 : break;
3721 : case AT_AttachPartition:
3722 : case AT_DetachPartition:
3723 66 : ATSimplePermissions(rel, ATT_TABLE);
3724 : /* No command-specific prep needed */
3725 66 : pass = AT_PASS_MISC;
3726 66 : break;
3727 : default: /* oops */
3728 0 : elog(ERROR, "unrecognized alter table type: %d",
3729 : (int) cmd->subtype);
3730 : pass = AT_PASS_UNSET; /* keep compiler quiet */
3731 : break;
3732 : }
3733 1078 : Assert(pass > AT_PASS_UNSET);
3734 :
3735 : /* Add the subcommand to the appropriate list for phase 2 */
3736 1078 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
3737 1078 : }
3738 :
3739 : /*
3740 : * ATRewriteCatalogs
3741 : *
3742 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
3743 : * dispatched in a "safe" execution order (designed to avoid unnecessary
3744 : * conflicts).
3745 : */
3746 : static void
3747 981 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
3748 : {
3749 : int pass;
3750 : ListCell *ltab;
3751 :
3752 : /*
3753 : * We process all the tables "in parallel", one pass at a time. This is
3754 : * needed because we may have to propagate work from one table to another
3755 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
3756 : * re-adding of the foreign key constraint to the other table). Work can
3757 : * only be propagated into later passes, however.
3758 : */
3759 9088 : for (pass = 0; pass < AT_NUM_PASSES; pass++)
3760 : {
3761 : /* Go through each table that needs to be processed */
3762 17224 : foreach(ltab, *wqueue)
3763 : {
3764 9117 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
3765 9117 : List *subcmds = tab->subcmds[pass];
3766 : Relation rel;
3767 : ListCell *lcmd;
3768 :
3769 9117 : if (subcmds == NIL)
3770 8045 : continue;
3771 :
3772 : /*
3773 : * Appropriate lock was obtained by phase 1, needn't get it again
3774 : */
3775 1072 : rel = relation_open(tab->relid, NoLock);
3776 :
3777 1979 : foreach(lcmd, subcmds)
3778 1113 : ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd), lockmode);
3779 :
3780 : /*
3781 : * After the ALTER TYPE pass, do cleanup work (this is not done in
3782 : * ATExecAlterColumnType since it should be done only once if
3783 : * multiple columns of a table are altered).
3784 : */
3785 866 : if (pass == AT_PASS_ALTER_TYPE)
3786 67 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
3787 :
3788 866 : relation_close(rel, NoLock);
3789 : }
3790 : }
3791 :
3792 : /* Check to see if a toast table must be added. */
3793 1715 : foreach(ltab, *wqueue)
3794 : {
3795 940 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
3796 :
3797 : /*
3798 : * If the table is source table of ATTACH PARTITION command, we did
3799 : * not modify anything about it that will change its toasting
3800 : * requirement, so no need to check.
3801 : */
3802 1144 : if (((tab->relkind == RELKIND_RELATION ||
3803 1012 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
3804 963 : tab->partition_constraint == NULL) ||
3805 155 : tab->relkind == RELKIND_MATVIEW)
3806 785 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
3807 : }
3808 775 : }
3809 :
3810 : /*
3811 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
3812 : */
3813 : static void
3814 1113 : ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
3815 : AlterTableCmd *cmd, LOCKMODE lockmode)
3816 : {
3817 1113 : ObjectAddress address = InvalidObjectAddress;
3818 :
3819 1113 : switch (cmd->subtype)
3820 : {
3821 : case AT_AddColumn: /* ADD COLUMN */
3822 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
3823 11 : address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
3824 : false, false, false,
3825 : false, lockmode);
3826 7 : break;
3827 : case AT_AddColumnRecurse:
3828 137 : address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
3829 : false, true, false,
3830 137 : cmd->missing_ok, lockmode);
3831 126 : break;
3832 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
3833 36 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
3834 29 : break;
3835 : case AT_AddIdentity:
3836 6 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
3837 2 : break;
3838 : case AT_SetIdentity:
3839 4 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
3840 3 : break;
3841 : case AT_DropIdentity:
3842 4 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
3843 3 : break;
3844 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
3845 18 : address = ATExecDropNotNull(rel, cmd->name, lockmode);
3846 10 : break;
3847 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
3848 53 : address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
3849 49 : break;
3850 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
3851 15 : address = ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
3852 13 : break;
3853 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
3854 3 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
3855 3 : break;
3856 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
3857 0 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
3858 0 : break;
3859 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
3860 17 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
3861 15 : break;
3862 : case AT_DropColumn: /* DROP COLUMN */
3863 13 : address = ATExecDropColumn(wqueue, rel, cmd->name,
3864 : cmd->behavior, false, false,
3865 13 : cmd->missing_ok, lockmode);
3866 10 : break;
3867 : case AT_DropColumnRecurse: /* DROP COLUMN with recursion */
3868 99 : address = ATExecDropColumn(wqueue, rel, cmd->name,
3869 : cmd->behavior, true, false,
3870 99 : cmd->missing_ok, lockmode);
3871 80 : break;
3872 : case AT_AddIndex: /* ADD INDEX */
3873 34 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
3874 : lockmode);
3875 21 : break;
3876 : case AT_ReAddIndex: /* ADD INDEX */
3877 19 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
3878 : lockmode);
3879 19 : break;
3880 : case AT_AddConstraint: /* ADD CONSTRAINT */
3881 2 : address =
3882 2 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3883 : false, false, lockmode);
3884 1 : break;
3885 : case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
3886 202 : address =
3887 202 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3888 : true, false, lockmode);
3889 152 : break;
3890 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
3891 13 : address =
3892 13 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3893 : true, true, lockmode);
3894 11 : break;
3895 : case AT_ReAddComment: /* Re-add existing comment */
3896 7 : address = CommentObject((CommentStmt *) cmd->def);
3897 7 : break;
3898 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
3899 2 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
3900 : lockmode);
3901 2 : break;
3902 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
3903 5 : address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
3904 5 : break;
3905 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
3906 0 : address = ATExecValidateConstraint(rel, cmd->name, false, false,
3907 : lockmode);
3908 0 : break;
3909 : case AT_ValidateConstraintRecurse: /* VALIDATE CONSTRAINT with
3910 : * recursion */
3911 15 : address = ATExecValidateConstraint(rel, cmd->name, true, false,
3912 : lockmode);
3913 11 : break;
3914 : case AT_DropConstraint: /* DROP CONSTRAINT */
3915 4 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
3916 : false, false,
3917 4 : cmd->missing_ok, lockmode);
3918 3 : break;
3919 : case AT_DropConstraintRecurse: /* DROP CONSTRAINT with recursion */
3920 41 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
3921 : true, false,
3922 41 : cmd->missing_ok, lockmode);
3923 30 : break;
3924 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
3925 69 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
3926 68 : break;
3927 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
3928 7 : address =
3929 7 : ATExecAlterColumnGenericOptions(rel, cmd->name,
3930 7 : (List *) cmd->def, lockmode);
3931 6 : break;
3932 : case AT_ChangeOwner: /* ALTER OWNER */
3933 17 : ATExecChangeOwner(RelationGetRelid(rel),
3934 17 : get_rolespec_oid(cmd->newowner, false),
3935 : false, lockmode);
3936 14 : break;
3937 : case AT_ClusterOn: /* CLUSTER ON */
3938 3 : address = ATExecClusterOn(rel, cmd->name, lockmode);
3939 3 : break;
3940 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
3941 2 : ATExecDropCluster(rel, lockmode);
3942 2 : break;
3943 : case AT_SetLogged: /* SET LOGGED */
3944 : case AT_SetUnLogged: /* SET UNLOGGED */
3945 7 : break;
3946 : case AT_AddOids: /* SET WITH OIDS */
3947 : /* Use the ADD COLUMN code, unless prep decided to do nothing */
3948 0 : if (cmd->def != NULL)
3949 0 : address =
3950 0 : ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
3951 : true, false, false,
3952 0 : cmd->missing_ok, lockmode);
3953 0 : break;
3954 : case AT_AddOidsRecurse: /* SET WITH OIDS */
3955 : /* Use the ADD COLUMN code, unless prep decided to do nothing */
3956 9 : if (cmd->def != NULL)
3957 9 : address =
3958 9 : ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
3959 : true, true, false,
3960 9 : cmd->missing_ok, lockmode);
3961 9 : break;
3962 : case AT_DropOids: /* SET WITHOUT OIDS */
3963 :
3964 : /*
3965 : * Nothing to do here; we'll have generated a DropColumn
3966 : * subcommand to do the real work
3967 : */
3968 8 : break;
3969 : case AT_SetTableSpace: /* SET TABLESPACE */
3970 :
3971 : /*
3972 : * Nothing to do here; Phase 3 does the work
3973 : */
3974 9 : break;
3975 : case AT_SetRelOptions: /* SET (...) */
3976 : case AT_ResetRelOptions: /* RESET (...) */
3977 : case AT_ReplaceRelOptions: /* replace entire option list */
3978 44 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
3979 41 : break;
3980 : case AT_EnableTrig: /* ENABLE TRIGGER name */
3981 2 : ATExecEnableDisableTrigger(rel, cmd->name,
3982 : TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
3983 2 : break;
3984 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
3985 0 : ATExecEnableDisableTrigger(rel, cmd->name,
3986 : TRIGGER_FIRES_ALWAYS, false, lockmode);
3987 0 : break;
3988 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
3989 0 : ATExecEnableDisableTrigger(rel, cmd->name,
3990 : TRIGGER_FIRES_ON_REPLICA, false, lockmode);
3991 0 : break;
3992 : case AT_DisableTrig: /* DISABLE TRIGGER name */
3993 2 : ATExecEnableDisableTrigger(rel, cmd->name,
3994 : TRIGGER_DISABLED, false, lockmode);
3995 2 : break;
3996 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
3997 0 : ATExecEnableDisableTrigger(rel, NULL,
3998 : TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
3999 0 : break;
4000 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
4001 1 : ATExecEnableDisableTrigger(rel, NULL,
4002 : TRIGGER_DISABLED, false, lockmode);
4003 1 : break;
4004 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
4005 0 : ATExecEnableDisableTrigger(rel, NULL,
4006 : TRIGGER_FIRES_ON_ORIGIN, true, lockmode);
4007 0 : break;
4008 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
4009 1 : ATExecEnableDisableTrigger(rel, NULL,
4010 : TRIGGER_DISABLED, true, lockmode);
4011 1 : break;
4012 :
4013 : case AT_EnableRule: /* ENABLE RULE name */
4014 0 : ATExecEnableDisableRule(rel, cmd->name,
4015 : RULE_FIRES_ON_ORIGIN, lockmode);
4016 0 : break;
4017 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
4018 0 : ATExecEnableDisableRule(rel, cmd->name,
4019 : RULE_FIRES_ALWAYS, lockmode);
4020 0 : break;
4021 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
4022 0 : ATExecEnableDisableRule(rel, cmd->name,
4023 : RULE_FIRES_ON_REPLICA, lockmode);
4024 0 : break;
4025 : case AT_DisableRule: /* DISABLE RULE name */
4026 0 : ATExecEnableDisableRule(rel, cmd->name,
4027 : RULE_DISABLED, lockmode);
4028 0 : break;
4029 :
4030 : case AT_AddInherit:
4031 22 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
4032 11 : break;
4033 : case AT_DropInherit:
4034 5 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
4035 4 : break;
4036 : case AT_AddOf:
4037 9 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
4038 3 : break;
4039 : case AT_DropOf:
4040 1 : ATExecDropOf(rel, lockmode);
4041 1 : break;
4042 : case AT_ReplicaIdentity:
4043 15 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
4044 8 : break;
4045 : case AT_EnableRowSecurity:
4046 37 : ATExecEnableRowSecurity(rel);
4047 37 : break;
4048 : case AT_DisableRowSecurity:
4049 2 : ATExecDisableRowSecurity(rel);
4050 2 : break;
4051 : case AT_ForceRowSecurity:
4052 10 : ATExecForceNoForceRowSecurity(rel, true);
4053 10 : break;
4054 : case AT_NoForceRowSecurity:
4055 4 : ATExecForceNoForceRowSecurity(rel, false);
4056 4 : break;
4057 : case AT_GenericOptions:
4058 1 : ATExecGenericOptions(rel, (List *) cmd->def);
4059 1 : break;
4060 : case AT_AttachPartition:
4061 55 : ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
4062 33 : break;
4063 : case AT_DetachPartition:
4064 11 : ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
4065 8 : break;
4066 : default: /* oops */
4067 0 : elog(ERROR, "unrecognized alter table type: %d",
4068 : (int) cmd->subtype);
4069 : break;
4070 : }
4071 :
4072 : /*
4073 : * Report the subcommand to interested event triggers.
4074 : */
4075 907 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
4076 :
4077 : /*
4078 : * Bump the command counter to ensure the next subcommand in the sequence
4079 : * can see the changes so far
4080 : */
4081 907 : CommandCounterIncrement();
4082 907 : }
4083 :
4084 : /*
4085 : * ATRewriteTables: ALTER TABLE phase 3
4086 : */
4087 : static void
4088 775 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
4089 : {
4090 : ListCell *ltab;
4091 :
4092 : /* Go through each table that needs to be checked or rewritten */
4093 1693 : foreach(ltab, *wqueue)
4094 : {
4095 940 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4096 :
4097 : /* Foreign tables have no storage, nor do partitioned tables. */
4098 1805 : if (tab->relkind == RELKIND_FOREIGN_TABLE ||
4099 865 : tab->relkind == RELKIND_PARTITIONED_TABLE)
4100 147 : continue;
4101 :
4102 : /*
4103 : * If we change column data types or add/remove OIDs, the operation
4104 : * has to be propagated to tables that use this table's rowtype as a
4105 : * column type. tab->newvals will also be non-NULL in the case where
4106 : * we're adding a column with a default. We choose to forbid that
4107 : * case as well, since composite types might eventually support
4108 : * defaults.
4109 : *
4110 : * (Eventually we'll probably need to check for composite type
4111 : * dependencies even when we're just scanning the table without a
4112 : * rewrite, but at the moment a composite type does not enforce any
4113 : * constraints, so it's not necessary/appropriate to enforce them just
4114 : * during ALTER.)
4115 : */
4116 793 : if (tab->newvals != NIL || tab->rewrite > 0)
4117 : {
4118 : Relation rel;
4119 :
4120 87 : rel = heap_open(tab->relid, NoLock);
4121 87 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
4122 85 : heap_close(rel, NoLock);
4123 : }
4124 :
4125 : /*
4126 : * We only need to rewrite the table if at least one column needs to
4127 : * be recomputed, we are adding/removing the OID column, or we are
4128 : * changing its persistence.
4129 : *
4130 : * There are two reasons for requiring a rewrite when changing
4131 : * persistence: on one hand, we need to ensure that the buffers
4132 : * belonging to each of the two relations are marked with or without
4133 : * BM_PERMANENT properly. On the other hand, since rewriting creates
4134 : * and assigns a new relfilenode, we automatically create or drop an
4135 : * init fork for the relation as appropriate.
4136 : */
4137 791 : if (tab->rewrite > 0)
4138 : {
4139 : /* Build a temporary relation and copy data */
4140 : Relation OldHeap;
4141 : Oid OIDNewHeap;
4142 : Oid NewTableSpace;
4143 : char persistence;
4144 :
4145 75 : OldHeap = heap_open(tab->relid, NoLock);
4146 :
4147 : /*
4148 : * We don't support rewriting of system catalogs; there are too
4149 : * many corner cases and too little benefit. In particular this
4150 : * is certainly not going to work for mapped catalogs.
4151 : */
4152 75 : if (IsSystemRelation(OldHeap))
4153 0 : ereport(ERROR,
4154 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4155 : errmsg("cannot rewrite system relation \"%s\"",
4156 : RelationGetRelationName(OldHeap))));
4157 :
4158 75 : if (RelationIsUsedAsCatalogTable(OldHeap))
4159 0 : ereport(ERROR,
4160 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4161 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
4162 : RelationGetRelationName(OldHeap))));
4163 :
4164 : /*
4165 : * Don't allow rewrite on temp tables of other backends ... their
4166 : * local buffer manager is not going to cope.
4167 : */
4168 75 : if (RELATION_IS_OTHER_TEMP(OldHeap))
4169 0 : ereport(ERROR,
4170 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4171 : errmsg("cannot rewrite temporary tables of other sessions")));
4172 :
4173 : /*
4174 : * Select destination tablespace (same as original unless user
4175 : * requested a change)
4176 : */
4177 75 : if (tab->newTableSpace)
4178 0 : NewTableSpace = tab->newTableSpace;
4179 : else
4180 75 : NewTableSpace = OldHeap->rd_rel->reltablespace;
4181 :
4182 : /*
4183 : * Select persistence of transient table (same as original unless
4184 : * user requested a change)
4185 : */
4186 145 : persistence = tab->chgPersistence ?
4187 70 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
4188 :
4189 75 : heap_close(OldHeap, NoLock);
4190 :
4191 : /*
4192 : * Fire off an Event Trigger now, before actually rewriting the
4193 : * table.
4194 : *
4195 : * We don't support Event Trigger for nested commands anywhere,
4196 : * here included, and parsetree is given NULL when coming from
4197 : * AlterTableInternal.
4198 : *
4199 : * And fire it only once.
4200 : */
4201 75 : if (parsetree)
4202 75 : EventTriggerTableRewrite((Node *) parsetree,
4203 : tab->relid,
4204 : tab->rewrite);
4205 :
4206 : /*
4207 : * Create transient table that will receive the modified data.
4208 : *
4209 : * Ensure it is marked correctly as logged or unlogged. We have
4210 : * to do this here so that buffers for the new relfilenode will
4211 : * have the right persistence set, and at the same time ensure
4212 : * that the original filenode's buffers will get read in with the
4213 : * correct setting (i.e. the original one). Otherwise a rollback
4214 : * after the rewrite would possibly result with buffers for the
4215 : * original filenode having the wrong persistence setting.
4216 : *
4217 : * NB: This relies on swap_relation_files() also swapping the
4218 : * persistence. That wouldn't work for pg_class, but that can't be
4219 : * unlogged anyway.
4220 : */
4221 73 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
4222 : lockmode);
4223 :
4224 : /*
4225 : * Copy the heap data into the new table with the desired
4226 : * modifications, and test the current data within the table
4227 : * against new constraints generated by ALTER TABLE commands.
4228 : */
4229 73 : ATRewriteTable(tab, OIDNewHeap, lockmode);
4230 :
4231 : /*
4232 : * Swap the physical files of the old and new heaps, then rebuild
4233 : * indexes and discard the old heap. We can use RecentXmin for
4234 : * the table's new relfrozenxid because we rewrote all the tuples
4235 : * in ATRewriteTable, so no older Xid remains in the table. Also,
4236 : * we never try to swap toast tables by content, since we have no
4237 : * interest in letting this code work on system catalogs.
4238 : */
4239 142 : finish_heap_swap(tab->relid, OIDNewHeap,
4240 : false, false, true,
4241 71 : !OidIsValid(tab->newTableSpace),
4242 : RecentXmin,
4243 : ReadNextMultiXactId(),
4244 : persistence);
4245 : }
4246 : else
4247 : {
4248 : /*
4249 : * Test the current data within the table against new constraints
4250 : * generated by ALTER TABLE commands, but don't rebuild data.
4251 : */
4252 1306 : if (tab->constraints != NIL || tab->new_notnull ||
4253 590 : tab->partition_constraint != NULL)
4254 149 : ATRewriteTable(tab, InvalidOid, lockmode);
4255 :
4256 : /*
4257 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
4258 : * just do a block-by-block copy.
4259 : */
4260 701 : if (tab->newTableSpace)
4261 9 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
4262 : }
4263 : }
4264 :
4265 : /*
4266 : * Foreign key constraints are checked in a final pass, since (a) it's
4267 : * generally best to examine each one separately, and (b) it's at least
4268 : * theoretically possible that we have changed both relations of the
4269 : * foreign key, and we'd better have finished both rewrites before we try
4270 : * to read the tables.
4271 : */
4272 1662 : foreach(ltab, *wqueue)
4273 : {
4274 911 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4275 911 : Relation rel = NULL;
4276 : ListCell *lcon;
4277 :
4278 1018 : foreach(lcon, tab->constraints)
4279 : {
4280 109 : NewConstraint *con = lfirst(lcon);
4281 :
4282 109 : if (con->contype == CONSTR_FOREIGN)
4283 : {
4284 23 : Constraint *fkconstraint = (Constraint *) con->qual;
4285 : Relation refrel;
4286 :
4287 23 : if (rel == NULL)
4288 : {
4289 : /* Long since locked, no need for another */
4290 23 : rel = heap_open(tab->relid, NoLock);
4291 : }
4292 :
4293 23 : refrel = heap_open(con->refrelid, RowShareLock);
4294 :
4295 23 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
4296 : con->refindid,
4297 : con->conid);
4298 :
4299 : /*
4300 : * No need to mark the constraint row as validated, we did
4301 : * that when we inserted the row earlier.
4302 : */
4303 :
4304 21 : heap_close(refrel, NoLock);
4305 : }
4306 : }
4307 :
4308 909 : if (rel)
4309 21 : heap_close(rel, NoLock);
4310 : }
4311 751 : }
4312 :
4313 : /*
4314 : * ATRewriteTable: scan or rewrite one table
4315 : *
4316 : * OIDNewHeap is InvalidOid if we don't need to rewrite
4317 : */
4318 : static void
4319 222 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
4320 : {
4321 : Relation oldrel;
4322 : Relation newrel;
4323 : TupleDesc oldTupDesc;
4324 : TupleDesc newTupDesc;
4325 222 : bool needscan = false;
4326 : List *notnull_attrs;
4327 : int i;
4328 : ListCell *l;
4329 : EState *estate;
4330 : CommandId mycid;
4331 : BulkInsertState bistate;
4332 : int hi_options;
4333 222 : ExprState *partqualstate = NULL;
4334 :
4335 : /*
4336 : * Open the relation(s). We have surely already locked the existing
4337 : * table.
4338 : */
4339 222 : oldrel = heap_open(tab->relid, NoLock);
4340 222 : oldTupDesc = tab->oldDesc;
4341 222 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
4342 :
4343 222 : if (OidIsValid(OIDNewHeap))
4344 73 : newrel = heap_open(OIDNewHeap, lockmode);
4345 : else
4346 149 : newrel = NULL;
4347 :
4348 : /*
4349 : * Prepare a BulkInsertState and options for heap_insert. Because we're
4350 : * building a new heap, we can skip WAL-logging and fsync it to disk at
4351 : * the end instead (unless WAL-logging is required for archiving or
4352 : * streaming replication). The FSM is empty too, so don't bother using it.
4353 : */
4354 222 : if (newrel)
4355 : {
4356 73 : mycid = GetCurrentCommandId(true);
4357 73 : bistate = GetBulkInsertState();
4358 :
4359 73 : hi_options = HEAP_INSERT_SKIP_FSM;
4360 73 : if (!XLogIsNeeded())
4361 0 : hi_options |= HEAP_INSERT_SKIP_WAL;
4362 : }
4363 : else
4364 : {
4365 : /* keep compiler quiet about using these uninitialized */
4366 149 : mycid = 0;
4367 149 : bistate = NULL;
4368 149 : hi_options = 0;
4369 : }
4370 :
4371 : /*
4372 : * Generate the constraint and default execution states
4373 : */
4374 :
4375 222 : estate = CreateExecutorState();
4376 :
4377 : /* Build the needed expression execution states */
4378 320 : foreach(l, tab->constraints)
4379 : {
4380 98 : NewConstraint *con = lfirst(l);
4381 :
4382 98 : switch (con->contype)
4383 : {
4384 : case CONSTR_CHECK:
4385 75 : needscan = true;
4386 75 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
4387 75 : break;
4388 : case CONSTR_FOREIGN:
4389 : /* Nothing to do here */
4390 23 : break;
4391 : default:
4392 0 : elog(ERROR, "unrecognized constraint type: %d",
4393 : (int) con->contype);
4394 : }
4395 : }
4396 :
4397 : /* Build expression execution states for partition check quals */
4398 222 : if (tab->partition_constraint)
4399 : {
4400 23 : needscan = true;
4401 23 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
4402 : }
4403 :
4404 277 : foreach(l, tab->newvals)
4405 : {
4406 55 : NewColumnValue *ex = lfirst(l);
4407 :
4408 : /* expr already planned */
4409 55 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
4410 : }
4411 :
4412 222 : notnull_attrs = NIL;
4413 222 : if (newrel || tab->new_notnull)
4414 : {
4415 : /*
4416 : * If we are rebuilding the tuples OR if we added any new NOT NULL
4417 : * constraints, check all not-null constraints. This is a bit of
4418 : * overkill but it minimizes risk of bugs, and heap_attisnull is a
4419 : * pretty cheap test anyway.
4420 : */
4421 420 : for (i = 0; i < newTupDesc->natts; i++)
4422 : {
4423 309 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
4424 :
4425 309 : if (attr->attnotnull && !attr->attisdropped)
4426 87 : notnull_attrs = lappend_int(notnull_attrs, i);
4427 : }
4428 111 : if (notnull_attrs)
4429 69 : needscan = true;
4430 : }
4431 :
4432 222 : if (newrel || needscan)
4433 : {
4434 : ExprContext *econtext;
4435 : Datum *values;
4436 : bool *isnull;
4437 : TupleTableSlot *oldslot;
4438 : TupleTableSlot *newslot;
4439 : HeapScanDesc scan;
4440 : HeapTuple tuple;
4441 : MemoryContext oldCxt;
4442 200 : List *dropped_attrs = NIL;
4443 : ListCell *lc;
4444 : Snapshot snapshot;
4445 :
4446 200 : if (newrel)
4447 73 : ereport(DEBUG1,
4448 : (errmsg("rewriting table \"%s\"",
4449 : RelationGetRelationName(oldrel))));
4450 : else
4451 127 : ereport(DEBUG1,
4452 : (errmsg("verifying table \"%s\"",
4453 : RelationGetRelationName(oldrel))));
4454 :
4455 200 : if (newrel)
4456 : {
4457 : /*
4458 : * All predicate locks on the tuples or pages are about to be made
4459 : * invalid, because we move tuples around. Promote them to
4460 : * relation locks.
4461 : */
4462 73 : TransferPredicateLocksToHeapRelation(oldrel);
4463 : }
4464 :
4465 200 : econtext = GetPerTupleExprContext(estate);
4466 :
4467 : /*
4468 : * Make tuple slots for old and new tuples. Note that even when the
4469 : * tuples are the same, the tupDescs might not be (consider ADD COLUMN
4470 : * without a default).
4471 : */
4472 200 : oldslot = MakeSingleTupleTableSlot(oldTupDesc);
4473 200 : newslot = MakeSingleTupleTableSlot(newTupDesc);
4474 :
4475 : /* Preallocate values/isnull arrays */
4476 200 : i = Max(newTupDesc->natts, oldTupDesc->natts);
4477 200 : values = (Datum *) palloc(i * sizeof(Datum));
4478 200 : isnull = (bool *) palloc(i * sizeof(bool));
4479 200 : memset(values, 0, i * sizeof(Datum));
4480 200 : memset(isnull, true, i * sizeof(bool));
4481 :
4482 : /*
4483 : * Any attributes that are dropped according to the new tuple
4484 : * descriptor can be set to NULL. We precompute the list of dropped
4485 : * attributes to avoid needing to do so in the per-tuple loop.
4486 : */
4487 758 : for (i = 0; i < newTupDesc->natts; i++)
4488 : {
4489 558 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
4490 65 : dropped_attrs = lappend_int(dropped_attrs, i);
4491 : }
4492 :
4493 : /*
4494 : * Scan through the rows, generating a new row if needed and then
4495 : * checking all the constraints.
4496 : */
4497 200 : snapshot = RegisterSnapshot(GetLatestSnapshot());
4498 200 : scan = heap_beginscan(oldrel, snapshot, 0, NULL);
4499 :
4500 : /*
4501 : * Switch to per-tuple memory context and reset it for each tuple
4502 : * produced, so we don't leak memory.
4503 : */
4504 200 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
4505 :
4506 7000 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
4507 : {
4508 6617 : if (tab->rewrite > 0)
4509 : {
4510 5553 : Oid tupOid = InvalidOid;
4511 :
4512 : /* Extract data from old tuple */
4513 5553 : heap_deform_tuple(tuple, oldTupDesc, values, isnull);
4514 5553 : if (oldTupDesc->tdhasoid)
4515 7 : tupOid = HeapTupleGetOid(tuple);
4516 :
4517 : /* Set dropped attributes to null in new tuple */
4518 5568 : foreach(lc, dropped_attrs)
4519 15 : isnull[lfirst_int(lc)] = true;
4520 :
4521 : /*
4522 : * Process supplied expressions to replace selected columns.
4523 : * Expression inputs come from the old tuple.
4524 : */
4525 5553 : ExecStoreTuple(tuple, oldslot, InvalidBuffer, false);
4526 5553 : econtext->ecxt_scantuple = oldslot;
4527 :
4528 12098 : foreach(l, tab->newvals)
4529 : {
4530 6547 : NewColumnValue *ex = lfirst(l);
4531 :
4532 13094 : values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
4533 : econtext,
4534 6547 : &isnull[ex->attnum - 1]);
4535 : }
4536 :
4537 : /*
4538 : * Form the new tuple. Note that we don't explicitly pfree it,
4539 : * since the per-tuple memory context will be reset shortly.
4540 : */
4541 5551 : tuple = heap_form_tuple(newTupDesc, values, isnull);
4542 :
4543 : /* Preserve OID, if any */
4544 5551 : if (newTupDesc->tdhasoid)
4545 6 : HeapTupleSetOid(tuple, tupOid);
4546 :
4547 : /*
4548 : * Constraints might reference the tableoid column, so
4549 : * initialize t_tableOid before evaluating them.
4550 : */
4551 5551 : tuple->t_tableOid = RelationGetRelid(oldrel);
4552 : }
4553 :
4554 : /* Now check any constraints on the possibly-changed tuple */
4555 6615 : ExecStoreTuple(tuple, newslot, InvalidBuffer, false);
4556 6615 : econtext->ecxt_scantuple = newslot;
4557 :
4558 7155 : foreach(l, notnull_attrs)
4559 : {
4560 545 : int attn = lfirst_int(l);
4561 :
4562 545 : if (heap_attisnull(tuple, attn + 1))
4563 : {
4564 5 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
4565 :
4566 5 : ereport(ERROR,
4567 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
4568 : errmsg("column \"%s\" contains null values",
4569 : NameStr(attr->attname)),
4570 : errtablecol(oldrel, attn + 1)));
4571 : }
4572 : }
4573 :
4574 7658 : foreach(l, tab->constraints)
4575 : {
4576 1053 : NewConstraint *con = lfirst(l);
4577 :
4578 1053 : switch (con->contype)
4579 : {
4580 : case CONSTR_CHECK:
4581 1048 : if (!ExecCheck(con->qualstate, econtext))
4582 5 : ereport(ERROR,
4583 : (errcode(ERRCODE_CHECK_VIOLATION),
4584 : errmsg("check constraint \"%s\" is violated by some row",
4585 : con->name),
4586 : errtableconstraint(oldrel, con->name)));
4587 1043 : break;
4588 : case CONSTR_FOREIGN:
4589 : /* Nothing to do here */
4590 5 : break;
4591 : default:
4592 0 : elog(ERROR, "unrecognized constraint type: %d",
4593 : (int) con->contype);
4594 : }
4595 : }
4596 :
4597 6605 : if (partqualstate && !ExecCheck(partqualstate, econtext))
4598 5 : ereport(ERROR,
4599 : (errcode(ERRCODE_CHECK_VIOLATION),
4600 : errmsg("partition constraint is violated by some row")));
4601 :
4602 : /* Write the tuple out to the new relation */
4603 6600 : if (newrel)
4604 5551 : heap_insert(newrel, tuple, mycid, hi_options, bistate);
4605 :
4606 6600 : ResetExprContext(econtext);
4607 :
4608 6600 : CHECK_FOR_INTERRUPTS();
4609 : }
4610 :
4611 183 : MemoryContextSwitchTo(oldCxt);
4612 183 : heap_endscan(scan);
4613 183 : UnregisterSnapshot(snapshot);
4614 :
4615 183 : ExecDropSingleTupleTableSlot(oldslot);
4616 183 : ExecDropSingleTupleTableSlot(newslot);
4617 : }
4618 :
4619 205 : FreeExecutorState(estate);
4620 :
4621 205 : heap_close(oldrel, NoLock);
4622 205 : if (newrel)
4623 : {
4624 71 : FreeBulkInsertState(bistate);
4625 :
4626 : /* If we skipped writing WAL, then we need to sync the heap. */
4627 71 : if (hi_options & HEAP_INSERT_SKIP_WAL)
4628 0 : heap_sync(newrel);
4629 :
4630 71 : heap_close(newrel, NoLock);
4631 : }
4632 205 : }
4633 :
4634 : /*
4635 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
4636 : */
4637 : static AlteredTableInfo *
4638 1277 : ATGetQueueEntry(List **wqueue, Relation rel)
4639 : {
4640 1277 : Oid relid = RelationGetRelid(rel);
4641 : AlteredTableInfo *tab;
4642 : ListCell *ltab;
4643 :
4644 1614 : foreach(ltab, *wqueue)
4645 : {
4646 430 : tab = (AlteredTableInfo *) lfirst(ltab);
4647 430 : if (tab->relid == relid)
4648 93 : return tab;
4649 : }
4650 :
4651 : /*
4652 : * Not there, so add it. Note that we make a copy of the relation's
4653 : * existing descriptor before anything interesting can happen to it.
4654 : */
4655 1184 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
4656 1184 : tab->relid = relid;
4657 1184 : tab->relkind = rel->rd_rel->relkind;
4658 1184 : tab->oldDesc = CreateTupleDescCopy(RelationGetDescr(rel));
4659 1184 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4660 1184 : tab->chgPersistence = false;
4661 :
4662 1184 : *wqueue = lappend(*wqueue, tab);
4663 :
4664 1184 : return tab;
4665 : }
4666 :
4667 : /*
4668 : * ATSimplePermissions
4669 : *
4670 : * - Ensure that it is a relation (or possibly a view)
4671 : * - Ensure this user is the owner
4672 : * - Ensure that it is not a system table
4673 : */
4674 : static void
4675 1286 : ATSimplePermissions(Relation rel, int allowed_targets)
4676 : {
4677 : int actual_target;
4678 :
4679 1286 : switch (rel->rd_rel->relkind)
4680 : {
4681 : case RELKIND_RELATION:
4682 : case RELKIND_PARTITIONED_TABLE:
4683 1118 : actual_target = ATT_TABLE;
4684 1118 : break;
4685 : case RELKIND_VIEW:
4686 44 : actual_target = ATT_VIEW;
4687 44 : break;
4688 : case RELKIND_MATVIEW:
4689 0 : actual_target = ATT_MATVIEW;
4690 0 : break;
4691 : case RELKIND_INDEX:
4692 5 : actual_target = ATT_INDEX;
4693 5 : break;
4694 : case RELKIND_COMPOSITE_TYPE:
4695 25 : actual_target = ATT_COMPOSITE_TYPE;
4696 25 : break;
4697 : case RELKIND_FOREIGN_TABLE:
4698 94 : actual_target = ATT_FOREIGN_TABLE;
4699 94 : break;
4700 : default:
4701 0 : actual_target = 0;
4702 0 : break;
4703 : }
4704 :
4705 : /* Wrong target type? */
4706 1286 : if ((actual_target & allowed_targets) == 0)
4707 4 : ATWrongRelkindError(rel, allowed_targets);
4708 :
4709 : /* Permissions checks */
4710 1282 : if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
4711 1 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
4712 1 : RelationGetRelationName(rel));
4713 :
4714 1281 : if (!allowSystemTableMods && IsSystemRelation(rel))
4715 0 : ereport(ERROR,
4716 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4717 : errmsg("permission denied: \"%s\" is a system catalog",
4718 : RelationGetRelationName(rel))));
4719 1281 : }
4720 :
4721 : /*
4722 : * ATWrongRelkindError
4723 : *
4724 : * Throw an error when a relation has been determined to be of the wrong
4725 : * type.
4726 : */
4727 : static void
4728 4 : ATWrongRelkindError(Relation rel, int allowed_targets)
4729 : {
4730 : char *msg;
4731 :
4732 4 : switch (allowed_targets)
4733 : {
4734 : case ATT_TABLE:
4735 1 : msg = _("\"%s\" is not a table");
4736 1 : break;
4737 : case ATT_TABLE | ATT_VIEW:
4738 0 : msg = _("\"%s\" is not a table or view");
4739 0 : break;
4740 : case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
4741 0 : msg = _("\"%s\" is not a table, view, or foreign table");
4742 0 : break;
4743 : case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
4744 0 : msg = _("\"%s\" is not a table, view, materialized view, or index");
4745 0 : break;
4746 : case ATT_TABLE | ATT_MATVIEW:
4747 0 : msg = _("\"%s\" is not a table or materialized view");
4748 0 : break;
4749 : case ATT_TABLE | ATT_MATVIEW | ATT_INDEX:
4750 0 : msg = _("\"%s\" is not a table, materialized view, or index");
4751 0 : break;
4752 : case ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE:
4753 0 : msg = _("\"%s\" is not a table, materialized view, or foreign table");
4754 0 : break;
4755 : case ATT_TABLE | ATT_FOREIGN_TABLE:
4756 2 : msg = _("\"%s\" is not a table or foreign table");
4757 2 : break;
4758 : case ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE:
4759 1 : msg = _("\"%s\" is not a table, composite type, or foreign table");
4760 1 : break;
4761 : case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE:
4762 0 : msg = _("\"%s\" is not a table, materialized view, index, or foreign table");
4763 0 : break;
4764 : case ATT_VIEW:
4765 0 : msg = _("\"%s\" is not a view");
4766 0 : break;
4767 : case ATT_FOREIGN_TABLE:
4768 0 : msg = _("\"%s\" is not a foreign table");
4769 0 : break;
4770 : default:
4771 : /* shouldn't get here, add all necessary cases above */
4772 0 : msg = _("\"%s\" is of the wrong type");
4773 0 : break;
4774 : }
4775 :
4776 4 : ereport(ERROR,
4777 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
4778 : errmsg(msg, RelationGetRelationName(rel))));
4779 : }
4780 :
4781 : /*
4782 : * ATSimpleRecursion
4783 : *
4784 : * Simple table recursion sufficient for most ALTER TABLE operations.
4785 : * All direct and indirect children are processed in an unspecified order.
4786 : * Note that if a child inherits from the original table via multiple
4787 : * inheritance paths, it will be visited just once.
4788 : */
4789 : static void
4790 139 : ATSimpleRecursion(List **wqueue, Relation rel,
4791 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
4792 : {
4793 : /*
4794 : * Propagate to children if desired. Only plain tables and foreign tables
4795 : * have children, so no need to search for other relkinds.
4796 : */
4797 241 : if (recurse &&
4798 120 : (rel->rd_rel->relkind == RELKIND_RELATION ||
4799 26 : rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
4800 8 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
4801 : {
4802 98 : Oid relid = RelationGetRelid(rel);
4803 : ListCell *child;
4804 : List *children;
4805 :
4806 98 : children = find_all_inheritors(relid, lockmode, NULL);
4807 :
4808 : /*
4809 : * find_all_inheritors does the recursive search of the inheritance
4810 : * hierarchy, so all we have to do is process all of the relids in the
4811 : * list that it returns.
4812 : */
4813 229 : foreach(child, children)
4814 : {
4815 131 : Oid childrelid = lfirst_oid(child);
4816 : Relation childrel;
4817 :
4818 131 : if (childrelid == relid)
4819 98 : continue;
4820 : /* find_all_inheritors already got lock */
4821 33 : childrel = relation_open(childrelid, NoLock);
4822 33 : CheckTableNotInUse(childrel, "ALTER TABLE");
4823 33 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
4824 33 : relation_close(childrel, NoLock);
4825 : }
4826 : }
4827 139 : }
4828 :
4829 : /*
4830 : * ATTypedTableRecursion
4831 : *
4832 : * Propagate ALTER TYPE operations to the typed tables of that type.
4833 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
4834 : * recursion to inheritance children of the typed tables.
4835 : */
4836 : static void
4837 22 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
4838 : LOCKMODE lockmode)
4839 : {
4840 : ListCell *child;
4841 : List *children;
4842 :
4843 22 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
4844 :
4845 44 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
4846 22 : RelationGetRelationName(rel),
4847 : cmd->behavior);
4848 :
4849 24 : foreach(child, children)
4850 : {
4851 5 : Oid childrelid = lfirst_oid(child);
4852 : Relation childrel;
4853 :
4854 5 : childrel = relation_open(childrelid, lockmode);
4855 5 : CheckTableNotInUse(childrel, "ALTER TABLE");
4856 5 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode);
4857 5 : relation_close(childrel, NoLock);
4858 : }
4859 19 : }
4860 :
4861 :
4862 : /*
4863 : * find_composite_type_dependencies
4864 : *
4865 : * Check to see if the type "typeOid" is being used as a column in some table
4866 : * (possibly nested several levels deep in composite types, arrays, etc!).
4867 : * Eventually, we'd like to propagate the check or rewrite operation
4868 : * into such tables, but for now, just error out if we find any.
4869 : *
4870 : * Caller should provide either the associated relation of a rowtype,
4871 : * or a type name (not both) for use in the error message, if any.
4872 : *
4873 : * Note that "typeOid" is not necessarily a composite type; it could also be
4874 : * another container type such as an array or range, or a domain over one of
4875 : * these things. The name of this function is therefore somewhat historical,
4876 : * but it's not worth changing.
4877 : *
4878 : * We assume that functions and views depending on the type are not reasons
4879 : * to reject the ALTER. (How safe is this really?)
4880 : */
4881 : void
4882 242 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
4883 : const char *origTypeName)
4884 : {
4885 : Relation depRel;
4886 : ScanKeyData key[2];
4887 : SysScanDesc depScan;
4888 : HeapTuple depTup;
4889 :
4890 : /* since this function recurses, it could be driven to stack overflow */
4891 242 : check_stack_depth();
4892 :
4893 : /*
4894 : * We scan pg_depend to find those things that depend on the given type.
4895 : * (We assume we can ignore refobjsubid for a type.)
4896 : */
4897 242 : depRel = heap_open(DependRelationId, AccessShareLock);
4898 :
4899 242 : ScanKeyInit(&key[0],
4900 : Anum_pg_depend_refclassid,
4901 : BTEqualStrategyNumber, F_OIDEQ,
4902 : ObjectIdGetDatum(TypeRelationId));
4903 242 : ScanKeyInit(&key[1],
4904 : Anum_pg_depend_refobjid,
4905 : BTEqualStrategyNumber, F_OIDEQ,
4906 : ObjectIdGetDatum(typeOid));
4907 :
4908 242 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
4909 : NULL, 2, key);
4910 :
4911 615 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
4912 : {
4913 143 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
4914 : Relation rel;
4915 : Form_pg_attribute att;
4916 :
4917 : /* Check for directly dependent types */
4918 143 : if (pg_depend->classid == TypeRelationId)
4919 : {
4920 : /*
4921 : * This must be an array, domain, or range containing the given
4922 : * type, so recursively check for uses of this type. Note that
4923 : * any error message will mention the original type not the
4924 : * container; this is intentional.
4925 : */
4926 118 : find_composite_type_dependencies(pg_depend->objid,
4927 : origRelation, origTypeName);
4928 115 : continue;
4929 : }
4930 :
4931 : /* Else, ignore dependees that aren't user columns of relations */
4932 : /* (we assume system columns are never of interesting types) */
4933 47 : if (pg_depend->classid != RelationRelationId ||
4934 22 : pg_depend->objsubid <= 0)
4935 16 : continue;
4936 :
4937 9 : rel = relation_open(pg_depend->objid, AccessShareLock);
4938 9 : att = TupleDescAttr(rel->rd_att, pg_depend->objsubid - 1);
4939 :
4940 9 : if (rel->rd_rel->relkind == RELKIND_RELATION ||
4941 0 : rel->rd_rel->relkind == RELKIND_MATVIEW ||
4942 0 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4943 : {
4944 9 : if (origTypeName)
4945 4 : ereport(ERROR,
4946 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4947 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
4948 : origTypeName,
4949 : RelationGetRelationName(rel),
4950 : NameStr(att->attname))));
4951 5 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
4952 2 : ereport(ERROR,
4953 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4954 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
4955 : RelationGetRelationName(origRelation),
4956 : RelationGetRelationName(rel),
4957 : NameStr(att->attname))));
4958 3 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4959 1 : ereport(ERROR,
4960 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4961 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
4962 : RelationGetRelationName(origRelation),
4963 : RelationGetRelationName(rel),
4964 : NameStr(att->attname))));
4965 : else
4966 2 : ereport(ERROR,
4967 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4968 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
4969 : RelationGetRelationName(origRelation),
4970 : RelationGetRelationName(rel),
4971 : NameStr(att->attname))));
4972 : }
4973 0 : else if (OidIsValid(rel->rd_rel->reltype))
4974 : {
4975 : /*
4976 : * A view or composite type itself isn't a problem, but we must
4977 : * recursively check for indirect dependencies via its rowtype.
4978 : */
4979 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
4980 : origRelation, origTypeName);
4981 : }
4982 :
4983 0 : relation_close(rel, AccessShareLock);
4984 : }
4985 :
4986 230 : systable_endscan(depScan);
4987 :
4988 230 : relation_close(depRel, AccessShareLock);
4989 230 : }
4990 :
4991 :
4992 : /*
4993 : * find_typed_table_dependencies
4994 : *
4995 : * Check to see if a composite type is being used as the type of a
4996 : * typed table. Abort if any are found and behavior is RESTRICT.
4997 : * Else return the list of tables.
4998 : */
4999 : static List *
5000 26 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
5001 : {
5002 : Relation classRel;
5003 : ScanKeyData key[1];
5004 : HeapScanDesc scan;
5005 : HeapTuple tuple;
5006 26 : List *result = NIL;
5007 :
5008 26 : classRel = heap_open(RelationRelationId, AccessShareLock);
5009 :
5010 26 : ScanKeyInit(&key[0],
5011 : Anum_pg_class_reloftype,
5012 : BTEqualStrategyNumber, F_OIDEQ,
5013 : ObjectIdGetDatum(typeOid));
5014 :
5015 26 : scan = heap_beginscan_catalog(classRel, 1, key);
5016 :
5017 58 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
5018 : {
5019 10 : if (behavior == DROP_RESTRICT)
5020 4 : ereport(ERROR,
5021 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
5022 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
5023 : typeName),
5024 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
5025 : else
5026 6 : result = lappend_oid(result, HeapTupleGetOid(tuple));
5027 : }
5028 :
5029 22 : heap_endscan(scan);
5030 22 : heap_close(classRel, AccessShareLock);
5031 :
5032 22 : return result;
5033 : }
5034 :
5035 :
5036 : /*
5037 : * check_of_type
5038 : *
5039 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
5040 : * isn't suitable, throw an error. Currently, we require that the type
5041 : * originated with CREATE TYPE AS. We could support any row type, but doing so
5042 : * would require handling a number of extra corner cases in the DDL commands.
5043 : */
5044 : void
5045 24 : check_of_type(HeapTuple typetuple)
5046 : {
5047 24 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
5048 24 : bool typeOk = false;
5049 :
5050 24 : if (typ->typtype == TYPTYPE_COMPOSITE)
5051 : {
5052 : Relation typeRelation;
5053 :
5054 24 : Assert(OidIsValid(typ->typrelid));
5055 24 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
5056 24 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
5057 :
5058 : /*
5059 : * Close the parent rel, but keep our AccessShareLock on it until xact
5060 : * commit. That will prevent someone else from deleting or ALTERing
5061 : * the type before the typed table creation/conversion commits.
5062 : */
5063 24 : relation_close(typeRelation, NoLock);
5064 : }
5065 24 : if (!typeOk)
5066 1 : ereport(ERROR,
5067 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5068 : errmsg("type %s is not a composite type",
5069 : format_type_be(HeapTupleGetOid(typetuple)))));
5070 23 : }
5071 :
5072 :
5073 : /*
5074 : * ALTER TABLE ADD COLUMN
5075 : *
5076 : * Adds an additional attribute to a relation making the assumption that
5077 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
5078 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
5079 : * AlterTableCmd's.
5080 : *
5081 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
5082 : * have to decide at runtime whether to recurse or not depending on whether we
5083 : * actually add a column or merely merge with an existing column. (We can't
5084 : * check this in a static pre-pass because it won't handle multiple inheritance
5085 : * situations correctly.)
5086 : */
5087 : static void
5088 160 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
5089 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode)
5090 : {
5091 160 : if (rel->rd_rel->reloftype && !recursing)
5092 1 : ereport(ERROR,
5093 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5094 : errmsg("cannot add column to typed table")));
5095 :
5096 159 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
5097 8 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
5098 :
5099 158 : if (recurse && !is_view)
5100 147 : cmd->subtype = AT_AddColumnRecurse;
5101 158 : }
5102 :
5103 : /*
5104 : * Add a column to a table; this handles the AT_AddOids cases as well. The
5105 : * return value is the address of the new column in the parent relation.
5106 : */
5107 : static ObjectAddress
5108 202 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
5109 : ColumnDef *colDef, bool isOid,
5110 : bool recurse, bool recursing,
5111 : bool if_not_exists, LOCKMODE lockmode)
5112 : {
5113 202 : Oid myrelid = RelationGetRelid(rel);
5114 : Relation pgclass,
5115 : attrdesc;
5116 : HeapTuple reltup;
5117 : FormData_pg_attribute attribute;
5118 : int newattnum;
5119 : char relkind;
5120 : HeapTuple typeTuple;
5121 : Oid typeOid;
5122 : int32 typmod;
5123 : Oid collOid;
5124 : Form_pg_type tform;
5125 : Expr *defval;
5126 : List *children;
5127 : ListCell *child;
5128 : AclResult aclresult;
5129 : ObjectAddress address;
5130 :
5131 : /* At top level, permission check was done in ATPrepCmd, else do it */
5132 202 : if (recursing)
5133 45 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5134 :
5135 202 : if (rel->rd_rel->relispartition && !recursing)
5136 2 : ereport(ERROR,
5137 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5138 : errmsg("cannot add column to a partition")));
5139 :
5140 200 : attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
5141 :
5142 : /*
5143 : * Are we adding the column to a recursion child? If so, check whether to
5144 : * merge with an existing definition for the column. If we do merge, we
5145 : * must not recurse. Children will already have the column, and recursing
5146 : * into them would mess up attinhcount.
5147 : */
5148 200 : if (colDef->inhcount > 0)
5149 : {
5150 : HeapTuple tuple;
5151 :
5152 : /* Does child already have a column by this name? */
5153 45 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
5154 45 : if (HeapTupleIsValid(tuple))
5155 : {
5156 5 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
5157 : Oid ctypeId;
5158 : int32 ctypmod;
5159 : Oid ccollid;
5160 :
5161 : /* Child column must match on type, typmod, and collation */
5162 5 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
5163 10 : if (ctypeId != childatt->atttypid ||
5164 5 : ctypmod != childatt->atttypmod)
5165 0 : ereport(ERROR,
5166 : (errcode(ERRCODE_DATATYPE_MISMATCH),
5167 : errmsg("child table \"%s\" has different type for column \"%s\"",
5168 : RelationGetRelationName(rel), colDef->colname)));
5169 5 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
5170 5 : if (ccollid != childatt->attcollation)
5171 0 : ereport(ERROR,
5172 : (errcode(ERRCODE_COLLATION_MISMATCH),
5173 : errmsg("child table \"%s\" has different collation for column \"%s\"",
5174 : RelationGetRelationName(rel), colDef->colname),
5175 : errdetail("\"%s\" versus \"%s\"",
5176 : get_collation_name(ccollid),
5177 : get_collation_name(childatt->attcollation))));
5178 :
5179 : /* If it's OID, child column must actually be OID */
5180 5 : if (isOid && childatt->attnum != ObjectIdAttributeNumber)
5181 0 : ereport(ERROR,
5182 : (errcode(ERRCODE_DATATYPE_MISMATCH),
5183 : errmsg("child table \"%s\" has a conflicting \"%s\" column",
5184 : RelationGetRelationName(rel), colDef->colname)));
5185 :
5186 : /* Bump the existing child att's inhcount */
5187 5 : childatt->attinhcount++;
5188 5 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
5189 :
5190 5 : heap_freetuple(tuple);
5191 :
5192 : /* Inform the user about the merge */
5193 5 : ereport(NOTICE,
5194 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
5195 : colDef->colname, RelationGetRelationName(rel))));
5196 :
5197 5 : heap_close(attrdesc, RowExclusiveLock);
5198 5 : return InvalidObjectAddress;
5199 : }
5200 : }
5201 :
5202 195 : pgclass = heap_open(RelationRelationId, RowExclusiveLock);
5203 :
5204 195 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
5205 195 : if (!HeapTupleIsValid(reltup))
5206 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
5207 195 : relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
5208 :
5209 : /*
5210 : * Cannot add identity column if table has children, because identity does
5211 : * not inherit. (Adding column and identity separately will work.)
5212 : */
5213 195 : if (colDef->identity &&
5214 1 : recurse &&
5215 1 : find_inheritance_children(myrelid, NoLock) != NIL)
5216 1 : ereport(ERROR,
5217 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5218 : errmsg("cannot recursively add identity column to table that has child tables")));
5219 :
5220 : /* skip if the name already exists and if_not_exists is true */
5221 194 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
5222 : {
5223 6 : heap_close(attrdesc, RowExclusiveLock);
5224 6 : heap_freetuple(reltup);
5225 6 : heap_close(pgclass, RowExclusiveLock);
5226 6 : return InvalidObjectAddress;
5227 : }
5228 :
5229 : /* Determine the new attribute's number */
5230 184 : if (isOid)
5231 11 : newattnum = ObjectIdAttributeNumber;
5232 : else
5233 : {
5234 173 : newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
5235 173 : if (newattnum > MaxHeapAttributeNumber)
5236 0 : ereport(ERROR,
5237 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
5238 : errmsg("tables can have at most %d columns",
5239 : MaxHeapAttributeNumber)));
5240 : }
5241 :
5242 184 : typeTuple = typenameType(NULL, colDef->typeName, &typmod);
5243 184 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
5244 184 : typeOid = HeapTupleGetOid(typeTuple);
5245 :
5246 184 : aclresult = pg_type_aclcheck(typeOid, GetUserId(), ACL_USAGE);
5247 184 : if (aclresult != ACLCHECK_OK)
5248 2 : aclcheck_error_type(aclresult, typeOid);
5249 :
5250 182 : collOid = GetColumnDefCollation(NULL, colDef, typeOid);
5251 :
5252 : /* make sure datatype is legal for a column */
5253 182 : CheckAttributeType(colDef->colname, typeOid, collOid,
5254 182 : list_make1_oid(rel->rd_rel->reltype),
5255 : false);
5256 :
5257 : /* construct new attribute's pg_attribute entry */
5258 178 : attribute.attrelid = myrelid;
5259 178 : namestrcpy(&(attribute.attname), colDef->colname);
5260 178 : attribute.atttypid = typeOid;
5261 178 : attribute.attstattarget = (newattnum > 0) ? -1 : 0;
5262 178 : attribute.attlen = tform->typlen;
5263 178 : attribute.attcacheoff = -1;
5264 178 : attribute.atttypmod = typmod;
5265 178 : attribute.attnum = newattnum;
5266 178 : attribute.attbyval = tform->typbyval;
5267 178 : attribute.attndims = list_length(colDef->typeName->arrayBounds);
5268 178 : attribute.attstorage = tform->typstorage;
5269 178 : attribute.attalign = tform->typalign;
5270 178 : attribute.attnotnull = colDef->is_not_null;
5271 178 : attribute.atthasdef = false;
5272 178 : attribute.attidentity = colDef->identity;
5273 178 : attribute.attisdropped = false;
5274 178 : attribute.attislocal = colDef->is_local;
5275 178 : attribute.attinhcount = colDef->inhcount;
5276 178 : attribute.attcollation = collOid;
5277 : /* attribute.attacl is handled by InsertPgAttributeTuple */
5278 :
5279 178 : ReleaseSysCache(typeTuple);
5280 :
5281 178 : InsertPgAttributeTuple(attrdesc, &attribute, NULL);
5282 :
5283 178 : heap_close(attrdesc, RowExclusiveLock);
5284 :
5285 : /*
5286 : * Update pg_class tuple as appropriate
5287 : */
5288 178 : if (isOid)
5289 11 : ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true;
5290 : else
5291 167 : ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
5292 :
5293 178 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
5294 :
5295 178 : heap_freetuple(reltup);
5296 :
5297 : /* Post creation hook for new attribute */
5298 178 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
5299 :
5300 178 : heap_close(pgclass, RowExclusiveLock);
5301 :
5302 : /* Make the attribute's catalog entry visible */
5303 178 : CommandCounterIncrement();
5304 :
5305 : /*
5306 : * Store the DEFAULT, if any, in the catalogs
5307 : */
5308 178 : if (colDef->raw_default)
5309 : {
5310 : RawColumnDefault *rawEnt;
5311 :
5312 15 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
5313 15 : rawEnt->attnum = attribute.attnum;
5314 15 : rawEnt->raw_default = copyObject(colDef->raw_default);
5315 :
5316 : /*
5317 : * This function is intended for CREATE TABLE, so it processes a
5318 : * _list_ of defaults, but we just do one.
5319 : */
5320 15 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
5321 : false, true, false);
5322 :
5323 : /* Make the additional catalog changes visible */
5324 15 : CommandCounterIncrement();
5325 : }
5326 :
5327 : /*
5328 : * Tell Phase 3 to fill in the default expression, if there is one.
5329 : *
5330 : * If there is no default, Phase 3 doesn't have to do anything, because
5331 : * that effectively means that the default is NULL. The heap tuple access
5332 : * routines always check for attnum > # of attributes in tuple, and return
5333 : * NULL if so, so without any modification of the tuple data we will get
5334 : * the effect of NULL values in the new column.
5335 : *
5336 : * An exception occurs when the new column is of a domain type: the domain
5337 : * might have a NOT NULL constraint, or a check constraint that indirectly
5338 : * rejects nulls. If there are any domain constraints then we construct
5339 : * an explicit NULL default value that will be passed through
5340 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
5341 : * rewriting the table which we really don't have to do, but the present
5342 : * design of domain processing doesn't offer any simple way of checking
5343 : * the constraints more directly.)
5344 : *
5345 : * Note: we use build_column_default, and not just the cooked default
5346 : * returned by AddRelationNewConstraints, so that the right thing happens
5347 : * when a datatype's default applies.
5348 : *
5349 : * We skip this step completely for views and foreign tables. For a view,
5350 : * we can only get here from CREATE OR REPLACE VIEW, which historically
5351 : * doesn't set up defaults, not even for domain-typed columns. And in any
5352 : * case we mustn't invoke Phase 3 on a view or foreign table, since they
5353 : * have no storage.
5354 : */
5355 178 : if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE
5356 171 : && relkind != RELKIND_FOREIGN_TABLE && attribute.attnum > 0)
5357 : {
5358 143 : defval = (Expr *) build_column_default(rel, attribute.attnum);
5359 :
5360 143 : if (!defval && DomainHasConstraints(typeOid))
5361 : {
5362 : Oid baseTypeId;
5363 : int32 baseTypeMod;
5364 : Oid baseTypeColl;
5365 :
5366 1 : baseTypeMod = typmod;
5367 1 : baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
5368 1 : baseTypeColl = get_typcollation(baseTypeId);
5369 1 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
5370 1 : defval = (Expr *) coerce_to_target_type(NULL,
5371 : (Node *) defval,
5372 : baseTypeId,
5373 : typeOid,
5374 : typmod,
5375 : COERCION_ASSIGNMENT,
5376 : COERCE_IMPLICIT_CAST,
5377 : -1);
5378 1 : if (defval == NULL) /* should not happen */
5379 0 : elog(ERROR, "failed to coerce base type to domain");
5380 : }
5381 :
5382 143 : if (defval)
5383 : {
5384 : NewColumnValue *newval;
5385 :
5386 13 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
5387 13 : newval->attnum = attribute.attnum;
5388 13 : newval->expr = expression_planner(defval);
5389 :
5390 13 : tab->newvals = lappend(tab->newvals, newval);
5391 13 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
5392 : }
5393 :
5394 : /*
5395 : * If the new column is NOT NULL, tell Phase 3 it needs to test that.
5396 : * (Note we don't do this for an OID column. OID will be marked not
5397 : * null, but since it's filled specially, there's no need to test
5398 : * anything.)
5399 : */
5400 143 : tab->new_notnull |= colDef->is_not_null;
5401 : }
5402 :
5403 : /*
5404 : * If we are adding an OID column, we have to tell Phase 3 to rewrite the
5405 : * table to fix that.
5406 : */
5407 178 : if (isOid)
5408 11 : tab->rewrite |= AT_REWRITE_ALTER_OID;
5409 :
5410 : /*
5411 : * Add needed dependency entries for the new column.
5412 : */
5413 178 : add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
5414 178 : add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
5415 :
5416 : /*
5417 : * Propagate to children as appropriate. Unlike most other ALTER
5418 : * routines, we have to do this one level of recursion at a time; we can't
5419 : * use find_all_inheritors to do it in one pass.
5420 : */
5421 178 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
5422 :
5423 : /*
5424 : * If we are told not to recurse, there had better not be any child
5425 : * tables; else the addition would put them out of step.
5426 : */
5427 178 : if (children && !recurse)
5428 2 : ereport(ERROR,
5429 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5430 : errmsg("column must be added to child tables too")));
5431 :
5432 : /* Children should see column as singly inherited */
5433 176 : if (!recursing)
5434 : {
5435 136 : colDef = copyObject(colDef);
5436 136 : colDef->inhcount = 1;
5437 136 : colDef->is_local = false;
5438 : }
5439 :
5440 221 : foreach(child, children)
5441 : {
5442 45 : Oid childrelid = lfirst_oid(child);
5443 : Relation childrel;
5444 : AlteredTableInfo *childtab;
5445 :
5446 : /* find_inheritance_children already got lock */
5447 45 : childrel = heap_open(childrelid, NoLock);
5448 45 : CheckTableNotInUse(childrel, "ALTER TABLE");
5449 :
5450 : /* Find or create work queue entry for this table */
5451 45 : childtab = ATGetQueueEntry(wqueue, childrel);
5452 :
5453 : /* Recurse to child; return value is ignored */
5454 45 : ATExecAddColumn(wqueue, childtab, childrel,
5455 : colDef, isOid, recurse, true,
5456 : if_not_exists, lockmode);
5457 :
5458 45 : heap_close(childrel, NoLock);
5459 : }
5460 :
5461 176 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
5462 176 : return address;
5463 : }
5464 :
5465 : /*
5466 : * If a new or renamed column will collide with the name of an existing
5467 : * column and if_not_exists is false then error out, else do nothing.
5468 : */
5469 : static bool
5470 260 : check_for_column_name_collision(Relation rel, const char *colname,
5471 : bool if_not_exists)
5472 : {
5473 : HeapTuple attTuple;
5474 : int attnum;
5475 :
5476 : /*
5477 : * this test is deliberately not attisdropped-aware, since if one tries to
5478 : * add a column matching a dropped column name, it's gonna fail anyway.
5479 : */
5480 260 : attTuple = SearchSysCache2(ATTNAME,
5481 : ObjectIdGetDatum(RelationGetRelid(rel)),
5482 : PointerGetDatum(colname));
5483 260 : if (!HeapTupleIsValid(attTuple))
5484 248 : return true;
5485 :
5486 12 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
5487 12 : ReleaseSysCache(attTuple);
5488 :
5489 : /*
5490 : * We throw a different error message for conflicts with system column
5491 : * names, since they are normally not shown and the user might otherwise
5492 : * be confused about the reason for the conflict.
5493 : */
5494 12 : if (attnum <= 0)
5495 2 : ereport(ERROR,
5496 : (errcode(ERRCODE_DUPLICATE_COLUMN),
5497 : errmsg("column name \"%s\" conflicts with a system column name",
5498 : colname)));
5499 : else
5500 : {
5501 10 : if (if_not_exists)
5502 : {
5503 6 : ereport(NOTICE,
5504 : (errcode(ERRCODE_DUPLICATE_COLUMN),
5505 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
5506 : colname, RelationGetRelationName(rel))));
5507 6 : return false;
5508 : }
5509 :
5510 4 : ereport(ERROR,
5511 : (errcode(ERRCODE_DUPLICATE_COLUMN),
5512 : errmsg("column \"%s\" of relation \"%s\" already exists",
5513 : colname, RelationGetRelationName(rel))));
5514 : }
5515 :
5516 : return true;
5517 : }
5518 :
5519 : /*
5520 : * Install a column's dependency on its datatype.
5521 : */
5522 : static void
5523 246 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
5524 : {
5525 : ObjectAddress myself,
5526 : referenced;
5527 :
5528 246 : myself.classId = RelationRelationId;
5529 246 : myself.objectId = relid;
5530 246 : myself.objectSubId = attnum;
5531 246 : referenced.classId = TypeRelationId;
5532 246 : referenced.objectId = typid;
5533 246 : referenced.objectSubId = 0;
5534 246 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
5535 246 : }
5536 :
5537 : /*
5538 : * Install a column's dependency on its collation.
5539 : */
5540 : static void
5541 246 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
5542 : {
5543 : ObjectAddress myself,
5544 : referenced;
5545 :
5546 : /* We know the default collation is pinned, so don't bother recording it */
5547 246 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
5548 : {
5549 1 : myself.classId = RelationRelationId;
5550 1 : myself.objectId = relid;
5551 1 : myself.objectSubId = attnum;
5552 1 : referenced.classId = CollationRelationId;
5553 1 : referenced.objectId = collid;
5554 1 : referenced.objectSubId = 0;
5555 1 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
5556 : }
5557 246 : }
5558 :
5559 : /*
5560 : * ALTER TABLE SET WITH OIDS
5561 : *
5562 : * Basically this is an ADD COLUMN for the special OID column. We have
5563 : * to cons up a ColumnDef node because the ADD COLUMN code needs one.
5564 : */
5565 : static void
5566 9 : ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode)
5567 : {
5568 : /* If we're recursing to a child table, the ColumnDef is already set up */
5569 9 : if (cmd->def == NULL)
5570 : {
5571 9 : ColumnDef *cdef = makeNode(ColumnDef);
5572 :
5573 9 : cdef->colname = pstrdup("oid");
5574 9 : cdef->typeName = makeTypeNameFromOid(OIDOID, -1);
5575 9 : cdef->inhcount = 0;
5576 9 : cdef->is_local = true;
5577 9 : cdef->is_not_null = true;
5578 9 : cdef->storage = 0;
5579 9 : cdef->location = -1;
5580 9 : cmd->def = (Node *) cdef;
5581 : }
5582 9 : ATPrepAddColumn(wqueue, rel, recurse, false, false, cmd, lockmode);
5583 :
5584 9 : if (recurse)
5585 9 : cmd->subtype = AT_AddOidsRecurse;
5586 9 : }
5587 :
5588 : /*
5589 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
5590 : *
5591 : * Return the address of the modified column. If the column was already
5592 : * nullable, InvalidObjectAddress is returned.
5593 : */
5594 :
5595 : static void
5596 19 : ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
5597 : {
5598 : /*
5599 : * If the parent is a partitioned table, like check constraints, we do not
5600 : * support removing the NOT NULL while partitions exist.
5601 : */
5602 19 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5603 : {
5604 2 : PartitionDesc partdesc = RelationGetPartitionDesc(rel);
5605 :
5606 2 : Assert(partdesc != NULL);
5607 2 : if (partdesc->nparts > 0 && !recurse && !recursing)
5608 1 : ereport(ERROR,
5609 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5610 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
5611 : errhint("Do not specify the ONLY keyword.")));
5612 : }
5613 18 : }
5614 : static ObjectAddress
5615 18 : ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
5616 : {
5617 : HeapTuple tuple;
5618 : AttrNumber attnum;
5619 : Relation attr_rel;
5620 : List *indexoidlist;
5621 : ListCell *indexoidscan;
5622 : ObjectAddress address;
5623 :
5624 : /*
5625 : * lookup the attribute
5626 : */
5627 18 : attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
5628 :
5629 18 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
5630 :
5631 18 : if (!HeapTupleIsValid(tuple))
5632 3 : ereport(ERROR,
5633 : (errcode(ERRCODE_UNDEFINED_COLUMN),
5634 : errmsg("column \"%s\" of relation \"%s\" does not exist",
5635 : colName, RelationGetRelationName(rel))));
5636 :
5637 15 : attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
5638 :
5639 : /* Prevent them from altering a system attribute */
5640 15 : if (attnum <= 0)
5641 1 : ereport(ERROR,
5642 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5643 : errmsg("cannot alter system column \"%s\"",
5644 : colName)));
5645 :
5646 14 : if (get_attidentity(RelationGetRelid(rel), attnum))
5647 1 : ereport(ERROR,
5648 : (errcode(ERRCODE_SYNTAX_ERROR),
5649 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
5650 : colName, RelationGetRelationName(rel))));
5651 :
5652 : /*
5653 : * Check that the attribute is not in a primary key
5654 : *
5655 : * Note: we'll throw error even if the pkey index is not valid.
5656 : */
5657 :
5658 : /* Loop over all indexes on the relation */
5659 13 : indexoidlist = RelationGetIndexList(rel);
5660 :
5661 13 : foreach(indexoidscan, indexoidlist)
5662 : {
5663 1 : Oid indexoid = lfirst_oid(indexoidscan);
5664 : HeapTuple indexTuple;
5665 : Form_pg_index indexStruct;
5666 : int i;
5667 :
5668 1 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
5669 1 : if (!HeapTupleIsValid(indexTuple))
5670 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
5671 1 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
5672 :
5673 : /* If the index is not a primary key, skip the check */
5674 1 : if (indexStruct->indisprimary)
5675 : {
5676 : /*
5677 : * Loop over each attribute in the primary key and see if it
5678 : * matches the to-be-altered attribute
5679 : */
5680 1 : for (i = 0; i < indexStruct->indnatts; i++)
5681 : {
5682 1 : if (indexStruct->indkey.values[i] == attnum)
5683 1 : ereport(ERROR,
5684 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5685 : errmsg("column \"%s\" is in a primary key",
5686 : colName)));
5687 : }
5688 : }
5689 :
5690 0 : ReleaseSysCache(indexTuple);
5691 : }
5692 :
5693 12 : list_free(indexoidlist);
5694 :
5695 : /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
5696 12 : if (rel->rd_rel->relispartition)
5697 : {
5698 2 : Oid parentId = get_partition_parent(RelationGetRelid(rel));
5699 2 : Relation parent = heap_open(parentId, AccessShareLock);
5700 2 : TupleDesc tupDesc = RelationGetDescr(parent);
5701 : AttrNumber parent_attnum;
5702 :
5703 2 : parent_attnum = get_attnum(parentId, colName);
5704 2 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
5705 2 : ereport(ERROR,
5706 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5707 : errmsg("column \"%s\" is marked NOT NULL in parent table",
5708 : colName)));
5709 0 : heap_close(parent, AccessShareLock);
5710 : }
5711 :
5712 : /*
5713 : * Okay, actually perform the catalog change ... if needed
5714 : */
5715 10 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
5716 : {
5717 10 : ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE;
5718 :
5719 10 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
5720 :
5721 10 : ObjectAddressSubSet(address, RelationRelationId,
5722 : RelationGetRelid(rel), attnum);
5723 : }
5724 : else
5725 0 : address = InvalidObjectAddress;
5726 :
5727 10 : InvokeObjectPostAlterHook(RelationRelationId,
5728 : RelationGetRelid(rel), attnum);
5729 :
5730 10 : heap_close(attr_rel, RowExclusiveLock);
5731 :
5732 10 : return address;
5733 : }
5734 :
5735 : /*
5736 : * ALTER TABLE ALTER COLUMN SET NOT NULL
5737 : *
5738 : * Return the address of the modified column. If the column was already NOT
5739 : * NULL, InvalidObjectAddress is returned.
5740 : */
5741 :
5742 : static void
5743 54 : ATPrepSetNotNull(Relation rel, bool recurse, bool recursing)
5744 : {
5745 : /*
5746 : * If the parent is a partitioned table, like check constraints, NOT NULL
5747 : * constraints must be added to the child tables. Complain if requested
5748 : * otherwise and partitions exist.
5749 : */
5750 54 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5751 : {
5752 8 : PartitionDesc partdesc = RelationGetPartitionDesc(rel);
5753 :
5754 8 : if (partdesc && partdesc->nparts > 0 && !recurse && !recursing)
5755 1 : ereport(ERROR,
5756 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5757 : errmsg("cannot add constraint to only the partitioned table when partitions exist"),
5758 : errhint("Do not specify the ONLY keyword.")));
5759 : }
5760 53 : }
5761 :
5762 : static ObjectAddress
5763 53 : ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
5764 : const char *colName, LOCKMODE lockmode)
5765 : {
5766 : HeapTuple tuple;
5767 : AttrNumber attnum;
5768 : Relation attr_rel;
5769 : ObjectAddress address;
5770 :
5771 : /*
5772 : * lookup the attribute
5773 : */
5774 53 : attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
5775 :
5776 53 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
5777 :
5778 53 : if (!HeapTupleIsValid(tuple))
5779 3 : ereport(ERROR,
5780 : (errcode(ERRCODE_UNDEFINED_COLUMN),
5781 : errmsg("column \"%s\" of relation \"%s\" does not exist",
5782 : colName, RelationGetRelationName(rel))));
5783 :
5784 50 : attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
5785 :
5786 : /* Prevent them from altering a system attribute */
5787 50 : if (attnum <= 0)
5788 1 : ereport(ERROR,
5789 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5790 : errmsg("cannot alter system column \"%s\"",
5791 : colName)));
5792 :
5793 : /*
5794 : * Okay, actually perform the catalog change ... if needed
5795 : */
5796 49 : if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
5797 : {
5798 43 : ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
5799 :
5800 43 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
5801 :
5802 : /* Tell Phase 3 it needs to test the constraint */
5803 43 : tab->new_notnull = true;
5804 :
5805 43 : ObjectAddressSubSet(address, RelationRelationId,
5806 : RelationGetRelid(rel), attnum);
5807 : }
5808 : else
5809 6 : address = InvalidObjectAddress;
5810 :
5811 49 : InvokeObjectPostAlterHook(RelationRelationId,
5812 : RelationGetRelid(rel), attnum);
5813 :
5814 49 : heap_close(attr_rel, RowExclusiveLock);
5815 :
5816 49 : return address;
5817 : }
5818 :
5819 : /*
5820 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
5821 : *
5822 : * Return the address of the affected column.
5823 : */
5824 : static ObjectAddress
5825 36 : ATExecColumnDefault(Relation rel, const char *colName,
5826 : Node *newDefault, LOCKMODE lockmode)
5827 : {
5828 : AttrNumber attnum;
5829 : ObjectAddress address;
5830 :
5831 : /*
5832 : * get the number of the attribute
5833 : */
5834 36 : attnum = get_attnum(RelationGetRelid(rel), colName);
5835 36 : if (attnum == InvalidAttrNumber)
5836 5 : ereport(ERROR,
5837 : (errcode(ERRCODE_UNDEFINED_COLUMN),
5838 : errmsg("column \"%s\" of relation \"%s\" does not exist",
5839 : colName, RelationGetRelationName(rel))));
5840 :
5841 : /* Prevent them from altering a system attribute */
5842 31 : if (attnum <= 0)
5843 0 : ereport(ERROR,
5844 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5845 : errmsg("cannot alter system column \"%s\"",
5846 : colName)));
5847 :
5848 31 : if (get_attidentity(RelationGetRelid(rel), attnum))
5849 1 : ereport(ERROR,
5850 : (errcode(ERRCODE_SYNTAX_ERROR),
5851 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
5852 : colName, RelationGetRelationName(rel)),
5853 : newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
5854 :
5855 : /*
5856 : * Remove any old default for the column. We use RESTRICT here for
5857 : * safety, but at present we do not expect anything to depend on the
5858 : * default.
5859 : *
5860 : * We treat removing the existing default as an internal operation when it
5861 : * is preparatory to adding a new default, but as a user-initiated
5862 : * operation when the user asked for a drop.
5863 : */
5864 30 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
5865 : newDefault == NULL ? false : true);
5866 :
5867 30 : if (newDefault)
5868 : {
5869 : /* SET DEFAULT */
5870 : RawColumnDefault *rawEnt;
5871 :
5872 19 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
5873 19 : rawEnt->attnum = attnum;
5874 19 : rawEnt->raw_default = newDefault;
5875 :
5876 : /*
5877 : * This function is intended for CREATE TABLE, so it processes a
5878 : * _list_ of defaults, but we just do one.
5879 : */
5880 19 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
5881 : false, true, false);
5882 : }
5883 :
5884 29 : ObjectAddressSubSet(address, RelationRelationId,
5885 : RelationGetRelid(rel), attnum);
5886 29 : return address;
5887 : }
5888 :
5889 : /*
5890 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
5891 : *
5892 : * Return the address of the affected column.
5893 : */
5894 : static ObjectAddress
5895 6 : ATExecAddIdentity(Relation rel, const char *colName,
5896 : Node *def, LOCKMODE lockmode)
5897 : {
5898 : Relation attrelation;
5899 : HeapTuple tuple;
5900 : Form_pg_attribute attTup;
5901 : AttrNumber attnum;
5902 : ObjectAddress address;
5903 6 : ColumnDef *cdef = castNode(ColumnDef, def);
5904 :
5905 6 : attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
5906 :
5907 6 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
5908 6 : if (!HeapTupleIsValid(tuple))
5909 0 : ereport(ERROR,
5910 : (errcode(ERRCODE_UNDEFINED_COLUMN),
5911 : errmsg("column \"%s\" of relation \"%s\" does not exist",
5912 : colName, RelationGetRelationName(rel))));
5913 6 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
5914 6 : attnum = attTup->attnum;
5915 :
5916 : /* Can't alter a system attribute */
5917 6 : if (attnum <= 0)
5918 0 : ereport(ERROR,
5919 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5920 : errmsg("cannot alter system column \"%s\"",
5921 : colName)));
5922 :
5923 : /*
5924 : * Creating a column as identity implies NOT NULL, so adding the identity
5925 : * to an existing column that is not NOT NULL would create a state that
5926 : * cannot be reproduced without contortions.
5927 : */
5928 6 : if (!attTup->attnotnull)
5929 1 : ereport(ERROR,
5930 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
5931 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
5932 : colName, RelationGetRelationName(rel))));
5933 :
5934 5 : if (attTup->attidentity)
5935 2 : ereport(ERROR,
5936 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
5937 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
5938 : colName, RelationGetRelationName(rel))));
5939 :
5940 3 : if (attTup->atthasdef)
5941 1 : ereport(ERROR,
5942 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
5943 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
5944 : colName, RelationGetRelationName(rel))));
5945 :
5946 2 : attTup->attidentity = cdef->identity;
5947 2 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
5948 :
5949 2 : InvokeObjectPostAlterHook(RelationRelationId,
5950 : RelationGetRelid(rel),
5951 : attTup->attnum);
5952 2 : ObjectAddressSubSet(address, RelationRelationId,
5953 : RelationGetRelid(rel), attnum);
5954 2 : heap_freetuple(tuple);
5955 :
5956 2 : heap_close(attrelation, RowExclusiveLock);
5957 :
5958 2 : return address;
5959 : }
5960 :
5961 : /*
5962 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
5963 : *
5964 : * Return the address of the affected column.
5965 : */
5966 : static ObjectAddress
5967 4 : ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
5968 : {
5969 : ListCell *option;
5970 4 : DefElem *generatedEl = NULL;
5971 : HeapTuple tuple;
5972 : Form_pg_attribute attTup;
5973 : AttrNumber attnum;
5974 : Relation attrelation;
5975 : ObjectAddress address;
5976 :
5977 6 : foreach(option, castNode(List, def))
5978 : {
5979 2 : DefElem *defel = lfirst_node(DefElem, option);
5980 :
5981 2 : if (strcmp(defel->defname, "generated") == 0)
5982 : {
5983 2 : if (generatedEl)
5984 0 : ereport(ERROR,
5985 : (errcode(ERRCODE_SYNTAX_ERROR),
5986 : errmsg("conflicting or redundant options")));
5987 2 : generatedEl = defel;
5988 : }
5989 : else
5990 0 : elog(ERROR, "option \"%s\" not recognized",
5991 : defel->defname);
5992 : }
5993 :
5994 : /*
5995 : * Even if there is nothing to change here, we run all the checks. There
5996 : * will be a subsequent ALTER SEQUENCE that relies on everything being
5997 : * there.
5998 : */
5999 :
6000 4 : attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6001 4 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6002 4 : if (!HeapTupleIsValid(tuple))
6003 0 : ereport(ERROR,
6004 : (errcode(ERRCODE_UNDEFINED_COLUMN),
6005 : errmsg("column \"%s\" of relation \"%s\" does not exist",
6006 : colName, RelationGetRelationName(rel))));
6007 :
6008 4 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6009 4 : attnum = attTup->attnum;
6010 :
6011 4 : if (attnum <= 0)
6012 0 : ereport(ERROR,
6013 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6014 : errmsg("cannot alter system column \"%s\"",
6015 : colName)));
6016 :
6017 4 : if (!attTup->attidentity)
6018 1 : ereport(ERROR,
6019 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6020 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
6021 : colName, RelationGetRelationName(rel))));
6022 :
6023 3 : if (generatedEl)
6024 : {
6025 2 : attTup->attidentity = defGetInt32(generatedEl);
6026 2 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6027 :
6028 2 : InvokeObjectPostAlterHook(RelationRelationId,
6029 : RelationGetRelid(rel),
6030 : attTup->attnum);
6031 2 : ObjectAddressSubSet(address, RelationRelationId,
6032 : RelationGetRelid(rel), attnum);
6033 : }
6034 : else
6035 1 : address = InvalidObjectAddress;
6036 :
6037 3 : heap_freetuple(tuple);
6038 3 : heap_close(attrelation, RowExclusiveLock);
6039 :
6040 3 : return address;
6041 : }
6042 :
6043 : /*
6044 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
6045 : *
6046 : * Return the address of the affected column.
6047 : */
6048 : static ObjectAddress
6049 4 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
6050 : {
6051 : HeapTuple tuple;
6052 : Form_pg_attribute attTup;
6053 : AttrNumber attnum;
6054 : Relation attrelation;
6055 : ObjectAddress address;
6056 : Oid seqid;
6057 : ObjectAddress seqaddress;
6058 :
6059 4 : attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6060 4 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6061 4 : if (!HeapTupleIsValid(tuple))
6062 0 : ereport(ERROR,
6063 : (errcode(ERRCODE_UNDEFINED_COLUMN),
6064 : errmsg("column \"%s\" of relation \"%s\" does not exist",
6065 : colName, RelationGetRelationName(rel))));
6066 :
6067 4 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
6068 4 : attnum = attTup->attnum;
6069 :
6070 4 : if (attnum <= 0)
6071 0 : ereport(ERROR,
6072 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6073 : errmsg("cannot alter system column \"%s\"",
6074 : colName)));
6075 :
6076 4 : if (!attTup->attidentity)
6077 : {
6078 2 : if (!missing_ok)
6079 1 : ereport(ERROR,
6080 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
6081 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
6082 : colName, RelationGetRelationName(rel))));
6083 : else
6084 : {
6085 1 : ereport(NOTICE,
6086 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
6087 : colName, RelationGetRelationName(rel))));
6088 1 : heap_freetuple(tuple);
6089 1 : heap_close(attrelation, RowExclusiveLock);
6090 1 : return InvalidObjectAddress;
6091 : }
6092 : }
6093 :
6094 2 : attTup->attidentity = '\0';
6095 2 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6096 :
6097 2 : InvokeObjectPostAlterHook(RelationRelationId,
6098 : RelationGetRelid(rel),
6099 : attTup->attnum);
6100 2 : ObjectAddressSubSet(address, RelationRelationId,
6101 : RelationGetRelid(rel), attnum);
6102 2 : heap_freetuple(tuple);
6103 :
6104 2 : heap_close(attrelation, RowExclusiveLock);
6105 :
6106 : /* drop the internal sequence */
6107 2 : seqid = getOwnedSequence(RelationGetRelid(rel), attnum);
6108 2 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
6109 : RelationRelationId, DEPENDENCY_INTERNAL);
6110 2 : CommandCounterIncrement();
6111 2 : seqaddress.classId = RelationRelationId;
6112 2 : seqaddress.objectId = seqid;
6113 2 : seqaddress.objectSubId = 0;
6114 2 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
6115 :
6116 2 : return address;
6117 : }
6118 :
6119 : /*
6120 : * ALTER TABLE ALTER COLUMN SET STATISTICS
6121 : */
6122 : static void
6123 15 : ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6124 : {
6125 : /*
6126 : * We do our own permission checking because (a) we want to allow SET
6127 : * STATISTICS on indexes (for expressional index columns), and (b) we want
6128 : * to allow SET STATISTICS on system catalogs without requiring
6129 : * allowSystemTableMods to be turned on.
6130 : */
6131 21 : if (rel->rd_rel->relkind != RELKIND_RELATION &&
6132 12 : rel->rd_rel->relkind != RELKIND_MATVIEW &&
6133 12 : rel->rd_rel->relkind != RELKIND_INDEX &&
6134 6 : rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
6135 0 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
6136 0 : ereport(ERROR,
6137 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6138 : errmsg("\"%s\" is not a table, materialized view, index, or foreign table",
6139 : RelationGetRelationName(rel))));
6140 :
6141 : /* Permissions checks */
6142 15 : if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
6143 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
6144 0 : RelationGetRelationName(rel));
6145 15 : }
6146 :
6147 : /*
6148 : * Return value is the address of the modified column
6149 : */
6150 : static ObjectAddress
6151 15 : ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6152 : {
6153 : int newtarget;
6154 : Relation attrelation;
6155 : HeapTuple tuple;
6156 : Form_pg_attribute attrtuple;
6157 : AttrNumber attnum;
6158 : ObjectAddress address;
6159 :
6160 15 : Assert(IsA(newValue, Integer));
6161 15 : newtarget = intVal(newValue);
6162 :
6163 : /*
6164 : * Limit target to a sane range
6165 : */
6166 15 : if (newtarget < -1)
6167 : {
6168 0 : ereport(ERROR,
6169 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6170 : errmsg("statistics target %d is too low",
6171 : newtarget)));
6172 : }
6173 15 : else if (newtarget > 10000)
6174 : {
6175 0 : newtarget = 10000;
6176 0 : ereport(WARNING,
6177 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6178 : errmsg("lowering statistics target to %d",
6179 : newtarget)));
6180 : }
6181 :
6182 15 : attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6183 :
6184 15 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6185 :
6186 15 : if (!HeapTupleIsValid(tuple))
6187 2 : ereport(ERROR,
6188 : (errcode(ERRCODE_UNDEFINED_COLUMN),
6189 : errmsg("column \"%s\" of relation \"%s\" does not exist",
6190 : colName, RelationGetRelationName(rel))));
6191 13 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6192 :
6193 13 : attnum = attrtuple->attnum;
6194 13 : if (attnum <= 0)
6195 0 : ereport(ERROR,
6196 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6197 : errmsg("cannot alter system column \"%s\"",
6198 : colName)));
6199 :
6200 13 : attrtuple->attstattarget = newtarget;
6201 :
6202 13 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6203 :
6204 13 : InvokeObjectPostAlterHook(RelationRelationId,
6205 : RelationGetRelid(rel),
6206 : attrtuple->attnum);
6207 13 : ObjectAddressSubSet(address, RelationRelationId,
6208 : RelationGetRelid(rel), attnum);
6209 13 : heap_freetuple(tuple);
6210 :
6211 13 : heap_close(attrelation, RowExclusiveLock);
6212 :
6213 13 : return address;
6214 : }
6215 :
6216 : /*
6217 : * Return value is the address of the modified column
6218 : */
6219 : static ObjectAddress
6220 3 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
6221 : bool isReset, LOCKMODE lockmode)
6222 : {
6223 : Relation attrelation;
6224 : HeapTuple tuple,
6225 : newtuple;
6226 : Form_pg_attribute attrtuple;
6227 : AttrNumber attnum;
6228 : Datum datum,
6229 : newOptions;
6230 : bool isnull;
6231 : ObjectAddress address;
6232 : Datum repl_val[Natts_pg_attribute];
6233 : bool repl_null[Natts_pg_attribute];
6234 : bool repl_repl[Natts_pg_attribute];
6235 :
6236 3 : attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6237 :
6238 3 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
6239 :
6240 3 : if (!HeapTupleIsValid(tuple))
6241 0 : ereport(ERROR,
6242 : (errcode(ERRCODE_UNDEFINED_COLUMN),
6243 : errmsg("column \"%s\" of relation \"%s\" does not exist",
6244 : colName, RelationGetRelationName(rel))));
6245 3 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6246 :
6247 3 : attnum = attrtuple->attnum;
6248 3 : if (attnum <= 0)
6249 0 : ereport(ERROR,
6250 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6251 : errmsg("cannot alter system column \"%s\"",
6252 : colName)));
6253 :
6254 : /* Generate new proposed attoptions (text array) */
6255 3 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
6256 : &isnull);
6257 6 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
6258 3 : castNode(List, options), NULL, NULL,
6259 : false, isReset);
6260 : /* Validate new options */
6261 3 : (void) attribute_reloptions(newOptions, true);
6262 :
6263 : /* Build new tuple. */
6264 3 : memset(repl_null, false, sizeof(repl_null));
6265 3 : memset(repl_repl, false, sizeof(repl_repl));
6266 3 : if (newOptions != (Datum) 0)
6267 3 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
6268 : else
6269 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
6270 3 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
6271 3 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
6272 : repl_val, repl_null, repl_repl);
6273 :
6274 : /* Update system catalog. */
6275 3 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
6276 :
6277 3 : InvokeObjectPostAlterHook(RelationRelationId,
6278 : RelationGetRelid(rel),
6279 : attrtuple->attnum);
6280 3 : ObjectAddressSubSet(address, RelationRelationId,
6281 : RelationGetRelid(rel), attnum);
6282 :
6283 3 : heap_freetuple(newtuple);
6284 :
6285 3 : ReleaseSysCache(tuple);
6286 :
6287 3 : heap_close(attrelation, RowExclusiveLock);
6288 :
6289 3 : return address;
6290 : }
6291 :
6292 : /*
6293 : * ALTER TABLE ALTER COLUMN SET STORAGE
6294 : *
6295 : * Return value is the address of the modified column
6296 : */
6297 : static ObjectAddress
6298 17 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6299 : {
6300 : char *storagemode;
6301 : char newstorage;
6302 : Relation attrelation;
6303 : HeapTuple tuple;
6304 : Form_pg_attribute attrtuple;
6305 : AttrNumber attnum;
6306 : ObjectAddress address;
6307 :
6308 17 : Assert(IsA(newValue, String));
6309 17 : storagemode = strVal(newValue);
6310 :
6311 17 : if (pg_strcasecmp(storagemode, "plain") == 0)
6312 4 : newstorage = 'p';
6313 13 : else if (pg_strcasecmp(storagemode, "external") == 0)
6314 9 : newstorage = 'e';
6315 4 : else if (pg_strcasecmp(storagemode, "extended") == 0)
6316 2 : newstorage = 'x';
6317 2 : else if (pg_strcasecmp(storagemode, "main") == 0)
6318 2 : newstorage = 'm';
6319 : else
6320 : {
6321 0 : ereport(ERROR,
6322 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6323 : errmsg("invalid storage type \"%s\"",
6324 : storagemode)));
6325 : newstorage = 0; /* keep compiler quiet */
6326 : }
6327 :
6328 17 : attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
6329 :
6330 17 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6331 :
6332 17 : if (!HeapTupleIsValid(tuple))
6333 2 : ereport(ERROR,
6334 : (errcode(ERRCODE_UNDEFINED_COLUMN),
6335 : errmsg("column \"%s\" of relation \"%s\" does not exist",
6336 : colName, RelationGetRelationName(rel))));
6337 15 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
6338 :
6339 15 : attnum = attrtuple->attnum;
6340 15 : if (attnum <= 0)
6341 0 : ereport(ERROR,
6342 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6343 : errmsg("cannot alter system column \"%s\"",
6344 : colName)));
6345 :
6346 : /*
6347 : * safety check: do not allow toasted storage modes unless column datatype
6348 : * is TOAST-aware.
6349 : */
6350 15 : if (newstorage == 'p' || TypeIsToastable(attrtuple->atttypid))
6351 15 : attrtuple->attstorage = newstorage;
6352 : else
6353 0 : ereport(ERROR,
6354 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6355 : errmsg("column data type %s can only have storage PLAIN",
6356 : format_type_be(attrtuple->atttypid))));
6357 :
6358 15 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
6359 :
6360 15 : InvokeObjectPostAlterHook(RelationRelationId,
6361 : RelationGetRelid(rel),
6362 : attrtuple->attnum);
6363 :
6364 15 : heap_freetuple(tuple);
6365 :
6366 15 : heap_close(attrelation, RowExclusiveLock);
6367 :
6368 15 : ObjectAddressSubSet(address, RelationRelationId,
6369 : RelationGetRelid(rel), attnum);
6370 15 : return address;
6371 : }
6372 :
6373 :
6374 : /*
6375 : * ALTER TABLE DROP COLUMN
6376 : *
6377 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
6378 : * because we have to decide at runtime whether to recurse or not depending
6379 : * on whether attinhcount goes to zero or not. (We can't check this in a
6380 : * static pre-pass because it won't handle multiple inheritance situations
6381 : * correctly.)
6382 : */
6383 : static void
6384 114 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6385 : AlterTableCmd *cmd, LOCKMODE lockmode)
6386 : {
6387 114 : if (rel->rd_rel->reloftype && !recursing)
6388 1 : ereport(ERROR,
6389 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6390 : errmsg("cannot drop column from typed table")));
6391 :
6392 113 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6393 8 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
6394 :
6395 112 : if (recurse)
6396 99 : cmd->subtype = AT_DropColumnRecurse;
6397 112 : }
6398 :
6399 : /*
6400 : * Checks if attnum is a partition attribute for rel
6401 : *
6402 : * Sets *used_in_expr if attnum is found to be referenced in some partition
6403 : * key expression. It's possible for a column to be both used directly and
6404 : * as part of an expression; if that happens, *used_in_expr may end up as
6405 : * either true or false. That's OK for current uses of this function, because
6406 : * *used_in_expr is only used to tailor the error message text.
6407 : */
6408 : static bool
6409 208 : is_partition_attr(Relation rel, AttrNumber attnum, bool *used_in_expr)
6410 : {
6411 : PartitionKey key;
6412 : int partnatts;
6413 : List *partexprs;
6414 : ListCell *partexprs_item;
6415 : int i;
6416 :
6417 208 : if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
6418 198 : return false;
6419 :
6420 10 : key = RelationGetPartitionKey(rel);
6421 10 : partnatts = get_partition_natts(key);
6422 10 : partexprs = get_partition_exprs(key);
6423 :
6424 10 : partexprs_item = list_head(partexprs);
6425 16 : for (i = 0; i < partnatts; i++)
6426 : {
6427 12 : AttrNumber partattno = get_partition_col_attnum(key, i);
6428 :
6429 12 : if (partattno != 0)
6430 : {
6431 10 : if (attnum == partattno)
6432 : {
6433 4 : if (used_in_expr)
6434 4 : *used_in_expr = false;
6435 4 : return true;
6436 : }
6437 : }
6438 : else
6439 : {
6440 : /* Arbitrary expression */
6441 2 : Node *expr = (Node *) lfirst(partexprs_item);
6442 2 : Bitmapset *expr_attrs = NULL;
6443 :
6444 : /* Find all attributes referenced */
6445 2 : pull_varattnos(expr, 1, &expr_attrs);
6446 2 : partexprs_item = lnext(partexprs_item);
6447 :
6448 2 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
6449 : expr_attrs))
6450 : {
6451 2 : if (used_in_expr)
6452 2 : *used_in_expr = true;
6453 2 : return true;
6454 : }
6455 : }
6456 : }
6457 :
6458 4 : return false;
6459 : }
6460 :
6461 : /*
6462 : * Return value is the address of the dropped column.
6463 : */
6464 : static ObjectAddress
6465 140 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
6466 : DropBehavior behavior,
6467 : bool recurse, bool recursing,
6468 : bool missing_ok, LOCKMODE lockmode)
6469 : {
6470 : HeapTuple tuple;
6471 : Form_pg_attribute targetatt;
6472 : AttrNumber attnum;
6473 : List *children;
6474 : ObjectAddress object;
6475 : bool is_expr;
6476 :
6477 : /* At top level, permission check was done in ATPrepCmd, else do it */
6478 140 : if (recursing)
6479 28 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6480 :
6481 : /*
6482 : * get the number of the attribute
6483 : */
6484 140 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
6485 140 : if (!HeapTupleIsValid(tuple))
6486 : {
6487 9 : if (!missing_ok)
6488 : {
6489 6 : ereport(ERROR,
6490 : (errcode(ERRCODE_UNDEFINED_COLUMN),
6491 : errmsg("column \"%s\" of relation \"%s\" does not exist",
6492 : colName, RelationGetRelationName(rel))));
6493 : }
6494 : else
6495 : {
6496 3 : ereport(NOTICE,
6497 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
6498 : colName, RelationGetRelationName(rel))));
6499 3 : return InvalidObjectAddress;
6500 : }
6501 : }
6502 131 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
6503 :
6504 131 : attnum = targetatt->attnum;
6505 :
6506 : /* Can't drop a system attribute, except OID */
6507 131 : if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
6508 1 : ereport(ERROR,
6509 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6510 : errmsg("cannot drop system column \"%s\"",
6511 : colName)));
6512 :
6513 : /* Don't drop inherited columns */
6514 130 : if (targetatt->attinhcount > 0 && !recursing)
6515 10 : ereport(ERROR,
6516 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6517 : errmsg("cannot drop inherited column \"%s\"",
6518 : colName)));
6519 :
6520 : /* Don't drop columns used in the partition key */
6521 120 : if (is_partition_attr(rel, attnum, &is_expr))
6522 : {
6523 3 : if (!is_expr)
6524 2 : ereport(ERROR,
6525 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6526 : errmsg("cannot drop column named in partition key")));
6527 : else
6528 1 : ereport(ERROR,
6529 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6530 : errmsg("cannot drop column referenced in partition key expression")));
6531 : }
6532 :
6533 117 : ReleaseSysCache(tuple);
6534 :
6535 : /*
6536 : * Propagate to children as appropriate. Unlike most other ALTER
6537 : * routines, we have to do this one level of recursion at a time; we can't
6538 : * use find_all_inheritors to do it in one pass.
6539 : */
6540 117 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
6541 :
6542 117 : if (children)
6543 : {
6544 : Relation attr_rel;
6545 : ListCell *child;
6546 :
6547 : /*
6548 : * In case of a partitioned table, the column must be dropped from the
6549 : * partitions as well.
6550 : */
6551 31 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
6552 1 : ereport(ERROR,
6553 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6554 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
6555 : errhint("Do not specify the ONLY keyword.")));
6556 :
6557 30 : attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
6558 65 : foreach(child, children)
6559 : {
6560 36 : Oid childrelid = lfirst_oid(child);
6561 : Relation childrel;
6562 : Form_pg_attribute childatt;
6563 :
6564 : /* find_inheritance_children already got lock */
6565 36 : childrel = heap_open(childrelid, NoLock);
6566 36 : CheckTableNotInUse(childrel, "ALTER TABLE");
6567 :
6568 36 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
6569 36 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
6570 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
6571 : colName, childrelid);
6572 36 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
6573 :
6574 36 : if (childatt->attinhcount <= 0) /* shouldn't happen */
6575 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
6576 : childrelid, colName);
6577 :
6578 36 : if (recurse)
6579 : {
6580 : /*
6581 : * If the child column has other definition sources, just
6582 : * decrement its inheritance count; if not, recurse to delete
6583 : * it.
6584 : */
6585 32 : if (childatt->attinhcount == 1 && !childatt->attislocal)
6586 : {
6587 : /* Time to delete this child column, too */
6588 28 : ATExecDropColumn(wqueue, childrel, colName,
6589 : behavior, true, true,
6590 : false, lockmode);
6591 : }
6592 : else
6593 : {
6594 : /* Child column must survive my deletion */
6595 4 : childatt->attinhcount--;
6596 :
6597 4 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
6598 :
6599 : /* Make update visible */
6600 4 : CommandCounterIncrement();
6601 : }
6602 : }
6603 : else
6604 : {
6605 : /*
6606 : * If we were told to drop ONLY in this table (no recursion),
6607 : * we need to mark the inheritors' attributes as locally
6608 : * defined rather than inherited.
6609 : */
6610 4 : childatt->attinhcount--;
6611 4 : childatt->attislocal = true;
6612 :
6613 4 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
6614 :
6615 : /* Make update visible */
6616 4 : CommandCounterIncrement();
6617 : }
6618 :
6619 35 : heap_freetuple(tuple);
6620 :
6621 35 : heap_close(childrel, NoLock);
6622 : }
6623 29 : heap_close(attr_rel, RowExclusiveLock);
6624 : }
6625 :
6626 : /*
6627 : * Perform the actual column deletion
6628 : */
6629 115 : object.classId = RelationRelationId;
6630 115 : object.objectId = RelationGetRelid(rel);
6631 115 : object.objectSubId = attnum;
6632 :
6633 115 : performDeletion(&object, behavior, 0);
6634 :
6635 : /*
6636 : * If we dropped the OID column, must adjust pg_class.relhasoids and tell
6637 : * Phase 3 to physically get rid of the column. We formerly left the
6638 : * column in place physically, but this caused subtle problems. See
6639 : * http://archives.postgresql.org/pgsql-hackers/2009-02/msg00363.php
6640 : */
6641 114 : if (attnum == ObjectIdAttributeNumber)
6642 : {
6643 : Relation class_rel;
6644 : Form_pg_class tuple_class;
6645 : AlteredTableInfo *tab;
6646 :
6647 11 : class_rel = heap_open(RelationRelationId, RowExclusiveLock);
6648 :
6649 11 : tuple = SearchSysCacheCopy1(RELOID,
6650 : ObjectIdGetDatum(RelationGetRelid(rel)));
6651 11 : if (!HeapTupleIsValid(tuple))
6652 0 : elog(ERROR, "cache lookup failed for relation %u",
6653 : RelationGetRelid(rel));
6654 11 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
6655 :
6656 11 : tuple_class->relhasoids = false;
6657 11 : CatalogTupleUpdate(class_rel, &tuple->t_self, tuple);
6658 :
6659 11 : heap_close(class_rel, RowExclusiveLock);
6660 :
6661 : /* Find or create work queue entry for this table */
6662 11 : tab = ATGetQueueEntry(wqueue, rel);
6663 :
6664 : /* Tell Phase 3 to physically remove the OID column */
6665 11 : tab->rewrite |= AT_REWRITE_ALTER_OID;
6666 : }
6667 :
6668 114 : return object;
6669 : }
6670 :
6671 : /*
6672 : * ALTER TABLE ADD INDEX
6673 : *
6674 : * There is no such command in the grammar, but parse_utilcmd.c converts
6675 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
6676 : * us schedule creation of the index at the appropriate time during ALTER.
6677 : *
6678 : * Return value is the address of the new index.
6679 : */
6680 : static ObjectAddress
6681 53 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
6682 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
6683 : {
6684 : bool check_rights;
6685 : bool skip_build;
6686 : bool quiet;
6687 : ObjectAddress address;
6688 :
6689 53 : Assert(IsA(stmt, IndexStmt));
6690 53 : Assert(!stmt->concurrent);
6691 :
6692 : /* The IndexStmt has already been through transformIndexStmt */
6693 53 : Assert(stmt->transformed);
6694 :
6695 : /* suppress schema rights check when rebuilding existing index */
6696 53 : check_rights = !is_rebuild;
6697 : /* skip index build if phase 3 will do it or we're reusing an old one */
6698 53 : skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode);
6699 : /* suppress notices when rebuilding existing index */
6700 53 : quiet = is_rebuild;
6701 :
6702 53 : address = DefineIndex(RelationGetRelid(rel),
6703 : stmt,
6704 : InvalidOid, /* no predefined OID */
6705 : true, /* is_alter_table */
6706 : check_rights,
6707 : false, /* check_not_in_use - we did it already */
6708 : skip_build,
6709 : quiet);
6710 :
6711 : /*
6712 : * If TryReuseIndex() stashed a relfilenode for us, we used it for the new
6713 : * index instead of building from scratch. The DROP of the old edition of
6714 : * this index will have scheduled the storage for deletion at commit, so
6715 : * cancel that pending deletion.
6716 : */
6717 40 : if (OidIsValid(stmt->oldNode))
6718 : {
6719 7 : Relation irel = index_open(address.objectId, NoLock);
6720 :
6721 7 : RelationPreserveStorage(irel->rd_node, true);
6722 7 : index_close(irel, NoLock);
6723 : }
6724 :
6725 40 : return address;
6726 : }
6727 :
6728 : /*
6729 : * ALTER TABLE ADD CONSTRAINT USING INDEX
6730 : *
6731 : * Returns the address of the new constraint.
6732 : */
6733 : static ObjectAddress
6734 2 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
6735 : IndexStmt *stmt, LOCKMODE lockmode)
6736 : {
6737 2 : Oid index_oid = stmt->indexOid;
6738 : Relation indexRel;
6739 : char *indexName;
6740 : IndexInfo *indexInfo;
6741 : char *constraintName;
6742 : char constraintType;
6743 : ObjectAddress address;
6744 :
6745 2 : Assert(IsA(stmt, IndexStmt));
6746 2 : Assert(OidIsValid(index_oid));
6747 2 : Assert(stmt->isconstraint);
6748 :
6749 2 : indexRel = index_open(index_oid, AccessShareLock);
6750 :
6751 2 : indexName = pstrdup(RelationGetRelationName(indexRel));
6752 :
6753 2 : indexInfo = BuildIndexInfo(indexRel);
6754 :
6755 : /* this should have been checked at parse time */
6756 2 : if (!indexInfo->ii_Unique)
6757 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
6758 :
6759 : /*
6760 : * Determine name to assign to constraint. We require a constraint to
6761 : * have the same name as the underlying index; therefore, use the index's
6762 : * existing name as the default constraint name, and if the user
6763 : * explicitly gives some other name for the constraint, rename the index
6764 : * to match.
6765 : */
6766 2 : constraintName = stmt->idxname;
6767 2 : if (constraintName == NULL)
6768 1 : constraintName = indexName;
6769 1 : else if (strcmp(constraintName, indexName) != 0)
6770 : {
6771 1 : ereport(NOTICE,
6772 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
6773 : indexName, constraintName)));
6774 1 : RenameRelationInternal(index_oid, constraintName, false);
6775 : }
6776 :
6777 : /* Extra checks needed if making primary key */
6778 2 : if (stmt->primary)
6779 2 : index_check_primary_key(rel, indexInfo, true);
6780 :
6781 : /* Note we currently don't support EXCLUSION constraints here */
6782 2 : if (stmt->primary)
6783 2 : constraintType = CONSTRAINT_PRIMARY;
6784 : else
6785 0 : constraintType = CONSTRAINT_UNIQUE;
6786 :
6787 : /* Create the catalog entries for the constraint */
6788 8 : address = index_constraint_create(rel,
6789 : index_oid,
6790 : indexInfo,
6791 : constraintName,
6792 : constraintType,
6793 2 : stmt->deferrable,
6794 2 : stmt->initdeferred,
6795 2 : stmt->primary,
6796 : true, /* update pg_index */
6797 : true, /* remove old dependencies */
6798 : allowSystemTableMods,
6799 : false); /* is_internal */
6800 :
6801 2 : index_close(indexRel, NoLock);
6802 :
6803 2 : return address;
6804 : }
6805 :
6806 : /*
6807 : * ALTER TABLE ADD CONSTRAINT
6808 : *
6809 : * Return value is the address of the new constraint; if no constraint was
6810 : * added, InvalidObjectAddress is returned.
6811 : */
6812 : static ObjectAddress
6813 217 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
6814 : Constraint *newConstraint, bool recurse, bool is_readd,
6815 : LOCKMODE lockmode)
6816 : {
6817 217 : ObjectAddress address = InvalidObjectAddress;
6818 :
6819 217 : Assert(IsA(newConstraint, Constraint));
6820 :
6821 : /*
6822 : * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
6823 : * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
6824 : * switch anyway to make it easier to add more code later.
6825 : */
6826 217 : switch (newConstraint->contype)
6827 : {
6828 : case CONSTR_CHECK:
6829 79 : address =
6830 79 : ATAddCheckConstraint(wqueue, tab, rel,
6831 : newConstraint, recurse, false, is_readd,
6832 : lockmode);
6833 71 : break;
6834 :
6835 : case CONSTR_FOREIGN:
6836 :
6837 : /*
6838 : * Note that we currently never recurse for FK constraints, so the
6839 : * "recurse" flag is silently ignored.
6840 : *
6841 : * Assign or validate constraint name
6842 : */
6843 138 : if (newConstraint->conname)
6844 : {
6845 40 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
6846 : RelationGetRelid(rel),
6847 40 : RelationGetNamespace(rel),
6848 40 : newConstraint->conname))
6849 0 : ereport(ERROR,
6850 : (errcode(ERRCODE_DUPLICATE_OBJECT),
6851 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
6852 : newConstraint->conname,
6853 : RelationGetRelationName(rel))));
6854 : }
6855 : else
6856 98 : newConstraint->conname =
6857 196 : ChooseConstraintName(RelationGetRelationName(rel),
6858 98 : strVal(linitial(newConstraint->fk_attrs)),
6859 : "fkey",
6860 98 : RelationGetNamespace(rel),
6861 : NIL);
6862 :
6863 138 : address = ATAddForeignKeyConstraint(tab, rel, newConstraint,
6864 : lockmode);
6865 93 : break;
6866 :
6867 : default:
6868 0 : elog(ERROR, "unrecognized constraint type: %d",
6869 : (int) newConstraint->contype);
6870 : }
6871 :
6872 164 : return address;
6873 : }
6874 :
6875 : /*
6876 : * Add a check constraint to a single table and its children. Returns the
6877 : * address of the constraint added to the parent relation, if one gets added,
6878 : * or InvalidObjectAddress otherwise.
6879 : *
6880 : * Subroutine for ATExecAddConstraint.
6881 : *
6882 : * We must recurse to child tables during execution, rather than using
6883 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
6884 : * constraints *must* be given the same name, else they won't be seen as
6885 : * related later. If the user didn't explicitly specify a name, then
6886 : * AddRelationNewConstraints would normally assign different names to the
6887 : * child constraints. To fix that, we must capture the name assigned at
6888 : * the parent table and pass that down.
6889 : */
6890 : static ObjectAddress
6891 129 : ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
6892 : Constraint *constr, bool recurse, bool recursing,
6893 : bool is_readd, LOCKMODE lockmode)
6894 : {
6895 : List *newcons;
6896 : ListCell *lcon;
6897 : List *children;
6898 : ListCell *child;
6899 129 : ObjectAddress address = InvalidObjectAddress;
6900 :
6901 : /* At top level, permission check was done in ATPrepCmd, else do it */
6902 129 : if (recursing)
6903 50 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6904 :
6905 : /*
6906 : * Call AddRelationNewConstraints to do the work, making sure it works on
6907 : * a copy of the Constraint so transformExpr can't modify the original. It
6908 : * returns a list of cooked constraints.
6909 : *
6910 : * If the constraint ends up getting merged with a pre-existing one, it's
6911 : * omitted from the returned list, which is what we want: we do not need
6912 : * to do any validation work. That can only happen at child tables,
6913 : * though, since we disallow merging at the top level.
6914 : */
6915 129 : newcons = AddRelationNewConstraints(rel, NIL,
6916 : list_make1(copyObject(constr)),
6917 : recursing | is_readd, /* allow_merge */
6918 : !recursing, /* is_local */
6919 : is_readd); /* is_internal */
6920 :
6921 : /* we don't expect more than one constraint here */
6922 122 : Assert(list_length(newcons) <= 1);
6923 :
6924 : /* Add each to-be-validated constraint to Phase 3's queue */
6925 237 : foreach(lcon, newcons)
6926 : {
6927 115 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
6928 :
6929 115 : if (!ccon->skip_validation)
6930 : {
6931 : NewConstraint *newcon;
6932 :
6933 95 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
6934 95 : newcon->name = ccon->name;
6935 95 : newcon->contype = ccon->contype;
6936 95 : newcon->qual = ccon->expr;
6937 :
6938 95 : tab->constraints = lappend(tab->constraints, newcon);
6939 : }
6940 :
6941 : /* Save the actually assigned name if it was defaulted */
6942 115 : if (constr->conname == NULL)
6943 13 : constr->conname = ccon->name;
6944 :
6945 115 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
6946 : }
6947 :
6948 : /* At this point we must have a locked-down name to use */
6949 122 : Assert(constr->conname != NULL);
6950 :
6951 : /* Advance command counter in case same table is visited multiple times */
6952 122 : CommandCounterIncrement();
6953 :
6954 : /*
6955 : * If the constraint got merged with an existing constraint, we're done.
6956 : * We mustn't recurse to child tables in this case, because they've
6957 : * already got the constraint, and visiting them again would lead to an
6958 : * incorrect value for coninhcount.
6959 : */
6960 122 : if (newcons == NIL)
6961 7 : return address;
6962 :
6963 : /*
6964 : * If adding a NO INHERIT constraint, no need to find our children.
6965 : */
6966 115 : if (constr->is_no_inherit)
6967 8 : return address;
6968 :
6969 : /*
6970 : * Propagate to children as appropriate. Unlike most other ALTER
6971 : * routines, we have to do this one level of recursion at a time; we can't
6972 : * use find_all_inheritors to do it in one pass.
6973 : */
6974 107 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
6975 :
6976 : /*
6977 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
6978 : * constraint creation only if there are no children currently. Error out
6979 : * otherwise.
6980 : */
6981 107 : if (!recurse && children != NIL)
6982 1 : ereport(ERROR,
6983 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6984 : errmsg("constraint must be added to child tables too")));
6985 :
6986 155 : foreach(child, children)
6987 : {
6988 50 : Oid childrelid = lfirst_oid(child);
6989 : Relation childrel;
6990 : AlteredTableInfo *childtab;
6991 :
6992 : /* find_inheritance_children already got lock */
6993 50 : childrel = heap_open(childrelid, NoLock);
6994 50 : CheckTableNotInUse(childrel, "ALTER TABLE");
6995 :
6996 : /* Find or create work queue entry for this table */
6997 50 : childtab = ATGetQueueEntry(wqueue, childrel);
6998 :
6999 : /* Recurse to child */
7000 50 : ATAddCheckConstraint(wqueue, childtab, childrel,
7001 : constr, recurse, true, is_readd, lockmode);
7002 :
7003 49 : heap_close(childrel, NoLock);
7004 : }
7005 :
7006 105 : return address;
7007 : }
7008 :
7009 : /*
7010 : * Add a foreign-key constraint to a single table; return the new constraint's
7011 : * address.
7012 : *
7013 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
7014 : * lock on the rel, and have done appropriate validity checks for it.
7015 : * We do permissions checks here, however.
7016 : */
7017 : static ObjectAddress
7018 138 : ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
7019 : Constraint *fkconstraint, LOCKMODE lockmode)
7020 : {
7021 : Relation pkrel;
7022 : int16 pkattnum[INDEX_MAX_KEYS];
7023 : int16 fkattnum[INDEX_MAX_KEYS];
7024 : Oid pktypoid[INDEX_MAX_KEYS];
7025 : Oid fktypoid[INDEX_MAX_KEYS];
7026 : Oid opclasses[INDEX_MAX_KEYS];
7027 : Oid pfeqoperators[INDEX_MAX_KEYS];
7028 : Oid ppeqoperators[INDEX_MAX_KEYS];
7029 : Oid ffeqoperators[INDEX_MAX_KEYS];
7030 : int i;
7031 : int numfks,
7032 : numpks;
7033 : Oid indexOid;
7034 : Oid constrOid;
7035 : bool old_check_ok;
7036 : ObjectAddress address;
7037 138 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
7038 :
7039 : /*
7040 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
7041 : * delete rows out from under us.
7042 : */
7043 138 : if (OidIsValid(fkconstraint->old_pktable_oid))
7044 4 : pkrel = heap_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
7045 : else
7046 134 : pkrel = heap_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
7047 :
7048 : /*
7049 : * Validity checks (permission checks wait till we have the column
7050 : * numbers)
7051 : */
7052 138 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7053 0 : ereport(ERROR,
7054 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7055 : errmsg("cannot reference partitioned table \"%s\"",
7056 : RelationGetRelationName(pkrel))));
7057 :
7058 138 : if (pkrel->rd_rel->relkind != RELKIND_RELATION)
7059 0 : ereport(ERROR,
7060 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7061 : errmsg("referenced relation \"%s\" is not a table",
7062 : RelationGetRelationName(pkrel))));
7063 :
7064 138 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
7065 0 : ereport(ERROR,
7066 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
7067 : errmsg("permission denied: \"%s\" is a system catalog",
7068 : RelationGetRelationName(pkrel))));
7069 :
7070 : /*
7071 : * References from permanent or unlogged tables to temp tables, and from
7072 : * permanent tables to unlogged tables, are disallowed because the
7073 : * referenced data can vanish out from under us. References from temp
7074 : * tables to any other table type are also disallowed, because other
7075 : * backends might need to run the RI triggers on the perm table, but they
7076 : * can't reliably see tuples in the local buffers of other backends.
7077 : */
7078 138 : switch (rel->rd_rel->relpersistence)
7079 : {
7080 : case RELPERSISTENCE_PERMANENT:
7081 89 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
7082 0 : ereport(ERROR,
7083 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7084 : errmsg("constraints on permanent tables may reference only permanent tables")));
7085 89 : break;
7086 : case RELPERSISTENCE_UNLOGGED:
7087 2 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT
7088 2 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
7089 0 : ereport(ERROR,
7090 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7091 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
7092 2 : break;
7093 : case RELPERSISTENCE_TEMP:
7094 47 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
7095 0 : ereport(ERROR,
7096 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7097 : errmsg("constraints on temporary tables may reference only temporary tables")));
7098 47 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
7099 0 : ereport(ERROR,
7100 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7101 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
7102 47 : break;
7103 : }
7104 :
7105 : /*
7106 : * Look up the referencing attributes to make sure they exist, and record
7107 : * their attnums and type OIDs.
7108 : */
7109 138 : MemSet(pkattnum, 0, sizeof(pkattnum));
7110 138 : MemSet(fkattnum, 0, sizeof(fkattnum));
7111 138 : MemSet(pktypoid, 0, sizeof(pktypoid));
7112 138 : MemSet(fktypoid, 0, sizeof(fktypoid));
7113 138 : MemSet(opclasses, 0, sizeof(opclasses));
7114 138 : MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
7115 138 : MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
7116 138 : MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
7117 :
7118 138 : numfks = transformColumnNameList(RelationGetRelid(rel),
7119 : fkconstraint->fk_attrs,
7120 : fkattnum, fktypoid);
7121 :
7122 : /*
7123 : * If the attribute list for the referenced table was omitted, lookup the
7124 : * definition of the primary key and use it. Otherwise, validate the
7125 : * supplied attribute list. In either case, discover the index OID and
7126 : * index opclasses, and the attnums and type OIDs of the attributes.
7127 : */
7128 134 : if (fkconstraint->pk_attrs == NIL)
7129 : {
7130 73 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
7131 : &fkconstraint->pk_attrs,
7132 : pkattnum, pktypoid,
7133 : opclasses);
7134 : }
7135 : else
7136 : {
7137 61 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
7138 : fkconstraint->pk_attrs,
7139 : pkattnum, pktypoid);
7140 : /* Look for an index matching the column list */
7141 57 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
7142 : opclasses);
7143 : }
7144 :
7145 : /*
7146 : * Now we can check permissions.
7147 : */
7148 128 : checkFkeyPermissions(pkrel, pkattnum, numpks);
7149 :
7150 : /*
7151 : * Look up the equality operators to use in the constraint.
7152 : *
7153 : * Note that we have to be careful about the difference between the actual
7154 : * PK column type and the opclass' declared input type, which might be
7155 : * only binary-compatible with it. The declared opcintype is the right
7156 : * thing to probe pg_amop with.
7157 : */
7158 128 : if (numfks != numpks)
7159 0 : ereport(ERROR,
7160 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
7161 : errmsg("number of referencing and referenced columns for foreign key disagree")));
7162 :
7163 : /*
7164 : * On the strength of a previous constraint, we might avoid scanning
7165 : * tables to validate this one. See below.
7166 : */
7167 128 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
7168 128 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
7169 :
7170 249 : for (i = 0; i < numpks; i++)
7171 : {
7172 156 : Oid pktype = pktypoid[i];
7173 156 : Oid fktype = fktypoid[i];
7174 : Oid fktyped;
7175 : HeapTuple cla_ht;
7176 : Form_pg_opclass cla_tup;
7177 : Oid amid;
7178 : Oid opfamily;
7179 : Oid opcintype;
7180 : Oid pfeqop;
7181 : Oid ppeqop;
7182 : Oid ffeqop;
7183 : int16 eqstrategy;
7184 : Oid pfeqop_right;
7185 :
7186 : /* We need several fields out of the pg_opclass entry */
7187 156 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
7188 156 : if (!HeapTupleIsValid(cla_ht))
7189 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
7190 156 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
7191 156 : amid = cla_tup->opcmethod;
7192 156 : opfamily = cla_tup->opcfamily;
7193 156 : opcintype = cla_tup->opcintype;
7194 156 : ReleaseSysCache(cla_ht);
7195 :
7196 : /*
7197 : * Check it's a btree; currently this can never fail since no other
7198 : * index AMs support unique indexes. If we ever did have other types
7199 : * of unique indexes, we'd need a way to determine which operator
7200 : * strategy number is equality. (Is it reasonable to insist that
7201 : * every such index AM use btree's number for equality?)
7202 : */
7203 156 : if (amid != BTREE_AM_OID)
7204 0 : elog(ERROR, "only b-tree indexes are supported for foreign keys");
7205 156 : eqstrategy = BTEqualStrategyNumber;
7206 :
7207 : /*
7208 : * There had better be a primary equality operator for the index.
7209 : * We'll use it for PK = PK comparisons.
7210 : */
7211 156 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
7212 : eqstrategy);
7213 :
7214 156 : if (!OidIsValid(ppeqop))
7215 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
7216 : eqstrategy, opcintype, opcintype, opfamily);
7217 :
7218 : /*
7219 : * Are there equality operators that take exactly the FK type? Assume
7220 : * we should look through any domain here.
7221 : */
7222 156 : fktyped = getBaseType(fktype);
7223 :
7224 156 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
7225 : eqstrategy);
7226 156 : if (OidIsValid(pfeqop))
7227 : {
7228 113 : pfeqop_right = fktyped;
7229 113 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
7230 : eqstrategy);
7231 : }
7232 : else
7233 : {
7234 : /* keep compiler quiet */
7235 43 : pfeqop_right = InvalidOid;
7236 43 : ffeqop = InvalidOid;
7237 : }
7238 :
7239 156 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
7240 : {
7241 : /*
7242 : * Otherwise, look for an implicit cast from the FK type to the
7243 : * opcintype, and if found, use the primary equality operator.
7244 : * This is a bit tricky because opcintype might be a polymorphic
7245 : * type such as ANYARRAY or ANYENUM; so what we have to test is
7246 : * whether the two actual column types can be concurrently cast to
7247 : * that type. (Otherwise, we'd fail to reject combinations such
7248 : * as int[] and point[].)
7249 : */
7250 : Oid input_typeids[2];
7251 : Oid target_typeids[2];
7252 :
7253 43 : input_typeids[0] = pktype;
7254 43 : input_typeids[1] = fktype;
7255 43 : target_typeids[0] = opcintype;
7256 43 : target_typeids[1] = opcintype;
7257 43 : if (can_coerce_type(2, input_typeids, target_typeids,
7258 : COERCION_IMPLICIT))
7259 : {
7260 8 : pfeqop = ffeqop = ppeqop;
7261 8 : pfeqop_right = opcintype;
7262 : }
7263 : }
7264 :
7265 156 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
7266 35 : ereport(ERROR,
7267 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7268 : errmsg("foreign key constraint \"%s\" "
7269 : "cannot be implemented",
7270 : fkconstraint->conname),
7271 : errdetail("Key columns \"%s\" and \"%s\" "
7272 : "are of incompatible types: %s and %s.",
7273 : strVal(list_nth(fkconstraint->fk_attrs, i)),
7274 : strVal(list_nth(fkconstraint->pk_attrs, i)),
7275 : format_type_be(fktype),
7276 : format_type_be(pktype))));
7277 :
7278 121 : if (old_check_ok)
7279 : {
7280 : /*
7281 : * When a pfeqop changes, revalidate the constraint. We could
7282 : * permit intra-opfamily changes, but that adds subtle complexity
7283 : * without any concrete benefit for core types. We need not
7284 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
7285 : */
7286 1 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
7287 1 : old_pfeqop_item = lnext(old_pfeqop_item);
7288 : }
7289 121 : if (old_check_ok)
7290 : {
7291 : Oid old_fktype;
7292 : Oid new_fktype;
7293 : CoercionPathType old_pathtype;
7294 : CoercionPathType new_pathtype;
7295 : Oid old_castfunc;
7296 : Oid new_castfunc;
7297 1 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
7298 : fkattnum[i] - 1);
7299 :
7300 : /*
7301 : * Identify coercion pathways from each of the old and new FK-side
7302 : * column types to the right (foreign) operand type of the pfeqop.
7303 : * We may assume that pg_constraint.conkey is not changing.
7304 : */
7305 1 : old_fktype = attr->atttypid;
7306 1 : new_fktype = fktype;
7307 1 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
7308 : &old_castfunc);
7309 1 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
7310 : &new_castfunc);
7311 :
7312 : /*
7313 : * Upon a change to the cast from the FK column to its pfeqop
7314 : * operand, revalidate the constraint. For this evaluation, a
7315 : * binary coercion cast is equivalent to no cast at all. While
7316 : * type implementors should design implicit casts with an eye
7317 : * toward consistency of operations like equality, we cannot
7318 : * assume here that they have done so.
7319 : *
7320 : * A function with a polymorphic argument could change behavior
7321 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
7322 : * when the cast destination is polymorphic, we only avoid
7323 : * revalidation if the input type has not changed at all. Given
7324 : * just the core data types and operator classes, this requirement
7325 : * prevents no would-be optimizations.
7326 : *
7327 : * If the cast converts from a base type to a domain thereon, then
7328 : * that domain type must be the opcintype of the unique index.
7329 : * Necessarily, the primary key column must then be of the domain
7330 : * type. Since the constraint was previously valid, all values on
7331 : * the foreign side necessarily exist on the primary side and in
7332 : * turn conform to the domain. Consequently, we need not treat
7333 : * domains specially here.
7334 : *
7335 : * Since we require that all collations share the same notion of
7336 : * equality (which they do, because texteq reduces to bitwise
7337 : * equality), we don't compare collation here.
7338 : *
7339 : * We need not directly consider the PK type. It's necessarily
7340 : * binary coercible to the opcintype of the unique index column,
7341 : * and ri_triggers.c will only deal with PK datums in terms of
7342 : * that opcintype. Changing the opcintype also changes pfeqop.
7343 : */
7344 2 : old_check_ok = (new_pathtype == old_pathtype &&
7345 3 : new_castfunc == old_castfunc &&
7346 1 : (!IsPolymorphicType(pfeqop_right) ||
7347 : new_fktype == old_fktype));
7348 :
7349 : }
7350 :
7351 121 : pfeqoperators[i] = pfeqop;
7352 121 : ppeqoperators[i] = ppeqop;
7353 121 : ffeqoperators[i] = ffeqop;
7354 : }
7355 :
7356 : /*
7357 : * Record the FK constraint in pg_constraint.
7358 : */
7359 651 : constrOid = CreateConstraintEntry(fkconstraint->conname,
7360 93 : RelationGetNamespace(rel),
7361 : CONSTRAINT_FOREIGN,
7362 93 : fkconstraint->deferrable,
7363 93 : fkconstraint->initdeferred,
7364 93 : fkconstraint->initially_valid,
7365 : RelationGetRelid(rel),
7366 : fkattnum,
7367 : numfks,
7368 : InvalidOid, /* not a domain constraint */
7369 : indexOid,
7370 : RelationGetRelid(pkrel),
7371 : pkattnum,
7372 : pfeqoperators,
7373 : ppeqoperators,
7374 : ffeqoperators,
7375 : numpks,
7376 93 : fkconstraint->fk_upd_action,
7377 93 : fkconstraint->fk_del_action,
7378 93 : fkconstraint->fk_matchtype,
7379 : NULL, /* no exclusion constraint */
7380 : NULL, /* no check constraint */
7381 : NULL,
7382 : NULL,
7383 : true, /* islocal */
7384 : 0, /* inhcount */
7385 : true, /* isnoinherit */
7386 : false); /* is_internal */
7387 93 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
7388 :
7389 : /*
7390 : * Create the triggers that will enforce the constraint.
7391 : */
7392 93 : createForeignKeyTriggers(rel, RelationGetRelid(pkrel), fkconstraint,
7393 : constrOid, indexOid);
7394 :
7395 : /*
7396 : * Tell Phase 3 to check that the constraint is satisfied by existing
7397 : * rows. We can skip this during table creation, when requested explicitly
7398 : * by specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
7399 : * recreating a constraint following a SET DATA TYPE operation that did
7400 : * not impugn its validity.
7401 : */
7402 93 : if (!old_check_ok && !fkconstraint->skip_validation)
7403 : {
7404 : NewConstraint *newcon;
7405 :
7406 23 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
7407 23 : newcon->name = fkconstraint->conname;
7408 23 : newcon->contype = CONSTR_FOREIGN;
7409 23 : newcon->refrelid = RelationGetRelid(pkrel);
7410 23 : newcon->refindid = indexOid;
7411 23 : newcon->conid = constrOid;
7412 23 : newcon->qual = (Node *) fkconstraint;
7413 :
7414 23 : tab->constraints = lappend(tab->constraints, newcon);
7415 : }
7416 :
7417 : /*
7418 : * Close pk table, but keep lock until we've committed.
7419 : */
7420 93 : heap_close(pkrel, NoLock);
7421 :
7422 93 : return address;
7423 : }
7424 :
7425 : /*
7426 : * ALTER TABLE ALTER CONSTRAINT
7427 : *
7428 : * Update the attributes of a constraint.
7429 : *
7430 : * Currently only works for Foreign Key constraints.
7431 : * Foreign keys do not inherit, so we purposely ignore the
7432 : * recursion bit here, but we keep the API the same for when
7433 : * other constraint types are supported.
7434 : *
7435 : * If the constraint is modified, returns its address; otherwise, return
7436 : * InvalidObjectAddress.
7437 : */
7438 : static ObjectAddress
7439 5 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
7440 : bool recurse, bool recursing, LOCKMODE lockmode)
7441 : {
7442 : Constraint *cmdcon;
7443 : Relation conrel;
7444 : SysScanDesc scan;
7445 : ScanKeyData key;
7446 : HeapTuple contuple;
7447 5 : Form_pg_constraint currcon = NULL;
7448 5 : bool found = false;
7449 : ObjectAddress address;
7450 :
7451 5 : cmdcon = castNode(Constraint, cmd->def);
7452 :
7453 5 : conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
7454 :
7455 : /*
7456 : * Find and check the target constraint
7457 : */
7458 5 : ScanKeyInit(&key,
7459 : Anum_pg_constraint_conrelid,
7460 : BTEqualStrategyNumber, F_OIDEQ,
7461 : ObjectIdGetDatum(RelationGetRelid(rel)));
7462 5 : scan = systable_beginscan(conrel, ConstraintRelidIndexId,
7463 : true, NULL, 1, &key);
7464 :
7465 5 : while (HeapTupleIsValid(contuple = systable_getnext(scan)))
7466 : {
7467 5 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
7468 5 : if (strcmp(NameStr(currcon->conname), cmdcon->conname) == 0)
7469 : {
7470 5 : found = true;
7471 5 : break;
7472 : }
7473 : }
7474 :
7475 5 : if (!found)
7476 0 : ereport(ERROR,
7477 : (errcode(ERRCODE_UNDEFINED_OBJECT),
7478 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
7479 : cmdcon->conname, RelationGetRelationName(rel))));
7480 :
7481 5 : if (currcon->contype != CONSTRAINT_FOREIGN)
7482 0 : ereport(ERROR,
7483 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7484 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
7485 : cmdcon->conname, RelationGetRelationName(rel))));
7486 :
7487 6 : if (currcon->condeferrable != cmdcon->deferrable ||
7488 1 : currcon->condeferred != cmdcon->initdeferred)
7489 5 : {
7490 : HeapTuple copyTuple;
7491 : HeapTuple tgtuple;
7492 : Form_pg_constraint copy_con;
7493 5 : List *otherrelids = NIL;
7494 : ScanKeyData tgkey;
7495 : SysScanDesc tgscan;
7496 : Relation tgrel;
7497 : ListCell *lc;
7498 :
7499 : /*
7500 : * Now update the catalog, while we have the door open.
7501 : */
7502 5 : copyTuple = heap_copytuple(contuple);
7503 5 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
7504 5 : copy_con->condeferrable = cmdcon->deferrable;
7505 5 : copy_con->condeferred = cmdcon->initdeferred;
7506 5 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
7507 :
7508 5 : InvokeObjectPostAlterHook(ConstraintRelationId,
7509 : HeapTupleGetOid(contuple), 0);
7510 :
7511 5 : heap_freetuple(copyTuple);
7512 :
7513 : /*
7514 : * Now we need to update the multiple entries in pg_trigger that
7515 : * implement the constraint.
7516 : */
7517 5 : tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
7518 :
7519 10 : ScanKeyInit(&tgkey,
7520 : Anum_pg_trigger_tgconstraint,
7521 : BTEqualStrategyNumber, F_OIDEQ,
7522 10 : ObjectIdGetDatum(HeapTupleGetOid(contuple)));
7523 :
7524 5 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
7525 : NULL, 1, &tgkey);
7526 :
7527 30 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
7528 : {
7529 20 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
7530 : Form_pg_trigger copy_tg;
7531 :
7532 : /*
7533 : * Remember OIDs of other relation(s) involved in FK constraint.
7534 : * (Note: it's likely that we could skip forcing a relcache inval
7535 : * for other rels that don't have a trigger whose properties
7536 : * change, but let's be conservative.)
7537 : */
7538 20 : if (tgform->tgrelid != RelationGetRelid(rel))
7539 10 : otherrelids = list_append_unique_oid(otherrelids,
7540 : tgform->tgrelid);
7541 :
7542 : /*
7543 : * Update deferrability of RI_FKey_noaction_del,
7544 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
7545 : * triggers, but not others; see createForeignKeyTriggers and
7546 : * CreateFKCheckTrigger.
7547 : */
7548 38 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
7549 31 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
7550 21 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
7551 8 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
7552 3 : continue;
7553 :
7554 17 : copyTuple = heap_copytuple(tgtuple);
7555 17 : copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
7556 :
7557 17 : copy_tg->tgdeferrable = cmdcon->deferrable;
7558 17 : copy_tg->tginitdeferred = cmdcon->initdeferred;
7559 17 : CatalogTupleUpdate(tgrel, ©Tuple->t_self, copyTuple);
7560 :
7561 17 : InvokeObjectPostAlterHook(TriggerRelationId,
7562 : HeapTupleGetOid(tgtuple), 0);
7563 :
7564 17 : heap_freetuple(copyTuple);
7565 : }
7566 :
7567 5 : systable_endscan(tgscan);
7568 :
7569 5 : heap_close(tgrel, RowExclusiveLock);
7570 :
7571 : /*
7572 : * Invalidate relcache so that others see the new attributes. We must
7573 : * inval both the named rel and any others having relevant triggers.
7574 : * (At present there should always be exactly one other rel, but
7575 : * there's no need to hard-wire such an assumption here.)
7576 : */
7577 5 : CacheInvalidateRelcache(rel);
7578 10 : foreach(lc, otherrelids)
7579 : {
7580 5 : CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
7581 : }
7582 :
7583 5 : ObjectAddressSet(address, ConstraintRelationId,
7584 : HeapTupleGetOid(contuple));
7585 : }
7586 : else
7587 0 : address = InvalidObjectAddress;
7588 :
7589 5 : systable_endscan(scan);
7590 :
7591 5 : heap_close(conrel, RowExclusiveLock);
7592 :
7593 5 : return address;
7594 : }
7595 :
7596 : /*
7597 : * ALTER TABLE VALIDATE CONSTRAINT
7598 : *
7599 : * XXX The reason we handle recursion here rather than at Phase 1 is because
7600 : * there's no good way to skip recursing when handling foreign keys: there is
7601 : * no need to lock children in that case, yet we wouldn't be able to avoid
7602 : * doing so at that level.
7603 : *
7604 : * Return value is the address of the validated constraint. If the constraint
7605 : * was already validated, InvalidObjectAddress is returned.
7606 : */
7607 : static ObjectAddress
7608 22 : ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
7609 : bool recursing, LOCKMODE lockmode)
7610 : {
7611 : Relation conrel;
7612 : SysScanDesc scan;
7613 : ScanKeyData key;
7614 : HeapTuple tuple;
7615 22 : Form_pg_constraint con = NULL;
7616 22 : bool found = false;
7617 : ObjectAddress address;
7618 :
7619 22 : conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
7620 :
7621 : /*
7622 : * Find and check the target constraint
7623 : */
7624 22 : ScanKeyInit(&key,
7625 : Anum_pg_constraint_conrelid,
7626 : BTEqualStrategyNumber, F_OIDEQ,
7627 : ObjectIdGetDatum(RelationGetRelid(rel)));
7628 22 : scan = systable_beginscan(conrel, ConstraintRelidIndexId,
7629 : true, NULL, 1, &key);
7630 :
7631 22 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
7632 : {
7633 22 : con = (Form_pg_constraint) GETSTRUCT(tuple);
7634 22 : if (strcmp(NameStr(con->conname), constrName) == 0)
7635 : {
7636 22 : found = true;
7637 22 : break;
7638 : }
7639 : }
7640 :
7641 22 : if (!found)
7642 0 : ereport(ERROR,
7643 : (errcode(ERRCODE_UNDEFINED_OBJECT),
7644 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
7645 : constrName, RelationGetRelationName(rel))));
7646 :
7647 40 : if (con->contype != CONSTRAINT_FOREIGN &&
7648 18 : con->contype != CONSTRAINT_CHECK)
7649 0 : ereport(ERROR,
7650 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7651 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
7652 : constrName, RelationGetRelationName(rel))));
7653 :
7654 22 : if (!con->convalidated)
7655 : {
7656 : HeapTuple copyTuple;
7657 : Form_pg_constraint copy_con;
7658 :
7659 19 : if (con->contype == CONSTRAINT_FOREIGN)
7660 : {
7661 : Relation refrel;
7662 :
7663 : /*
7664 : * Triggers are already in place on both tables, so a concurrent
7665 : * write that alters the result here is not possible. Normally we
7666 : * can run a query here to do the validation, which would only
7667 : * require AccessShareLock. In some cases, it is possible that we
7668 : * might need to fire triggers to perform the check, so we take a
7669 : * lock at RowShareLock level just in case.
7670 : */
7671 3 : refrel = heap_open(con->confrelid, RowShareLock);
7672 :
7673 6 : validateForeignKeyConstraint(constrName, rel, refrel,
7674 : con->conindid,
7675 6 : HeapTupleGetOid(tuple));
7676 2 : heap_close(refrel, NoLock);
7677 :
7678 : /*
7679 : * Foreign keys do not inherit, so we purposely ignore the
7680 : * recursion bit here
7681 : */
7682 : }
7683 16 : else if (con->contype == CONSTRAINT_CHECK)
7684 : {
7685 16 : List *children = NIL;
7686 : ListCell *child;
7687 :
7688 : /*
7689 : * If we're recursing, the parent has already done this, so skip
7690 : * it. Also, if the constraint is a NO INHERIT constraint, we
7691 : * shouldn't try to look for it in the children.
7692 : */
7693 16 : if (!recursing && !con->connoinherit)
7694 8 : children = find_all_inheritors(RelationGetRelid(rel),
7695 : lockmode, NULL);
7696 :
7697 : /*
7698 : * For CHECK constraints, we must ensure that we only mark the
7699 : * constraint as validated on the parent if it's already validated
7700 : * on the children.
7701 : *
7702 : * We recurse before validating on the parent, to reduce risk of
7703 : * deadlocks.
7704 : */
7705 30 : foreach(child, children)
7706 : {
7707 15 : Oid childoid = lfirst_oid(child);
7708 : Relation childrel;
7709 :
7710 15 : if (childoid == RelationGetRelid(rel))
7711 8 : continue;
7712 :
7713 : /*
7714 : * If we are told not to recurse, there had better not be any
7715 : * child tables, because we can't mark the constraint on the
7716 : * parent valid unless it is valid for all child tables.
7717 : */
7718 7 : if (!recurse)
7719 0 : ereport(ERROR,
7720 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7721 : errmsg("constraint must be validated on child tables too")));
7722 :
7723 : /* find_all_inheritors already got lock */
7724 7 : childrel = heap_open(childoid, NoLock);
7725 :
7726 7 : ATExecValidateConstraint(childrel, constrName, false,
7727 : true, lockmode);
7728 6 : heap_close(childrel, NoLock);
7729 : }
7730 :
7731 15 : validateCheckConstraint(rel, tuple);
7732 :
7733 : /*
7734 : * Invalidate relcache so that others see the new validated
7735 : * constraint.
7736 : */
7737 12 : CacheInvalidateRelcache(rel);
7738 : }
7739 :
7740 : /*
7741 : * Now update the catalog, while we have the door open.
7742 : */
7743 14 : copyTuple = heap_copytuple(tuple);
7744 14 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
7745 14 : copy_con->convalidated = true;
7746 14 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
7747 :
7748 14 : InvokeObjectPostAlterHook(ConstraintRelationId,
7749 : HeapTupleGetOid(tuple), 0);
7750 :
7751 14 : heap_freetuple(copyTuple);
7752 :
7753 14 : ObjectAddressSet(address, ConstraintRelationId,
7754 : HeapTupleGetOid(tuple));
7755 : }
7756 : else
7757 3 : address = InvalidObjectAddress; /* already validated */
7758 :
7759 17 : systable_endscan(scan);
7760 :
7761 17 : heap_close(conrel, RowExclusiveLock);
7762 :
7763 17 : return address;
7764 : }
7765 :
7766 :
7767 : /*
7768 : * transformColumnNameList - transform list of column names
7769 : *
7770 : * Lookup each name and return its attnum and type OID
7771 : */
7772 : static int
7773 199 : transformColumnNameList(Oid relId, List *colList,
7774 : int16 *attnums, Oid *atttypids)
7775 : {
7776 : ListCell *l;
7777 : int attnum;
7778 :
7779 199 : attnum = 0;
7780 476 : foreach(l, colList)
7781 : {
7782 285 : char *attname = strVal(lfirst(l));
7783 : HeapTuple atttuple;
7784 :
7785 285 : atttuple = SearchSysCacheAttName(relId, attname);
7786 285 : if (!HeapTupleIsValid(atttuple))
7787 8 : ereport(ERROR,
7788 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7789 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
7790 : attname)));
7791 277 : if (attnum >= INDEX_MAX_KEYS)
7792 0 : ereport(ERROR,
7793 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7794 : errmsg("cannot have more than %d keys in a foreign key",
7795 : INDEX_MAX_KEYS)));
7796 277 : attnums[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->attnum;
7797 277 : atttypids[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
7798 277 : ReleaseSysCache(atttuple);
7799 277 : attnum++;
7800 : }
7801 :
7802 191 : return attnum;
7803 : }
7804 :
7805 : /*
7806 : * transformFkeyGetPrimaryKey -
7807 : *
7808 : * Look up the names, attnums, and types of the primary key attributes
7809 : * for the pkrel. Also return the index OID and index opclasses of the
7810 : * index supporting the primary key.
7811 : *
7812 : * All parameters except pkrel are output parameters. Also, the function
7813 : * return value is the number of attributes in the primary key.
7814 : *
7815 : * Used when the column list in the REFERENCES specification is omitted.
7816 : */
7817 : static int
7818 73 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
7819 : List **attnamelist,
7820 : int16 *attnums, Oid *atttypids,
7821 : Oid *opclasses)
7822 : {
7823 : List *indexoidlist;
7824 : ListCell *indexoidscan;
7825 73 : HeapTuple indexTuple = NULL;
7826 73 : Form_pg_index indexStruct = NULL;
7827 : Datum indclassDatum;
7828 : bool isnull;
7829 : oidvector *indclass;
7830 : int i;
7831 :
7832 : /*
7833 : * Get the list of index OIDs for the table from the relcache, and look up
7834 : * each one in the pg_index syscache until we find one marked primary key
7835 : * (hopefully there isn't more than one such). Insist it's valid, too.
7836 : */
7837 73 : *indexOid = InvalidOid;
7838 :
7839 73 : indexoidlist = RelationGetIndexList(pkrel);
7840 :
7841 74 : foreach(indexoidscan, indexoidlist)
7842 : {
7843 74 : Oid indexoid = lfirst_oid(indexoidscan);
7844 :
7845 74 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7846 74 : if (!HeapTupleIsValid(indexTuple))
7847 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
7848 74 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
7849 74 : if (indexStruct->indisprimary && IndexIsValid(indexStruct))
7850 : {
7851 : /*
7852 : * Refuse to use a deferrable primary key. This is per SQL spec,
7853 : * and there would be a lot of interesting semantic problems if we
7854 : * tried to allow it.
7855 : */
7856 73 : if (!indexStruct->indimmediate)
7857 0 : ereport(ERROR,
7858 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7859 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
7860 : RelationGetRelationName(pkrel))));
7861 :
7862 73 : *indexOid = indexoid;
7863 73 : break;
7864 : }
7865 1 : ReleaseSysCache(indexTuple);
7866 : }
7867 :
7868 73 : list_free(indexoidlist);
7869 :
7870 : /*
7871 : * Check that we found it
7872 : */
7873 73 : if (!OidIsValid(*indexOid))
7874 0 : ereport(ERROR,
7875 : (errcode(ERRCODE_UNDEFINED_OBJECT),
7876 : errmsg("there is no primary key for referenced table \"%s\"",
7877 : RelationGetRelationName(pkrel))));
7878 :
7879 : /* Must get indclass the hard way */
7880 73 : indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
7881 : Anum_pg_index_indclass, &isnull);
7882 73 : Assert(!isnull);
7883 73 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
7884 :
7885 : /*
7886 : * Now build the list of PK attributes from the indkey definition (we
7887 : * assume a primary key cannot have expressional elements)
7888 : */
7889 73 : *attnamelist = NIL;
7890 164 : for (i = 0; i < indexStruct->indnatts; i++)
7891 : {
7892 91 : int pkattno = indexStruct->indkey.values[i];
7893 :
7894 91 : attnums[i] = pkattno;
7895 91 : atttypids[i] = attnumTypeId(pkrel, pkattno);
7896 91 : opclasses[i] = indclass->values[i];
7897 91 : *attnamelist = lappend(*attnamelist,
7898 91 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
7899 : }
7900 :
7901 73 : ReleaseSysCache(indexTuple);
7902 :
7903 73 : return i;
7904 : }
7905 :
7906 : /*
7907 : * transformFkeyCheckAttrs -
7908 : *
7909 : * Make sure that the attributes of a referenced table belong to a unique
7910 : * (or primary key) constraint. Return the OID of the index supporting
7911 : * the constraint, as well as the opclasses associated with the index
7912 : * columns.
7913 : */
7914 : static Oid
7915 57 : transformFkeyCheckAttrs(Relation pkrel,
7916 : int numattrs, int16 *attnums,
7917 : Oid *opclasses) /* output parameter */
7918 : {
7919 57 : Oid indexoid = InvalidOid;
7920 57 : bool found = false;
7921 57 : bool found_deferrable = false;
7922 : List *indexoidlist;
7923 : ListCell *indexoidscan;
7924 : int i,
7925 : j;
7926 :
7927 : /*
7928 : * Reject duplicate appearances of columns in the referenced-columns list.
7929 : * Such a case is forbidden by the SQL standard, and even if we thought it
7930 : * useful to allow it, there would be ambiguity about how to match the
7931 : * list to unique indexes (in particular, it'd be unclear which index
7932 : * opclass goes with which FK column).
7933 : */
7934 148 : for (i = 0; i < numattrs; i++)
7935 : {
7936 130 : for (j = i + 1; j < numattrs; j++)
7937 : {
7938 39 : if (attnums[i] == attnums[j])
7939 0 : ereport(ERROR,
7940 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
7941 : errmsg("foreign key referenced-columns list must not contain duplicates")));
7942 : }
7943 : }
7944 :
7945 : /*
7946 : * Get the list of index OIDs for the table from the relcache, and look up
7947 : * each one in the pg_index syscache, and match unique indexes to the list
7948 : * of attnums we are given.
7949 : */
7950 57 : indexoidlist = RelationGetIndexList(pkrel);
7951 :
7952 86 : foreach(indexoidscan, indexoidlist)
7953 : {
7954 : HeapTuple indexTuple;
7955 : Form_pg_index indexStruct;
7956 :
7957 84 : indexoid = lfirst_oid(indexoidscan);
7958 84 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7959 84 : if (!HeapTupleIsValid(indexTuple))
7960 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
7961 84 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
7962 :
7963 : /*
7964 : * Must have the right number of columns; must be unique and not a
7965 : * partial index; forget it if there are any expressions, too. Invalid
7966 : * indexes are out as well.
7967 : */
7968 150 : if (indexStruct->indnatts == numattrs &&
7969 130 : indexStruct->indisunique &&
7970 128 : IndexIsValid(indexStruct) &&
7971 128 : heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
7972 64 : heap_attisnull(indexTuple, Anum_pg_index_indexprs))
7973 : {
7974 : Datum indclassDatum;
7975 : bool isnull;
7976 : oidvector *indclass;
7977 :
7978 : /* Must get indclass the hard way */
7979 64 : indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
7980 : Anum_pg_index_indclass, &isnull);
7981 64 : Assert(!isnull);
7982 64 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
7983 :
7984 : /*
7985 : * The given attnum list may match the index columns in any order.
7986 : * Check for a match, and extract the appropriate opclasses while
7987 : * we're at it.
7988 : *
7989 : * We know that attnums[] is duplicate-free per the test at the
7990 : * start of this function, and we checked above that the number of
7991 : * index columns agrees, so if we find a match for each attnums[]
7992 : * entry then we must have a one-to-one match in some order.
7993 : */
7994 153 : for (i = 0; i < numattrs; i++)
7995 : {
7996 98 : found = false;
7997 146 : for (j = 0; j < numattrs; j++)
7998 : {
7999 137 : if (attnums[i] == indexStruct->indkey.values[j])
8000 : {
8001 89 : opclasses[i] = indclass->values[j];
8002 89 : found = true;
8003 89 : break;
8004 : }
8005 : }
8006 98 : if (!found)
8007 9 : break;
8008 : }
8009 :
8010 : /*
8011 : * Refuse to use a deferrable unique/primary key. This is per SQL
8012 : * spec, and there would be a lot of interesting semantic problems
8013 : * if we tried to allow it.
8014 : */
8015 64 : if (found && !indexStruct->indimmediate)
8016 : {
8017 : /*
8018 : * Remember that we found an otherwise matching index, so that
8019 : * we can generate a more appropriate error message.
8020 : */
8021 0 : found_deferrable = true;
8022 0 : found = false;
8023 : }
8024 : }
8025 84 : ReleaseSysCache(indexTuple);
8026 84 : if (found)
8027 55 : break;
8028 : }
8029 :
8030 57 : if (!found)
8031 : {
8032 2 : if (found_deferrable)
8033 0 : ereport(ERROR,
8034 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8035 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
8036 : RelationGetRelationName(pkrel))));
8037 : else
8038 2 : ereport(ERROR,
8039 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
8040 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
8041 : RelationGetRelationName(pkrel))));
8042 : }
8043 :
8044 55 : list_free(indexoidlist);
8045 :
8046 55 : return indexoid;
8047 : }
8048 :
8049 : /*
8050 : * findFkeyCast -
8051 : *
8052 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
8053 : * Caller has equal regard for binary coercibility and for an exact match.
8054 : */
8055 : static CoercionPathType
8056 2 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
8057 : {
8058 : CoercionPathType ret;
8059 :
8060 2 : if (targetTypeId == sourceTypeId)
8061 : {
8062 2 : ret = COERCION_PATH_RELABELTYPE;
8063 2 : *funcid = InvalidOid;
8064 : }
8065 : else
8066 : {
8067 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
8068 : COERCION_IMPLICIT, funcid);
8069 0 : if (ret == COERCION_PATH_NONE)
8070 : /* A previously-relied-upon cast is now gone. */
8071 0 : elog(ERROR, "could not find cast from %u to %u",
8072 : sourceTypeId, targetTypeId);
8073 : }
8074 :
8075 2 : return ret;
8076 : }
8077 :
8078 : /*
8079 : * Permissions checks on the referenced table for ADD FOREIGN KEY
8080 : *
8081 : * Note: we have already checked that the user owns the referencing table,
8082 : * else we'd have failed much earlier; no additional checks are needed for it.
8083 : */
8084 : static void
8085 128 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
8086 : {
8087 128 : Oid roleid = GetUserId();
8088 : AclResult aclresult;
8089 : int i;
8090 :
8091 : /* Okay if we have relation-level REFERENCES permission */
8092 128 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
8093 : ACL_REFERENCES);
8094 128 : if (aclresult == ACLCHECK_OK)
8095 256 : return;
8096 : /* Else we must have REFERENCES on each column */
8097 0 : for (i = 0; i < natts; i++)
8098 : {
8099 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
8100 : roleid, ACL_REFERENCES);
8101 0 : if (aclresult != ACLCHECK_OK)
8102 0 : aclcheck_error(aclresult, ACL_KIND_CLASS,
8103 0 : RelationGetRelationName(rel));
8104 : }
8105 : }
8106 :
8107 : /*
8108 : * Scan the existing rows in a table to verify they meet a proposed
8109 : * CHECK constraint.
8110 : *
8111 : * The caller must have opened and locked the relation appropriately.
8112 : */
8113 : static void
8114 15 : validateCheckConstraint(Relation rel, HeapTuple constrtup)
8115 : {
8116 : EState *estate;
8117 : Datum val;
8118 : char *conbin;
8119 : Expr *origexpr;
8120 : ExprState *exprstate;
8121 : TupleDesc tupdesc;
8122 : HeapScanDesc scan;
8123 : HeapTuple tuple;
8124 : ExprContext *econtext;
8125 : MemoryContext oldcxt;
8126 : TupleTableSlot *slot;
8127 : Form_pg_constraint constrForm;
8128 : bool isnull;
8129 : Snapshot snapshot;
8130 :
8131 : /*
8132 : * VALIDATE CONSTRAINT is a no-op for foreign tables and partitioned
8133 : * tables.
8134 : */
8135 29 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
8136 14 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8137 14 : return;
8138 :
8139 13 : constrForm = (Form_pg_constraint) GETSTRUCT(constrtup);
8140 :
8141 13 : estate = CreateExecutorState();
8142 :
8143 : /*
8144 : * XXX this tuple doesn't really come from a syscache, but this doesn't
8145 : * matter to SysCacheGetAttr, because it only wants to be able to fetch
8146 : * the tupdesc
8147 : */
8148 13 : val = SysCacheGetAttr(CONSTROID, constrtup, Anum_pg_constraint_conbin,
8149 : &isnull);
8150 13 : if (isnull)
8151 0 : elog(ERROR, "null conbin for constraint %u",
8152 : HeapTupleGetOid(constrtup));
8153 13 : conbin = TextDatumGetCString(val);
8154 13 : origexpr = (Expr *) stringToNode(conbin);
8155 13 : exprstate = ExecPrepareExpr(origexpr, estate);
8156 :
8157 13 : econtext = GetPerTupleExprContext(estate);
8158 13 : tupdesc = RelationGetDescr(rel);
8159 13 : slot = MakeSingleTupleTableSlot(tupdesc);
8160 13 : econtext->ecxt_scantuple = slot;
8161 :
8162 13 : snapshot = RegisterSnapshot(GetLatestSnapshot());
8163 13 : scan = heap_beginscan(rel, snapshot, 0, NULL);
8164 :
8165 : /*
8166 : * Switch to per-tuple memory context and reset it for each tuple
8167 : * produced, so we don't leak memory.
8168 : */
8169 13 : oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
8170 :
8171 32 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
8172 : {
8173 9 : ExecStoreTuple(tuple, slot, InvalidBuffer, false);
8174 :
8175 9 : if (!ExecCheck(exprstate, econtext))
8176 3 : ereport(ERROR,
8177 : (errcode(ERRCODE_CHECK_VIOLATION),
8178 : errmsg("check constraint \"%s\" is violated by some row",
8179 : NameStr(constrForm->conname)),
8180 : errtableconstraint(rel, NameStr(constrForm->conname))));
8181 :
8182 6 : ResetExprContext(econtext);
8183 : }
8184 :
8185 10 : MemoryContextSwitchTo(oldcxt);
8186 10 : heap_endscan(scan);
8187 10 : UnregisterSnapshot(snapshot);
8188 10 : ExecDropSingleTupleTableSlot(slot);
8189 10 : FreeExecutorState(estate);
8190 : }
8191 :
8192 : /*
8193 : * Scan the existing rows in a table to verify they meet a proposed FK
8194 : * constraint.
8195 : *
8196 : * Caller must have opened and locked both relations appropriately.
8197 : */
8198 : static void
8199 26 : validateForeignKeyConstraint(char *conname,
8200 : Relation rel,
8201 : Relation pkrel,
8202 : Oid pkindOid,
8203 : Oid constraintOid)
8204 : {
8205 : HeapScanDesc scan;
8206 : HeapTuple tuple;
8207 : Trigger trig;
8208 : Snapshot snapshot;
8209 :
8210 26 : ereport(DEBUG1,
8211 : (errmsg("validating foreign key constraint \"%s\"", conname)));
8212 :
8213 : /*
8214 : * Build a trigger call structure; we'll need it either way.
8215 : */
8216 26 : MemSet(&trig, 0, sizeof(trig));
8217 26 : trig.tgoid = InvalidOid;
8218 26 : trig.tgname = conname;
8219 26 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
8220 26 : trig.tgisinternal = TRUE;
8221 26 : trig.tgconstrrelid = RelationGetRelid(pkrel);
8222 26 : trig.tgconstrindid = pkindOid;
8223 26 : trig.tgconstraint = constraintOid;
8224 26 : trig.tgdeferrable = FALSE;
8225 26 : trig.tginitdeferred = FALSE;
8226 : /* we needn't fill in remaining fields */
8227 :
8228 : /*
8229 : * See if we can do it with a single LEFT JOIN query. A FALSE result
8230 : * indicates we must proceed with the fire-the-trigger method.
8231 : */
8232 26 : if (RI_Initial_Check(&trig, rel, pkrel))
8233 46 : return;
8234 :
8235 : /*
8236 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
8237 : * if that tuple had just been inserted. If any of those fail, it should
8238 : * ereport(ERROR) and that's that.
8239 : */
8240 0 : snapshot = RegisterSnapshot(GetLatestSnapshot());
8241 0 : scan = heap_beginscan(rel, snapshot, 0, NULL);
8242 :
8243 0 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
8244 : {
8245 : FunctionCallInfoData fcinfo;
8246 : TriggerData trigdata;
8247 :
8248 : /*
8249 : * Make a call to the trigger function
8250 : *
8251 : * No parameters are passed, but we do set a context
8252 : */
8253 0 : MemSet(&fcinfo, 0, sizeof(fcinfo));
8254 :
8255 : /*
8256 : * We assume RI_FKey_check_ins won't look at flinfo...
8257 : */
8258 0 : trigdata.type = T_TriggerData;
8259 0 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
8260 0 : trigdata.tg_relation = rel;
8261 0 : trigdata.tg_trigtuple = tuple;
8262 0 : trigdata.tg_newtuple = NULL;
8263 0 : trigdata.tg_trigger = &trig;
8264 0 : trigdata.tg_trigtuplebuf = scan->rs_cbuf;
8265 0 : trigdata.tg_newtuplebuf = InvalidBuffer;
8266 :
8267 0 : fcinfo.context = (Node *) &trigdata;
8268 :
8269 0 : RI_FKey_check_ins(&fcinfo);
8270 : }
8271 :
8272 0 : heap_endscan(scan);
8273 0 : UnregisterSnapshot(snapshot);
8274 : }
8275 :
8276 : static void
8277 186 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
8278 : Oid constraintOid, Oid indexOid, bool on_insert)
8279 : {
8280 : CreateTrigStmt *fk_trigger;
8281 :
8282 : /*
8283 : * Note: for a self-referential FK (referencing and referenced tables are
8284 : * the same), it is important that the ON UPDATE action fires before the
8285 : * CHECK action, since both triggers will fire on the same row during an
8286 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
8287 : * state of the row. Triggers fire in name order, so we ensure this by
8288 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
8289 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
8290 : */
8291 186 : fk_trigger = makeNode(CreateTrigStmt);
8292 186 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
8293 186 : fk_trigger->relation = NULL;
8294 186 : fk_trigger->row = true;
8295 186 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
8296 :
8297 : /* Either ON INSERT or ON UPDATE */
8298 186 : if (on_insert)
8299 : {
8300 93 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
8301 93 : fk_trigger->events = TRIGGER_TYPE_INSERT;
8302 : }
8303 : else
8304 : {
8305 93 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
8306 93 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
8307 : }
8308 :
8309 186 : fk_trigger->columns = NIL;
8310 186 : fk_trigger->transitionRels = NIL;
8311 186 : fk_trigger->whenClause = NULL;
8312 186 : fk_trigger->isconstraint = true;
8313 186 : fk_trigger->deferrable = fkconstraint->deferrable;
8314 186 : fk_trigger->initdeferred = fkconstraint->initdeferred;
8315 186 : fk_trigger->constrrel = NULL;
8316 186 : fk_trigger->args = NIL;
8317 :
8318 186 : (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid,
8319 : indexOid, true);
8320 :
8321 : /* Make changes-so-far visible */
8322 186 : CommandCounterIncrement();
8323 186 : }
8324 :
8325 : /*
8326 : * Create the triggers that implement an FK constraint.
8327 : *
8328 : * NB: if you change any trigger properties here, see also
8329 : * ATExecAlterConstraint.
8330 : */
8331 : static void
8332 93 : createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
8333 : Oid constraintOid, Oid indexOid)
8334 : {
8335 : Oid myRelOid;
8336 : CreateTrigStmt *fk_trigger;
8337 :
8338 93 : myRelOid = RelationGetRelid(rel);
8339 :
8340 : /* Make changes-so-far visible */
8341 93 : CommandCounterIncrement();
8342 :
8343 : /*
8344 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
8345 : * DELETE action on the referenced table.
8346 : */
8347 93 : fk_trigger = makeNode(CreateTrigStmt);
8348 93 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
8349 93 : fk_trigger->relation = NULL;
8350 93 : fk_trigger->row = true;
8351 93 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
8352 93 : fk_trigger->events = TRIGGER_TYPE_DELETE;
8353 93 : fk_trigger->columns = NIL;
8354 93 : fk_trigger->transitionRels = NIL;
8355 93 : fk_trigger->whenClause = NULL;
8356 93 : fk_trigger->isconstraint = true;
8357 93 : fk_trigger->constrrel = NULL;
8358 93 : switch (fkconstraint->fk_del_action)
8359 : {
8360 : case FKCONSTR_ACTION_NOACTION:
8361 72 : fk_trigger->deferrable = fkconstraint->deferrable;
8362 72 : fk_trigger->initdeferred = fkconstraint->initdeferred;
8363 72 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
8364 72 : break;
8365 : case FKCONSTR_ACTION_RESTRICT:
8366 0 : fk_trigger->deferrable = false;
8367 0 : fk_trigger->initdeferred = false;
8368 0 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
8369 0 : break;
8370 : case FKCONSTR_ACTION_CASCADE:
8371 11 : fk_trigger->deferrable = false;
8372 11 : fk_trigger->initdeferred = false;
8373 11 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
8374 11 : break;
8375 : case FKCONSTR_ACTION_SETNULL:
8376 7 : fk_trigger->deferrable = false;
8377 7 : fk_trigger->initdeferred = false;
8378 7 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
8379 7 : break;
8380 : case FKCONSTR_ACTION_SETDEFAULT:
8381 3 : fk_trigger->deferrable = false;
8382 3 : fk_trigger->initdeferred = false;
8383 3 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
8384 3 : break;
8385 : default:
8386 0 : elog(ERROR, "unrecognized FK action type: %d",
8387 : (int) fkconstraint->fk_del_action);
8388 : break;
8389 : }
8390 93 : fk_trigger->args = NIL;
8391 :
8392 93 : (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid,
8393 : indexOid, true);
8394 :
8395 : /* Make changes-so-far visible */
8396 93 : CommandCounterIncrement();
8397 :
8398 : /*
8399 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
8400 : * UPDATE action on the referenced table.
8401 : */
8402 93 : fk_trigger = makeNode(CreateTrigStmt);
8403 93 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
8404 93 : fk_trigger->relation = NULL;
8405 93 : fk_trigger->row = true;
8406 93 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
8407 93 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
8408 93 : fk_trigger->columns = NIL;
8409 93 : fk_trigger->transitionRels = NIL;
8410 93 : fk_trigger->whenClause = NULL;
8411 93 : fk_trigger->isconstraint = true;
8412 93 : fk_trigger->constrrel = NULL;
8413 93 : switch (fkconstraint->fk_upd_action)
8414 : {
8415 : case FKCONSTR_ACTION_NOACTION:
8416 79 : fk_trigger->deferrable = fkconstraint->deferrable;
8417 79 : fk_trigger->initdeferred = fkconstraint->initdeferred;
8418 79 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
8419 79 : break;
8420 : case FKCONSTR_ACTION_RESTRICT:
8421 1 : fk_trigger->deferrable = false;
8422 1 : fk_trigger->initdeferred = false;
8423 1 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
8424 1 : break;
8425 : case FKCONSTR_ACTION_CASCADE:
8426 7 : fk_trigger->deferrable = false;
8427 7 : fk_trigger->initdeferred = false;
8428 7 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
8429 7 : break;
8430 : case FKCONSTR_ACTION_SETNULL:
8431 4 : fk_trigger->deferrable = false;
8432 4 : fk_trigger->initdeferred = false;
8433 4 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
8434 4 : break;
8435 : case FKCONSTR_ACTION_SETDEFAULT:
8436 2 : fk_trigger->deferrable = false;
8437 2 : fk_trigger->initdeferred = false;
8438 2 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
8439 2 : break;
8440 : default:
8441 0 : elog(ERROR, "unrecognized FK action type: %d",
8442 : (int) fkconstraint->fk_upd_action);
8443 : break;
8444 : }
8445 93 : fk_trigger->args = NIL;
8446 :
8447 93 : (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid,
8448 : indexOid, true);
8449 :
8450 : /* Make changes-so-far visible */
8451 93 : CommandCounterIncrement();
8452 :
8453 : /*
8454 : * Build and execute CREATE CONSTRAINT TRIGGER statements for the CHECK
8455 : * action for both INSERTs and UPDATEs on the referencing table.
8456 : */
8457 93 : CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
8458 : indexOid, true);
8459 93 : CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
8460 : indexOid, false);
8461 93 : }
8462 :
8463 : /*
8464 : * ALTER TABLE DROP CONSTRAINT
8465 : *
8466 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
8467 : */
8468 : static void
8469 51 : ATExecDropConstraint(Relation rel, const char *constrName,
8470 : DropBehavior behavior,
8471 : bool recurse, bool recursing,
8472 : bool missing_ok, LOCKMODE lockmode)
8473 : {
8474 : List *children;
8475 : ListCell *child;
8476 : Relation conrel;
8477 : Form_pg_constraint con;
8478 : SysScanDesc scan;
8479 : ScanKeyData key;
8480 : HeapTuple tuple;
8481 51 : bool found = false;
8482 51 : bool is_no_inherit_constraint = false;
8483 :
8484 : /* At top level, permission check was done in ATPrepCmd, else do it */
8485 51 : if (recursing)
8486 6 : ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8487 :
8488 51 : conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
8489 :
8490 : /*
8491 : * Find and drop the target constraint
8492 : */
8493 51 : ScanKeyInit(&key,
8494 : Anum_pg_constraint_conrelid,
8495 : BTEqualStrategyNumber, F_OIDEQ,
8496 : ObjectIdGetDatum(RelationGetRelid(rel)));
8497 51 : scan = systable_beginscan(conrel, ConstraintRelidIndexId,
8498 : true, NULL, 1, &key);
8499 :
8500 51 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
8501 : {
8502 : ObjectAddress conobj;
8503 :
8504 62 : con = (Form_pg_constraint) GETSTRUCT(tuple);
8505 :
8506 62 : if (strcmp(NameStr(con->conname), constrName) != 0)
8507 15 : continue;
8508 :
8509 : /* Don't drop inherited constraints */
8510 47 : if (con->coninhcount > 0 && !recursing)
8511 3 : ereport(ERROR,
8512 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8513 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
8514 : constrName, RelationGetRelationName(rel))));
8515 :
8516 44 : is_no_inherit_constraint = con->connoinherit;
8517 :
8518 : /*
8519 : * If it's a foreign-key constraint, we'd better lock the referenced
8520 : * table and check that that's not in use, just as we've already done
8521 : * for the constrained table (else we might, eg, be dropping a trigger
8522 : * that has unfired events). But we can/must skip that in the
8523 : * self-referential case.
8524 : */
8525 48 : if (con->contype == CONSTRAINT_FOREIGN &&
8526 4 : con->confrelid != RelationGetRelid(rel))
8527 : {
8528 : Relation frel;
8529 :
8530 : /* Must match lock taken by RemoveTriggerById: */
8531 4 : frel = heap_open(con->confrelid, AccessExclusiveLock);
8532 4 : CheckTableNotInUse(frel, "ALTER TABLE");
8533 3 : heap_close(frel, NoLock);
8534 : }
8535 :
8536 : /*
8537 : * Perform the actual constraint deletion
8538 : */
8539 43 : conobj.classId = ConstraintRelationId;
8540 43 : conobj.objectId = HeapTupleGetOid(tuple);
8541 43 : conobj.objectSubId = 0;
8542 :
8543 43 : performDeletion(&conobj, behavior, 0);
8544 :
8545 38 : found = true;
8546 :
8547 : /* constraint found and dropped -- no need to keep looping */
8548 38 : break;
8549 : }
8550 :
8551 42 : systable_endscan(scan);
8552 :
8553 42 : if (!found)
8554 : {
8555 4 : if (!missing_ok)
8556 : {
8557 2 : ereport(ERROR,
8558 : (errcode(ERRCODE_UNDEFINED_OBJECT),
8559 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
8560 : constrName, RelationGetRelationName(rel))));
8561 : }
8562 : else
8563 : {
8564 2 : ereport(NOTICE,
8565 : (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
8566 : constrName, RelationGetRelationName(rel))));
8567 2 : heap_close(conrel, RowExclusiveLock);
8568 41 : return;
8569 : }
8570 : }
8571 :
8572 : /*
8573 : * Propagate to children as appropriate. Unlike most other ALTER
8574 : * routines, we have to do this one level of recursion at a time; we can't
8575 : * use find_all_inheritors to do it in one pass.
8576 : */
8577 38 : if (!is_no_inherit_constraint)
8578 21 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8579 : else
8580 17 : children = NIL;
8581 :
8582 : /*
8583 : * For a partitioned table, if partitions exist and we are told not to
8584 : * recurse, it's a user error. It doesn't make sense to have a constraint
8585 : * be defined only on the parent, especially if it's a partitioned table.
8586 : */
8587 38 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
8588 5 : children != NIL && !recurse)
8589 1 : ereport(ERROR,
8590 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8591 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
8592 : errhint("Do not specify the ONLY keyword.")));
8593 :
8594 45 : foreach(child, children)
8595 : {
8596 8 : Oid childrelid = lfirst_oid(child);
8597 : Relation childrel;
8598 : HeapTuple copy_tuple;
8599 :
8600 : /* find_inheritance_children already got lock */
8601 8 : childrel = heap_open(childrelid, NoLock);
8602 8 : CheckTableNotInUse(childrel, "ALTER TABLE");
8603 :
8604 8 : ScanKeyInit(&key,
8605 : Anum_pg_constraint_conrelid,
8606 : BTEqualStrategyNumber, F_OIDEQ,
8607 : ObjectIdGetDatum(childrelid));
8608 8 : scan = systable_beginscan(conrel, ConstraintRelidIndexId,
8609 : true, NULL, 1, &key);
8610 :
8611 : /* scan for matching tuple - there should only be one */
8612 8 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
8613 : {
8614 8 : con = (Form_pg_constraint) GETSTRUCT(tuple);
8615 :
8616 : /* Right now only CHECK constraints can be inherited */
8617 8 : if (con->contype != CONSTRAINT_CHECK)
8618 0 : continue;
8619 :
8620 8 : if (strcmp(NameStr(con->conname), constrName) == 0)
8621 8 : break;
8622 : }
8623 :
8624 8 : if (!HeapTupleIsValid(tuple))
8625 0 : ereport(ERROR,
8626 : (errcode(ERRCODE_UNDEFINED_OBJECT),
8627 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
8628 : constrName,
8629 : RelationGetRelationName(childrel))));
8630 :
8631 8 : copy_tuple = heap_copytuple(tuple);
8632 :
8633 8 : systable_endscan(scan);
8634 :
8635 8 : con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
8636 :
8637 8 : if (con->coninhcount <= 0) /* shouldn't happen */
8638 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
8639 : childrelid, constrName);
8640 :
8641 8 : if (recurse)
8642 : {
8643 : /*
8644 : * If the child constraint has other definition sources, just
8645 : * decrement its inheritance count; if not, recurse to delete it.
8646 : */
8647 7 : if (con->coninhcount == 1 && !con->conislocal)
8648 : {
8649 : /* Time to delete this child constraint, too */
8650 6 : ATExecDropConstraint(childrel, constrName, behavior,
8651 : true, true,
8652 : false, lockmode);
8653 : }
8654 : else
8655 : {
8656 : /* Child constraint must survive my deletion */
8657 1 : con->coninhcount--;
8658 1 : CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
8659 :
8660 : /* Make update visible */
8661 1 : CommandCounterIncrement();
8662 : }
8663 : }
8664 : else
8665 : {
8666 : /*
8667 : * If we were told to drop ONLY in this table (no recursion), we
8668 : * need to mark the inheritors' constraints as locally defined
8669 : * rather than inherited.
8670 : */
8671 1 : con->coninhcount--;
8672 1 : con->conislocal = true;
8673 :
8674 1 : CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
8675 :
8676 : /* Make update visible */
8677 1 : CommandCounterIncrement();
8678 : }
8679 :
8680 8 : heap_freetuple(copy_tuple);
8681 :
8682 8 : heap_close(childrel, NoLock);
8683 : }
8684 :
8685 37 : heap_close(conrel, RowExclusiveLock);
8686 : }
8687 :
8688 : /*
8689 : * ALTER COLUMN TYPE
8690 : */
8691 : static void
8692 90 : ATPrepAlterColumnType(List **wqueue,
8693 : AlteredTableInfo *tab, Relation rel,
8694 : bool recurse, bool recursing,
8695 : AlterTableCmd *cmd, LOCKMODE lockmode)
8696 : {
8697 90 : char *colName = cmd->name;
8698 90 : ColumnDef *def = (ColumnDef *) cmd->def;
8699 90 : TypeName *typeName = def->typeName;
8700 90 : Node *transform = def->cooked_default;
8701 : HeapTuple tuple;
8702 : Form_pg_attribute attTup;
8703 : AttrNumber attnum;
8704 : Oid targettype;
8705 : int32 targettypmod;
8706 : Oid targetcollid;
8707 : NewColumnValue *newval;
8708 90 : ParseState *pstate = make_parsestate(NULL);
8709 : AclResult aclresult;
8710 : bool is_expr;
8711 :
8712 90 : if (rel->rd_rel->reloftype && !recursing)
8713 1 : ereport(ERROR,
8714 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8715 : errmsg("cannot alter column type of typed table")));
8716 :
8717 : /* lookup the attribute so we can check inheritance status */
8718 89 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8719 89 : if (!HeapTupleIsValid(tuple))
8720 0 : ereport(ERROR,
8721 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8722 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8723 : colName, RelationGetRelationName(rel))));
8724 89 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8725 89 : attnum = attTup->attnum;
8726 :
8727 : /* Can't alter a system attribute */
8728 89 : if (attnum <= 0)
8729 0 : ereport(ERROR,
8730 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8731 : errmsg("cannot alter system column \"%s\"",
8732 : colName)));
8733 :
8734 : /* Don't alter inherited columns */
8735 89 : if (attTup->attinhcount > 0 && !recursing)
8736 1 : ereport(ERROR,
8737 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8738 : errmsg("cannot alter inherited column \"%s\"",
8739 : colName)));
8740 :
8741 : /* Don't alter columns used in the partition key */
8742 88 : if (is_partition_attr(rel, attnum, &is_expr))
8743 : {
8744 3 : if (!is_expr)
8745 2 : ereport(ERROR,
8746 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8747 : errmsg("cannot alter type of column named in partition key")));
8748 : else
8749 1 : ereport(ERROR,
8750 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8751 : errmsg("cannot alter type of column referenced in partition key expression")));
8752 : }
8753 :
8754 : /* Look up the target type */
8755 85 : typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
8756 :
8757 85 : aclresult = pg_type_aclcheck(targettype, GetUserId(), ACL_USAGE);
8758 85 : if (aclresult != ACLCHECK_OK)
8759 2 : aclcheck_error_type(aclresult, targettype);
8760 :
8761 : /* And the collation */
8762 83 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
8763 :
8764 : /* make sure datatype is legal for a column */
8765 83 : CheckAttributeType(colName, targettype, targetcollid,
8766 83 : list_make1_oid(rel->rd_rel->reltype),
8767 : false);
8768 :
8769 100 : if (tab->relkind == RELKIND_RELATION ||
8770 18 : tab->relkind == RELKIND_PARTITIONED_TABLE)
8771 : {
8772 : /*
8773 : * Set up an expression to transform the old data value to the new
8774 : * type. If a USING option was given, use the expression as
8775 : * transformed by transformAlterTableStmt, else just take the old
8776 : * value and try to coerce it. We do this first so that type
8777 : * incompatibility can be detected before we waste effort, and because
8778 : * we need the expression to be parsed against the original table row
8779 : * type.
8780 : */
8781 65 : if (!transform)
8782 : {
8783 41 : transform = (Node *) makeVar(1, attnum,
8784 : attTup->atttypid, attTup->atttypmod,
8785 : attTup->attcollation,
8786 : 0);
8787 : }
8788 :
8789 65 : transform = coerce_to_target_type(pstate,
8790 : transform, exprType(transform),
8791 : targettype, targettypmod,
8792 : COERCION_ASSIGNMENT,
8793 : COERCE_IMPLICIT_CAST,
8794 : -1);
8795 65 : if (transform == NULL)
8796 : {
8797 : /* error text depends on whether USING was specified or not */
8798 3 : if (def->cooked_default != NULL)
8799 1 : ereport(ERROR,
8800 : (errcode(ERRCODE_DATATYPE_MISMATCH),
8801 : errmsg("result of USING clause for column \"%s\""
8802 : " cannot be cast automatically to type %s",
8803 : colName, format_type_be(targettype)),
8804 : errhint("You might need to add an explicit cast.")));
8805 : else
8806 2 : ereport(ERROR,
8807 : (errcode(ERRCODE_DATATYPE_MISMATCH),
8808 : errmsg("column \"%s\" cannot be cast automatically to type %s",
8809 : colName, format_type_be(targettype)),
8810 : /* translator: USING is SQL, don't translate it */
8811 : errhint("You might need to specify \"USING %s::%s\".",
8812 : quote_identifier(colName),
8813 : format_type_with_typemod(targettype,
8814 : targettypmod))));
8815 : }
8816 :
8817 : /* Fix collations after all else */
8818 62 : assign_expr_collations(pstate, transform);
8819 :
8820 : /* Plan the expr now so we can accurately assess the need to rewrite. */
8821 62 : transform = (Node *) expression_planner((Expr *) transform);
8822 :
8823 : /*
8824 : * Add a work queue item to make ATRewriteTable update the column
8825 : * contents.
8826 : */
8827 62 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8828 62 : newval->attnum = attnum;
8829 62 : newval->expr = (Expr *) transform;
8830 :
8831 62 : tab->newvals = lappend(tab->newvals, newval);
8832 124 : if (ATColumnChangeRequiresRewrite(transform, attnum))
8833 51 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
8834 : }
8835 17 : else if (transform)
8836 2 : ereport(ERROR,
8837 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8838 : errmsg("\"%s\" is not a table",
8839 : RelationGetRelationName(rel))));
8840 :
8841 146 : if (tab->relkind == RELKIND_COMPOSITE_TYPE ||
8842 69 : tab->relkind == RELKIND_FOREIGN_TABLE)
8843 : {
8844 : /*
8845 : * For composite types, do this check now. Tables will check it later
8846 : * when the table is being rewritten.
8847 : */
8848 15 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
8849 : }
8850 :
8851 74 : ReleaseSysCache(tuple);
8852 :
8853 : /*
8854 : * Recurse manually by queueing a new command for each child, if
8855 : * necessary. We cannot apply ATSimpleRecursion here because we need to
8856 : * remap attribute numbers in the USING expression, if any.
8857 : *
8858 : * If we are told not to recurse, there had better not be any child
8859 : * tables; else the alter would put them out of step.
8860 : */
8861 74 : if (recurse)
8862 : {
8863 50 : Oid relid = RelationGetRelid(rel);
8864 : ListCell *child;
8865 : List *children;
8866 :
8867 50 : children = find_all_inheritors(relid, lockmode, NULL);
8868 :
8869 : /*
8870 : * find_all_inheritors does the recursive search of the inheritance
8871 : * hierarchy, so all we have to do is process all of the relids in the
8872 : * list that it returns.
8873 : */
8874 118 : foreach(child, children)
8875 : {
8876 71 : Oid childrelid = lfirst_oid(child);
8877 : Relation childrel;
8878 :
8879 71 : if (childrelid == relid)
8880 50 : continue;
8881 :
8882 : /* find_all_inheritors already got lock */
8883 21 : childrel = relation_open(childrelid, NoLock);
8884 21 : CheckTableNotInUse(childrel, "ALTER TABLE");
8885 :
8886 : /*
8887 : * Remap the attribute numbers. If no USING expression was
8888 : * specified, there is no need for this step.
8889 : */
8890 21 : if (def->cooked_default)
8891 : {
8892 : AttrNumber *attmap;
8893 : bool found_whole_row;
8894 :
8895 : /* create a copy to scribble on */
8896 10 : cmd = copyObject(cmd);
8897 :
8898 10 : attmap = convert_tuples_by_name_map(RelationGetDescr(childrel),
8899 : RelationGetDescr(rel),
8900 : gettext_noop("could not convert row type"));
8901 20 : ((ColumnDef *) cmd->def)->cooked_default =
8902 10 : map_variable_attnos(def->cooked_default,
8903 : 1, 0,
8904 10 : attmap, RelationGetDescr(rel)->natts,
8905 : InvalidOid, &found_whole_row);
8906 10 : if (found_whole_row)
8907 1 : ereport(ERROR,
8908 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8909 : errmsg("cannot convert whole-row table reference"),
8910 : errdetail("USING expression contains a whole-row table reference.")));
8911 9 : pfree(attmap);
8912 : }
8913 20 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
8914 18 : relation_close(childrel, NoLock);
8915 : }
8916 : }
8917 30 : else if (!recursing &&
8918 6 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
8919 0 : ereport(ERROR,
8920 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8921 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
8922 : colName)));
8923 :
8924 71 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
8925 6 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
8926 70 : }
8927 :
8928 : /*
8929 : * When the data type of a column is changed, a rewrite might not be required
8930 : * if the new type is sufficiently identical to the old one, and the USING
8931 : * clause isn't trying to insert some other value. It's safe to skip the
8932 : * rewrite if the old type is binary coercible to the new type, or if the
8933 : * new type is an unconstrained domain over the old type. In the case of a
8934 : * constrained domain, we could get by with scanning the table and checking
8935 : * the constraint rather than actually rewriting it, but we don't currently
8936 : * try to do that.
8937 : */
8938 : static bool
8939 62 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
8940 : {
8941 62 : Assert(expr != NULL);
8942 :
8943 : for (;;)
8944 : {
8945 : /* only one varno, so no need to check that */
8946 67 : if (IsA(expr, Var) &&((Var *) expr)->varattno == varattno)
8947 11 : return false;
8948 56 : else if (IsA(expr, RelabelType))
8949 4 : expr = (Node *) ((RelabelType *) expr)->arg;
8950 52 : else if (IsA(expr, CoerceToDomain))
8951 : {
8952 1 : CoerceToDomain *d = (CoerceToDomain *) expr;
8953 :
8954 1 : if (DomainHasConstraints(d->resulttype))
8955 0 : return true;
8956 1 : expr = (Node *) d->arg;
8957 : }
8958 : else
8959 51 : return true;
8960 5 : }
8961 : }
8962 :
8963 : /*
8964 : * ALTER COLUMN .. SET DATA TYPE
8965 : *
8966 : * Return the address of the modified column.
8967 : */
8968 : static ObjectAddress
8969 69 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
8970 : AlterTableCmd *cmd, LOCKMODE lockmode)
8971 : {
8972 69 : char *colName = cmd->name;
8973 69 : ColumnDef *def = (ColumnDef *) cmd->def;
8974 69 : TypeName *typeName = def->typeName;
8975 : HeapTuple heapTup;
8976 : Form_pg_attribute attTup,
8977 : attOldTup;
8978 : AttrNumber attnum;
8979 : HeapTuple typeTuple;
8980 : Form_pg_type tform;
8981 : Oid targettype;
8982 : int32 targettypmod;
8983 : Oid targetcollid;
8984 : Node *defaultexpr;
8985 : Relation attrelation;
8986 : Relation depRel;
8987 : ScanKeyData key[3];
8988 : SysScanDesc scan;
8989 : HeapTuple depTup;
8990 : ObjectAddress address;
8991 :
8992 69 : attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
8993 :
8994 : /* Look up the target column */
8995 69 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8996 69 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
8997 0 : ereport(ERROR,
8998 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8999 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9000 : colName, RelationGetRelationName(rel))));
9001 69 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
9002 69 : attnum = attTup->attnum;
9003 69 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
9004 :
9005 : /* Check for multiple ALTER TYPE on same column --- can't cope */
9006 138 : if (attTup->atttypid != attOldTup->atttypid ||
9007 69 : attTup->atttypmod != attOldTup->atttypmod)
9008 0 : ereport(ERROR,
9009 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9010 : errmsg("cannot alter type of column \"%s\" twice",
9011 : colName)));
9012 :
9013 : /* Look up the target type (should not fail, since prep found it) */
9014 69 : typeTuple = typenameType(NULL, typeName, &targettypmod);
9015 69 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
9016 69 : targettype = HeapTupleGetOid(typeTuple);
9017 : /* And the collation */
9018 69 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
9019 :
9020 : /*
9021 : * If there is a default expression for the column, get it and ensure we
9022 : * can coerce it to the new datatype. (We must do this before changing
9023 : * the column type, because build_column_default itself will try to
9024 : * coerce, and will not issue the error message we want if it fails.)
9025 : *
9026 : * We remove any implicit coercion steps at the top level of the old
9027 : * default expression; this has been agreed to satisfy the principle of
9028 : * least surprise. (The conversion to the new column type should act like
9029 : * it started from what the user sees as the stored expression, and the
9030 : * implicit coercions aren't going to be shown.)
9031 : */
9032 69 : if (attTup->atthasdef)
9033 : {
9034 2 : defaultexpr = build_column_default(rel, attnum);
9035 2 : Assert(defaultexpr);
9036 2 : defaultexpr = strip_implicit_coercions(defaultexpr);
9037 2 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
9038 : defaultexpr, exprType(defaultexpr),
9039 : targettype, targettypmod,
9040 : COERCION_ASSIGNMENT,
9041 : COERCE_IMPLICIT_CAST,
9042 : -1);
9043 2 : if (defaultexpr == NULL)
9044 1 : ereport(ERROR,
9045 : (errcode(ERRCODE_DATATYPE_MISMATCH),
9046 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
9047 : colName, format_type_be(targettype))));
9048 : }
9049 : else
9050 67 : defaultexpr = NULL;
9051 :
9052 : /*
9053 : * Find everything that depends on the column (constraints, indexes, etc),
9054 : * and record enough information to let us recreate the objects.
9055 : *
9056 : * The actual recreation does not happen here, but only after we have
9057 : * performed all the individual ALTER TYPE operations. We have to save
9058 : * the info before executing ALTER TYPE, though, else the deparser will
9059 : * get confused.
9060 : *
9061 : * There could be multiple entries for the same object, so we must check
9062 : * to ensure we process each one only once. Note: we assume that an index
9063 : * that implements a constraint will not show a direct dependency on the
9064 : * column.
9065 : */
9066 68 : depRel = heap_open(DependRelationId, RowExclusiveLock);
9067 :
9068 68 : ScanKeyInit(&key[0],
9069 : Anum_pg_depend_refclassid,
9070 : BTEqualStrategyNumber, F_OIDEQ,
9071 : ObjectIdGetDatum(RelationRelationId));
9072 68 : ScanKeyInit(&key[1],
9073 : Anum_pg_depend_refobjid,
9074 : BTEqualStrategyNumber, F_OIDEQ,
9075 : ObjectIdGetDatum(RelationGetRelid(rel)));
9076 68 : ScanKeyInit(&key[2],
9077 : Anum_pg_depend_refobjsubid,
9078 : BTEqualStrategyNumber, F_INT4EQ,
9079 : Int32GetDatum((int32) attnum));
9080 :
9081 68 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
9082 : NULL, 3, key);
9083 :
9084 185 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
9085 : {
9086 49 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
9087 : ObjectAddress foundObject;
9088 :
9089 : /* We don't expect any PIN dependencies on columns */
9090 49 : if (foundDep->deptype == DEPENDENCY_PIN)
9091 0 : elog(ERROR, "cannot alter type of a pinned column");
9092 :
9093 49 : foundObject.classId = foundDep->classid;
9094 49 : foundObject.objectId = foundDep->objid;
9095 49 : foundObject.objectSubId = foundDep->objsubid;
9096 :
9097 49 : switch (getObjectClass(&foundObject))
9098 : {
9099 : case OCLASS_CLASS:
9100 : {
9101 16 : char relKind = get_rel_relkind(foundObject.objectId);
9102 :
9103 16 : if (relKind == RELKIND_INDEX)
9104 : {
9105 12 : Assert(foundObject.objectSubId == 0);
9106 12 : if (!list_member_oid(tab->changedIndexOids, foundObject.objectId))
9107 : {
9108 12 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
9109 : foundObject.objectId);
9110 12 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
9111 12 : pg_get_indexdef_string(foundObject.objectId));
9112 : }
9113 : }
9114 4 : else if (relKind == RELKIND_SEQUENCE)
9115 : {
9116 : /*
9117 : * This must be a SERIAL column's sequence. We need
9118 : * not do anything to it.
9119 : */
9120 4 : Assert(foundObject.objectSubId == 0);
9121 : }
9122 : else
9123 : {
9124 : /* Not expecting any other direct dependencies... */
9125 0 : elog(ERROR, "unexpected object depending on column: %s",
9126 : getObjectDescription(&foundObject));
9127 : }
9128 16 : break;
9129 : }
9130 :
9131 : case OCLASS_CONSTRAINT:
9132 31 : Assert(foundObject.objectSubId == 0);
9133 31 : if (!list_member_oid(tab->changedConstraintOids,
9134 : foundObject.objectId))
9135 : {
9136 21 : char *defstring = pg_get_constraintdef_command(foundObject.objectId);
9137 :
9138 : /*
9139 : * Put NORMAL dependencies at the front of the list and
9140 : * AUTO dependencies at the back. This makes sure that
9141 : * foreign-key constraints depending on this column will
9142 : * be dropped before unique or primary-key constraints of
9143 : * the column; which we must have because the FK
9144 : * constraints depend on the indexes belonging to the
9145 : * unique constraints.
9146 : */
9147 21 : if (foundDep->deptype == DEPENDENCY_NORMAL)
9148 : {
9149 13 : tab->changedConstraintOids =
9150 13 : lcons_oid(foundObject.objectId,
9151 : tab->changedConstraintOids);
9152 13 : tab->changedConstraintDefs =
9153 13 : lcons(defstring,
9154 : tab->changedConstraintDefs);
9155 : }
9156 : else
9157 : {
9158 8 : tab->changedConstraintOids =
9159 8 : lappend_oid(tab->changedConstraintOids,
9160 : foundObject.objectId);
9161 8 : tab->changedConstraintDefs =
9162 8 : lappend(tab->changedConstraintDefs,
9163 : defstring);
9164 : }
9165 : }
9166 31 : break;
9167 :
9168 : case OCLASS_REWRITE:
9169 : /* XXX someday see if we can cope with revising views */
9170 0 : ereport(ERROR,
9171 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9172 : errmsg("cannot alter type of a column used by a view or rule"),
9173 : errdetail("%s depends on column \"%s\"",
9174 : getObjectDescription(&foundObject),
9175 : colName)));
9176 : break;
9177 :
9178 : case OCLASS_TRIGGER:
9179 :
9180 : /*
9181 : * A trigger can depend on a column because the column is
9182 : * specified as an update target, or because the column is
9183 : * used in the trigger's WHEN condition. The first case would
9184 : * not require any extra work, but the second case would
9185 : * require updating the WHEN expression, which will take a
9186 : * significant amount of new code. Since we can't easily tell
9187 : * which case applies, we punt for both. FIXME someday.
9188 : */
9189 0 : ereport(ERROR,
9190 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9191 : errmsg("cannot alter type of a column used in a trigger definition"),
9192 : errdetail("%s depends on column \"%s\"",
9193 : getObjectDescription(&foundObject),
9194 : colName)));
9195 : break;
9196 :
9197 : case OCLASS_POLICY:
9198 :
9199 : /*
9200 : * A policy can depend on a column because the column is
9201 : * specified in the policy's USING or WITH CHECK qual
9202 : * expressions. It might be possible to rewrite and recheck
9203 : * the policy expression, but punt for now. It's certainly
9204 : * easy enough to remove and recreate the policy; still, FIXME
9205 : * someday.
9206 : */
9207 0 : ereport(ERROR,
9208 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9209 : errmsg("cannot alter type of a column used in a policy definition"),
9210 : errdetail("%s depends on column \"%s\"",
9211 : getObjectDescription(&foundObject),
9212 : colName)));
9213 : break;
9214 :
9215 : case OCLASS_DEFAULT:
9216 :
9217 : /*
9218 : * Ignore the column's default expression, since we will fix
9219 : * it below.
9220 : */
9221 1 : Assert(defaultexpr);
9222 1 : break;
9223 :
9224 : case OCLASS_STATISTIC_EXT:
9225 :
9226 : /*
9227 : * Give the extended-stats machinery a chance to fix anything
9228 : * that this column type change would break.
9229 : */
9230 1 : UpdateStatisticsForTypeChange(foundObject.objectId,
9231 : RelationGetRelid(rel), attnum,
9232 : attTup->atttypid, targettype);
9233 1 : break;
9234 :
9235 : case OCLASS_PROC:
9236 : case OCLASS_TYPE:
9237 : case OCLASS_CAST:
9238 : case OCLASS_COLLATION:
9239 : case OCLASS_CONVERSION:
9240 : case OCLASS_LANGUAGE:
9241 : case OCLASS_LARGEOBJECT:
9242 : case OCLASS_OPERATOR:
9243 : case OCLASS_OPCLASS:
9244 : case OCLASS_OPFAMILY:
9245 : case OCLASS_AM:
9246 : case OCLASS_AMOP:
9247 : case OCLASS_AMPROC:
9248 : case OCLASS_SCHEMA:
9249 : case OCLASS_TSPARSER:
9250 : case OCLASS_TSDICT:
9251 : case OCLASS_TSTEMPLATE:
9252 : case OCLASS_TSCONFIG:
9253 : case OCLASS_ROLE:
9254 : case OCLASS_DATABASE:
9255 : case OCLASS_TBLSPACE:
9256 : case OCLASS_FDW:
9257 : case OCLASS_FOREIGN_SERVER:
9258 : case OCLASS_USER_MAPPING:
9259 : case OCLASS_DEFACL:
9260 : case OCLASS_EXTENSION:
9261 : case OCLASS_EVENT_TRIGGER:
9262 : case OCLASS_PUBLICATION:
9263 : case OCLASS_PUBLICATION_REL:
9264 : case OCLASS_SUBSCRIPTION:
9265 : case OCLASS_TRANSFORM:
9266 :
9267 : /*
9268 : * We don't expect any of these sorts of objects to depend on
9269 : * a column.
9270 : */
9271 0 : elog(ERROR, "unexpected object depending on column: %s",
9272 : getObjectDescription(&foundObject));
9273 : break;
9274 :
9275 : /*
9276 : * There's intentionally no default: case here; we want the
9277 : * compiler to warn if a new OCLASS hasn't been handled above.
9278 : */
9279 : }
9280 : }
9281 :
9282 68 : systable_endscan(scan);
9283 :
9284 : /*
9285 : * Now scan for dependencies of this column on other things. The only
9286 : * thing we should find is the dependency on the column datatype, which we
9287 : * want to remove, and possibly a collation dependency.
9288 : */
9289 68 : ScanKeyInit(&key[0],
9290 : Anum_pg_depend_classid,
9291 : BTEqualStrategyNumber, F_OIDEQ,
9292 : ObjectIdGetDatum(RelationRelationId));
9293 68 : ScanKeyInit(&key[1],
9294 : Anum_pg_depend_objid,
9295 : BTEqualStrategyNumber, F_OIDEQ,
9296 : ObjectIdGetDatum(RelationGetRelid(rel)));
9297 68 : ScanKeyInit(&key[2],
9298 : Anum_pg_depend_objsubid,
9299 : BTEqualStrategyNumber, F_INT4EQ,
9300 : Int32GetDatum((int32) attnum));
9301 :
9302 68 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
9303 : NULL, 3, key);
9304 :
9305 136 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
9306 : {
9307 0 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
9308 :
9309 0 : if (foundDep->deptype != DEPENDENCY_NORMAL)
9310 0 : elog(ERROR, "found unexpected dependency type '%c'",
9311 : foundDep->deptype);
9312 0 : if (!(foundDep->refclassid == TypeRelationId &&
9313 0 : foundDep->refobjid == attTup->atttypid) &&
9314 0 : !(foundDep->refclassid == CollationRelationId &&
9315 0 : foundDep->refobjid == attTup->attcollation))
9316 0 : elog(ERROR, "found unexpected dependency for column");
9317 :
9318 0 : CatalogTupleDelete(depRel, &depTup->t_self);
9319 : }
9320 :
9321 68 : systable_endscan(scan);
9322 :
9323 68 : heap_close(depRel, RowExclusiveLock);
9324 :
9325 : /*
9326 : * Here we go --- change the recorded column type and collation. (Note
9327 : * heapTup is a copy of the syscache entry, so okay to scribble on.)
9328 : */
9329 68 : attTup->atttypid = targettype;
9330 68 : attTup->atttypmod = targettypmod;
9331 68 : attTup->attcollation = targetcollid;
9332 68 : attTup->attndims = list_length(typeName->arrayBounds);
9333 68 : attTup->attlen = tform->typlen;
9334 68 : attTup->attbyval = tform->typbyval;
9335 68 : attTup->attalign = tform->typalign;
9336 68 : attTup->attstorage = tform->typstorage;
9337 :
9338 68 : ReleaseSysCache(typeTuple);
9339 :
9340 68 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
9341 :
9342 68 : heap_close(attrelation, RowExclusiveLock);
9343 :
9344 : /* Install dependencies on new datatype and collation */
9345 68 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
9346 68 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
9347 :
9348 : /*
9349 : * Drop any pg_statistic entry for the column, since it's now wrong type
9350 : */
9351 68 : RemoveStatistics(RelationGetRelid(rel), attnum);
9352 :
9353 68 : InvokeObjectPostAlterHook(RelationRelationId,
9354 : RelationGetRelid(rel), attnum);
9355 :
9356 : /*
9357 : * Update the default, if present, by brute force --- remove and re-add
9358 : * the default. Probably unsafe to take shortcuts, since the new version
9359 : * may well have additional dependencies. (It's okay to do this now,
9360 : * rather than after other ALTER TYPE commands, since the default won't
9361 : * depend on other column types.)
9362 : */
9363 68 : if (defaultexpr)
9364 : {
9365 : /* Must make new row visible since it will be updated again */
9366 1 : CommandCounterIncrement();
9367 :
9368 : /*
9369 : * We use RESTRICT here for safety, but at present we do not expect
9370 : * anything to depend on the default.
9371 : */
9372 1 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
9373 : true);
9374 :
9375 1 : StoreAttrDefault(rel, attnum, defaultexpr, true);
9376 : }
9377 :
9378 68 : ObjectAddressSubSet(address, RelationRelationId,
9379 : RelationGetRelid(rel), attnum);
9380 :
9381 : /* Cleanup */
9382 68 : heap_freetuple(heapTup);
9383 :
9384 68 : return address;
9385 : }
9386 :
9387 : /*
9388 : * Returns the address of the modified column
9389 : */
9390 : static ObjectAddress
9391 7 : ATExecAlterColumnGenericOptions(Relation rel,
9392 : const char *colName,
9393 : List *options,
9394 : LOCKMODE lockmode)
9395 : {
9396 : Relation ftrel;
9397 : Relation attrel;
9398 : ForeignServer *server;
9399 : ForeignDataWrapper *fdw;
9400 : HeapTuple tuple;
9401 : HeapTuple newtuple;
9402 : bool isnull;
9403 : Datum repl_val[Natts_pg_attribute];
9404 : bool repl_null[Natts_pg_attribute];
9405 : bool repl_repl[Natts_pg_attribute];
9406 : Datum datum;
9407 : Form_pg_foreign_table fttableform;
9408 : Form_pg_attribute atttableform;
9409 : AttrNumber attnum;
9410 : ObjectAddress address;
9411 :
9412 7 : if (options == NIL)
9413 0 : return InvalidObjectAddress;
9414 :
9415 : /* First, determine FDW validator associated to the foreign table. */
9416 7 : ftrel = heap_open(ForeignTableRelationId, AccessShareLock);
9417 7 : tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
9418 7 : if (!HeapTupleIsValid(tuple))
9419 0 : ereport(ERROR,
9420 : (errcode(ERRCODE_UNDEFINED_OBJECT),
9421 : errmsg("foreign table \"%s\" does not exist",
9422 : RelationGetRelationName(rel))));
9423 7 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
9424 7 : server = GetForeignServer(fttableform->ftserver);
9425 7 : fdw = GetForeignDataWrapper(server->fdwid);
9426 :
9427 7 : heap_close(ftrel, AccessShareLock);
9428 7 : ReleaseSysCache(tuple);
9429 :
9430 7 : attrel = heap_open(AttributeRelationId, RowExclusiveLock);
9431 7 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9432 7 : if (!HeapTupleIsValid(tuple))
9433 0 : ereport(ERROR,
9434 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9435 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9436 : colName, RelationGetRelationName(rel))));
9437 :
9438 : /* Prevent them from altering a system attribute */
9439 7 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
9440 7 : attnum = atttableform->attnum;
9441 7 : if (attnum <= 0)
9442 1 : ereport(ERROR,
9443 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9444 : errmsg("cannot alter system column \"%s\"", colName)));
9445 :
9446 :
9447 : /* Initialize buffers for new tuple values */
9448 6 : memset(repl_val, 0, sizeof(repl_val));
9449 6 : memset(repl_null, false, sizeof(repl_null));
9450 6 : memset(repl_repl, false, sizeof(repl_repl));
9451 :
9452 : /* Extract the current options */
9453 6 : datum = SysCacheGetAttr(ATTNAME,
9454 : tuple,
9455 : Anum_pg_attribute_attfdwoptions,
9456 : &isnull);
9457 6 : if (isnull)
9458 5 : datum = PointerGetDatum(NULL);
9459 :
9460 : /* Transform the options */
9461 6 : datum = transformGenericOptions(AttributeRelationId,
9462 : datum,
9463 : options,
9464 : fdw->fdwvalidator);
9465 :
9466 6 : if (PointerIsValid(DatumGetPointer(datum)))
9467 6 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
9468 : else
9469 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
9470 :
9471 6 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
9472 :
9473 : /* Everything looks good - update the tuple */
9474 :
9475 6 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
9476 : repl_val, repl_null, repl_repl);
9477 :
9478 6 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
9479 :
9480 6 : InvokeObjectPostAlterHook(RelationRelationId,
9481 : RelationGetRelid(rel),
9482 : atttableform->attnum);
9483 6 : ObjectAddressSubSet(address, RelationRelationId,
9484 : RelationGetRelid(rel), attnum);
9485 :
9486 6 : ReleaseSysCache(tuple);
9487 :
9488 6 : heap_close(attrel, RowExclusiveLock);
9489 :
9490 6 : heap_freetuple(newtuple);
9491 :
9492 6 : return address;
9493 : }
9494 :
9495 : /*
9496 : * Cleanup after we've finished all the ALTER TYPE operations for a
9497 : * particular relation. We have to drop and recreate all the indexes
9498 : * and constraints that depend on the altered columns.
9499 : */
9500 : static void
9501 67 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
9502 : {
9503 : ObjectAddress obj;
9504 : ListCell *def_item;
9505 : ListCell *oid_item;
9506 :
9507 : /*
9508 : * Re-parse the index and constraint definitions, and attach them to the
9509 : * appropriate work queue entries. We do this before dropping because in
9510 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
9511 : * lock on the table the constraint is attached to, and we need to get
9512 : * that before dropping. It's safe because the parser won't actually look
9513 : * at the catalogs to detect the existing entry.
9514 : *
9515 : * We can't rely on the output of deparsing to tell us which relation to
9516 : * operate on, because concurrent activity might have made the name
9517 : * resolve differently. Instead, we've got to use the OID of the
9518 : * constraint or index we're processing to figure out which relation to
9519 : * operate on.
9520 : */
9521 88 : forboth(oid_item, tab->changedConstraintOids,
9522 : def_item, tab->changedConstraintDefs)
9523 : {
9524 21 : Oid oldId = lfirst_oid(oid_item);
9525 : HeapTuple tup;
9526 : Form_pg_constraint con;
9527 : Oid relid;
9528 : Oid confrelid;
9529 : bool conislocal;
9530 :
9531 21 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
9532 21 : if (!HeapTupleIsValid(tup)) /* should not happen */
9533 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
9534 21 : con = (Form_pg_constraint) GETSTRUCT(tup);
9535 21 : relid = con->conrelid;
9536 21 : confrelid = con->confrelid;
9537 21 : conislocal = con->conislocal;
9538 21 : ReleaseSysCache(tup);
9539 :
9540 : /*
9541 : * If the constraint is inherited (only), we don't want to inject a
9542 : * new definition here; it'll get recreated when ATAddCheckConstraint
9543 : * recurses from adding the parent table's constraint. But we had to
9544 : * carry the info this far so that we can drop the constraint below.
9545 : */
9546 21 : if (!conislocal)
9547 1 : continue;
9548 :
9549 40 : ATPostAlterTypeParse(oldId, relid, confrelid,
9550 20 : (char *) lfirst(def_item),
9551 20 : wqueue, lockmode, tab->rewrite);
9552 : }
9553 79 : forboth(oid_item, tab->changedIndexOids,
9554 : def_item, tab->changedIndexDefs)
9555 : {
9556 12 : Oid oldId = lfirst_oid(oid_item);
9557 : Oid relid;
9558 :
9559 12 : relid = IndexGetRelation(oldId, false);
9560 24 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
9561 12 : (char *) lfirst(def_item),
9562 12 : wqueue, lockmode, tab->rewrite);
9563 : }
9564 :
9565 : /*
9566 : * Now we can drop the existing constraints and indexes --- constraints
9567 : * first, since some of them might depend on the indexes. In fact, we
9568 : * have to delete FOREIGN KEY constraints before UNIQUE constraints, but
9569 : * we already ordered the constraint list to ensure that would happen. It
9570 : * should be okay to use DROP_RESTRICT here, since nothing else should be
9571 : * depending on these objects.
9572 : */
9573 88 : foreach(oid_item, tab->changedConstraintOids)
9574 : {
9575 21 : obj.classId = ConstraintRelationId;
9576 21 : obj.objectId = lfirst_oid(oid_item);
9577 21 : obj.objectSubId = 0;
9578 21 : performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
9579 : }
9580 :
9581 79 : foreach(oid_item, tab->changedIndexOids)
9582 : {
9583 12 : obj.classId = RelationRelationId;
9584 12 : obj.objectId = lfirst_oid(oid_item);
9585 12 : obj.objectSubId = 0;
9586 12 : performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
9587 : }
9588 :
9589 : /*
9590 : * The objects will get recreated during subsequent passes over the work
9591 : * queue.
9592 : */
9593 67 : }
9594 :
9595 : static void
9596 32 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
9597 : List **wqueue, LOCKMODE lockmode, bool rewrite)
9598 : {
9599 : List *raw_parsetree_list;
9600 : List *querytree_list;
9601 : ListCell *list_item;
9602 : Relation rel;
9603 :
9604 : /*
9605 : * We expect that we will get only ALTER TABLE and CREATE INDEX
9606 : * statements. Hence, there is no need to pass them through
9607 : * parse_analyze() or the rewriter, but instead we need to pass them
9608 : * through parse_utilcmd.c to make them ready for execution.
9609 : */
9610 32 : raw_parsetree_list = raw_parser(cmd);
9611 32 : querytree_list = NIL;
9612 64 : foreach(list_item, raw_parsetree_list)
9613 : {
9614 32 : RawStmt *rs = lfirst_node(RawStmt, list_item);
9615 32 : Node *stmt = rs->stmt;
9616 :
9617 32 : if (IsA(stmt, IndexStmt))
9618 12 : querytree_list = lappend(querytree_list,
9619 12 : transformIndexStmt(oldRelId,
9620 : (IndexStmt *) stmt,
9621 : cmd));
9622 20 : else if (IsA(stmt, AlterTableStmt))
9623 20 : querytree_list = list_concat(querytree_list,
9624 : transformAlterTableStmt(oldRelId,
9625 : (AlterTableStmt *) stmt,
9626 : cmd));
9627 : else
9628 0 : querytree_list = lappend(querytree_list, stmt);
9629 : }
9630 :
9631 : /* Caller should already have acquired whatever lock we need. */
9632 32 : rel = relation_open(oldRelId, NoLock);
9633 :
9634 : /*
9635 : * Attach each generated command to the proper place in the work queue.
9636 : * Note this could result in creation of entirely new work-queue entries.
9637 : *
9638 : * Also note that we have to tweak the command subtypes, because it turns
9639 : * out that re-creation of indexes and constraints has to act a bit
9640 : * differently from initial creation.
9641 : */
9642 64 : foreach(list_item, querytree_list)
9643 : {
9644 32 : Node *stm = (Node *) lfirst(list_item);
9645 : AlteredTableInfo *tab;
9646 :
9647 32 : tab = ATGetQueueEntry(wqueue, rel);
9648 :
9649 32 : if (IsA(stm, IndexStmt))
9650 : {
9651 12 : IndexStmt *stmt = (IndexStmt *) stm;
9652 : AlterTableCmd *newcmd;
9653 :
9654 12 : if (!rewrite)
9655 5 : TryReuseIndex(oldId, stmt);
9656 : /* keep the index's comment */
9657 12 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
9658 :
9659 12 : newcmd = makeNode(AlterTableCmd);
9660 12 : newcmd->subtype = AT_ReAddIndex;
9661 12 : newcmd->def = (Node *) stmt;
9662 12 : tab->subcmds[AT_PASS_OLD_INDEX] =
9663 12 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
9664 : }
9665 20 : else if (IsA(stm, AlterTableStmt))
9666 : {
9667 20 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
9668 : ListCell *lcmd;
9669 :
9670 40 : foreach(lcmd, stmt->cmds)
9671 : {
9672 20 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
9673 :
9674 20 : if (cmd->subtype == AT_AddIndex)
9675 : {
9676 : IndexStmt *indstmt;
9677 : Oid indoid;
9678 :
9679 7 : indstmt = castNode(IndexStmt, cmd->def);
9680 7 : indoid = get_constraint_index(oldId);
9681 :
9682 7 : if (!rewrite)
9683 2 : TryReuseIndex(indoid, indstmt);
9684 : /* keep any comment on the index */
9685 7 : indstmt->idxcomment = GetComment(indoid,
9686 : RelationRelationId, 0);
9687 :
9688 7 : cmd->subtype = AT_ReAddIndex;
9689 7 : tab->subcmds[AT_PASS_OLD_INDEX] =
9690 7 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
9691 :
9692 : /* recreate any comment on the constraint */
9693 7 : RebuildConstraintComment(tab,
9694 : AT_PASS_OLD_INDEX,
9695 : oldId,
9696 : rel, indstmt->idxname);
9697 : }
9698 13 : else if (cmd->subtype == AT_AddConstraint)
9699 : {
9700 : Constraint *con;
9701 :
9702 13 : con = castNode(Constraint, cmd->def);
9703 13 : con->old_pktable_oid = refRelId;
9704 : /* rewriting neither side of a FK */
9705 13 : if (con->contype == CONSTR_FOREIGN &&
9706 1 : !rewrite && tab->rewrite == 0)
9707 1 : TryReuseForeignKey(oldId, con);
9708 13 : cmd->subtype = AT_ReAddConstraint;
9709 13 : tab->subcmds[AT_PASS_OLD_CONSTR] =
9710 13 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
9711 :
9712 : /* recreate any comment on the constraint */
9713 13 : RebuildConstraintComment(tab,
9714 : AT_PASS_OLD_CONSTR,
9715 : oldId,
9716 : rel, con->conname);
9717 : }
9718 : else
9719 0 : elog(ERROR, "unexpected statement subtype: %d",
9720 : (int) cmd->subtype);
9721 : }
9722 : }
9723 : else
9724 0 : elog(ERROR, "unexpected statement type: %d",
9725 : (int) nodeTag(stm));
9726 : }
9727 :
9728 32 : relation_close(rel, NoLock);
9729 32 : }
9730 :
9731 : /*
9732 : * Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
9733 : * a constraint that is being re-added.
9734 : */
9735 : static void
9736 20 : RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
9737 : Relation rel, char *conname)
9738 : {
9739 : CommentStmt *cmd;
9740 : char *comment_str;
9741 : AlterTableCmd *newcmd;
9742 :
9743 : /* Look for comment for object wanted, and leave if none */
9744 20 : comment_str = GetComment(objid, ConstraintRelationId, 0);
9745 20 : if (comment_str == NULL)
9746 32 : return;
9747 :
9748 : /* Build node CommentStmt */
9749 8 : cmd = makeNode(CommentStmt);
9750 8 : cmd->objtype = OBJECT_TABCONSTRAINT;
9751 8 : cmd->object = (Node *) list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
9752 : makeString(pstrdup(RelationGetRelationName(rel))),
9753 : makeString(pstrdup(conname)));
9754 8 : cmd->comment = comment_str;
9755 :
9756 : /* Append it to list of commands */
9757 8 : newcmd = makeNode(AlterTableCmd);
9758 8 : newcmd->subtype = AT_ReAddComment;
9759 8 : newcmd->def = (Node *) cmd;
9760 8 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
9761 : }
9762 :
9763 : /*
9764 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
9765 : * for the real analysis, then mutates the IndexStmt based on that verdict.
9766 : */
9767 : static void
9768 7 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
9769 : {
9770 7 : if (CheckIndexCompatible(oldId,
9771 : stmt->accessMethod,
9772 : stmt->indexParams,
9773 : stmt->excludeOpNames))
9774 : {
9775 7 : Relation irel = index_open(oldId, NoLock);
9776 :
9777 7 : stmt->oldNode = irel->rd_node.relNode;
9778 7 : index_close(irel, NoLock);
9779 : }
9780 7 : }
9781 :
9782 : /*
9783 : * Subroutine for ATPostAlterTypeParse().
9784 : *
9785 : * Stash the old P-F equality operator into the Constraint node, for possible
9786 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
9787 : * this constraint can be skipped.
9788 : */
9789 : static void
9790 1 : TryReuseForeignKey(Oid oldId, Constraint *con)
9791 : {
9792 : HeapTuple tup;
9793 : Datum adatum;
9794 : bool isNull;
9795 : ArrayType *arr;
9796 : Oid *rawarr;
9797 : int numkeys;
9798 : int i;
9799 :
9800 1 : Assert(con->contype == CONSTR_FOREIGN);
9801 1 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
9802 :
9803 1 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
9804 1 : if (!HeapTupleIsValid(tup)) /* should not happen */
9805 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
9806 :
9807 1 : adatum = SysCacheGetAttr(CONSTROID, tup,
9808 : Anum_pg_constraint_conpfeqop, &isNull);
9809 1 : if (isNull)
9810 0 : elog(ERROR, "null conpfeqop for constraint %u", oldId);
9811 1 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
9812 1 : numkeys = ARR_DIMS(arr)[0];
9813 : /* test follows the one in ri_FetchConstraintInfo() */
9814 2 : if (ARR_NDIM(arr) != 1 ||
9815 2 : ARR_HASNULL(arr) ||
9816 1 : ARR_ELEMTYPE(arr) != OIDOID)
9817 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
9818 1 : rawarr = (Oid *) ARR_DATA_PTR(arr);
9819 :
9820 : /* stash a List of the operator Oids in our Constraint node */
9821 2 : for (i = 0; i < numkeys; i++)
9822 1 : con->old_conpfeqop = lcons_oid(rawarr[i], con->old_conpfeqop);
9823 :
9824 1 : ReleaseSysCache(tup);
9825 1 : }
9826 :
9827 : /*
9828 : * ALTER TABLE OWNER
9829 : *
9830 : * recursing is true if we are recursing from a table to its indexes,
9831 : * sequences, or toast table. We don't allow the ownership of those things to
9832 : * be changed separately from the parent table. Also, we can skip permission
9833 : * checks (this is necessary not just an optimization, else we'd fail to
9834 : * handle toast tables properly).
9835 : *
9836 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
9837 : * free-standing composite type.
9838 : */
9839 : void
9840 37 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
9841 : {
9842 : Relation target_rel;
9843 : Relation class_rel;
9844 : HeapTuple tuple;
9845 : Form_pg_class tuple_class;
9846 :
9847 : /*
9848 : * Get exclusive lock till end of transaction on the target table. Use
9849 : * relation_open so that we can work on indexes and sequences.
9850 : */
9851 37 : target_rel = relation_open(relationOid, lockmode);
9852 :
9853 : /* Get its pg_class tuple, too */
9854 37 : class_rel = heap_open(RelationRelationId, RowExclusiveLock);
9855 :
9856 37 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
9857 37 : if (!HeapTupleIsValid(tuple))
9858 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
9859 37 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
9860 :
9861 : /* Can we change the ownership of this tuple? */
9862 37 : switch (tuple_class->relkind)
9863 : {
9864 : case RELKIND_RELATION:
9865 : case RELKIND_VIEW:
9866 : case RELKIND_MATVIEW:
9867 : case RELKIND_FOREIGN_TABLE:
9868 : case RELKIND_PARTITIONED_TABLE:
9869 : /* ok to change owner */
9870 16 : break;
9871 : case RELKIND_INDEX:
9872 10 : if (!recursing)
9873 : {
9874 : /*
9875 : * Because ALTER INDEX OWNER used to be allowed, and in fact
9876 : * is generated by old versions of pg_dump, we give a warning
9877 : * and do nothing rather than erroring out. Also, to avoid
9878 : * unnecessary chatter while restoring those old dumps, say
9879 : * nothing at all if the command would be a no-op anyway.
9880 : */
9881 0 : if (tuple_class->relowner != newOwnerId)
9882 0 : ereport(WARNING,
9883 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9884 : errmsg("cannot change owner of index \"%s\"",
9885 : NameStr(tuple_class->relname)),
9886 : errhint("Change the ownership of the index's table, instead.")));
9887 : /* quick hack to exit via the no-op path */
9888 0 : newOwnerId = tuple_class->relowner;
9889 : }
9890 10 : break;
9891 : case RELKIND_SEQUENCE:
9892 5 : if (!recursing &&
9893 0 : tuple_class->relowner != newOwnerId)
9894 : {
9895 : /* if it's an owned sequence, disallow changing it by itself */
9896 : Oid tableId;
9897 : int32 colId;
9898 :
9899 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
9900 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
9901 0 : ereport(ERROR,
9902 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9903 : errmsg("cannot change owner of sequence \"%s\"",
9904 : NameStr(tuple_class->relname)),
9905 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
9906 : NameStr(tuple_class->relname),
9907 : get_rel_name(tableId))));
9908 : }
9909 5 : break;
9910 : case RELKIND_COMPOSITE_TYPE:
9911 1 : if (recursing)
9912 1 : break;
9913 0 : ereport(ERROR,
9914 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9915 : errmsg("\"%s\" is a composite type",
9916 : NameStr(tuple_class->relname)),
9917 : errhint("Use ALTER TYPE instead.")));
9918 : break;
9919 : case RELKIND_TOASTVALUE:
9920 5 : if (recursing)
9921 5 : break;
9922 : /* FALL THRU */
9923 : default:
9924 0 : ereport(ERROR,
9925 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9926 : errmsg("\"%s\" is not a table, view, sequence, or foreign table",
9927 : NameStr(tuple_class->relname))));
9928 : }
9929 :
9930 : /*
9931 : * If the new owner is the same as the existing owner, consider the
9932 : * command to have succeeded. This is for dump restoration purposes.
9933 : */
9934 37 : if (tuple_class->relowner != newOwnerId)
9935 : {
9936 : Datum repl_val[Natts_pg_class];
9937 : bool repl_null[Natts_pg_class];
9938 : bool repl_repl[Natts_pg_class];
9939 : Acl *newAcl;
9940 : Datum aclDatum;
9941 : bool isNull;
9942 : HeapTuple newtuple;
9943 :
9944 : /* skip permission checks when recursing to index or toast table */
9945 35 : if (!recursing)
9946 : {
9947 : /* Superusers can always do it */
9948 14 : if (!superuser())
9949 : {
9950 0 : Oid namespaceOid = tuple_class->relnamespace;
9951 : AclResult aclresult;
9952 :
9953 : /* Otherwise, must be owner of the existing object */
9954 0 : if (!pg_class_ownercheck(relationOid, GetUserId()))
9955 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
9956 0 : RelationGetRelationName(target_rel));
9957 :
9958 : /* Must be able to become new owner */
9959 0 : check_is_member_of_role(GetUserId(), newOwnerId);
9960 :
9961 : /* New owner must have CREATE privilege on namespace */
9962 0 : aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
9963 : ACL_CREATE);
9964 0 : if (aclresult != ACLCHECK_OK)
9965 0 : aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
9966 0 : get_namespace_name(namespaceOid));
9967 : }
9968 : }
9969 :
9970 35 : memset(repl_null, false, sizeof(repl_null));
9971 35 : memset(repl_repl, false, sizeof(repl_repl));
9972 :
9973 35 : repl_repl[Anum_pg_class_relowner - 1] = true;
9974 35 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
9975 :
9976 : /*
9977 : * Determine the modified ACL for the new owner. This is only
9978 : * necessary when the ACL is non-null.
9979 : */
9980 35 : aclDatum = SysCacheGetAttr(RELOID, tuple,
9981 : Anum_pg_class_relacl,
9982 : &isNull);
9983 35 : if (!isNull)
9984 : {
9985 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
9986 : tuple_class->relowner, newOwnerId);
9987 2 : repl_repl[Anum_pg_class_relacl - 1] = true;
9988 2 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
9989 : }
9990 :
9991 35 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
9992 :
9993 35 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
9994 :
9995 35 : heap_freetuple(newtuple);
9996 :
9997 : /*
9998 : * We must similarly update any per-column ACLs to reflect the new
9999 : * owner; for neatness reasons that's split out as a subroutine.
10000 : */
10001 35 : change_owner_fix_column_acls(relationOid,
10002 : tuple_class->relowner,
10003 : newOwnerId);
10004 :
10005 : /*
10006 : * Update owner dependency reference, if any. A composite type has
10007 : * none, because it's tracked for the pg_type entry instead of here;
10008 : * indexes and TOAST tables don't have their own entries either.
10009 : */
10010 69 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
10011 58 : tuple_class->relkind != RELKIND_INDEX &&
10012 24 : tuple_class->relkind != RELKIND_TOASTVALUE)
10013 19 : changeDependencyOnOwner(RelationRelationId, relationOid,
10014 : newOwnerId);
10015 :
10016 : /*
10017 : * Also change the ownership of the table's row type, if it has one
10018 : */
10019 35 : if (tuple_class->relkind != RELKIND_INDEX)
10020 25 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
10021 :
10022 : /*
10023 : * If we are operating on a table or materialized view, also change
10024 : * the ownership of any indexes and sequences that belong to the
10025 : * relation, as well as its toast table (if it has one).
10026 : */
10027 55 : if (tuple_class->relkind == RELKIND_RELATION ||
10028 40 : tuple_class->relkind == RELKIND_MATVIEW ||
10029 20 : tuple_class->relkind == RELKIND_TOASTVALUE)
10030 : {
10031 : List *index_oid_list;
10032 : ListCell *i;
10033 :
10034 : /* Find all the indexes belonging to this relation */
10035 20 : index_oid_list = RelationGetIndexList(target_rel);
10036 :
10037 : /* For each index, recursively change its ownership */
10038 30 : foreach(i, index_oid_list)
10039 10 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
10040 :
10041 20 : list_free(index_oid_list);
10042 : }
10043 :
10044 55 : if (tuple_class->relkind == RELKIND_RELATION ||
10045 20 : tuple_class->relkind == RELKIND_MATVIEW)
10046 : {
10047 : /* If it has a toast table, recurse to change its ownership */
10048 15 : if (tuple_class->reltoastrelid != InvalidOid)
10049 5 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
10050 : true, lockmode);
10051 :
10052 : /* If it has dependent sequences, recurse to change them too */
10053 15 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
10054 : }
10055 : }
10056 :
10057 37 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
10058 :
10059 37 : ReleaseSysCache(tuple);
10060 37 : heap_close(class_rel, RowExclusiveLock);
10061 37 : relation_close(target_rel, NoLock);
10062 37 : }
10063 :
10064 : /*
10065 : * change_owner_fix_column_acls
10066 : *
10067 : * Helper function for ATExecChangeOwner. Scan the columns of the table
10068 : * and fix any non-null column ACLs to reflect the new owner.
10069 : */
10070 : static void
10071 35 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
10072 : {
10073 : Relation attRelation;
10074 : SysScanDesc scan;
10075 : ScanKeyData key[1];
10076 : HeapTuple attributeTuple;
10077 :
10078 35 : attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
10079 35 : ScanKeyInit(&key[0],
10080 : Anum_pg_attribute_attrelid,
10081 : BTEqualStrategyNumber, F_OIDEQ,
10082 : ObjectIdGetDatum(relationOid));
10083 35 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
10084 : true, NULL, 1, key);
10085 286 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
10086 : {
10087 216 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
10088 : Datum repl_val[Natts_pg_attribute];
10089 : bool repl_null[Natts_pg_attribute];
10090 : bool repl_repl[Natts_pg_attribute];
10091 : Acl *newAcl;
10092 : Datum aclDatum;
10093 : bool isNull;
10094 : HeapTuple newtuple;
10095 :
10096 : /* Ignore dropped columns */
10097 216 : if (att->attisdropped)
10098 216 : continue;
10099 :
10100 216 : aclDatum = heap_getattr(attributeTuple,
10101 : Anum_pg_attribute_attacl,
10102 : RelationGetDescr(attRelation),
10103 : &isNull);
10104 : /* Null ACLs do not require changes */
10105 216 : if (isNull)
10106 216 : continue;
10107 :
10108 0 : memset(repl_null, false, sizeof(repl_null));
10109 0 : memset(repl_repl, false, sizeof(repl_repl));
10110 :
10111 0 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
10112 : oldOwnerId, newOwnerId);
10113 0 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
10114 0 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
10115 :
10116 0 : newtuple = heap_modify_tuple(attributeTuple,
10117 : RelationGetDescr(attRelation),
10118 : repl_val, repl_null, repl_repl);
10119 :
10120 0 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
10121 :
10122 0 : heap_freetuple(newtuple);
10123 : }
10124 35 : systable_endscan(scan);
10125 35 : heap_close(attRelation, RowExclusiveLock);
10126 35 : }
10127 :
10128 : /*
10129 : * change_owner_recurse_to_sequences
10130 : *
10131 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
10132 : * for sequences that are dependent on serial columns, and changes their
10133 : * ownership.
10134 : */
10135 : static void
10136 15 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
10137 : {
10138 : Relation depRel;
10139 : SysScanDesc scan;
10140 : ScanKeyData key[2];
10141 : HeapTuple tup;
10142 :
10143 : /*
10144 : * SERIAL sequences are those having an auto dependency on one of the
10145 : * table's columns (we don't care *which* column, exactly).
10146 : */
10147 15 : depRel = heap_open(DependRelationId, AccessShareLock);
10148 :
10149 15 : ScanKeyInit(&key[0],
10150 : Anum_pg_depend_refclassid,
10151 : BTEqualStrategyNumber, F_OIDEQ,
10152 : ObjectIdGetDatum(RelationRelationId));
10153 15 : ScanKeyInit(&key[1],
10154 : Anum_pg_depend_refobjid,
10155 : BTEqualStrategyNumber, F_OIDEQ,
10156 : ObjectIdGetDatum(relationOid));
10157 : /* we leave refobjsubid unspecified */
10158 :
10159 15 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
10160 : NULL, 2, key);
10161 :
10162 63 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
10163 : {
10164 33 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
10165 : Relation seqRel;
10166 :
10167 : /* skip dependencies other than auto dependencies on columns */
10168 46 : if (depForm->refobjsubid == 0 ||
10169 17 : depForm->classid != RelationRelationId ||
10170 8 : depForm->objsubid != 0 ||
10171 4 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
10172 29 : continue;
10173 :
10174 : /* Use relation_open just in case it's an index */
10175 4 : seqRel = relation_open(depForm->objid, lockmode);
10176 :
10177 : /* skip non-sequence relations */
10178 4 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
10179 : {
10180 : /* No need to keep the lock */
10181 1 : relation_close(seqRel, lockmode);
10182 1 : continue;
10183 : }
10184 :
10185 : /* We don't need to close the sequence while we alter it. */
10186 3 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
10187 :
10188 : /* Now we can close it. Keep the lock till end of transaction. */
10189 3 : relation_close(seqRel, NoLock);
10190 : }
10191 :
10192 15 : systable_endscan(scan);
10193 :
10194 15 : relation_close(depRel, AccessShareLock);
10195 15 : }
10196 :
10197 : /*
10198 : * ALTER TABLE CLUSTER ON
10199 : *
10200 : * The only thing we have to do is to change the indisclustered bits.
10201 : *
10202 : * Return the address of the new clustering index.
10203 : */
10204 : static ObjectAddress
10205 3 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
10206 : {
10207 : Oid indexOid;
10208 : ObjectAddress address;
10209 :
10210 3 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
10211 :
10212 3 : if (!OidIsValid(indexOid))
10213 0 : ereport(ERROR,
10214 : (errcode(ERRCODE_UNDEFINED_OBJECT),
10215 : errmsg("index \"%s\" for table \"%s\" does not exist",
10216 : indexName, RelationGetRelationName(rel))));
10217 :
10218 : /* Check index is valid to cluster on */
10219 3 : check_index_is_clusterable(rel, indexOid, false, lockmode);
10220 :
10221 : /* And do the work */
10222 3 : mark_index_clustered(rel, indexOid, false);
10223 :
10224 3 : ObjectAddressSet(address,
10225 : RelationRelationId, indexOid);
10226 :
10227 3 : return address;
10228 : }
10229 :
10230 : /*
10231 : * ALTER TABLE SET WITHOUT CLUSTER
10232 : *
10233 : * We have to find any indexes on the table that have indisclustered bit
10234 : * set and turn it off.
10235 : */
10236 : static void
10237 2 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
10238 : {
10239 2 : mark_index_clustered(rel, InvalidOid, false);
10240 2 : }
10241 :
10242 : /*
10243 : * ALTER TABLE SET TABLESPACE
10244 : */
10245 : static void
10246 9 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
10247 : {
10248 : Oid tablespaceId;
10249 :
10250 : /* Check that the tablespace exists */
10251 9 : tablespaceId = get_tablespace_oid(tablespacename, false);
10252 :
10253 : /* Check permissions except when moving to database's default */
10254 9 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
10255 : {
10256 : AclResult aclresult;
10257 :
10258 2 : aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
10259 2 : if (aclresult != ACLCHECK_OK)
10260 0 : aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
10261 : }
10262 :
10263 : /* Save info for Phase 3 to do the real work */
10264 9 : if (OidIsValid(tab->newTableSpace))
10265 0 : ereport(ERROR,
10266 : (errcode(ERRCODE_SYNTAX_ERROR),
10267 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
10268 :
10269 9 : tab->newTableSpace = tablespaceId;
10270 9 : }
10271 :
10272 : /*
10273 : * Set, reset, or replace reloptions.
10274 : */
10275 : static void
10276 44 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
10277 : LOCKMODE lockmode)
10278 : {
10279 : Oid relid;
10280 : Relation pgclass;
10281 : HeapTuple tuple;
10282 : HeapTuple newtuple;
10283 : Datum datum;
10284 : bool isnull;
10285 : Datum newOptions;
10286 : Datum repl_val[Natts_pg_class];
10287 : bool repl_null[Natts_pg_class];
10288 : bool repl_repl[Natts_pg_class];
10289 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
10290 :
10291 44 : if (defList == NIL && operation != AT_ReplaceRelOptions)
10292 41 : return; /* nothing to do */
10293 :
10294 44 : pgclass = heap_open(RelationRelationId, RowExclusiveLock);
10295 :
10296 : /* Fetch heap tuple */
10297 44 : relid = RelationGetRelid(rel);
10298 44 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
10299 44 : if (!HeapTupleIsValid(tuple))
10300 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
10301 :
10302 44 : if (operation == AT_ReplaceRelOptions)
10303 : {
10304 : /*
10305 : * If we're supposed to replace the reloptions list, we just pretend
10306 : * there were none before.
10307 : */
10308 20 : datum = (Datum) 0;
10309 20 : isnull = true;
10310 : }
10311 : else
10312 : {
10313 : /* Get the old reloptions */
10314 24 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
10315 : &isnull);
10316 : }
10317 :
10318 : /* Generate new proposed reloptions (text array) */
10319 44 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
10320 : defList, NULL, validnsps, false,
10321 : operation == AT_ResetRelOptions);
10322 :
10323 : /* Validate */
10324 44 : switch (rel->rd_rel->relkind)
10325 : {
10326 : case RELKIND_RELATION:
10327 : case RELKIND_TOASTVALUE:
10328 : case RELKIND_MATVIEW:
10329 : case RELKIND_PARTITIONED_TABLE:
10330 8 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
10331 8 : break;
10332 : case RELKIND_VIEW:
10333 35 : (void) view_reloptions(newOptions, true);
10334 32 : break;
10335 : case RELKIND_INDEX:
10336 1 : (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
10337 1 : break;
10338 : default:
10339 0 : ereport(ERROR,
10340 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10341 : errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
10342 : RelationGetRelationName(rel))));
10343 : break;
10344 : }
10345 :
10346 : /* Special-case validation of view options */
10347 41 : if (rel->rd_rel->relkind == RELKIND_VIEW)
10348 : {
10349 32 : Query *view_query = get_view_query(rel);
10350 32 : List *view_options = untransformRelOptions(newOptions);
10351 : ListCell *cell;
10352 32 : bool check_option = false;
10353 :
10354 43 : foreach(cell, view_options)
10355 : {
10356 11 : DefElem *defel = (DefElem *) lfirst(cell);
10357 :
10358 11 : if (pg_strcasecmp(defel->defname, "check_option") == 0)
10359 4 : check_option = true;
10360 : }
10361 :
10362 : /*
10363 : * If the check option is specified, look to see if the view is
10364 : * actually auto-updatable or not.
10365 : */
10366 32 : if (check_option)
10367 : {
10368 4 : const char *view_updatable_error =
10369 : view_query_is_auto_updatable(view_query, true);
10370 :
10371 4 : if (view_updatable_error)
10372 0 : ereport(ERROR,
10373 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10374 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
10375 : errhint("%s", view_updatable_error)));
10376 : }
10377 : }
10378 :
10379 : /*
10380 : * All we need do here is update the pg_class row; the new options will be
10381 : * propagated into relcaches during post-commit cache inval.
10382 : */
10383 41 : memset(repl_val, 0, sizeof(repl_val));
10384 41 : memset(repl_null, false, sizeof(repl_null));
10385 41 : memset(repl_repl, false, sizeof(repl_repl));
10386 :
10387 41 : if (newOptions != (Datum) 0)
10388 17 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
10389 : else
10390 24 : repl_null[Anum_pg_class_reloptions - 1] = true;
10391 :
10392 41 : repl_repl[Anum_pg_class_reloptions - 1] = true;
10393 :
10394 41 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
10395 : repl_val, repl_null, repl_repl);
10396 :
10397 41 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
10398 :
10399 41 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
10400 :
10401 41 : heap_freetuple(newtuple);
10402 :
10403 41 : ReleaseSysCache(tuple);
10404 :
10405 : /* repeat the whole exercise for the toast table, if there's one */
10406 41 : if (OidIsValid(rel->rd_rel->reltoastrelid))
10407 : {
10408 : Relation toastrel;
10409 5 : Oid toastid = rel->rd_rel->reltoastrelid;
10410 :
10411 5 : toastrel = heap_open(toastid, lockmode);
10412 :
10413 : /* Fetch heap tuple */
10414 5 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
10415 5 : if (!HeapTupleIsValid(tuple))
10416 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
10417 :
10418 5 : if (operation == AT_ReplaceRelOptions)
10419 : {
10420 : /*
10421 : * If we're supposed to replace the reloptions list, we just
10422 : * pretend there were none before.
10423 : */
10424 0 : datum = (Datum) 0;
10425 0 : isnull = true;
10426 : }
10427 : else
10428 : {
10429 : /* Get the old reloptions */
10430 5 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
10431 : &isnull);
10432 : }
10433 :
10434 5 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
10435 : defList, "toast", validnsps, false,
10436 : operation == AT_ResetRelOptions);
10437 :
10438 5 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
10439 :
10440 5 : memset(repl_val, 0, sizeof(repl_val));
10441 5 : memset(repl_null, false, sizeof(repl_null));
10442 5 : memset(repl_repl, false, sizeof(repl_repl));
10443 :
10444 5 : if (newOptions != (Datum) 0)
10445 3 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
10446 : else
10447 2 : repl_null[Anum_pg_class_reloptions - 1] = true;
10448 :
10449 5 : repl_repl[Anum_pg_class_reloptions - 1] = true;
10450 :
10451 5 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
10452 : repl_val, repl_null, repl_repl);
10453 :
10454 5 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
10455 :
10456 5 : InvokeObjectPostAlterHookArg(RelationRelationId,
10457 : RelationGetRelid(toastrel), 0,
10458 : InvalidOid, true);
10459 :
10460 5 : heap_freetuple(newtuple);
10461 :
10462 5 : ReleaseSysCache(tuple);
10463 :
10464 5 : heap_close(toastrel, NoLock);
10465 : }
10466 :
10467 41 : heap_close(pgclass, RowExclusiveLock);
10468 : }
10469 :
10470 : /*
10471 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
10472 : * rewriting to be done, so we just want to copy the data as fast as possible.
10473 : */
10474 : static void
10475 9 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
10476 : {
10477 : Relation rel;
10478 : Oid oldTableSpace;
10479 : Oid reltoastrelid;
10480 : Oid newrelfilenode;
10481 : RelFileNode newrnode;
10482 : SMgrRelation dstrel;
10483 : Relation pg_class;
10484 : HeapTuple tuple;
10485 : Form_pg_class rd_rel;
10486 : ForkNumber forkNum;
10487 9 : List *reltoastidxids = NIL;
10488 : ListCell *lc;
10489 :
10490 : /*
10491 : * Need lock here in case we are recursing to toast table or index
10492 : */
10493 9 : rel = relation_open(tableOid, lockmode);
10494 :
10495 : /*
10496 : * No work if no change in tablespace.
10497 : */
10498 9 : oldTableSpace = rel->rd_rel->reltablespace;
10499 18 : if (newTableSpace == oldTableSpace ||
10500 16 : (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
10501 : {
10502 0 : InvokeObjectPostAlterHook(RelationRelationId,
10503 : RelationGetRelid(rel), 0);
10504 :
10505 0 : relation_close(rel, NoLock);
10506 9 : return;
10507 : }
10508 :
10509 : /*
10510 : * We cannot support moving mapped relations into different tablespaces.
10511 : * (In particular this eliminates all shared catalogs.)
10512 : */
10513 9 : if (RelationIsMapped(rel))
10514 0 : ereport(ERROR,
10515 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10516 : errmsg("cannot move system relation \"%s\"",
10517 : RelationGetRelationName(rel))));
10518 :
10519 : /* Can't move a non-shared relation into pg_global */
10520 9 : if (newTableSpace == GLOBALTABLESPACE_OID)
10521 0 : ereport(ERROR,
10522 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
10523 : errmsg("only shared relations can be placed in pg_global tablespace")));
10524 :
10525 : /*
10526 : * Don't allow moving temp tables of other backends ... their local buffer
10527 : * manager is not going to cope.
10528 : */
10529 9 : if (RELATION_IS_OTHER_TEMP(rel))
10530 0 : ereport(ERROR,
10531 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10532 : errmsg("cannot move temporary tables of other sessions")));
10533 :
10534 9 : reltoastrelid = rel->rd_rel->reltoastrelid;
10535 : /* Fetch the list of indexes on toast relation if necessary */
10536 9 : if (OidIsValid(reltoastrelid))
10537 : {
10538 0 : Relation toastRel = relation_open(reltoastrelid, lockmode);
10539 :
10540 0 : reltoastidxids = RelationGetIndexList(toastRel);
10541 0 : relation_close(toastRel, lockmode);
10542 : }
10543 :
10544 : /* Get a modifiable copy of the relation's pg_class row */
10545 9 : pg_class = heap_open(RelationRelationId, RowExclusiveLock);
10546 :
10547 9 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tableOid));
10548 9 : if (!HeapTupleIsValid(tuple))
10549 0 : elog(ERROR, "cache lookup failed for relation %u", tableOid);
10550 9 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
10551 :
10552 : /*
10553 : * Since we copy the file directly without looking at the shared buffers,
10554 : * we'd better first flush out any pages of the source relation that are
10555 : * in shared buffers. We assume no new changes will be made while we are
10556 : * holding exclusive lock on the rel.
10557 : */
10558 9 : FlushRelationBuffers(rel);
10559 :
10560 : /*
10561 : * Relfilenodes are not unique in databases across tablespaces, so we need
10562 : * to allocate a new one in the new tablespace.
10563 : */
10564 9 : newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
10565 9 : rel->rd_rel->relpersistence);
10566 :
10567 : /* Open old and new relation */
10568 9 : newrnode = rel->rd_node;
10569 9 : newrnode.relNode = newrelfilenode;
10570 9 : newrnode.spcNode = newTableSpace;
10571 9 : dstrel = smgropen(newrnode, rel->rd_backend);
10572 :
10573 9 : RelationOpenSmgr(rel);
10574 :
10575 : /*
10576 : * Create and copy all forks of the relation, and schedule unlinking of
10577 : * old physical files.
10578 : *
10579 : * NOTE: any conflict in relfilenode value will be caught in
10580 : * RelationCreateStorage().
10581 : */
10582 9 : RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
10583 :
10584 : /* copy main fork */
10585 9 : copy_relation_data(rel->rd_smgr, dstrel, MAIN_FORKNUM,
10586 9 : rel->rd_rel->relpersistence);
10587 :
10588 : /* copy those extra forks that exist */
10589 36 : for (forkNum = MAIN_FORKNUM + 1; forkNum <= MAX_FORKNUM; forkNum++)
10590 : {
10591 27 : if (smgrexists(rel->rd_smgr, forkNum))
10592 : {
10593 0 : smgrcreate(dstrel, forkNum, false);
10594 :
10595 : /*
10596 : * WAL log creation if the relation is persistent, or this is the
10597 : * init fork of an unlogged relation.
10598 : */
10599 0 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
10600 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
10601 : forkNum == INIT_FORKNUM))
10602 0 : log_smgrcreate(&newrnode, forkNum);
10603 0 : copy_relation_data(rel->rd_smgr, dstrel, forkNum,
10604 0 : rel->rd_rel->relpersistence);
10605 : }
10606 : }
10607 :
10608 : /* drop old relation, and close new one */
10609 9 : RelationDropStorage(rel);
10610 9 : smgrclose(dstrel);
10611 :
10612 : /* update the pg_class row */
10613 9 : rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
10614 9 : rd_rel->relfilenode = newrelfilenode;
10615 9 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
10616 :
10617 9 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
10618 :
10619 9 : heap_freetuple(tuple);
10620 :
10621 9 : heap_close(pg_class, RowExclusiveLock);
10622 :
10623 9 : relation_close(rel, NoLock);
10624 :
10625 : /* Make sure the reltablespace change is visible */
10626 9 : CommandCounterIncrement();
10627 :
10628 : /* Move associated toast relation and/or indexes, too */
10629 9 : if (OidIsValid(reltoastrelid))
10630 0 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
10631 9 : foreach(lc, reltoastidxids)
10632 0 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
10633 :
10634 : /* Clean up */
10635 9 : list_free(reltoastidxids);
10636 : }
10637 :
10638 : /*
10639 : * Alter Table ALL ... SET TABLESPACE
10640 : *
10641 : * Allows a user to move all objects of some type in a given tablespace in the
10642 : * current database to another tablespace. Objects can be chosen based on the
10643 : * owner of the object also, to allow users to move only their objects.
10644 : * The user must have CREATE rights on the new tablespace, as usual. The main
10645 : * permissions handling is done by the lower-level table move function.
10646 : *
10647 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
10648 : * lock can't be acquired then we ereport(ERROR).
10649 : */
10650 : Oid
10651 3 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
10652 : {
10653 3 : List *relations = NIL;
10654 : ListCell *l;
10655 : ScanKeyData key[1];
10656 : Relation rel;
10657 : HeapScanDesc scan;
10658 : HeapTuple tuple;
10659 : Oid orig_tablespaceoid;
10660 : Oid new_tablespaceoid;
10661 3 : List *role_oids = roleSpecsToIds(stmt->roles);
10662 :
10663 : /* Ensure we were not asked to move something we can't */
10664 3 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
10665 0 : stmt->objtype != OBJECT_MATVIEW)
10666 0 : ereport(ERROR,
10667 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
10668 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
10669 :
10670 : /* Get the orig and new tablespace OIDs */
10671 3 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
10672 3 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
10673 :
10674 : /* Can't move shared relations in to or out of pg_global */
10675 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
10676 3 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
10677 : new_tablespaceoid == GLOBALTABLESPACE_OID)
10678 0 : ereport(ERROR,
10679 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
10680 : errmsg("cannot move relations in to or out of pg_global tablespace")));
10681 :
10682 : /*
10683 : * Must have CREATE rights on the new tablespace, unless it is the
10684 : * database default tablespace (which all users implicitly have CREATE
10685 : * rights on).
10686 : */
10687 3 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
10688 : {
10689 : AclResult aclresult;
10690 :
10691 0 : aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(),
10692 : ACL_CREATE);
10693 0 : if (aclresult != ACLCHECK_OK)
10694 0 : aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
10695 0 : get_tablespace_name(new_tablespaceoid));
10696 : }
10697 :
10698 : /*
10699 : * Now that the checks are done, check if we should set either to
10700 : * InvalidOid because it is our database's default tablespace.
10701 : */
10702 3 : if (orig_tablespaceoid == MyDatabaseTableSpace)
10703 0 : orig_tablespaceoid = InvalidOid;
10704 :
10705 3 : if (new_tablespaceoid == MyDatabaseTableSpace)
10706 3 : new_tablespaceoid = InvalidOid;
10707 :
10708 : /* no-op */
10709 3 : if (orig_tablespaceoid == new_tablespaceoid)
10710 0 : return new_tablespaceoid;
10711 :
10712 : /*
10713 : * Walk the list of objects in the tablespace and move them. This will
10714 : * only find objects in our database, of course.
10715 : */
10716 3 : ScanKeyInit(&key[0],
10717 : Anum_pg_class_reltablespace,
10718 : BTEqualStrategyNumber, F_OIDEQ,
10719 : ObjectIdGetDatum(orig_tablespaceoid));
10720 :
10721 3 : rel = heap_open(RelationRelationId, AccessShareLock);
10722 3 : scan = heap_beginscan_catalog(rel, 1, key);
10723 16 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
10724 : {
10725 10 : Oid relOid = HeapTupleGetOid(tuple);
10726 : Form_pg_class relForm;
10727 :
10728 10 : relForm = (Form_pg_class) GETSTRUCT(tuple);
10729 :
10730 : /*
10731 : * Do not move objects in pg_catalog as part of this, if an admin
10732 : * really wishes to do so, they can issue the individual ALTER
10733 : * commands directly.
10734 : *
10735 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
10736 : * (TOAST will be moved with the main table).
10737 : */
10738 20 : if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared ||
10739 20 : isAnyTempNamespace(relForm->relnamespace) ||
10740 10 : relForm->relnamespace == PG_TOAST_NAMESPACE)
10741 0 : continue;
10742 :
10743 : /* Only move the object type requested */
10744 17 : if ((stmt->objtype == OBJECT_TABLE &&
10745 10 : relForm->relkind != RELKIND_RELATION &&
10746 10 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
10747 10 : (stmt->objtype == OBJECT_INDEX &&
10748 10 : relForm->relkind != RELKIND_INDEX) ||
10749 7 : (stmt->objtype == OBJECT_MATVIEW &&
10750 0 : relForm->relkind != RELKIND_MATVIEW))
10751 3 : continue;
10752 :
10753 : /* Check if we are only moving objects owned by certain roles */
10754 7 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
10755 0 : continue;
10756 :
10757 : /*
10758 : * Handle permissions-checking here since we are locking the tables
10759 : * and also to avoid doing a bunch of work only to fail part-way. Note
10760 : * that permissions will also be checked by AlterTableInternal().
10761 : *
10762 : * Caller must be considered an owner on the table to move it.
10763 : */
10764 7 : if (!pg_class_ownercheck(relOid, GetUserId()))
10765 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
10766 0 : NameStr(relForm->relname));
10767 :
10768 7 : if (stmt->nowait &&
10769 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
10770 0 : ereport(ERROR,
10771 : (errcode(ERRCODE_OBJECT_IN_USE),
10772 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
10773 : get_namespace_name(relForm->relnamespace),
10774 : NameStr(relForm->relname))));
10775 : else
10776 7 : LockRelationOid(relOid, AccessExclusiveLock);
10777 :
10778 : /* Add to our list of objects to move */
10779 7 : relations = lappend_oid(relations, relOid);
10780 : }
10781 :
10782 3 : heap_endscan(scan);
10783 3 : heap_close(rel, AccessShareLock);
10784 :
10785 3 : if (relations == NIL)
10786 1 : ereport(NOTICE,
10787 : (errcode(ERRCODE_NO_DATA_FOUND),
10788 : errmsg("no matching relations in tablespace \"%s\" found",
10789 : orig_tablespaceoid == InvalidOid ? "(database default)" :
10790 : get_tablespace_name(orig_tablespaceoid))));
10791 :
10792 : /* Everything is locked, loop through and move all of the relations. */
10793 10 : foreach(l, relations)
10794 : {
10795 7 : List *cmds = NIL;
10796 7 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
10797 :
10798 7 : cmd->subtype = AT_SetTableSpace;
10799 7 : cmd->name = stmt->new_tablespacename;
10800 :
10801 7 : cmds = lappend(cmds, cmd);
10802 :
10803 7 : EventTriggerAlterTableStart((Node *) stmt);
10804 : /* OID is set by AlterTableInternal */
10805 7 : AlterTableInternal(lfirst_oid(l), cmds, false);
10806 7 : EventTriggerAlterTableEnd();
10807 : }
10808 :
10809 3 : return new_tablespaceoid;
10810 : }
10811 :
10812 : /*
10813 : * Copy data, block by block
10814 : */
10815 : static void
10816 9 : copy_relation_data(SMgrRelation src, SMgrRelation dst,
10817 : ForkNumber forkNum, char relpersistence)
10818 : {
10819 : char *buf;
10820 : Page page;
10821 : bool use_wal;
10822 : bool copying_initfork;
10823 : BlockNumber nblocks;
10824 : BlockNumber blkno;
10825 :
10826 : /*
10827 : * palloc the buffer so that it's MAXALIGN'd. If it were just a local
10828 : * char[] array, the compiler might align it on any byte boundary, which
10829 : * can seriously hurt transfer speed to and from the kernel; not to
10830 : * mention possibly making log_newpage's accesses to the page header fail.
10831 : */
10832 9 : buf = (char *) palloc(BLCKSZ);
10833 9 : page = (Page) buf;
10834 :
10835 : /*
10836 : * The init fork for an unlogged relation in many respects has to be
10837 : * treated the same as normal relation, changes need to be WAL logged and
10838 : * it needs to be synced to disk.
10839 : */
10840 9 : copying_initfork = relpersistence == RELPERSISTENCE_UNLOGGED &&
10841 : forkNum == INIT_FORKNUM;
10842 :
10843 : /*
10844 : * We need to log the copied data in WAL iff WAL archiving/streaming is
10845 : * enabled AND it's a permanent relation.
10846 : */
10847 18 : use_wal = XLogIsNeeded() &&
10848 0 : (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork);
10849 :
10850 9 : nblocks = smgrnblocks(src, forkNum);
10851 :
10852 21 : for (blkno = 0; blkno < nblocks; blkno++)
10853 : {
10854 : /* If we got a cancel signal during the copy of the data, quit */
10855 12 : CHECK_FOR_INTERRUPTS();
10856 :
10857 12 : smgrread(src, forkNum, blkno, buf);
10858 :
10859 12 : if (!PageIsVerified(page, blkno))
10860 0 : ereport(ERROR,
10861 : (errcode(ERRCODE_DATA_CORRUPTED),
10862 : errmsg("invalid page in block %u of relation %s",
10863 : blkno,
10864 : relpathbackend(src->smgr_rnode.node,
10865 : src->smgr_rnode.backend,
10866 : forkNum))));
10867 :
10868 : /*
10869 : * WAL-log the copied page. Unfortunately we don't know what kind of a
10870 : * page this is, so we have to log the full page including any unused
10871 : * space.
10872 : */
10873 12 : if (use_wal)
10874 12 : log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
10875 :
10876 12 : PageSetChecksumInplace(page, blkno);
10877 :
10878 : /*
10879 : * Now write the page. We say isTemp = true even if it's not a temp
10880 : * rel, because there's no need for smgr to schedule an fsync for this
10881 : * write; we'll do it ourselves below.
10882 : */
10883 12 : smgrextend(dst, forkNum, blkno, buf, true);
10884 : }
10885 :
10886 9 : pfree(buf);
10887 :
10888 : /*
10889 : * If the rel is WAL-logged, must fsync before commit. We use heap_sync
10890 : * to ensure that the toast table gets fsync'd too. (For a temp or
10891 : * unlogged rel we don't care since the data will be gone after a crash
10892 : * anyway.)
10893 : *
10894 : * It's obvious that we must do this when not WAL-logging the copy. It's
10895 : * less obvious that we have to do it even if we did WAL-log the copied
10896 : * pages. The reason is that since we're copying outside shared buffers, a
10897 : * CHECKPOINT occurring during the copy has no way to flush the previously
10898 : * written data to disk (indeed it won't know the new rel even exists). A
10899 : * crash later on would replay WAL from the checkpoint, therefore it
10900 : * wouldn't replay our earlier WAL entries. If we do not fsync those pages
10901 : * here, they might still not be on disk when the crash occurs.
10902 : */
10903 9 : if (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork)
10904 9 : smgrimmedsync(dst, forkNum);
10905 9 : }
10906 :
10907 : /*
10908 : * ALTER TABLE ENABLE/DISABLE TRIGGER
10909 : *
10910 : * We just pass this off to trigger.c.
10911 : */
10912 : static void
10913 6 : ATExecEnableDisableTrigger(Relation rel, char *trigname,
10914 : char fires_when, bool skip_system, LOCKMODE lockmode)
10915 : {
10916 6 : EnableDisableTrigger(rel, trigname, fires_when, skip_system);
10917 6 : }
10918 :
10919 : /*
10920 : * ALTER TABLE ENABLE/DISABLE RULE
10921 : *
10922 : * We just pass this off to rewriteDefine.c.
10923 : */
10924 : static void
10925 0 : ATExecEnableDisableRule(Relation rel, char *rulename,
10926 : char fires_when, LOCKMODE lockmode)
10927 : {
10928 0 : EnableDisableRule(rel, rulename, fires_when);
10929 0 : }
10930 :
10931 : /*
10932 : * ALTER TABLE INHERIT
10933 : *
10934 : * Add a parent to the child's parents. This verifies that all the columns and
10935 : * check constraints of the parent appear in the child and that they have the
10936 : * same data types and expressions.
10937 : */
10938 : static void
10939 25 : ATPrepAddInherit(Relation child_rel)
10940 : {
10941 25 : if (child_rel->rd_rel->reloftype)
10942 1 : ereport(ERROR,
10943 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10944 : errmsg("cannot change inheritance of typed table")));
10945 :
10946 24 : if (child_rel->rd_rel->relispartition)
10947 1 : ereport(ERROR,
10948 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10949 : errmsg("cannot change inheritance of a partition")));
10950 :
10951 23 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10952 1 : ereport(ERROR,
10953 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10954 : errmsg("cannot change inheritance of partitioned table")));
10955 22 : }
10956 :
10957 : /*
10958 : * Return the address of the new parent relation.
10959 : */
10960 : static ObjectAddress
10961 22 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
10962 : {
10963 : Relation parent_rel;
10964 : List *children;
10965 : ObjectAddress address;
10966 : const char *trigger_name;
10967 :
10968 : /*
10969 : * A self-exclusive lock is needed here. See the similar case in
10970 : * MergeAttributes() for a full explanation.
10971 : */
10972 22 : parent_rel = heap_openrv(parent, ShareUpdateExclusiveLock);
10973 :
10974 : /*
10975 : * Must be owner of both parent and child -- child was checked by
10976 : * ATSimplePermissions call in ATPrepCmd
10977 : */
10978 22 : ATSimplePermissions(parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
10979 :
10980 : /* Permanent rels cannot inherit from temporary ones */
10981 23 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
10982 1 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10983 0 : ereport(ERROR,
10984 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10985 : errmsg("cannot inherit from temporary relation \"%s\"",
10986 : RelationGetRelationName(parent_rel))));
10987 :
10988 : /* If parent rel is temp, it must belong to this session */
10989 23 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
10990 1 : !parent_rel->rd_islocaltemp)
10991 0 : ereport(ERROR,
10992 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10993 : errmsg("cannot inherit from temporary relation of another session")));
10994 :
10995 : /* Ditto for the child */
10996 23 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
10997 1 : !child_rel->rd_islocaltemp)
10998 0 : ereport(ERROR,
10999 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11000 : errmsg("cannot inherit to temporary relation of another session")));
11001 :
11002 : /* Prevent partitioned tables from becoming inheritance parents */
11003 22 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11004 1 : ereport(ERROR,
11005 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11006 : errmsg("cannot inherit from partitioned table \"%s\"",
11007 : parent->relname)));
11008 :
11009 : /* Likewise for partitions */
11010 21 : if (parent_rel->rd_rel->relispartition)
11011 1 : ereport(ERROR,
11012 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11013 : errmsg("cannot inherit from a partition")));
11014 :
11015 : /*
11016 : * Prevent circularity by seeing if proposed parent inherits from child.
11017 : * (In particular, this disallows making a rel inherit from itself.)
11018 : *
11019 : * This is not completely bulletproof because of race conditions: in
11020 : * multi-level inheritance trees, someone else could concurrently be
11021 : * making another inheritance link that closes the loop but does not join
11022 : * either of the rels we have locked. Preventing that seems to require
11023 : * exclusive locks on the entire inheritance tree, which is a cure worse
11024 : * than the disease. find_all_inheritors() will cope with circularity
11025 : * anyway, so don't sweat it too much.
11026 : *
11027 : * We use weakest lock we can on child's children, namely AccessShareLock.
11028 : */
11029 20 : children = find_all_inheritors(RelationGetRelid(child_rel),
11030 : AccessShareLock, NULL);
11031 :
11032 20 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
11033 2 : ereport(ERROR,
11034 : (errcode(ERRCODE_DUPLICATE_TABLE),
11035 : errmsg("circular inheritance not allowed"),
11036 : errdetail("\"%s\" is already a child of \"%s\".",
11037 : parent->relname,
11038 : RelationGetRelationName(child_rel))));
11039 :
11040 : /* If parent has OIDs then child must have OIDs */
11041 18 : if (parent_rel->rd_rel->relhasoids && !child_rel->rd_rel->relhasoids)
11042 1 : ereport(ERROR,
11043 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11044 : errmsg("table \"%s\" without OIDs cannot inherit from table \"%s\" with OIDs",
11045 : RelationGetRelationName(child_rel),
11046 : RelationGetRelationName(parent_rel))));
11047 :
11048 : /*
11049 : * If child_rel has row-level triggers with transition tables, we
11050 : * currently don't allow it to become an inheritance child. See also
11051 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
11052 : */
11053 17 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
11054 17 : if (trigger_name != NULL)
11055 1 : ereport(ERROR,
11056 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11057 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
11058 : trigger_name, RelationGetRelationName(child_rel)),
11059 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies")));
11060 :
11061 : /* OK to create inheritance */
11062 16 : CreateInheritance(child_rel, parent_rel);
11063 :
11064 11 : ObjectAddressSet(address, RelationRelationId,
11065 : RelationGetRelid(parent_rel));
11066 :
11067 : /* keep our lock on the parent relation until commit */
11068 11 : heap_close(parent_rel, NoLock);
11069 :
11070 11 : return address;
11071 : }
11072 :
11073 : /*
11074 : * CreateInheritance
11075 : * Catalog manipulation portion of creating inheritance between a child
11076 : * table and a parent table.
11077 : *
11078 : * Common to ATExecAddInherit() and ATExecAttachPartition().
11079 : */
11080 : static void
11081 57 : CreateInheritance(Relation child_rel, Relation parent_rel)
11082 : {
11083 : Relation catalogRelation;
11084 : SysScanDesc scan;
11085 : ScanKeyData key;
11086 : HeapTuple inheritsTuple;
11087 : int32 inhseqno;
11088 :
11089 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
11090 57 : catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
11091 :
11092 : /*
11093 : * Check for duplicates in the list of parents, and determine the highest
11094 : * inhseqno already present; we'll use the next one for the new parent.
11095 : * Also, if proposed child is a partition, it cannot already be
11096 : * inheriting.
11097 : *
11098 : * Note: we do not reject the case where the child already inherits from
11099 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
11100 : */
11101 57 : ScanKeyInit(&key,
11102 : Anum_pg_inherits_inhrelid,
11103 : BTEqualStrategyNumber, F_OIDEQ,
11104 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
11105 57 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
11106 : true, NULL, 1, &key);
11107 :
11108 : /* inhseqno sequences start at 1 */
11109 57 : inhseqno = 0;
11110 119 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
11111 : {
11112 6 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
11113 :
11114 6 : if (inh->inhparent == RelationGetRelid(parent_rel))
11115 1 : ereport(ERROR,
11116 : (errcode(ERRCODE_DUPLICATE_TABLE),
11117 : errmsg("relation \"%s\" would be inherited from more than once",
11118 : RelationGetRelationName(parent_rel))));
11119 :
11120 5 : if (inh->inhseqno > inhseqno)
11121 5 : inhseqno = inh->inhseqno;
11122 : }
11123 56 : systable_endscan(scan);
11124 :
11125 : /* Match up the columns and bump attinhcount as needed */
11126 56 : MergeAttributesIntoExisting(child_rel, parent_rel);
11127 :
11128 : /* Match up the constraints and bump coninhcount as needed */
11129 50 : MergeConstraintsIntoExisting(child_rel, parent_rel);
11130 :
11131 : /*
11132 : * OK, it looks valid. Make the catalog entries that show inheritance.
11133 : */
11134 45 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
11135 : RelationGetRelid(parent_rel),
11136 : inhseqno + 1,
11137 : catalogRelation,
11138 45 : parent_rel->rd_rel->relkind ==
11139 : RELKIND_PARTITIONED_TABLE);
11140 :
11141 : /* Now we're done with pg_inherits */
11142 45 : heap_close(catalogRelation, RowExclusiveLock);
11143 45 : }
11144 :
11145 : /*
11146 : * Obtain the source-text form of the constraint expression for a check
11147 : * constraint, given its pg_constraint tuple
11148 : */
11149 : static char *
11150 14 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
11151 : {
11152 : Form_pg_constraint con;
11153 : bool isnull;
11154 : Datum attr;
11155 : Datum expr;
11156 :
11157 14 : con = (Form_pg_constraint) GETSTRUCT(contup);
11158 14 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
11159 14 : if (isnull)
11160 0 : elog(ERROR, "null conbin for constraint %u", HeapTupleGetOid(contup));
11161 :
11162 14 : expr = DirectFunctionCall2(pg_get_expr, attr,
11163 : ObjectIdGetDatum(con->conrelid));
11164 14 : return TextDatumGetCString(expr);
11165 : }
11166 :
11167 : /*
11168 : * Determine whether two check constraints are functionally equivalent
11169 : *
11170 : * The test we apply is to see whether they reverse-compile to the same
11171 : * source string. This insulates us from issues like whether attributes
11172 : * have the same physical column numbers in parent and child relations.
11173 : */
11174 : static bool
11175 7 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
11176 : {
11177 7 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
11178 7 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
11179 :
11180 14 : if (acon->condeferrable != bcon->condeferrable ||
11181 14 : acon->condeferred != bcon->condeferred ||
11182 7 : strcmp(decompile_conbin(a, tupleDesc),
11183 7 : decompile_conbin(b, tupleDesc)) != 0)
11184 1 : return false;
11185 : else
11186 6 : return true;
11187 : }
11188 :
11189 : /*
11190 : * Check columns in child table match up with columns in parent, and increment
11191 : * their attinhcount.
11192 : *
11193 : * Called by CreateInheritance
11194 : *
11195 : * Currently all parent columns must be found in child. Missing columns are an
11196 : * error. One day we might consider creating new columns like CREATE TABLE
11197 : * does. However, that is widely unpopular --- in the common use case of
11198 : * partitioned tables it's a foot-gun.
11199 : *
11200 : * The data type must match exactly. If the parent column is NOT NULL then
11201 : * the child must be as well. Defaults are not compared, however.
11202 : */
11203 : static void
11204 56 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
11205 : {
11206 : Relation attrrel;
11207 : AttrNumber parent_attno;
11208 : int parent_natts;
11209 : TupleDesc tupleDesc;
11210 : HeapTuple tuple;
11211 56 : bool child_is_partition = false;
11212 :
11213 56 : attrrel = heap_open(AttributeRelationId, RowExclusiveLock);
11214 :
11215 56 : tupleDesc = RelationGetDescr(parent_rel);
11216 56 : parent_natts = tupleDesc->natts;
11217 :
11218 : /* If parent_rel is a partitioned table, child_rel must be a partition */
11219 56 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11220 41 : child_is_partition = true;
11221 :
11222 182 : for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
11223 : {
11224 132 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
11225 : parent_attno - 1);
11226 132 : char *attributeName = NameStr(attribute->attname);
11227 :
11228 : /* Ignore dropped columns in the parent. */
11229 132 : if (attribute->attisdropped)
11230 11 : continue;
11231 :
11232 : /* Find same column in child (matching on column name). */
11233 121 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
11234 : attributeName);
11235 121 : if (HeapTupleIsValid(tuple))
11236 : {
11237 : /* Check they are same type, typmod, and collation */
11238 119 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
11239 :
11240 237 : if (attribute->atttypid != childatt->atttypid ||
11241 118 : attribute->atttypmod != childatt->atttypmod)
11242 2 : ereport(ERROR,
11243 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11244 : errmsg("child table \"%s\" has different type for column \"%s\"",
11245 : RelationGetRelationName(child_rel),
11246 : attributeName)));
11247 :
11248 117 : if (attribute->attcollation != childatt->attcollation)
11249 1 : ereport(ERROR,
11250 : (errcode(ERRCODE_COLLATION_MISMATCH),
11251 : errmsg("child table \"%s\" has different collation for column \"%s\"",
11252 : RelationGetRelationName(child_rel),
11253 : attributeName)));
11254 :
11255 : /*
11256 : * Check child doesn't discard NOT NULL property. (Other
11257 : * constraints are checked elsewhere.)
11258 : */
11259 116 : if (attribute->attnotnull && !childatt->attnotnull)
11260 1 : ereport(ERROR,
11261 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11262 : errmsg("column \"%s\" in child table must be marked NOT NULL",
11263 : attributeName)));
11264 :
11265 : /*
11266 : * OK, bump the child column's inheritance count. (If we fail
11267 : * later on, this change will just roll back.)
11268 : */
11269 115 : childatt->attinhcount++;
11270 :
11271 : /*
11272 : * In case of partitions, we must enforce that value of attislocal
11273 : * is same in all partitions. (Note: there are only inherited
11274 : * attributes in partitions)
11275 : */
11276 115 : if (child_is_partition)
11277 : {
11278 86 : Assert(childatt->attinhcount == 1);
11279 86 : childatt->attislocal = false;
11280 : }
11281 :
11282 115 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
11283 115 : heap_freetuple(tuple);
11284 : }
11285 : else
11286 : {
11287 2 : ereport(ERROR,
11288 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11289 : errmsg("child table is missing column \"%s\"",
11290 : attributeName)));
11291 : }
11292 : }
11293 :
11294 : /*
11295 : * If the parent has an OID column, so must the child, and we'd better
11296 : * update the child's attinhcount and attislocal the same as for normal
11297 : * columns. We needn't check data type or not-nullness though.
11298 : */
11299 50 : if (tupleDesc->tdhasoid)
11300 : {
11301 : /*
11302 : * Here we match by column number not name; the match *must* be the
11303 : * system column, not some random column named "oid".
11304 : */
11305 2 : tuple = SearchSysCacheCopy2(ATTNUM,
11306 : ObjectIdGetDatum(RelationGetRelid(child_rel)),
11307 : Int16GetDatum(ObjectIdAttributeNumber));
11308 2 : if (HeapTupleIsValid(tuple))
11309 : {
11310 2 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
11311 :
11312 : /* See comments above; these changes should be the same */
11313 2 : childatt->attinhcount++;
11314 :
11315 2 : if (child_is_partition)
11316 : {
11317 0 : Assert(childatt->attinhcount == 1);
11318 0 : childatt->attislocal = false;
11319 : }
11320 :
11321 2 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
11322 2 : heap_freetuple(tuple);
11323 : }
11324 : else
11325 : {
11326 0 : ereport(ERROR,
11327 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11328 : errmsg("child table is missing column \"%s\"",
11329 : "oid")));
11330 : }
11331 : }
11332 :
11333 50 : heap_close(attrrel, RowExclusiveLock);
11334 50 : }
11335 :
11336 : /*
11337 : * Check constraints in child table match up with constraints in parent,
11338 : * and increment their coninhcount.
11339 : *
11340 : * Constraints that are marked ONLY in the parent are ignored.
11341 : *
11342 : * Called by CreateInheritance
11343 : *
11344 : * Currently all constraints in parent must be present in the child. One day we
11345 : * may consider adding new constraints like CREATE TABLE does.
11346 : *
11347 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
11348 : * constraints. As long as tables have more like 10 constraints it shouldn't be
11349 : * a problem though. Even 100 constraints ought not be the end of the world.
11350 : *
11351 : * XXX See MergeWithExistingConstraint too if you change this code.
11352 : */
11353 : static void
11354 50 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
11355 : {
11356 : Relation catalog_relation;
11357 : TupleDesc tuple_desc;
11358 : SysScanDesc parent_scan;
11359 : ScanKeyData parent_key;
11360 : HeapTuple parent_tuple;
11361 50 : bool child_is_partition = false;
11362 :
11363 50 : catalog_relation = heap_open(ConstraintRelationId, RowExclusiveLock);
11364 50 : tuple_desc = RelationGetDescr(catalog_relation);
11365 :
11366 : /* If parent_rel is a partitioned table, child_rel must be a partition */
11367 50 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11368 37 : child_is_partition = true;
11369 :
11370 : /* Outer loop scans through the parent's constraint definitions */
11371 50 : ScanKeyInit(&parent_key,
11372 : Anum_pg_constraint_conrelid,
11373 : BTEqualStrategyNumber, F_OIDEQ,
11374 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
11375 50 : parent_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
11376 : true, NULL, 1, &parent_key);
11377 :
11378 107 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
11379 : {
11380 12 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
11381 : SysScanDesc child_scan;
11382 : ScanKeyData child_key;
11383 : HeapTuple child_tuple;
11384 12 : bool found = false;
11385 :
11386 12 : if (parent_con->contype != CONSTRAINT_CHECK)
11387 1 : continue;
11388 :
11389 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
11390 12 : if (parent_con->connoinherit)
11391 1 : continue;
11392 :
11393 : /* Search for a child constraint matching this one */
11394 11 : ScanKeyInit(&child_key,
11395 : Anum_pg_constraint_conrelid,
11396 : BTEqualStrategyNumber, F_OIDEQ,
11397 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
11398 11 : child_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
11399 : true, NULL, 1, &child_key);
11400 :
11401 11 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
11402 : {
11403 8 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
11404 : HeapTuple child_copy;
11405 :
11406 8 : if (child_con->contype != CONSTRAINT_CHECK)
11407 0 : continue;
11408 :
11409 8 : if (strcmp(NameStr(parent_con->conname),
11410 8 : NameStr(child_con->conname)) != 0)
11411 1 : continue;
11412 :
11413 7 : if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
11414 1 : ereport(ERROR,
11415 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11416 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
11417 : RelationGetRelationName(child_rel),
11418 : NameStr(parent_con->conname))));
11419 :
11420 : /* If the child constraint is "no inherit" then cannot merge */
11421 6 : if (child_con->connoinherit)
11422 0 : ereport(ERROR,
11423 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11424 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
11425 : NameStr(child_con->conname),
11426 : RelationGetRelationName(child_rel))));
11427 :
11428 : /*
11429 : * If the child constraint is "not valid" then cannot merge with a
11430 : * valid parent constraint
11431 : */
11432 6 : if (parent_con->convalidated && !child_con->convalidated)
11433 0 : ereport(ERROR,
11434 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11435 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
11436 : NameStr(child_con->conname),
11437 : RelationGetRelationName(child_rel))));
11438 :
11439 : /*
11440 : * OK, bump the child constraint's inheritance count. (If we fail
11441 : * later on, this change will just roll back.)
11442 : */
11443 6 : child_copy = heap_copytuple(child_tuple);
11444 6 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
11445 6 : child_con->coninhcount++;
11446 :
11447 : /*
11448 : * In case of partitions, an inherited constraint must be
11449 : * inherited only once since it cannot have multiple parents and
11450 : * it is never considered local.
11451 : */
11452 6 : if (child_is_partition)
11453 : {
11454 4 : Assert(child_con->coninhcount == 1);
11455 4 : child_con->conislocal = false;
11456 : }
11457 :
11458 6 : CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
11459 6 : heap_freetuple(child_copy);
11460 :
11461 6 : found = true;
11462 6 : break;
11463 : }
11464 :
11465 10 : systable_endscan(child_scan);
11466 :
11467 10 : if (!found)
11468 4 : ereport(ERROR,
11469 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11470 : errmsg("child table is missing constraint \"%s\"",
11471 : NameStr(parent_con->conname))));
11472 : }
11473 :
11474 45 : systable_endscan(parent_scan);
11475 45 : heap_close(catalog_relation, RowExclusiveLock);
11476 45 : }
11477 :
11478 : /*
11479 : * ALTER TABLE NO INHERIT
11480 : *
11481 : * Return value is the address of the relation that is no longer parent.
11482 : */
11483 : static ObjectAddress
11484 5 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
11485 : {
11486 : ObjectAddress address;
11487 : Relation parent_rel;
11488 :
11489 5 : if (rel->rd_rel->relispartition)
11490 0 : ereport(ERROR,
11491 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11492 : errmsg("cannot change inheritance of a partition")));
11493 :
11494 : /*
11495 : * AccessShareLock on the parent is probably enough, seeing that DROP
11496 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
11497 : * be inspecting the parent's schema.
11498 : */
11499 5 : parent_rel = heap_openrv(parent, AccessShareLock);
11500 :
11501 : /*
11502 : * We don't bother to check ownership of the parent table --- ownership of
11503 : * the child is presumed enough rights.
11504 : */
11505 :
11506 : /* Off to RemoveInheritance() where most of the work happens */
11507 5 : RemoveInheritance(rel, parent_rel);
11508 :
11509 : /* keep our lock on the parent relation until commit */
11510 4 : heap_close(parent_rel, NoLock);
11511 :
11512 4 : ObjectAddressSet(address, RelationRelationId,
11513 : RelationGetRelid(parent_rel));
11514 :
11515 4 : return address;
11516 : }
11517 :
11518 : /*
11519 : * RemoveInheritance
11520 : *
11521 : * Drop a parent from the child's parents. This just adjusts the attinhcount
11522 : * and attislocal of the columns and removes the pg_inherit and pg_depend
11523 : * entries.
11524 : *
11525 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
11526 : * up attislocal stays true, which means if a child is ever removed from a
11527 : * parent then its columns will never be automatically dropped which may
11528 : * surprise. But at least we'll never surprise by dropping columns someone
11529 : * isn't expecting to be dropped which would actually mean data loss.
11530 : *
11531 : * coninhcount and conislocal for inherited constraints are adjusted in
11532 : * exactly the same way.
11533 : *
11534 : * Common to ATExecDropInherit() and ATExecDetachPartition().
11535 : */
11536 : static void
11537 15 : RemoveInheritance(Relation child_rel, Relation parent_rel)
11538 : {
11539 : Relation catalogRelation;
11540 : SysScanDesc scan;
11541 : ScanKeyData key[3];
11542 : HeapTuple inheritsTuple,
11543 : attributeTuple,
11544 : constraintTuple;
11545 : List *connames;
11546 15 : bool found = false;
11547 15 : bool child_is_partition = false;
11548 :
11549 : /* If parent_rel is a partitioned table, child_rel must be a partition */
11550 15 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11551 10 : child_is_partition = true;
11552 :
11553 : /*
11554 : * Find and destroy the pg_inherits entry linking the two, or error out if
11555 : * there is none.
11556 : */
11557 15 : catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
11558 15 : ScanKeyInit(&key[0],
11559 : Anum_pg_inherits_inhrelid,
11560 : BTEqualStrategyNumber, F_OIDEQ,
11561 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
11562 15 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
11563 : true, NULL, 1, key);
11564 :
11565 15 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
11566 : {
11567 : Oid inhparent;
11568 :
11569 16 : inhparent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
11570 16 : if (inhparent == RelationGetRelid(parent_rel))
11571 : {
11572 12 : CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
11573 12 : found = true;
11574 12 : break;
11575 : }
11576 : }
11577 :
11578 15 : systable_endscan(scan);
11579 15 : heap_close(catalogRelation, RowExclusiveLock);
11580 :
11581 15 : if (!found)
11582 : {
11583 3 : if (child_is_partition)
11584 2 : ereport(ERROR,
11585 : (errcode(ERRCODE_UNDEFINED_TABLE),
11586 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
11587 : RelationGetRelationName(child_rel),
11588 : RelationGetRelationName(parent_rel))));
11589 : else
11590 1 : ereport(ERROR,
11591 : (errcode(ERRCODE_UNDEFINED_TABLE),
11592 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
11593 : RelationGetRelationName(parent_rel),
11594 : RelationGetRelationName(child_rel))));
11595 : }
11596 :
11597 : /*
11598 : * Search through child columns looking for ones matching parent rel
11599 : */
11600 12 : catalogRelation = heap_open(AttributeRelationId, RowExclusiveLock);
11601 12 : ScanKeyInit(&key[0],
11602 : Anum_pg_attribute_attrelid,
11603 : BTEqualStrategyNumber, F_OIDEQ,
11604 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
11605 12 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
11606 : true, NULL, 1, key);
11607 123 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
11608 : {
11609 99 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
11610 :
11611 : /* Ignore if dropped or not inherited */
11612 99 : if (att->attisdropped)
11613 0 : continue;
11614 99 : if (att->attinhcount <= 0)
11615 75 : continue;
11616 :
11617 24 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
11618 24 : NameStr(att->attname)))
11619 : {
11620 : /* Decrement inhcount and possibly set islocal to true */
11621 22 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
11622 22 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
11623 :
11624 22 : copy_att->attinhcount--;
11625 22 : if (copy_att->attinhcount == 0)
11626 22 : copy_att->attislocal = true;
11627 :
11628 22 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
11629 22 : heap_freetuple(copyTuple);
11630 : }
11631 : }
11632 12 : systable_endscan(scan);
11633 12 : heap_close(catalogRelation, RowExclusiveLock);
11634 :
11635 : /*
11636 : * Likewise, find inherited check constraints and disinherit them. To do
11637 : * this, we first need a list of the names of the parent's check
11638 : * constraints. (We cheat a bit by only checking for name matches,
11639 : * assuming that the expressions will match.)
11640 : */
11641 12 : catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
11642 12 : ScanKeyInit(&key[0],
11643 : Anum_pg_constraint_conrelid,
11644 : BTEqualStrategyNumber, F_OIDEQ,
11645 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
11646 12 : scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
11647 : true, NULL, 1, key);
11648 :
11649 12 : connames = NIL;
11650 :
11651 26 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
11652 : {
11653 2 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
11654 :
11655 2 : if (con->contype == CONSTRAINT_CHECK)
11656 2 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
11657 : }
11658 :
11659 12 : systable_endscan(scan);
11660 :
11661 : /* Now scan the child's constraints */
11662 12 : ScanKeyInit(&key[0],
11663 : Anum_pg_constraint_conrelid,
11664 : BTEqualStrategyNumber, F_OIDEQ,
11665 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
11666 12 : scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
11667 : true, NULL, 1, key);
11668 :
11669 34 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
11670 : {
11671 10 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
11672 : bool match;
11673 : ListCell *lc;
11674 :
11675 10 : if (con->contype != CONSTRAINT_CHECK)
11676 0 : continue;
11677 :
11678 10 : match = false;
11679 12 : foreach(lc, connames)
11680 : {
11681 4 : if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
11682 : {
11683 2 : match = true;
11684 2 : break;
11685 : }
11686 : }
11687 :
11688 10 : if (match)
11689 : {
11690 : /* Decrement inhcount and possibly set islocal to true */
11691 2 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
11692 2 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11693 :
11694 2 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
11695 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
11696 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
11697 :
11698 2 : copy_con->coninhcount--;
11699 2 : if (copy_con->coninhcount == 0)
11700 2 : copy_con->conislocal = true;
11701 :
11702 2 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
11703 2 : heap_freetuple(copyTuple);
11704 : }
11705 : }
11706 :
11707 12 : systable_endscan(scan);
11708 12 : heap_close(catalogRelation, RowExclusiveLock);
11709 :
11710 12 : drop_parent_dependency(RelationGetRelid(child_rel),
11711 : RelationRelationId,
11712 : RelationGetRelid(parent_rel),
11713 : child_dependency_type(child_is_partition));
11714 :
11715 : /*
11716 : * Post alter hook of this inherits. Since object_access_hook doesn't take
11717 : * multiple object identifiers, we relay oid of parent relation using
11718 : * auxiliary_id argument.
11719 : */
11720 12 : InvokeObjectPostAlterHookArg(InheritsRelationId,
11721 : RelationGetRelid(child_rel), 0,
11722 : RelationGetRelid(parent_rel), false);
11723 12 : }
11724 :
11725 : /*
11726 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
11727 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
11728 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
11729 : * be TypeRelationId). There's no convenient way to do this, so go trawling
11730 : * through pg_depend.
11731 : */
11732 : static void
11733 14 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
11734 : DependencyType deptype)
11735 : {
11736 : Relation catalogRelation;
11737 : SysScanDesc scan;
11738 : ScanKeyData key[3];
11739 : HeapTuple depTuple;
11740 :
11741 14 : catalogRelation = heap_open(DependRelationId, RowExclusiveLock);
11742 :
11743 14 : ScanKeyInit(&key[0],
11744 : Anum_pg_depend_classid,
11745 : BTEqualStrategyNumber, F_OIDEQ,
11746 : ObjectIdGetDatum(RelationRelationId));
11747 14 : ScanKeyInit(&key[1],
11748 : Anum_pg_depend_objid,
11749 : BTEqualStrategyNumber, F_OIDEQ,
11750 : ObjectIdGetDatum(relid));
11751 14 : ScanKeyInit(&key[2],
11752 : Anum_pg_depend_objsubid,
11753 : BTEqualStrategyNumber, F_INT4EQ,
11754 : Int32GetDatum(0));
11755 :
11756 14 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
11757 : NULL, 3, key);
11758 :
11759 60 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
11760 : {
11761 32 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
11762 :
11763 48 : if (dep->refclassid == refclassid &&
11764 30 : dep->refobjid == refobjid &&
11765 28 : dep->refobjsubid == 0 &&
11766 14 : dep->deptype == deptype)
11767 14 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
11768 : }
11769 :
11770 14 : systable_endscan(scan);
11771 14 : heap_close(catalogRelation, RowExclusiveLock);
11772 14 : }
11773 :
11774 : /*
11775 : * ALTER TABLE OF
11776 : *
11777 : * Attach a table to a composite type, as though it had been created with CREATE
11778 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
11779 : * subject table must not have inheritance parents. These restrictions ensure
11780 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
11781 : *
11782 : * The address of the type is returned.
11783 : */
11784 : static ObjectAddress
11785 9 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
11786 : {
11787 9 : Oid relid = RelationGetRelid(rel);
11788 : Type typetuple;
11789 : Oid typeid;
11790 : Relation inheritsRelation,
11791 : relationRelation;
11792 : SysScanDesc scan;
11793 : ScanKeyData key;
11794 : AttrNumber table_attno,
11795 : type_attno;
11796 : TupleDesc typeTupleDesc,
11797 : tableTupleDesc;
11798 : ObjectAddress tableobj,
11799 : typeobj;
11800 : HeapTuple classtuple;
11801 :
11802 : /* Validate the type. */
11803 9 : typetuple = typenameType(NULL, ofTypename, NULL);
11804 9 : check_of_type(typetuple);
11805 9 : typeid = HeapTupleGetOid(typetuple);
11806 :
11807 : /* Fail if the table has any inheritance parents. */
11808 9 : inheritsRelation = heap_open(InheritsRelationId, AccessShareLock);
11809 9 : ScanKeyInit(&key,
11810 : Anum_pg_inherits_inhrelid,
11811 : BTEqualStrategyNumber, F_OIDEQ,
11812 : ObjectIdGetDatum(relid));
11813 9 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
11814 : true, NULL, 1, &key);
11815 9 : if (HeapTupleIsValid(systable_getnext(scan)))
11816 1 : ereport(ERROR,
11817 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11818 : errmsg("typed tables cannot inherit")));
11819 8 : systable_endscan(scan);
11820 8 : heap_close(inheritsRelation, AccessShareLock);
11821 :
11822 : /*
11823 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
11824 : * require that the order also match. However, attnotnull need not match.
11825 : * Also unlike inheritance, we do not require matching relhasoids.
11826 : */
11827 8 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
11828 8 : tableTupleDesc = RelationGetDescr(rel);
11829 8 : table_attno = 1;
11830 26 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
11831 : {
11832 : Form_pg_attribute type_attr,
11833 : table_attr;
11834 : const char *type_attname,
11835 : *table_attname;
11836 :
11837 : /* Get the next non-dropped type attribute. */
11838 22 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
11839 22 : if (type_attr->attisdropped)
11840 7 : continue;
11841 15 : type_attname = NameStr(type_attr->attname);
11842 :
11843 : /* Get the next non-dropped table attribute. */
11844 : do
11845 : {
11846 17 : if (table_attno > tableTupleDesc->natts)
11847 1 : ereport(ERROR,
11848 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11849 : errmsg("table is missing column \"%s\"",
11850 : type_attname)));
11851 16 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
11852 16 : table_attno++;
11853 16 : } while (table_attr->attisdropped);
11854 14 : table_attname = NameStr(table_attr->attname);
11855 :
11856 : /* Compare name. */
11857 14 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
11858 1 : ereport(ERROR,
11859 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11860 : errmsg("table has column \"%s\" where type requires \"%s\"",
11861 : table_attname, type_attname)));
11862 :
11863 : /* Compare type. */
11864 25 : if (table_attr->atttypid != type_attr->atttypid ||
11865 23 : table_attr->atttypmod != type_attr->atttypmod ||
11866 11 : table_attr->attcollation != type_attr->attcollation)
11867 2 : ereport(ERROR,
11868 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11869 : errmsg("table \"%s\" has different type for column \"%s\"",
11870 : RelationGetRelationName(rel), type_attname)));
11871 : }
11872 4 : DecrTupleDescRefCount(typeTupleDesc);
11873 :
11874 : /* Any remaining columns at the end of the table had better be dropped. */
11875 4 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
11876 : {
11877 1 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
11878 : table_attno - 1);
11879 :
11880 1 : if (!table_attr->attisdropped)
11881 1 : ereport(ERROR,
11882 : (errcode(ERRCODE_DATATYPE_MISMATCH),
11883 : errmsg("table has extra column \"%s\"",
11884 : NameStr(table_attr->attname))));
11885 : }
11886 :
11887 : /* If the table was already typed, drop the existing dependency. */
11888 3 : if (rel->rd_rel->reloftype)
11889 1 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
11890 : DEPENDENCY_NORMAL);
11891 :
11892 : /* Record a dependency on the new type. */
11893 3 : tableobj.classId = RelationRelationId;
11894 3 : tableobj.objectId = relid;
11895 3 : tableobj.objectSubId = 0;
11896 3 : typeobj.classId = TypeRelationId;
11897 3 : typeobj.objectId = typeid;
11898 3 : typeobj.objectSubId = 0;
11899 3 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
11900 :
11901 : /* Update pg_class.reloftype */
11902 3 : relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
11903 3 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
11904 3 : if (!HeapTupleIsValid(classtuple))
11905 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
11906 3 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
11907 3 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
11908 :
11909 3 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
11910 :
11911 3 : heap_freetuple(classtuple);
11912 3 : heap_close(relationRelation, RowExclusiveLock);
11913 :
11914 3 : ReleaseSysCache(typetuple);
11915 :
11916 3 : return typeobj;
11917 : }
11918 :
11919 : /*
11920 : * ALTER TABLE NOT OF
11921 : *
11922 : * Detach a typed table from its originating type. Just clear reloftype and
11923 : * remove the dependency.
11924 : */
11925 : static void
11926 1 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
11927 : {
11928 1 : Oid relid = RelationGetRelid(rel);
11929 : Relation relationRelation;
11930 : HeapTuple tuple;
11931 :
11932 1 : if (!OidIsValid(rel->rd_rel->reloftype))
11933 0 : ereport(ERROR,
11934 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11935 : errmsg("\"%s\" is not a typed table",
11936 : RelationGetRelationName(rel))));
11937 :
11938 : /*
11939 : * We don't bother to check ownership of the type --- ownership of the
11940 : * table is presumed enough rights. No lock required on the type, either.
11941 : */
11942 :
11943 1 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
11944 : DEPENDENCY_NORMAL);
11945 :
11946 : /* Clear pg_class.reloftype */
11947 1 : relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
11948 1 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
11949 1 : if (!HeapTupleIsValid(tuple))
11950 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
11951 1 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
11952 1 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
11953 :
11954 1 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
11955 :
11956 1 : heap_freetuple(tuple);
11957 1 : heap_close(relationRelation, RowExclusiveLock);
11958 1 : }
11959 :
11960 : /*
11961 : * relation_mark_replica_identity: Update a table's replica identity
11962 : *
11963 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
11964 : * index. Otherwise, it should be InvalidOid.
11965 : */
11966 : static void
11967 8 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
11968 : bool is_internal)
11969 : {
11970 : Relation pg_index;
11971 : Relation pg_class;
11972 : HeapTuple pg_class_tuple;
11973 : HeapTuple pg_index_tuple;
11974 : Form_pg_class pg_class_form;
11975 : Form_pg_index pg_index_form;
11976 :
11977 : ListCell *index;
11978 :
11979 : /*
11980 : * Check whether relreplident has changed, and update it if so.
11981 : */
11982 8 : pg_class = heap_open(RelationRelationId, RowExclusiveLock);
11983 8 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
11984 : ObjectIdGetDatum(RelationGetRelid(rel)));
11985 8 : if (!HeapTupleIsValid(pg_class_tuple))
11986 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
11987 : RelationGetRelationName(rel));
11988 8 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
11989 8 : if (pg_class_form->relreplident != ri_type)
11990 : {
11991 4 : pg_class_form->relreplident = ri_type;
11992 4 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
11993 : }
11994 8 : heap_close(pg_class, RowExclusiveLock);
11995 8 : heap_freetuple(pg_class_tuple);
11996 :
11997 : /*
11998 : * Check whether the correct index is marked indisreplident; if so, we're
11999 : * done.
12000 : */
12001 8 : if (OidIsValid(indexOid))
12002 : {
12003 5 : Assert(ri_type == REPLICA_IDENTITY_INDEX);
12004 :
12005 5 : pg_index_tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
12006 5 : if (!HeapTupleIsValid(pg_index_tuple))
12007 0 : elog(ERROR, "cache lookup failed for index %u", indexOid);
12008 5 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
12009 :
12010 5 : if (pg_index_form->indisreplident)
12011 : {
12012 1 : ReleaseSysCache(pg_index_tuple);
12013 9 : return;
12014 : }
12015 4 : ReleaseSysCache(pg_index_tuple);
12016 : }
12017 :
12018 : /*
12019 : * Clear the indisreplident flag from any index that had it previously,
12020 : * and set it for any index that should have it now.
12021 : */
12022 7 : pg_index = heap_open(IndexRelationId, RowExclusiveLock);
12023 77 : foreach(index, RelationGetIndexList(rel))
12024 : {
12025 70 : Oid thisIndexOid = lfirst_oid(index);
12026 70 : bool dirty = false;
12027 :
12028 70 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
12029 : ObjectIdGetDatum(thisIndexOid));
12030 70 : if (!HeapTupleIsValid(pg_index_tuple))
12031 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
12032 70 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
12033 :
12034 : /*
12035 : * Unset the bit if set. We know it's wrong because we checked this
12036 : * earlier.
12037 : */
12038 70 : if (pg_index_form->indisreplident)
12039 : {
12040 4 : dirty = true;
12041 4 : pg_index_form->indisreplident = false;
12042 : }
12043 66 : else if (thisIndexOid == indexOid)
12044 : {
12045 4 : dirty = true;
12046 4 : pg_index_form->indisreplident = true;
12047 : }
12048 :
12049 70 : if (dirty)
12050 : {
12051 8 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
12052 8 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
12053 : InvalidOid, is_internal);
12054 : }
12055 70 : heap_freetuple(pg_index_tuple);
12056 : }
12057 :
12058 7 : heap_close(pg_index, RowExclusiveLock);
12059 : }
12060 :
12061 : /*
12062 : * ALTER TABLE <name> REPLICA IDENTITY ...
12063 : */
12064 : static void
12065 15 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
12066 : {
12067 : Oid indexOid;
12068 : Relation indexRel;
12069 : int key;
12070 :
12071 15 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
12072 : {
12073 1 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
12074 1 : return;
12075 : }
12076 14 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
12077 : {
12078 1 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
12079 1 : return;
12080 : }
12081 13 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
12082 : {
12083 1 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
12084 1 : return;
12085 : }
12086 12 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
12087 : {
12088 : /* fallthrough */ ;
12089 : }
12090 : else
12091 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
12092 :
12093 :
12094 : /* Check that the index exists */
12095 12 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
12096 12 : if (!OidIsValid(indexOid))
12097 0 : ereport(ERROR,
12098 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12099 : errmsg("index \"%s\" for table \"%s\" does not exist",
12100 : stmt->name, RelationGetRelationName(rel))));
12101 :
12102 12 : indexRel = index_open(indexOid, ShareLock);
12103 :
12104 : /* Check that the index is on the relation we're altering. */
12105 24 : if (indexRel->rd_index == NULL ||
12106 12 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
12107 1 : ereport(ERROR,
12108 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12109 : errmsg("\"%s\" is not an index for table \"%s\"",
12110 : RelationGetRelationName(indexRel),
12111 : RelationGetRelationName(rel))));
12112 : /* The AM must support uniqueness, and the index must in fact be unique. */
12113 21 : if (!indexRel->rd_amroutine->amcanunique ||
12114 10 : !indexRel->rd_index->indisunique)
12115 2 : ereport(ERROR,
12116 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12117 : errmsg("cannot use non-unique index \"%s\" as replica identity",
12118 : RelationGetRelationName(indexRel))));
12119 : /* Deferred indexes are not guaranteed to be always unique. */
12120 9 : if (!indexRel->rd_index->indimmediate)
12121 1 : ereport(ERROR,
12122 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12123 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
12124 : RelationGetRelationName(indexRel))));
12125 : /* Expression indexes aren't supported. */
12126 8 : if (RelationGetIndexExpressions(indexRel) != NIL)
12127 1 : ereport(ERROR,
12128 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12129 : errmsg("cannot use expression index \"%s\" as replica identity",
12130 : RelationGetRelationName(indexRel))));
12131 : /* Predicate indexes aren't supported. */
12132 7 : if (RelationGetIndexPredicate(indexRel) != NIL)
12133 1 : ereport(ERROR,
12134 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12135 : errmsg("cannot use partial index \"%s\" as replica identity",
12136 : RelationGetRelationName(indexRel))));
12137 : /* And neither are invalid indexes. */
12138 6 : if (!IndexIsValid(indexRel->rd_index))
12139 0 : ereport(ERROR,
12140 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12141 : errmsg("cannot use invalid index \"%s\" as replica identity",
12142 : RelationGetRelationName(indexRel))));
12143 :
12144 : /* Check index for nullable columns. */
12145 15 : for (key = 0; key < indexRel->rd_index->indnatts; key++)
12146 : {
12147 10 : int16 attno = indexRel->rd_index->indkey.values[key];
12148 : Form_pg_attribute attr;
12149 :
12150 : /* Allow OID column to be indexed; it's certainly not nullable */
12151 10 : if (attno == ObjectIdAttributeNumber)
12152 1 : continue;
12153 :
12154 : /*
12155 : * Reject any other system columns. (Going forward, we'll disallow
12156 : * indexes containing such columns in the first place, but they might
12157 : * exist in older branches.)
12158 : */
12159 9 : if (attno <= 0)
12160 0 : ereport(ERROR,
12161 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
12162 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
12163 : RelationGetRelationName(indexRel), attno)));
12164 :
12165 9 : attr = TupleDescAttr(rel->rd_att, attno - 1);
12166 9 : if (!attr->attnotnull)
12167 1 : ereport(ERROR,
12168 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12169 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
12170 : RelationGetRelationName(indexRel),
12171 : NameStr(attr->attname))));
12172 : }
12173 :
12174 : /* This index is suitable for use as a replica identity. Mark it. */
12175 5 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
12176 :
12177 5 : index_close(indexRel, NoLock);
12178 : }
12179 :
12180 : /*
12181 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
12182 : */
12183 : static void
12184 37 : ATExecEnableRowSecurity(Relation rel)
12185 : {
12186 : Relation pg_class;
12187 : Oid relid;
12188 : HeapTuple tuple;
12189 :
12190 37 : relid = RelationGetRelid(rel);
12191 :
12192 37 : pg_class = heap_open(RelationRelationId, RowExclusiveLock);
12193 :
12194 37 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
12195 :
12196 37 : if (!HeapTupleIsValid(tuple))
12197 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
12198 :
12199 37 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = true;
12200 37 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
12201 :
12202 37 : heap_close(pg_class, RowExclusiveLock);
12203 37 : heap_freetuple(tuple);
12204 37 : }
12205 :
12206 : static void
12207 2 : ATExecDisableRowSecurity(Relation rel)
12208 : {
12209 : Relation pg_class;
12210 : Oid relid;
12211 : HeapTuple tuple;
12212 :
12213 2 : relid = RelationGetRelid(rel);
12214 :
12215 : /* Pull the record for this relation and update it */
12216 2 : pg_class = heap_open(RelationRelationId, RowExclusiveLock);
12217 :
12218 2 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
12219 :
12220 2 : if (!HeapTupleIsValid(tuple))
12221 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
12222 :
12223 2 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = false;
12224 2 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
12225 :
12226 2 : heap_close(pg_class, RowExclusiveLock);
12227 2 : heap_freetuple(tuple);
12228 2 : }
12229 :
12230 : /*
12231 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
12232 : */
12233 : static void
12234 14 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
12235 : {
12236 : Relation pg_class;
12237 : Oid relid;
12238 : HeapTuple tuple;
12239 :
12240 14 : relid = RelationGetRelid(rel);
12241 :
12242 14 : pg_class = heap_open(RelationRelationId, RowExclusiveLock);
12243 :
12244 14 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
12245 :
12246 14 : if (!HeapTupleIsValid(tuple))
12247 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
12248 :
12249 14 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
12250 14 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
12251 :
12252 14 : heap_close(pg_class, RowExclusiveLock);
12253 14 : heap_freetuple(tuple);
12254 14 : }
12255 :
12256 : /*
12257 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
12258 : */
12259 : static void
12260 1 : ATExecGenericOptions(Relation rel, List *options)
12261 : {
12262 : Relation ftrel;
12263 : ForeignServer *server;
12264 : ForeignDataWrapper *fdw;
12265 : HeapTuple tuple;
12266 : bool isnull;
12267 : Datum repl_val[Natts_pg_foreign_table];
12268 : bool repl_null[Natts_pg_foreign_table];
12269 : bool repl_repl[Natts_pg_foreign_table];
12270 : Datum datum;
12271 : Form_pg_foreign_table tableform;
12272 :
12273 1 : if (options == NIL)
12274 1 : return;
12275 :
12276 1 : ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
12277 :
12278 1 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
12279 1 : if (!HeapTupleIsValid(tuple))
12280 0 : ereport(ERROR,
12281 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12282 : errmsg("foreign table \"%s\" does not exist",
12283 : RelationGetRelationName(rel))));
12284 1 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
12285 1 : server = GetForeignServer(tableform->ftserver);
12286 1 : fdw = GetForeignDataWrapper(server->fdwid);
12287 :
12288 1 : memset(repl_val, 0, sizeof(repl_val));
12289 1 : memset(repl_null, false, sizeof(repl_null));
12290 1 : memset(repl_repl, false, sizeof(repl_repl));
12291 :
12292 : /* Extract the current options */
12293 1 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
12294 : tuple,
12295 : Anum_pg_foreign_table_ftoptions,
12296 : &isnull);
12297 1 : if (isnull)
12298 0 : datum = PointerGetDatum(NULL);
12299 :
12300 : /* Transform the options */
12301 1 : datum = transformGenericOptions(ForeignTableRelationId,
12302 : datum,
12303 : options,
12304 : fdw->fdwvalidator);
12305 :
12306 1 : if (PointerIsValid(DatumGetPointer(datum)))
12307 1 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
12308 : else
12309 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
12310 :
12311 1 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
12312 :
12313 : /* Everything looks good - update the tuple */
12314 :
12315 1 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
12316 : repl_val, repl_null, repl_repl);
12317 :
12318 1 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
12319 :
12320 : /*
12321 : * Invalidate relcache so that all sessions will refresh any cached plans
12322 : * that might depend on the old options.
12323 : */
12324 1 : CacheInvalidateRelcache(rel);
12325 :
12326 1 : InvokeObjectPostAlterHook(ForeignTableRelationId,
12327 : RelationGetRelid(rel), 0);
12328 :
12329 1 : heap_close(ftrel, RowExclusiveLock);
12330 :
12331 1 : heap_freetuple(tuple);
12332 : }
12333 :
12334 : /*
12335 : * Preparation phase for SET LOGGED/UNLOGGED
12336 : *
12337 : * This verifies that we're not trying to change a temp table. Also,
12338 : * existing foreign key constraints are checked to avoid ending up with
12339 : * permanent tables referencing unlogged tables.
12340 : *
12341 : * Return value is false if the operation is a no-op (in which case the
12342 : * checks are skipped), otherwise true.
12343 : */
12344 : static bool
12345 9 : ATPrepChangePersistence(Relation rel, bool toLogged)
12346 : {
12347 : Relation pg_constraint;
12348 : HeapTuple tuple;
12349 : SysScanDesc scan;
12350 : ScanKeyData skey[1];
12351 :
12352 : /*
12353 : * Disallow changing status for a temp table. Also verify whether we can
12354 : * get away with doing nothing; in such cases we don't need to run the
12355 : * checks below, either.
12356 : */
12357 9 : switch (rel->rd_rel->relpersistence)
12358 : {
12359 : case RELPERSISTENCE_TEMP:
12360 0 : ereport(ERROR,
12361 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12362 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
12363 : RelationGetRelationName(rel)),
12364 : errtable(rel)));
12365 : break;
12366 : case RELPERSISTENCE_PERMANENT:
12367 5 : if (toLogged)
12368 : /* nothing to do */
12369 1 : return false;
12370 4 : break;
12371 : case RELPERSISTENCE_UNLOGGED:
12372 4 : if (!toLogged)
12373 : /* nothing to do */
12374 1 : return false;
12375 3 : break;
12376 : }
12377 :
12378 : /*
12379 : * Check that the table is not part any publication when changing to
12380 : * UNLOGGED as UNLOGGED tables can't be published.
12381 : */
12382 11 : if (!toLogged &&
12383 4 : list_length(GetRelationPublications(RelationGetRelid(rel))) > 0)
12384 0 : ereport(ERROR,
12385 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12386 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
12387 : RelationGetRelationName(rel)),
12388 : errdetail("Unlogged relations cannot be replicated.")));
12389 :
12390 : /*
12391 : * Check existing foreign key constraints to preserve the invariant that
12392 : * permanent tables cannot reference unlogged ones. Self-referencing
12393 : * foreign keys can safely be ignored.
12394 : */
12395 7 : pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
12396 :
12397 : /*
12398 : * Scan conrelid if changing to permanent, else confrelid. This also
12399 : * determines whether a useful index exists.
12400 : */
12401 7 : ScanKeyInit(&skey[0],
12402 : toLogged ? Anum_pg_constraint_conrelid :
12403 : Anum_pg_constraint_confrelid,
12404 : BTEqualStrategyNumber, F_OIDEQ,
12405 : ObjectIdGetDatum(RelationGetRelid(rel)));
12406 7 : scan = systable_beginscan(pg_constraint,
12407 : toLogged ? ConstraintRelidIndexId : InvalidOid,
12408 : true, NULL, 1, skey);
12409 :
12410 19 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
12411 : {
12412 7 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
12413 :
12414 7 : if (con->contype == CONSTRAINT_FOREIGN)
12415 : {
12416 : Oid foreignrelid;
12417 : Relation foreignrel;
12418 :
12419 : /* the opposite end of what we used as scankey */
12420 5 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
12421 :
12422 : /* ignore if self-referencing */
12423 5 : if (RelationGetRelid(rel) == foreignrelid)
12424 2 : continue;
12425 :
12426 3 : foreignrel = relation_open(foreignrelid, AccessShareLock);
12427 :
12428 3 : if (toLogged)
12429 : {
12430 1 : if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
12431 1 : ereport(ERROR,
12432 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12433 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
12434 : RelationGetRelationName(rel),
12435 : RelationGetRelationName(foreignrel)),
12436 : errtableconstraint(rel, NameStr(con->conname))));
12437 : }
12438 : else
12439 : {
12440 2 : if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
12441 1 : ereport(ERROR,
12442 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12443 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
12444 : RelationGetRelationName(rel),
12445 : RelationGetRelationName(foreignrel)),
12446 : errtableconstraint(rel, NameStr(con->conname))));
12447 : }
12448 :
12449 1 : relation_close(foreignrel, AccessShareLock);
12450 : }
12451 : }
12452 :
12453 5 : systable_endscan(scan);
12454 :
12455 5 : heap_close(pg_constraint, AccessShareLock);
12456 :
12457 5 : return true;
12458 : }
12459 :
12460 : /*
12461 : * Execute ALTER TABLE SET SCHEMA
12462 : */
12463 : ObjectAddress
12464 13 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
12465 : {
12466 : Relation rel;
12467 : Oid relid;
12468 : Oid oldNspOid;
12469 : Oid nspOid;
12470 : RangeVar *newrv;
12471 : ObjectAddresses *objsMoved;
12472 : ObjectAddress myself;
12473 :
12474 13 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
12475 13 : stmt->missing_ok, false,
12476 : RangeVarCallbackForAlterRelation,
12477 : (void *) stmt);
12478 :
12479 13 : if (!OidIsValid(relid))
12480 : {
12481 2 : ereport(NOTICE,
12482 : (errmsg("relation \"%s\" does not exist, skipping",
12483 : stmt->relation->relname)));
12484 2 : return InvalidObjectAddress;
12485 : }
12486 :
12487 11 : rel = relation_open(relid, NoLock);
12488 :
12489 11 : oldNspOid = RelationGetNamespace(rel);
12490 :
12491 : /* If it's an owned sequence, disallow moving it by itself. */
12492 11 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
12493 : {
12494 : Oid tableId;
12495 : int32 colId;
12496 :
12497 0 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
12498 0 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
12499 0 : ereport(ERROR,
12500 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12501 : errmsg("cannot move an owned sequence into another schema"),
12502 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
12503 : RelationGetRelationName(rel),
12504 : get_rel_name(tableId))));
12505 : }
12506 :
12507 : /* Get and lock schema OID and check its permissions. */
12508 11 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
12509 11 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
12510 :
12511 : /* common checks on switching namespaces */
12512 11 : CheckSetNamespace(oldNspOid, nspOid);
12513 :
12514 11 : objsMoved = new_object_addresses();
12515 11 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
12516 10 : free_object_addresses(objsMoved);
12517 :
12518 10 : ObjectAddressSet(myself, RelationRelationId, relid);
12519 :
12520 10 : if (oldschema)
12521 10 : *oldschema = oldNspOid;
12522 :
12523 : /* close rel, but keep lock until commit */
12524 10 : relation_close(rel, NoLock);
12525 :
12526 10 : return myself;
12527 : }
12528 :
12529 : /*
12530 : * The guts of relocating a table or materialized view to another namespace:
12531 : * besides moving the relation itself, its dependent objects are relocated to
12532 : * the new schema.
12533 : */
12534 : void
12535 11 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
12536 : ObjectAddresses *objsMoved)
12537 : {
12538 : Relation classRel;
12539 :
12540 11 : Assert(objsMoved != NULL);
12541 :
12542 : /* OK, modify the pg_class row and pg_depend entry */
12543 11 : classRel = heap_open(RelationRelationId, RowExclusiveLock);
12544 :
12545 11 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
12546 : nspOid, true, objsMoved);
12547 :
12548 : /* Fix the table's row type too */
12549 10 : AlterTypeNamespaceInternal(rel->rd_rel->reltype,
12550 : nspOid, false, false, objsMoved);
12551 :
12552 : /* Fix other dependent stuff */
12553 13 : if (rel->rd_rel->relkind == RELKIND_RELATION ||
12554 5 : rel->rd_rel->relkind == RELKIND_MATVIEW ||
12555 2 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12556 : {
12557 8 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
12558 8 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
12559 : objsMoved, AccessExclusiveLock);
12560 8 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
12561 : false, objsMoved);
12562 : }
12563 :
12564 10 : heap_close(classRel, RowExclusiveLock);
12565 10 : }
12566 :
12567 : /*
12568 : * The guts of relocating a relation to another namespace: fix the pg_class
12569 : * entry, and the pg_depend entry if any. Caller must already have
12570 : * opened and write-locked pg_class.
12571 : */
12572 : void
12573 22 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
12574 : Oid oldNspOid, Oid newNspOid,
12575 : bool hasDependEntry,
12576 : ObjectAddresses *objsMoved)
12577 : {
12578 : HeapTuple classTup;
12579 : Form_pg_class classForm;
12580 : ObjectAddress thisobj;
12581 22 : bool already_done = false;
12582 :
12583 22 : classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
12584 22 : if (!HeapTupleIsValid(classTup))
12585 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
12586 22 : classForm = (Form_pg_class) GETSTRUCT(classTup);
12587 :
12588 22 : Assert(classForm->relnamespace == oldNspOid);
12589 :
12590 22 : thisobj.classId = RelationRelationId;
12591 22 : thisobj.objectId = relOid;
12592 22 : thisobj.objectSubId = 0;
12593 :
12594 : /*
12595 : * If the object has already been moved, don't move it again. If it's
12596 : * already in the right place, don't move it, but still fire the object
12597 : * access hook.
12598 : */
12599 22 : already_done = object_address_present(&thisobj, objsMoved);
12600 22 : if (!already_done && oldNspOid != newNspOid)
12601 : {
12602 : /* check for duplicate name (more friendly than unique-index failure) */
12603 15 : if (get_relname_relid(NameStr(classForm->relname),
12604 : newNspOid) != InvalidOid)
12605 0 : ereport(ERROR,
12606 : (errcode(ERRCODE_DUPLICATE_TABLE),
12607 : errmsg("relation \"%s\" already exists in schema \"%s\"",
12608 : NameStr(classForm->relname),
12609 : get_namespace_name(newNspOid))));
12610 :
12611 : /* classTup is a copy, so OK to scribble on */
12612 15 : classForm->relnamespace = newNspOid;
12613 :
12614 15 : CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
12615 :
12616 : /* Update dependency on schema if caller said so */
12617 25 : if (hasDependEntry &&
12618 11 : changeDependencyFor(RelationRelationId,
12619 : relOid,
12620 : NamespaceRelationId,
12621 : oldNspOid,
12622 : newNspOid) != 1)
12623 0 : elog(ERROR, "failed to change schema dependency for relation \"%s\"",
12624 : NameStr(classForm->relname));
12625 : }
12626 21 : if (!already_done)
12627 : {
12628 21 : add_exact_object_address(&thisobj, objsMoved);
12629 :
12630 21 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
12631 : }
12632 :
12633 21 : heap_freetuple(classTup);
12634 21 : }
12635 :
12636 : /*
12637 : * Move all indexes for the specified relation to another namespace.
12638 : *
12639 : * Note: we assume adequate permission checking was done by the caller,
12640 : * and that the caller has a suitable lock on the owning relation.
12641 : */
12642 : static void
12643 8 : AlterIndexNamespaces(Relation classRel, Relation rel,
12644 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
12645 : {
12646 : List *indexList;
12647 : ListCell *l;
12648 :
12649 8 : indexList = RelationGetIndexList(rel);
12650 :
12651 13 : foreach(l, indexList)
12652 : {
12653 5 : Oid indexOid = lfirst_oid(l);
12654 : ObjectAddress thisobj;
12655 :
12656 5 : thisobj.classId = RelationRelationId;
12657 5 : thisobj.objectId = indexOid;
12658 5 : thisobj.objectSubId = 0;
12659 :
12660 : /*
12661 : * Note: currently, the index will not have its own dependency on the
12662 : * namespace, so we don't need to do changeDependencyFor(). There's no
12663 : * row type in pg_type, either.
12664 : *
12665 : * XXX this objsMoved test may be pointless -- surely we have a single
12666 : * dependency link from a relation to each index?
12667 : */
12668 5 : if (!object_address_present(&thisobj, objsMoved))
12669 : {
12670 5 : AlterRelationNamespaceInternal(classRel, indexOid,
12671 : oldNspOid, newNspOid,
12672 : false, objsMoved);
12673 5 : add_exact_object_address(&thisobj, objsMoved);
12674 : }
12675 : }
12676 :
12677 8 : list_free(indexList);
12678 8 : }
12679 :
12680 : /*
12681 : * Move all identity and SERIAL-column sequences of the specified relation to another
12682 : * namespace.
12683 : *
12684 : * Note: we assume adequate permission checking was done by the caller,
12685 : * and that the caller has a suitable lock on the owning relation.
12686 : */
12687 : static void
12688 8 : AlterSeqNamespaces(Relation classRel, Relation rel,
12689 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
12690 : LOCKMODE lockmode)
12691 : {
12692 : Relation depRel;
12693 : SysScanDesc scan;
12694 : ScanKeyData key[2];
12695 : HeapTuple tup;
12696 :
12697 : /*
12698 : * SERIAL sequences are those having an auto dependency on one of the
12699 : * table's columns (we don't care *which* column, exactly).
12700 : */
12701 8 : depRel = heap_open(DependRelationId, AccessShareLock);
12702 :
12703 8 : ScanKeyInit(&key[0],
12704 : Anum_pg_depend_refclassid,
12705 : BTEqualStrategyNumber, F_OIDEQ,
12706 : ObjectIdGetDatum(RelationRelationId));
12707 8 : ScanKeyInit(&key[1],
12708 : Anum_pg_depend_refobjid,
12709 : BTEqualStrategyNumber, F_OIDEQ,
12710 : ObjectIdGetDatum(RelationGetRelid(rel)));
12711 : /* we leave refobjsubid unspecified */
12712 :
12713 8 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
12714 : NULL, 2, key);
12715 :
12716 69 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
12717 : {
12718 53 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
12719 : Relation seqRel;
12720 :
12721 : /* skip dependencies other than auto dependencies on columns */
12722 91 : if (depForm->refobjsubid == 0 ||
12723 42 : depForm->classid != RelationRelationId ||
12724 8 : depForm->objsubid != 0 ||
12725 4 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
12726 49 : continue;
12727 :
12728 : /* Use relation_open just in case it's an index */
12729 4 : seqRel = relation_open(depForm->objid, lockmode);
12730 :
12731 : /* skip non-sequence relations */
12732 4 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
12733 : {
12734 : /* No need to keep the lock */
12735 0 : relation_close(seqRel, lockmode);
12736 0 : continue;
12737 : }
12738 :
12739 : /* Fix the pg_class and pg_depend entries */
12740 4 : AlterRelationNamespaceInternal(classRel, depForm->objid,
12741 : oldNspOid, newNspOid,
12742 : true, objsMoved);
12743 :
12744 : /*
12745 : * Sequences have entries in pg_type. We need to be careful to move
12746 : * them to the new namespace, too.
12747 : */
12748 4 : AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype,
12749 : newNspOid, false, false, objsMoved);
12750 :
12751 : /* Now we can close it. Keep the lock till end of transaction. */
12752 4 : relation_close(seqRel, NoLock);
12753 : }
12754 :
12755 8 : systable_endscan(scan);
12756 :
12757 8 : relation_close(depRel, AccessShareLock);
12758 8 : }
12759 :
12760 :
12761 : /*
12762 : * This code supports
12763 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
12764 : *
12765 : * Because we only support this for TEMP tables, it's sufficient to remember
12766 : * the state in a backend-local data structure.
12767 : */
12768 :
12769 : /*
12770 : * Register a newly-created relation's ON COMMIT action.
12771 : */
12772 : void
12773 8 : register_on_commit_action(Oid relid, OnCommitAction action)
12774 : {
12775 : OnCommitItem *oc;
12776 : MemoryContext oldcxt;
12777 :
12778 : /*
12779 : * We needn't bother registering the relation unless there is an ON COMMIT
12780 : * action we need to take.
12781 : */
12782 8 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
12783 8 : return;
12784 :
12785 8 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
12786 :
12787 8 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
12788 8 : oc->relid = relid;
12789 8 : oc->oncommit = action;
12790 8 : oc->creating_subid = GetCurrentSubTransactionId();
12791 8 : oc->deleting_subid = InvalidSubTransactionId;
12792 :
12793 8 : on_commits = lcons(oc, on_commits);
12794 :
12795 8 : MemoryContextSwitchTo(oldcxt);
12796 : }
12797 :
12798 : /*
12799 : * Unregister any ON COMMIT action when a relation is deleted.
12800 : *
12801 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
12802 : */
12803 : void
12804 2141 : remove_on_commit_action(Oid relid)
12805 : {
12806 : ListCell *l;
12807 :
12808 2148 : foreach(l, on_commits)
12809 : {
12810 14 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
12811 :
12812 14 : if (oc->relid == relid)
12813 : {
12814 7 : oc->deleting_subid = GetCurrentSubTransactionId();
12815 7 : break;
12816 : }
12817 : }
12818 2141 : }
12819 :
12820 : /*
12821 : * Perform ON COMMIT actions.
12822 : *
12823 : * This is invoked just before actually committing, since it's possible
12824 : * to encounter errors.
12825 : */
12826 : void
12827 22891 : PreCommit_on_commit_actions(void)
12828 : {
12829 : ListCell *l;
12830 22891 : List *oids_to_truncate = NIL;
12831 :
12832 22935 : foreach(l, on_commits)
12833 : {
12834 44 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
12835 :
12836 : /* Ignore entry if already dropped in this xact */
12837 44 : if (oc->deleting_subid != InvalidSubTransactionId)
12838 5 : continue;
12839 :
12840 39 : switch (oc->oncommit)
12841 : {
12842 : case ONCOMMIT_NOOP:
12843 : case ONCOMMIT_PRESERVE_ROWS:
12844 : /* Do nothing (there shouldn't be such entries, actually) */
12845 0 : break;
12846 : case ONCOMMIT_DELETE_ROWS:
12847 :
12848 : /*
12849 : * If this transaction hasn't accessed any temporary
12850 : * relations, we can skip truncating ON COMMIT DELETE ROWS
12851 : * tables, as they must still be empty.
12852 : */
12853 37 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPREL))
12854 16 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
12855 37 : break;
12856 : case ONCOMMIT_DROP:
12857 : {
12858 : ObjectAddress object;
12859 :
12860 2 : object.classId = RelationRelationId;
12861 2 : object.objectId = oc->relid;
12862 2 : object.objectSubId = 0;
12863 :
12864 : /*
12865 : * Since this is an automatic drop, rather than one
12866 : * directly initiated by the user, we pass the
12867 : * PERFORM_DELETION_INTERNAL flag.
12868 : */
12869 2 : performDeletion(&object,
12870 : DROP_CASCADE, PERFORM_DELETION_INTERNAL);
12871 :
12872 : /*
12873 : * Note that table deletion will call
12874 : * remove_on_commit_action, so the entry should get marked
12875 : * as deleted.
12876 : */
12877 2 : Assert(oc->deleting_subid != InvalidSubTransactionId);
12878 2 : break;
12879 : }
12880 : }
12881 : }
12882 22891 : if (oids_to_truncate != NIL)
12883 : {
12884 15 : heap_truncate(oids_to_truncate);
12885 14 : CommandCounterIncrement(); /* XXX needed? */
12886 : }
12887 22890 : }
12888 :
12889 : /*
12890 : * Post-commit or post-abort cleanup for ON COMMIT management.
12891 : *
12892 : * All we do here is remove no-longer-needed OnCommitItem entries.
12893 : *
12894 : * During commit, remove entries that were deleted during this transaction;
12895 : * during abort, remove those created during this transaction.
12896 : */
12897 : void
12898 26167 : AtEOXact_on_commit_actions(bool isCommit)
12899 : {
12900 : ListCell *cur_item;
12901 : ListCell *prev_item;
12902 :
12903 26167 : prev_item = NULL;
12904 26167 : cur_item = list_head(on_commits);
12905 :
12906 52378 : while (cur_item != NULL)
12907 : {
12908 44 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
12909 :
12910 46 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
12911 2 : oc->creating_subid != InvalidSubTransactionId)
12912 : {
12913 : /* cur_item must be removed */
12914 8 : on_commits = list_delete_cell(on_commits, cur_item, prev_item);
12915 8 : pfree(oc);
12916 8 : if (prev_item)
12917 0 : cur_item = lnext(prev_item);
12918 : else
12919 8 : cur_item = list_head(on_commits);
12920 : }
12921 : else
12922 : {
12923 : /* cur_item must be preserved */
12924 36 : oc->creating_subid = InvalidSubTransactionId;
12925 36 : oc->deleting_subid = InvalidSubTransactionId;
12926 36 : prev_item = cur_item;
12927 36 : cur_item = lnext(prev_item);
12928 : }
12929 : }
12930 26167 : }
12931 :
12932 : /*
12933 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
12934 : *
12935 : * During subabort, we can immediately remove entries created during this
12936 : * subtransaction. During subcommit, just relabel entries marked during
12937 : * this subtransaction as being the parent's responsibility.
12938 : */
12939 : void
12940 372 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
12941 : SubTransactionId parentSubid)
12942 : {
12943 : ListCell *cur_item;
12944 : ListCell *prev_item;
12945 :
12946 372 : prev_item = NULL;
12947 372 : cur_item = list_head(on_commits);
12948 :
12949 744 : while (cur_item != NULL)
12950 : {
12951 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
12952 :
12953 0 : if (!isCommit && oc->creating_subid == mySubid)
12954 : {
12955 : /* cur_item must be removed */
12956 0 : on_commits = list_delete_cell(on_commits, cur_item, prev_item);
12957 0 : pfree(oc);
12958 0 : if (prev_item)
12959 0 : cur_item = lnext(prev_item);
12960 : else
12961 0 : cur_item = list_head(on_commits);
12962 : }
12963 : else
12964 : {
12965 : /* cur_item must be preserved */
12966 0 : if (oc->creating_subid == mySubid)
12967 0 : oc->creating_subid = parentSubid;
12968 0 : if (oc->deleting_subid == mySubid)
12969 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
12970 0 : prev_item = cur_item;
12971 0 : cur_item = lnext(prev_item);
12972 : }
12973 : }
12974 372 : }
12975 :
12976 : /*
12977 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
12978 : * the relation to be locked only if (1) it's a plain table, materialized
12979 : * view, or TOAST table and (2) the current user is the owner (or the
12980 : * superuser). This meets the permission-checking needs of CLUSTER, REINDEX
12981 : * TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it can be
12982 : * used by all.
12983 : */
12984 : void
12985 34 : RangeVarCallbackOwnsTable(const RangeVar *relation,
12986 : Oid relId, Oid oldRelId, void *arg)
12987 : {
12988 : char relkind;
12989 :
12990 : /* Nothing to do if the relation was not found. */
12991 34 : if (!OidIsValid(relId))
12992 0 : return;
12993 :
12994 : /*
12995 : * If the relation does exist, check whether it's an index. But note that
12996 : * the relation might have been dropped between the time we did the name
12997 : * lookup and now. In that case, there's nothing to do.
12998 : */
12999 34 : relkind = get_rel_relkind(relId);
13000 34 : if (!relkind)
13001 0 : return;
13002 34 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
13003 0 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
13004 0 : ereport(ERROR,
13005 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13006 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
13007 :
13008 : /* Check permissions */
13009 34 : if (!pg_class_ownercheck(relId, GetUserId()))
13010 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname);
13011 : }
13012 :
13013 : /*
13014 : * Callback to RangeVarGetRelidExtended(), similar to
13015 : * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
13016 : */
13017 : void
13018 642 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
13019 : Oid relId, Oid oldRelId, void *arg)
13020 : {
13021 : HeapTuple tuple;
13022 :
13023 : /* Nothing to do if the relation was not found. */
13024 642 : if (!OidIsValid(relId))
13025 642 : return;
13026 :
13027 641 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
13028 641 : if (!HeapTupleIsValid(tuple)) /* should not happen */
13029 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
13030 :
13031 641 : if (!pg_class_ownercheck(relId, GetUserId()))
13032 1 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
13033 1 : relation->relname);
13034 :
13035 1280 : if (!allowSystemTableMods &&
13036 640 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
13037 0 : ereport(ERROR,
13038 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
13039 : errmsg("permission denied: \"%s\" is a system catalog",
13040 : relation->relname)));
13041 :
13042 640 : ReleaseSysCache(tuple);
13043 : }
13044 :
13045 : /*
13046 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
13047 : * processing.
13048 : */
13049 : static void
13050 1071 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
13051 : void *arg)
13052 : {
13053 1071 : Node *stmt = (Node *) arg;
13054 : ObjectType reltype;
13055 : HeapTuple tuple;
13056 : Form_pg_class classform;
13057 : AclResult aclresult;
13058 : char relkind;
13059 :
13060 1071 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
13061 1071 : if (!HeapTupleIsValid(tuple))
13062 1104 : return; /* concurrently dropped */
13063 1035 : classform = (Form_pg_class) GETSTRUCT(tuple);
13064 1035 : relkind = classform->relkind;
13065 :
13066 : /* Must own relation. */
13067 1035 : if (!pg_class_ownercheck(relid, GetUserId()))
13068 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname);
13069 :
13070 : /* No system table modifications unless explicitly allowed. */
13071 1035 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
13072 3 : ereport(ERROR,
13073 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
13074 : errmsg("permission denied: \"%s\" is a system catalog",
13075 : rv->relname)));
13076 :
13077 : /*
13078 : * Extract the specified relation type from the statement parse tree.
13079 : *
13080 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
13081 : * have CREATE rights on the containing namespace.
13082 : */
13083 1032 : if (IsA(stmt, RenameStmt))
13084 : {
13085 22 : aclresult = pg_namespace_aclcheck(classform->relnamespace,
13086 : GetUserId(), ACL_CREATE);
13087 22 : if (aclresult != ACLCHECK_OK)
13088 0 : aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
13089 0 : get_namespace_name(classform->relnamespace));
13090 22 : reltype = ((RenameStmt *) stmt)->renameType;
13091 : }
13092 1010 : else if (IsA(stmt, AlterObjectSchemaStmt))
13093 11 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
13094 :
13095 999 : else if (IsA(stmt, AlterTableStmt))
13096 999 : reltype = ((AlterTableStmt *) stmt)->relkind;
13097 : else
13098 : {
13099 0 : reltype = OBJECT_TABLE; /* placate compiler */
13100 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
13101 : }
13102 :
13103 : /*
13104 : * For compatibility with prior releases, we allow ALTER TABLE to be used
13105 : * with most other types of relations (but not composite types). We allow
13106 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
13107 : * otherwise. Otherwise, the user must select the correct form of the
13108 : * command for the relation at issue.
13109 : */
13110 1032 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
13111 0 : ereport(ERROR,
13112 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13113 : errmsg("\"%s\" is not a sequence", rv->relname)));
13114 :
13115 1032 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
13116 0 : ereport(ERROR,
13117 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13118 : errmsg("\"%s\" is not a view", rv->relname)));
13119 :
13120 1032 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
13121 0 : ereport(ERROR,
13122 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13123 : errmsg("\"%s\" is not a materialized view", rv->relname)));
13124 :
13125 1032 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
13126 0 : ereport(ERROR,
13127 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13128 : errmsg("\"%s\" is not a foreign table", rv->relname)));
13129 :
13130 1032 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
13131 0 : ereport(ERROR,
13132 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13133 : errmsg("\"%s\" is not a composite type", rv->relname)));
13134 :
13135 1032 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX
13136 0 : && !IsA(stmt, RenameStmt))
13137 0 : ereport(ERROR,
13138 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13139 : errmsg("\"%s\" is not an index", rv->relname)));
13140 :
13141 : /*
13142 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
13143 : * TYPE for that.
13144 : */
13145 1032 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
13146 0 : ereport(ERROR,
13147 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13148 : errmsg("\"%s\" is a composite type", rv->relname),
13149 : errhint("Use ALTER TYPE instead.")));
13150 :
13151 : /*
13152 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
13153 : * to a different schema, such as indexes and TOAST tables.
13154 : */
13155 1032 : if (IsA(stmt, AlterObjectSchemaStmt) &&
13156 3 : relkind != RELKIND_RELATION &&
13157 2 : relkind != RELKIND_VIEW &&
13158 1 : relkind != RELKIND_MATVIEW &&
13159 1 : relkind != RELKIND_SEQUENCE &&
13160 0 : relkind != RELKIND_FOREIGN_TABLE &&
13161 : relkind != RELKIND_PARTITIONED_TABLE)
13162 0 : ereport(ERROR,
13163 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13164 : errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table",
13165 : rv->relname)));
13166 :
13167 1032 : ReleaseSysCache(tuple);
13168 : }
13169 :
13170 : /*
13171 : * Transform any expressions present in the partition key
13172 : *
13173 : * Returns a transformed PartitionSpec, as well as the strategy code
13174 : */
13175 : static PartitionSpec *
13176 93 : transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
13177 : {
13178 : PartitionSpec *newspec;
13179 : ParseState *pstate;
13180 : RangeTblEntry *rte;
13181 : ListCell *l;
13182 :
13183 93 : newspec = makeNode(PartitionSpec);
13184 :
13185 93 : newspec->strategy = partspec->strategy;
13186 93 : newspec->partParams = NIL;
13187 93 : newspec->location = partspec->location;
13188 :
13189 : /* Parse partitioning strategy name */
13190 93 : if (pg_strcasecmp(partspec->strategy, "list") == 0)
13191 46 : *strategy = PARTITION_STRATEGY_LIST;
13192 47 : else if (pg_strcasecmp(partspec->strategy, "range") == 0)
13193 46 : *strategy = PARTITION_STRATEGY_RANGE;
13194 : else
13195 1 : ereport(ERROR,
13196 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13197 : errmsg("unrecognized partitioning strategy \"%s\"",
13198 : partspec->strategy)));
13199 :
13200 : /* Check valid number of columns for strategy */
13201 138 : if (*strategy == PARTITION_STRATEGY_LIST &&
13202 46 : list_length(partspec->partParams) != 1)
13203 1 : ereport(ERROR,
13204 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13205 : errmsg("cannot use \"list\" partition strategy with more than one column")));
13206 :
13207 : /*
13208 : * Create a dummy ParseState and insert the target relation as its sole
13209 : * rangetable entry. We need a ParseState for transformExpr.
13210 : */
13211 91 : pstate = make_parsestate(NULL);
13212 91 : rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
13213 91 : addRTEtoQuery(pstate, rte, true, true, true);
13214 :
13215 : /* take care of any partition expressions */
13216 197 : foreach(l, partspec->partParams)
13217 : {
13218 111 : PartitionElem *pelem = castNode(PartitionElem, lfirst(l));
13219 : ListCell *lc;
13220 :
13221 : /* Check for PARTITION BY ... (foo, foo) */
13222 136 : foreach(lc, newspec->partParams)
13223 : {
13224 26 : PartitionElem *pparam = castNode(PartitionElem, lfirst(lc));
13225 :
13226 39 : if (pelem->name && pparam->name &&
13227 13 : strcmp(pelem->name, pparam->name) == 0)
13228 1 : ereport(ERROR,
13229 : (errcode(ERRCODE_DUPLICATE_COLUMN),
13230 : errmsg("column \"%s\" appears more than once in partition key",
13231 : pelem->name),
13232 : parser_errposition(pstate, pelem->location)));
13233 : }
13234 :
13235 110 : if (pelem->expr)
13236 : {
13237 : /* Copy, to avoid scribbling on the input */
13238 21 : pelem = copyObject(pelem);
13239 :
13240 : /* Now do parse transformation of the expression */
13241 21 : pelem->expr = transformExpr(pstate, pelem->expr,
13242 : EXPR_KIND_PARTITION_EXPRESSION);
13243 :
13244 : /* we have to fix its collations too */
13245 17 : assign_expr_collations(pstate, pelem->expr);
13246 : }
13247 :
13248 106 : newspec->partParams = lappend(newspec->partParams, pelem);
13249 : }
13250 :
13251 86 : return newspec;
13252 : }
13253 :
13254 : /*
13255 : * Compute per-partition-column information from a list of PartitionElems.
13256 : * Expressions in the PartitionElems must be parse-analyzed already.
13257 : */
13258 : static void
13259 86 : ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
13260 : List **partexprs, Oid *partopclass, Oid *partcollation)
13261 : {
13262 : int attn;
13263 : ListCell *lc;
13264 :
13265 86 : attn = 0;
13266 180 : foreach(lc, partParams)
13267 : {
13268 105 : PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
13269 : Oid atttype;
13270 : Oid attcollation;
13271 :
13272 105 : if (pelem->name != NULL)
13273 : {
13274 : /* Simple attribute reference */
13275 : HeapTuple atttuple;
13276 : Form_pg_attribute attform;
13277 :
13278 88 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
13279 88 : pelem->name);
13280 88 : if (!HeapTupleIsValid(atttuple))
13281 2 : ereport(ERROR,
13282 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13283 : errmsg("column \"%s\" named in partition key does not exist",
13284 : pelem->name)));
13285 86 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13286 :
13287 86 : if (attform->attnum <= 0)
13288 1 : ereport(ERROR,
13289 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13290 : errmsg("cannot use system column \"%s\" in partition key",
13291 : pelem->name)));
13292 :
13293 85 : partattrs[attn] = attform->attnum;
13294 85 : atttype = attform->atttypid;
13295 85 : attcollation = attform->attcollation;
13296 85 : ReleaseSysCache(atttuple);
13297 : }
13298 : else
13299 : {
13300 : /* Expression */
13301 17 : Node *expr = pelem->expr;
13302 :
13303 17 : Assert(expr != NULL);
13304 17 : atttype = exprType(expr);
13305 17 : attcollation = exprCollation(expr);
13306 :
13307 : /*
13308 : * Strip any top-level COLLATE clause. This ensures that we treat
13309 : * "x COLLATE y" and "(x COLLATE y)" alike.
13310 : */
13311 34 : while (IsA(expr, CollateExpr))
13312 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
13313 :
13314 19 : if (IsA(expr, Var) &&
13315 2 : ((Var *) expr)->varattno > 0)
13316 : {
13317 : /*
13318 : * User wrote "(column)" or "(column COLLATE something)".
13319 : * Treat it like simple attribute anyway.
13320 : */
13321 1 : partattrs[attn] = ((Var *) expr)->varattno;
13322 : }
13323 : else
13324 : {
13325 16 : Bitmapset *expr_attrs = NULL;
13326 : int i;
13327 :
13328 16 : partattrs[attn] = 0; /* marks the column as expression */
13329 16 : *partexprs = lappend(*partexprs, expr);
13330 :
13331 : /*
13332 : * Try to simplify the expression before checking for
13333 : * mutability. The main practical value of doing it in this
13334 : * order is that an inline-able SQL-language function will be
13335 : * accepted if its expansion is immutable, whether or not the
13336 : * function itself is marked immutable.
13337 : *
13338 : * Note that expression_planner does not change the passed in
13339 : * expression destructively and we have already saved the
13340 : * expression to be stored into the catalog above.
13341 : */
13342 16 : expr = (Node *) expression_planner((Expr *) expr);
13343 :
13344 : /*
13345 : * Partition expression cannot contain mutable functions,
13346 : * because a given row must always map to the same partition
13347 : * as long as there is no change in the partition boundary
13348 : * structure.
13349 : */
13350 16 : if (contain_mutable_functions(expr))
13351 1 : ereport(ERROR,
13352 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13353 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
13354 :
13355 : /*
13356 : * transformPartitionSpec() should have already rejected
13357 : * subqueries, aggregates, window functions, and SRFs, based
13358 : * on the EXPR_KIND_ for partition expressions.
13359 : */
13360 :
13361 : /*
13362 : * Cannot have expressions containing whole-row references or
13363 : * system column references.
13364 : */
13365 15 : pull_varattnos(expr, 1, &expr_attrs);
13366 15 : if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
13367 : expr_attrs))
13368 1 : ereport(ERROR,
13369 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13370 : errmsg("partition key expressions cannot contain whole-row references")));
13371 126 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
13372 : {
13373 112 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
13374 : expr_attrs))
13375 0 : ereport(ERROR,
13376 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13377 : errmsg("partition key expressions cannot contain system column references")));
13378 : }
13379 :
13380 : /*
13381 : * While it is not exactly *wrong* for a partition expression
13382 : * to be a constant, it seems better to reject such keys.
13383 : */
13384 14 : if (IsA(expr, Const))
13385 2 : ereport(ERROR,
13386 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
13387 : errmsg("cannot use constant expression as partition key")));
13388 : }
13389 : }
13390 :
13391 : /*
13392 : * Apply collation override if any
13393 : */
13394 98 : if (pelem->collation)
13395 2 : attcollation = get_collation_oid(pelem->collation, false);
13396 :
13397 : /*
13398 : * Check we have a collation iff it's a collatable type. The only
13399 : * expected failures here are (1) COLLATE applied to a noncollatable
13400 : * type, or (2) partition expression had an unresolved collation. But
13401 : * we might as well code this to be a complete consistency check.
13402 : */
13403 98 : if (type_is_collatable(atttype))
13404 : {
13405 19 : if (!OidIsValid(attcollation))
13406 0 : ereport(ERROR,
13407 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
13408 : errmsg("could not determine which collation to use for partition expression"),
13409 : errhint("Use the COLLATE clause to set the collation explicitly.")));
13410 : }
13411 : else
13412 : {
13413 79 : if (OidIsValid(attcollation))
13414 0 : ereport(ERROR,
13415 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13416 : errmsg("collations are not supported by type %s",
13417 : format_type_be(atttype))));
13418 : }
13419 :
13420 98 : partcollation[attn] = attcollation;
13421 :
13422 : /*
13423 : * Identify a btree opclass to use. Currently, we use only btree
13424 : * operators, which seems enough for list and range partitioning.
13425 : */
13426 98 : if (!pelem->opclass)
13427 : {
13428 95 : partopclass[attn] = GetDefaultOpClass(atttype, BTREE_AM_OID);
13429 :
13430 95 : if (!OidIsValid(partopclass[attn]))
13431 2 : ereport(ERROR,
13432 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13433 : errmsg("data type %s has no default btree operator class",
13434 : format_type_be(atttype)),
13435 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
13436 : }
13437 : else
13438 3 : partopclass[attn] = ResolveOpClass(pelem->opclass,
13439 : atttype,
13440 : "btree",
13441 : BTREE_AM_OID);
13442 :
13443 94 : attn++;
13444 : }
13445 75 : }
13446 :
13447 : /*
13448 : * PartConstraintImpliedByRelConstraint
13449 : * Does scanrel's existing constraints imply the partition constraint?
13450 : *
13451 : * Existing constraints includes its check constraints and column-level
13452 : * NOT NULL constraints and partConstraint describes the partition constraint.
13453 : */
13454 : static bool
13455 33 : PartConstraintImpliedByRelConstraint(Relation scanrel,
13456 : List *partConstraint)
13457 : {
13458 33 : List *existConstraint = NIL;
13459 33 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
13460 : int num_check,
13461 : i;
13462 :
13463 33 : if (constr && constr->has_not_null)
13464 : {
13465 19 : int natts = scanrel->rd_att->natts;
13466 :
13467 71 : for (i = 1; i <= natts; i++)
13468 : {
13469 52 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
13470 :
13471 52 : if (att->attnotnull && !att->attisdropped)
13472 : {
13473 33 : NullTest *ntest = makeNode(NullTest);
13474 :
13475 33 : ntest->arg = (Expr *) makeVar(1,
13476 : i,
13477 : att->atttypid,
13478 : att->atttypmod,
13479 : att->attcollation,
13480 : 0);
13481 33 : ntest->nulltesttype = IS_NOT_NULL;
13482 :
13483 : /*
13484 : * argisrow=false is correct even for a composite column,
13485 : * because attnotnull does not represent a SQL-spec IS NOT
13486 : * NULL test in such a case, just IS DISTINCT FROM NULL.
13487 : */
13488 33 : ntest->argisrow = false;
13489 33 : ntest->location = -1;
13490 33 : existConstraint = lappend(existConstraint, ntest);
13491 : }
13492 : }
13493 : }
13494 :
13495 33 : num_check = (constr != NULL) ? constr->num_check : 0;
13496 51 : for (i = 0; i < num_check; i++)
13497 : {
13498 : Node *cexpr;
13499 :
13500 : /*
13501 : * If this constraint hasn't been fully validated yet, we must ignore
13502 : * it here.
13503 : */
13504 18 : if (!constr->check[i].ccvalid)
13505 0 : continue;
13506 :
13507 18 : cexpr = stringToNode(constr->check[i].ccbin);
13508 :
13509 : /*
13510 : * Run each expression through const-simplification and
13511 : * canonicalization. It is necessary, because we will be comparing it
13512 : * to similarly-processed partition constraint expressions, and may
13513 : * fail to detect valid matches without this.
13514 : */
13515 18 : cexpr = eval_const_expressions(NULL, cexpr);
13516 18 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr);
13517 :
13518 18 : existConstraint = list_concat(existConstraint,
13519 : make_ands_implicit((Expr *) cexpr));
13520 : }
13521 :
13522 33 : if (existConstraint != NIL)
13523 24 : existConstraint = list_make1(make_ands_explicit(existConstraint));
13524 :
13525 : /* And away we go ... */
13526 33 : return predicate_implied_by(partConstraint, existConstraint, true);
13527 : }
13528 :
13529 : /*
13530 : * ValidatePartitionConstraints
13531 : *
13532 : * Check whether all rows in the given table obey the given partition
13533 : * constraint; if so, it can be attached as a partition. We do this by
13534 : * scanning the table (or all of its leaf partitions) row by row, except when
13535 : * the existing constraints are sufficient to prove that the new partitioning
13536 : * constraint must already hold.
13537 : */
13538 : static void
13539 33 : ValidatePartitionConstraints(List **wqueue, Relation scanrel,
13540 : List *scanrel_children,
13541 : List *partConstraint)
13542 : {
13543 : bool found_whole_row;
13544 : ListCell *lc;
13545 :
13546 33 : if (partConstraint == NIL)
13547 7 : return;
13548 :
13549 : /*
13550 : * Based on the table's existing constraints, determine if we can skip
13551 : * scanning the table to validate the partition constraint.
13552 : */
13553 33 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
13554 : {
13555 7 : ereport(INFO,
13556 : (errmsg("partition constraint for table \"%s\" is implied by existing constraints",
13557 : RelationGetRelationName(scanrel))));
13558 7 : return;
13559 : }
13560 :
13561 : /* Constraints proved insufficient, so we need to scan the table. */
13562 58 : foreach(lc, scanrel_children)
13563 : {
13564 : AlteredTableInfo *tab;
13565 32 : Oid part_relid = lfirst_oid(lc);
13566 : Relation part_rel;
13567 32 : List *my_partconstr = partConstraint;
13568 :
13569 : /* Lock already taken */
13570 32 : if (part_relid != RelationGetRelid(scanrel))
13571 6 : part_rel = heap_open(part_relid, NoLock);
13572 : else
13573 26 : part_rel = scanrel;
13574 :
13575 : /*
13576 : * Skip if the partition is itself a partitioned table. We can only
13577 : * ever scan RELKIND_RELATION relations.
13578 : */
13579 32 : if (part_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13580 : {
13581 6 : if (part_rel != scanrel)
13582 0 : heap_close(part_rel, NoLock);
13583 6 : continue;
13584 : }
13585 :
13586 26 : if (part_rel != scanrel)
13587 : {
13588 : /*
13589 : * Adjust the constraint for scanrel so that it matches this
13590 : * partition's attribute numbers.
13591 : */
13592 6 : my_partconstr = map_partition_varattnos(my_partconstr, 1,
13593 : part_rel, scanrel,
13594 : &found_whole_row);
13595 : /* There can never be a whole-row reference here */
13596 6 : if (found_whole_row)
13597 0 : elog(ERROR, "unexpected whole-row reference found in partition key");
13598 : }
13599 :
13600 : /* Grab a work queue entry. */
13601 26 : tab = ATGetQueueEntry(wqueue, part_rel);
13602 26 : tab->partition_constraint = (Expr *) linitial(my_partconstr);
13603 :
13604 : /* keep our lock until commit */
13605 26 : if (part_rel != scanrel)
13606 6 : heap_close(part_rel, NoLock);
13607 : }
13608 : }
13609 :
13610 : /*
13611 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
13612 : *
13613 : * Return the address of the newly attached partition.
13614 : */
13615 : static ObjectAddress
13616 55 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
13617 : {
13618 : Relation attachrel,
13619 : catalog;
13620 : List *attachrel_children;
13621 : List *partConstraint;
13622 : SysScanDesc scan;
13623 : ScanKeyData skey;
13624 : AttrNumber attno;
13625 : int natts;
13626 : TupleDesc tupleDesc;
13627 : ObjectAddress address;
13628 : const char *trigger_name;
13629 : bool found_whole_row;
13630 :
13631 55 : attachrel = heap_openrv(cmd->name, AccessExclusiveLock);
13632 :
13633 : /*
13634 : * Must be owner of both parent and source table -- parent was checked by
13635 : * ATSimplePermissions call in ATPrepCmd
13636 : */
13637 54 : ATSimplePermissions(attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
13638 :
13639 : /* A partition can only have one parent */
13640 53 : if (attachrel->rd_rel->relispartition)
13641 1 : ereport(ERROR,
13642 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13643 : errmsg("\"%s\" is already a partition",
13644 : RelationGetRelationName(attachrel))));
13645 :
13646 52 : if (OidIsValid(attachrel->rd_rel->reloftype))
13647 1 : ereport(ERROR,
13648 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13649 : errmsg("cannot attach a typed table as partition")));
13650 :
13651 : /*
13652 : * Table being attached should not already be part of inheritance; either
13653 : * as a child table...
13654 : */
13655 51 : catalog = heap_open(InheritsRelationId, AccessShareLock);
13656 51 : ScanKeyInit(&skey,
13657 : Anum_pg_inherits_inhrelid,
13658 : BTEqualStrategyNumber, F_OIDEQ,
13659 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
13660 51 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
13661 : NULL, 1, &skey);
13662 51 : if (HeapTupleIsValid(systable_getnext(scan)))
13663 1 : ereport(ERROR,
13664 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13665 : errmsg("cannot attach inheritance child as partition")));
13666 50 : systable_endscan(scan);
13667 :
13668 : /* ...or as a parent table (except the case when it is partitioned) */
13669 50 : ScanKeyInit(&skey,
13670 : Anum_pg_inherits_inhparent,
13671 : BTEqualStrategyNumber, F_OIDEQ,
13672 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
13673 50 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
13674 : 1, &skey);
13675 62 : if (HeapTupleIsValid(systable_getnext(scan)) &&
13676 12 : attachrel->rd_rel->relkind == RELKIND_RELATION)
13677 1 : ereport(ERROR,
13678 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13679 : errmsg("cannot attach inheritance parent as partition")));
13680 49 : systable_endscan(scan);
13681 49 : heap_close(catalog, AccessShareLock);
13682 :
13683 : /*
13684 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
13685 : * particular, this disallows making a rel a partition of itself.)
13686 : *
13687 : * We do that by checking if rel is a member of the list of attachRel's
13688 : * partitions provided the latter is partitioned at all. We want to avoid
13689 : * having to construct this list again, so we request the strongest lock
13690 : * on all partitions. We need the strongest lock, because we may decide
13691 : * to scan them if we find out that the table being attached (or its leaf
13692 : * partitions) may contain rows that violate the partition constraint. If
13693 : * the table has a constraint that would prevent such rows, which by
13694 : * definition is present in all the partitions, we need not scan the
13695 : * table, nor its partitions. But we cannot risk a deadlock by taking a
13696 : * weaker lock now and the stronger one only when needed.
13697 : */
13698 49 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
13699 : AccessExclusiveLock, NULL);
13700 49 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
13701 2 : ereport(ERROR,
13702 : (errcode(ERRCODE_DUPLICATE_TABLE),
13703 : errmsg("circular inheritance not allowed"),
13704 : errdetail("\"%s\" is already a child of \"%s\".",
13705 : RelationGetRelationName(rel),
13706 : RelationGetRelationName(attachrel))));
13707 :
13708 : /* Temp parent cannot have a partition that is itself not a temp */
13709 48 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13710 1 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
13711 1 : ereport(ERROR,
13712 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13713 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
13714 : RelationGetRelationName(rel))));
13715 :
13716 : /* If the parent is temp, it must belong to this session */
13717 46 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13718 0 : !rel->rd_islocaltemp)
13719 0 : ereport(ERROR,
13720 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13721 : errmsg("cannot attach as partition of temporary relation of another session")));
13722 :
13723 : /* Ditto for the partition */
13724 46 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13725 0 : !attachrel->rd_islocaltemp)
13726 0 : ereport(ERROR,
13727 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13728 : errmsg("cannot attach temporary relation of another session as partition")));
13729 :
13730 : /* If parent has OIDs then child must have OIDs */
13731 46 : if (rel->rd_rel->relhasoids && !attachrel->rd_rel->relhasoids)
13732 1 : ereport(ERROR,
13733 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13734 : errmsg("cannot attach table \"%s\" without OIDs as partition of"
13735 : " table \"%s\" with OIDs", RelationGetRelationName(attachrel),
13736 : RelationGetRelationName(rel))));
13737 :
13738 : /* OTOH, if parent doesn't have them, do not allow in attachrel either */
13739 45 : if (attachrel->rd_rel->relhasoids && !rel->rd_rel->relhasoids)
13740 1 : ereport(ERROR,
13741 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13742 : errmsg("cannot attach table \"%s\" with OIDs as partition of table"
13743 : " \"%s\" without OIDs", RelationGetRelationName(attachrel),
13744 : RelationGetRelationName(rel))));
13745 :
13746 : /* Check if there are any columns in attachrel that aren't in the parent */
13747 44 : tupleDesc = RelationGetDescr(attachrel);
13748 44 : natts = tupleDesc->natts;
13749 155 : for (attno = 1; attno <= natts; attno++)
13750 : {
13751 113 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
13752 113 : char *attributeName = NameStr(attribute->attname);
13753 :
13754 : /* Ignore dropped */
13755 113 : if (attribute->attisdropped)
13756 14 : continue;
13757 :
13758 : /* Try to find the column in parent (matching on column name) */
13759 99 : if (!SearchSysCacheExists2(ATTNAME,
13760 : ObjectIdGetDatum(RelationGetRelid(rel)),
13761 : CStringGetDatum(attributeName)))
13762 2 : ereport(ERROR,
13763 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13764 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
13765 : RelationGetRelationName(attachrel), attributeName,
13766 : RelationGetRelationName(rel)),
13767 : errdetail("New partition should contain only the columns present in parent.")));
13768 : }
13769 :
13770 : /*
13771 : * If child_rel has row-level triggers with transition tables, we
13772 : * currently don't allow it to become a partition. See also prohibitions
13773 : * in ATExecAddInherit() and CreateTrigger().
13774 : */
13775 42 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
13776 42 : if (trigger_name != NULL)
13777 1 : ereport(ERROR,
13778 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13779 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
13780 : trigger_name, RelationGetRelationName(attachrel)),
13781 : errdetail("ROW triggers with transition tables are not supported on partitions")));
13782 :
13783 : /* OK to create inheritance. Rest of the checks performed there */
13784 41 : CreateInheritance(attachrel, rel);
13785 :
13786 : /*
13787 : * Check that the new partition's bound is valid and does not overlap any
13788 : * of existing partitions of the parent - note that it does not return on
13789 : * error.
13790 : */
13791 34 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
13792 : cmd->bound);
13793 :
13794 : /* Update the pg_class entry. */
13795 33 : StorePartitionBound(attachrel, rel, cmd->bound);
13796 :
13797 : /*
13798 : * Generate partition constraint from the partition bound specification.
13799 : * If the parent itself is a partition, make sure to include its
13800 : * constraint as well.
13801 : */
13802 33 : partConstraint = list_concat(get_qual_from_partbound(attachrel, rel,
13803 : cmd->bound),
13804 : RelationGetPartitionQual(rel));
13805 33 : partConstraint = (List *) eval_const_expressions(NULL,
13806 : (Node *) partConstraint);
13807 33 : partConstraint = (List *) canonicalize_qual((Expr *) partConstraint);
13808 33 : partConstraint = list_make1(make_ands_explicit(partConstraint));
13809 :
13810 : /*
13811 : * Adjust the generated constraint to match this partition's attribute
13812 : * numbers.
13813 : */
13814 33 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
13815 : rel, &found_whole_row);
13816 : /* There can never be a whole-row reference here */
13817 33 : if (found_whole_row)
13818 0 : elog(ERROR, "unexpected whole-row reference found in partition key");
13819 :
13820 : /* Validate partition constraints against the table being attached. */
13821 33 : ValidatePartitionConstraints(wqueue, attachrel, attachrel_children,
13822 : partConstraint);
13823 :
13824 33 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
13825 :
13826 : /* keep our lock until commit */
13827 33 : heap_close(attachrel, NoLock);
13828 :
13829 33 : return address;
13830 : }
13831 :
13832 : /*
13833 : * ALTER TABLE DETACH PARTITION
13834 : *
13835 : * Return the address of the relation that is no longer a partition of rel.
13836 : */
13837 : static ObjectAddress
13838 11 : ATExecDetachPartition(Relation rel, RangeVar *name)
13839 : {
13840 : Relation partRel,
13841 : classRel;
13842 : HeapTuple tuple,
13843 : newtuple;
13844 : Datum new_val[Natts_pg_class];
13845 : bool isnull,
13846 : new_null[Natts_pg_class],
13847 : new_repl[Natts_pg_class];
13848 : ObjectAddress address;
13849 :
13850 11 : partRel = heap_openrv(name, AccessShareLock);
13851 :
13852 : /* All inheritance related checks are performed within the function */
13853 10 : RemoveInheritance(partRel, rel);
13854 :
13855 : /* Update pg_class tuple */
13856 8 : classRel = heap_open(RelationRelationId, RowExclusiveLock);
13857 8 : tuple = SearchSysCacheCopy1(RELOID,
13858 : ObjectIdGetDatum(RelationGetRelid(partRel)));
13859 8 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
13860 :
13861 8 : (void) SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relpartbound,
13862 : &isnull);
13863 8 : Assert(!isnull);
13864 :
13865 : /* Clear relpartbound and reset relispartition */
13866 8 : memset(new_val, 0, sizeof(new_val));
13867 8 : memset(new_null, false, sizeof(new_null));
13868 8 : memset(new_repl, false, sizeof(new_repl));
13869 8 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
13870 8 : new_null[Anum_pg_class_relpartbound - 1] = true;
13871 8 : new_repl[Anum_pg_class_relpartbound - 1] = true;
13872 8 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
13873 : new_val, new_null, new_repl);
13874 :
13875 8 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
13876 8 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
13877 8 : heap_freetuple(newtuple);
13878 8 : heap_close(classRel, RowExclusiveLock);
13879 :
13880 : /*
13881 : * Invalidate the parent's relcache so that the partition is no longer
13882 : * included in its partition descriptor.
13883 : */
13884 8 : CacheInvalidateRelcache(rel);
13885 :
13886 8 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
13887 :
13888 : /* keep our lock until commit */
13889 8 : heap_close(partRel, NoLock);
13890 :
13891 8 : return address;
13892 : }
|