Age Owner TLA 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-2023, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/commands/tablecmds.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/attmap.h"
18 : #include "access/genam.h"
19 : #include "access/heapam.h"
20 : #include "access/heapam_xlog.h"
21 : #include "access/multixact.h"
22 : #include "access/reloptions.h"
23 : #include "access/relscan.h"
24 : #include "access/sysattr.h"
25 : #include "access/tableam.h"
26 : #include "access/toast_compression.h"
27 : #include "access/xact.h"
28 : #include "access/xlog.h"
29 : #include "access/xloginsert.h"
30 : #include "catalog/catalog.h"
31 : #include "catalog/heap.h"
32 : #include "catalog/index.h"
33 : #include "catalog/namespace.h"
34 : #include "catalog/objectaccess.h"
35 : #include "catalog/partition.h"
36 : #include "catalog/pg_am.h"
37 : #include "catalog/pg_attrdef.h"
38 : #include "catalog/pg_collation.h"
39 : #include "catalog/pg_constraint.h"
40 : #include "catalog/pg_depend.h"
41 : #include "catalog/pg_foreign_table.h"
42 : #include "catalog/pg_inherits.h"
43 : #include "catalog/pg_largeobject.h"
44 : #include "catalog/pg_namespace.h"
45 : #include "catalog/pg_opclass.h"
46 : #include "catalog/pg_statistic_ext.h"
47 : #include "catalog/pg_tablespace.h"
48 : #include "catalog/pg_trigger.h"
49 : #include "catalog/pg_type.h"
50 : #include "catalog/storage.h"
51 : #include "catalog/storage_xlog.h"
52 : #include "catalog/toasting.h"
53 : #include "commands/cluster.h"
54 : #include "commands/comment.h"
55 : #include "commands/defrem.h"
56 : #include "commands/event_trigger.h"
57 : #include "commands/policy.h"
58 : #include "commands/sequence.h"
59 : #include "commands/tablecmds.h"
60 : #include "commands/tablespace.h"
61 : #include "commands/trigger.h"
62 : #include "commands/typecmds.h"
63 : #include "commands/user.h"
64 : #include "executor/executor.h"
65 : #include "foreign/fdwapi.h"
66 : #include "foreign/foreign.h"
67 : #include "miscadmin.h"
68 : #include "nodes/makefuncs.h"
69 : #include "nodes/nodeFuncs.h"
70 : #include "nodes/parsenodes.h"
71 : #include "optimizer/optimizer.h"
72 : #include "parser/parse_clause.h"
73 : #include "parser/parse_coerce.h"
74 : #include "parser/parse_collate.h"
75 : #include "parser/parse_expr.h"
76 : #include "parser/parse_oper.h"
77 : #include "parser/parse_relation.h"
78 : #include "parser/parse_type.h"
79 : #include "parser/parse_utilcmd.h"
80 : #include "parser/parser.h"
81 : #include "partitioning/partbounds.h"
82 : #include "partitioning/partdesc.h"
83 : #include "pgstat.h"
84 : #include "rewrite/rewriteDefine.h"
85 : #include "rewrite/rewriteHandler.h"
86 : #include "rewrite/rewriteManip.h"
87 : #include "storage/bufmgr.h"
88 : #include "storage/lmgr.h"
89 : #include "storage/lock.h"
90 : #include "storage/predicate.h"
91 : #include "storage/smgr.h"
92 : #include "tcop/utility.h"
93 : #include "utils/acl.h"
94 : #include "utils/builtins.h"
95 : #include "utils/fmgroids.h"
96 : #include "utils/inval.h"
97 : #include "utils/lsyscache.h"
98 : #include "utils/memutils.h"
99 : #include "utils/partcache.h"
100 : #include "utils/relcache.h"
101 : #include "utils/ruleutils.h"
102 : #include "utils/snapmgr.h"
103 : #include "utils/syscache.h"
104 : #include "utils/timestamp.h"
105 : #include "utils/typcache.h"
106 : #include "utils/usercontext.h"
107 :
108 : /*
109 : * ON COMMIT action list
110 : */
111 : typedef struct OnCommitItem
112 : {
113 : Oid relid; /* relid of relation */
114 : OnCommitAction oncommit; /* what to do at end of xact */
115 :
116 : /*
117 : * If this entry was created during the current transaction,
118 : * creating_subid is the ID of the creating subxact; if created in a prior
119 : * transaction, creating_subid is zero. If deleted during the current
120 : * transaction, deleting_subid is the ID of the deleting subxact; if no
121 : * deletion request is pending, deleting_subid is zero.
122 : */
123 : SubTransactionId creating_subid;
124 : SubTransactionId deleting_subid;
125 : } OnCommitItem;
126 :
127 : static List *on_commits = NIL;
128 :
129 :
130 : /*
131 : * State information for ALTER TABLE
132 : *
133 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
134 : * structs, one for each table modified by the operation (the named table
135 : * plus any child tables that are affected). We save lists of subcommands
136 : * to apply to this table (possibly modified by parse transformation steps);
137 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
138 : * necessary information is stored in the constraints and newvals lists.
139 : *
140 : * Phase 2 is divided into multiple passes; subcommands are executed in
141 : * a pass determined by subcommand type.
142 : */
143 :
144 : #define AT_PASS_UNSET -1 /* UNSET will cause ERROR */
145 : #define AT_PASS_DROP 0 /* DROP (all flavors) */
146 : #define AT_PASS_ALTER_TYPE 1 /* ALTER COLUMN TYPE */
147 : #define AT_PASS_OLD_INDEX 2 /* re-add existing indexes */
148 : #define AT_PASS_OLD_CONSTR 3 /* re-add existing constraints */
149 : /* We could support a RENAME COLUMN pass here, but not currently used */
150 : #define AT_PASS_ADD_COL 4 /* ADD COLUMN */
151 : #define AT_PASS_ADD_CONSTR 5 /* ADD constraints (initial examination) */
152 : #define AT_PASS_COL_ATTRS 6 /* set column attributes, eg NOT NULL */
153 : #define AT_PASS_ADD_INDEXCONSTR 7 /* ADD index-based constraints */
154 : #define AT_PASS_ADD_INDEX 8 /* ADD indexes */
155 : #define AT_PASS_ADD_OTHERCONSTR 9 /* ADD other constraints, defaults */
156 : #define AT_PASS_MISC 10 /* other stuff */
157 : #define AT_NUM_PASSES 11
158 :
159 : typedef struct AlteredTableInfo
160 : {
161 : /* Information saved before any work commences: */
162 : Oid relid; /* Relation to work on */
163 : char relkind; /* Its relkind */
164 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
165 :
166 : /*
167 : * Transiently set during Phase 2, normally set to NULL.
168 : *
169 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
170 : * returns control. This can be exploited by ATExecCmd subroutines to
171 : * close/reopen across transaction boundaries.
172 : */
173 : Relation rel;
174 :
175 : /* Information saved by Phase 1 for Phase 2: */
176 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
177 : /* Information saved by Phases 1/2 for Phase 3: */
178 : List *constraints; /* List of NewConstraint */
179 : List *newvals; /* List of NewColumnValue */
180 : List *afterStmts; /* List of utility command parsetrees */
181 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
182 : int rewrite; /* Reason for forced rewrite, if any */
183 : Oid newAccessMethod; /* new access method; 0 means no change */
184 : Oid newTableSpace; /* new tablespace; 0 means no change */
185 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
186 : char newrelpersistence; /* if above is true */
187 : Expr *partition_constraint; /* for attach partition validation */
188 : /* true, if validating default due to some other attach/detach */
189 : bool validate_default;
190 : /* Objects to rebuild after completing ALTER TYPE operations */
191 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
192 : List *changedConstraintDefs; /* string definitions of same */
193 : List *changedIndexOids; /* OIDs of indexes to rebuild */
194 : List *changedIndexDefs; /* string definitions of same */
195 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
196 : char *clusterOnIndex; /* index to use for CLUSTER */
197 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
198 : List *changedStatisticsDefs; /* string definitions of same */
199 : } AlteredTableInfo;
200 :
201 : /* Struct describing one new constraint to check in Phase 3 scan */
202 : /* Note: new NOT NULL constraints are handled elsewhere */
203 : typedef struct NewConstraint
204 : {
205 : char *name; /* Constraint name, or NULL if none */
206 : ConstrType contype; /* CHECK, FOREIGN */
207 : Oid refrelid; /* PK rel, if FOREIGN */
208 : Oid refindid; /* OID of PK's index, if FOREIGN */
209 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
210 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
211 : ExprState *qualstate; /* Execution state for CHECK expr */
212 : } NewConstraint;
213 :
214 : /*
215 : * Struct describing one new column value that needs to be computed during
216 : * Phase 3 copy (this could be either a new column with a non-null default, or
217 : * a column that we're changing the type of). Columns without such an entry
218 : * are just copied from the old table during ATRewriteTable. Note that the
219 : * expr is an expression over *old* table values, except when is_generated
220 : * is true; then it is an expression over columns of the *new* tuple.
221 : */
222 : typedef struct NewColumnValue
223 : {
224 : AttrNumber attnum; /* which column */
225 : Expr *expr; /* expression to compute */
226 : ExprState *exprstate; /* execution state */
227 : bool is_generated; /* is it a GENERATED expression? */
228 : } NewColumnValue;
229 :
230 : /*
231 : * Error-reporting support for RemoveRelations
232 : */
233 : struct dropmsgstrings
234 : {
235 : char kind;
236 : int nonexistent_code;
237 : const char *nonexistent_msg;
238 : const char *skipping_msg;
239 : const char *nota_msg;
240 : const char *drophint_msg;
241 : };
242 :
243 : static const struct dropmsgstrings dropmsgstringarray[] = {
244 : {RELKIND_RELATION,
245 : ERRCODE_UNDEFINED_TABLE,
246 : gettext_noop("table \"%s\" does not exist"),
247 : gettext_noop("table \"%s\" does not exist, skipping"),
248 : gettext_noop("\"%s\" is not a table"),
249 : gettext_noop("Use DROP TABLE to remove a table.")},
250 : {RELKIND_SEQUENCE,
251 : ERRCODE_UNDEFINED_TABLE,
252 : gettext_noop("sequence \"%s\" does not exist"),
253 : gettext_noop("sequence \"%s\" does not exist, skipping"),
254 : gettext_noop("\"%s\" is not a sequence"),
255 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
256 : {RELKIND_VIEW,
257 : ERRCODE_UNDEFINED_TABLE,
258 : gettext_noop("view \"%s\" does not exist"),
259 : gettext_noop("view \"%s\" does not exist, skipping"),
260 : gettext_noop("\"%s\" is not a view"),
261 : gettext_noop("Use DROP VIEW to remove a view.")},
262 : {RELKIND_MATVIEW,
263 : ERRCODE_UNDEFINED_TABLE,
264 : gettext_noop("materialized view \"%s\" does not exist"),
265 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
266 : gettext_noop("\"%s\" is not a materialized view"),
267 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
268 : {RELKIND_INDEX,
269 : ERRCODE_UNDEFINED_OBJECT,
270 : gettext_noop("index \"%s\" does not exist"),
271 : gettext_noop("index \"%s\" does not exist, skipping"),
272 : gettext_noop("\"%s\" is not an index"),
273 : gettext_noop("Use DROP INDEX to remove an index.")},
274 : {RELKIND_COMPOSITE_TYPE,
275 : ERRCODE_UNDEFINED_OBJECT,
276 : gettext_noop("type \"%s\" does not exist"),
277 : gettext_noop("type \"%s\" does not exist, skipping"),
278 : gettext_noop("\"%s\" is not a type"),
279 : gettext_noop("Use DROP TYPE to remove a type.")},
280 : {RELKIND_FOREIGN_TABLE,
281 : ERRCODE_UNDEFINED_OBJECT,
282 : gettext_noop("foreign table \"%s\" does not exist"),
283 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
284 : gettext_noop("\"%s\" is not a foreign table"),
285 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
286 : {RELKIND_PARTITIONED_TABLE,
287 : ERRCODE_UNDEFINED_TABLE,
288 : gettext_noop("table \"%s\" does not exist"),
289 : gettext_noop("table \"%s\" does not exist, skipping"),
290 : gettext_noop("\"%s\" is not a table"),
291 : gettext_noop("Use DROP TABLE to remove a table.")},
292 : {RELKIND_PARTITIONED_INDEX,
293 : ERRCODE_UNDEFINED_OBJECT,
294 : gettext_noop("index \"%s\" does not exist"),
295 : gettext_noop("index \"%s\" does not exist, skipping"),
296 : gettext_noop("\"%s\" is not an index"),
297 : gettext_noop("Use DROP INDEX to remove an index.")},
298 : {'\0', 0, NULL, NULL, NULL, NULL}
299 : };
300 :
301 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
302 : struct DropRelationCallbackState
303 : {
304 : /* These fields are set by RemoveRelations: */
305 : char expected_relkind;
306 : LOCKMODE heap_lockmode;
307 : /* These fields are state to track which subsidiary locks are held: */
308 : Oid heapOid;
309 : Oid partParentOid;
310 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
311 : char actual_relkind;
312 : char actual_relpersistence;
313 : };
314 :
315 : /* Alter table target-type flags for ATSimplePermissions */
316 : #define ATT_TABLE 0x0001
317 : #define ATT_VIEW 0x0002
318 : #define ATT_MATVIEW 0x0004
319 : #define ATT_INDEX 0x0008
320 : #define ATT_COMPOSITE_TYPE 0x0010
321 : #define ATT_FOREIGN_TABLE 0x0020
322 : #define ATT_PARTITIONED_INDEX 0x0040
323 : #define ATT_SEQUENCE 0x0080
324 :
325 : /*
326 : * ForeignTruncateInfo
327 : *
328 : * Information related to truncation of foreign tables. This is used for
329 : * the elements in a hash table. It uses the server OID as lookup key,
330 : * and includes a per-server list of all foreign tables involved in the
331 : * truncation.
332 : */
333 : typedef struct ForeignTruncateInfo
334 : {
335 : Oid serverid;
336 : List *rels;
337 : } ForeignTruncateInfo;
338 :
339 : /*
340 : * Partition tables are expected to be dropped when the parent partitioned
341 : * table gets dropped. Hence for partitioning we use AUTO dependency.
342 : * Otherwise, for regular inheritance use NORMAL dependency.
343 : */
344 : #define child_dependency_type(child_is_partition) \
345 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
346 :
347 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
348 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
349 : static void truncate_check_activity(Relation rel);
350 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
351 : Oid relId, Oid oldRelId, void *arg);
352 : static List *MergeAttributes(List *schema, List *supers, char relpersistence,
353 : bool is_partition, List **supconstr,
354 : List **additional_notnulls);
355 : static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
356 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
357 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
358 : static void StoreCatalogInheritance(Oid relationId, List *supers,
359 : bool child_is_partition);
360 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
361 : int32 seqNumber, Relation inhRelation,
362 : bool child_is_partition);
363 : static int findAttrByName(const char *attributeName, List *schema);
364 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
365 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
366 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
367 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
368 : LOCKMODE lockmode);
369 : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
370 : bool recurse, bool recursing, LOCKMODE lockmode);
371 : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
372 : Relation rel, HeapTuple contuple, List **otherrelids,
373 : LOCKMODE lockmode);
374 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
375 : Relation rel, char *constrName,
376 : bool recurse, bool recursing, LOCKMODE lockmode);
377 : static int transformColumnNameList(Oid relId, List *colList,
378 : int16 *attnums, Oid *atttypids);
379 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
380 : List **attnamelist,
381 : int16 *attnums, Oid *atttypids,
382 : Oid *opclasses);
383 : static Oid transformFkeyCheckAttrs(Relation pkrel,
384 : int numattrs, int16 *attnums,
385 : Oid *opclasses);
386 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
387 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
388 : Oid *funcid);
389 : static void validateForeignKeyConstraint(char *conname,
390 : Relation rel, Relation pkrel,
391 : Oid pkindOid, Oid constraintOid);
392 : static void ATController(AlterTableStmt *parsetree,
393 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
394 : AlterTableUtilityContext *context);
395 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
396 : bool recurse, bool recursing, LOCKMODE lockmode,
397 : AlterTableUtilityContext *context);
398 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
399 : AlterTableUtilityContext *context);
400 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
401 : AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
402 : AlterTableUtilityContext *context);
403 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
404 : Relation rel, AlterTableCmd *cmd,
405 : bool recurse, LOCKMODE lockmode,
406 : int cur_pass,
407 : AlterTableUtilityContext *context);
408 : static void ATRewriteTables(AlterTableStmt *parsetree,
409 : List **wqueue, LOCKMODE lockmode,
410 : AlterTableUtilityContext *context);
411 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
412 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
413 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
414 : static void ATSimpleRecursion(List **wqueue, Relation rel,
415 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
416 : AlterTableUtilityContext *context);
417 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
418 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
419 : LOCKMODE lockmode,
420 : AlterTableUtilityContext *context);
421 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
422 : DropBehavior behavior);
423 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
424 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
425 : AlterTableUtilityContext *context);
426 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
427 : Relation rel, AlterTableCmd **cmd,
428 : bool recurse, bool recursing,
429 : LOCKMODE lockmode, int cur_pass,
430 : AlterTableUtilityContext *context);
431 : static bool check_for_column_name_collision(Relation rel, const char *colname,
432 : bool if_not_exists);
433 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
434 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
435 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
436 : LOCKMODE lockmode);
437 : static void set_attnotnull(List **wqueue, Relation rel,
438 : AttrNumber attnum, bool recurse, LOCKMODE lockmode);
439 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
440 : char *constrname, char *colName,
441 : bool recurse, bool recursing,
442 : List **readyRels, LOCKMODE lockmode);
443 : static void ATExecSetAttNotNull(List **wqueue, Relation rel,
444 : const char *colName, LOCKMODE lockmode);
445 : static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
446 : const char *colName, LOCKMODE lockmode);
447 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
448 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
449 : List *testConstraint, List *provenConstraint);
450 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
451 : Node *newDefault, LOCKMODE lockmode);
452 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
453 : Node *newDefault);
454 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
455 : Node *def, LOCKMODE lockmode);
456 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
457 : Node *def, LOCKMODE lockmode);
458 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
459 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
460 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
461 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
462 : Node *newValue, LOCKMODE lockmode);
463 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
464 : Node *options, bool isReset, LOCKMODE lockmode);
465 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
466 : Node *newValue, LOCKMODE lockmode);
467 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
468 : AlterTableCmd *cmd, LOCKMODE lockmode,
469 : AlterTableUtilityContext *context);
470 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
471 : DropBehavior behavior,
472 : bool recurse, bool recursing,
473 : bool missing_ok, LOCKMODE lockmode,
474 : ObjectAddresses *addrs);
475 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
476 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
477 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
478 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
479 : static ObjectAddress ATExecAddConstraint(List **wqueue,
480 : AlteredTableInfo *tab, Relation rel,
481 : Constraint *newConstraint, bool recurse, bool is_readd,
482 : LOCKMODE lockmode);
483 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
484 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
485 : IndexStmt *stmt, LOCKMODE lockmode);
486 : static ObjectAddress ATAddCheckConstraint(List **wqueue,
487 : AlteredTableInfo *tab, Relation rel,
488 : Constraint *constr,
489 : bool recurse, bool recursing, bool is_readd,
490 : LOCKMODE lockmode);
491 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
492 : Relation rel, Constraint *fkconstraint,
493 : bool recurse, bool recursing,
494 : LOCKMODE lockmode);
495 : static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
496 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
497 : int numfks, int16 *pkattnum, int16 *fkattnum,
498 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
499 : int numfkdelsetcols, int16 *fkdelsetcols,
500 : bool old_check_ok,
501 : Oid parentDelTrigger, Oid parentUpdTrigger);
502 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
503 : int numfksetcols, const int16 *fksetcolsattnums,
504 : List *fksetcols);
505 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
506 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
507 : int numfks, int16 *pkattnum, int16 *fkattnum,
508 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
509 : int numfkdelsetcols, int16 *fkdelsetcols,
510 : bool old_check_ok, LOCKMODE lockmode,
511 : Oid parentInsTrigger, Oid parentUpdTrigger);
512 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
513 : Relation partitionRel);
514 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
515 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
516 : Relation partRel);
517 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
518 : Constraint *fkconstraint, Oid constraintOid,
519 : Oid indexOid,
520 : Oid parentInsTrigger, Oid parentUpdTrigger,
521 : Oid *insertTrigOid, Oid *updateTrigOid);
522 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
523 : Constraint *fkconstraint, Oid constraintOid,
524 : Oid indexOid,
525 : Oid parentDelTrigger, Oid parentUpdTrigger,
526 : Oid *deleteTrigOid, Oid *updateTrigOid);
527 : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
528 : Oid partRelid,
529 : Oid parentConstrOid, int numfks,
530 : AttrNumber *mapped_conkey, AttrNumber *confkey,
531 : Oid *conpfeqop,
532 : Oid parentInsTrigger,
533 : Oid parentUpdTrigger,
534 : Relation trigrel);
535 : static void GetForeignKeyActionTriggers(Relation trigrel,
536 : Oid conoid, Oid confrelid, Oid conrelid,
537 : Oid *deleteTriggerOid,
538 : Oid *updateTriggerOid);
539 : static void GetForeignKeyCheckTriggers(Relation trigrel,
540 : Oid conoid, Oid confrelid, Oid conrelid,
541 : Oid *insertTriggerOid,
542 : Oid *updateTriggerOid);
543 : static void ATExecDropConstraint(Relation rel, const char *constrName,
544 : DropBehavior behavior,
545 : bool recurse, bool recursing,
546 : bool missing_ok, LOCKMODE lockmode);
547 : static ObjectAddress dropconstraint_internal(Relation rel,
548 : HeapTuple constraintTup, DropBehavior behavior,
549 : bool recurse, bool recursing,
550 : bool missing_ok, List **readyRels,
551 : LOCKMODE lockmode);
552 : static void ATPrepAlterColumnType(List **wqueue,
553 : AlteredTableInfo *tab, Relation rel,
554 : bool recurse, bool recursing,
555 : AlterTableCmd *cmd, LOCKMODE lockmode,
556 : AlterTableUtilityContext *context);
557 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
558 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
559 : AlterTableCmd *cmd, LOCKMODE lockmode);
560 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
561 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
562 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
563 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
564 : LOCKMODE lockmode);
565 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
566 : char *cmd, List **wqueue, LOCKMODE lockmode,
567 : bool rewrite);
568 : static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
569 : Oid objid, Relation rel, List *domname,
570 : const char *conname);
571 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
572 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
573 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
574 : List *options, LOCKMODE lockmode);
575 : static void change_owner_fix_column_acls(Oid relationOid,
576 : Oid oldOwnerId, Oid newOwnerId);
577 : static void change_owner_recurse_to_sequences(Oid relationOid,
578 : Oid newOwnerId, LOCKMODE lockmode);
579 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
580 : LOCKMODE lockmode);
581 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
582 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
583 : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
584 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
585 : const char *tablespacename, LOCKMODE lockmode);
586 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
587 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
588 : static void ATExecSetRelOptions(Relation rel, List *defList,
589 : AlterTableType operation,
590 : LOCKMODE lockmode);
591 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
592 : char fires_when, bool skip_system, bool recurse,
593 : LOCKMODE lockmode);
594 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
595 : char fires_when, LOCKMODE lockmode);
596 : static void ATPrepAddInherit(Relation child_rel);
597 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
598 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
599 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
600 : DependencyType deptype);
601 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
602 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
603 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
604 : static void ATExecGenericOptions(Relation rel, List *options);
605 : static void ATExecSetRowSecurity(Relation rel, bool rls);
606 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
607 : static ObjectAddress ATExecSetCompression(Relation rel,
608 : const char *column, Node *newValue, LOCKMODE lockmode);
609 :
610 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
611 : static const char *storage_name(char c);
612 :
613 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
614 : Oid oldRelOid, void *arg);
615 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
616 : Oid oldrelid, void *arg);
617 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
618 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
619 : List **partexprs, Oid *partopclass, Oid *partcollation,
620 : PartitionStrategy strategy);
621 : static void CreateInheritance(Relation child_rel, Relation parent_rel);
622 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
623 : bool expect_detached);
624 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
625 : PartitionCmd *cmd,
626 : AlterTableUtilityContext *context);
627 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
628 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
629 : List *partConstraint,
630 : bool validate_default);
631 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
632 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
633 : static void DropClonedTriggersFromPartition(Oid partitionId);
634 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
635 : Relation rel, RangeVar *name,
636 : bool concurrent);
637 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
638 : bool concurrent, Oid defaultPartOid);
639 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
640 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
641 : RangeVar *name);
642 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
643 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
644 : Relation partitionTbl);
645 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
646 : static List *GetParentedForeignKeyRefs(Relation partition);
647 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
648 : static char GetAttributeCompression(Oid atttypid, char *compression);
649 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
650 :
651 :
652 : /* ----------------------------------------------------------------
653 : * DefineRelation
654 : * Creates a new relation.
655 : *
656 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
657 : * The other arguments are used to extend the behavior for other cases:
658 : * relkind: relkind to assign to the new relation
659 : * ownerId: if not InvalidOid, use this as the new relation's owner.
660 : * typaddress: if not null, it's set to the pg_type entry's address.
661 : * queryString: for error reporting
662 : *
663 : * Note that permissions checks are done against current user regardless of
664 : * ownerId. A nonzero ownerId is used when someone is creating a relation
665 : * "on behalf of" someone else, so we still want to see that the current user
666 : * has permissions to do it.
667 : *
668 : * If successful, returns the address of the new relation.
669 : * ----------------------------------------------------------------
670 : */
671 : ObjectAddress
2959 alvherre 672 GIC 62952 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
673 : ObjectAddress *typaddress, const char *queryString)
674 : {
675 : char relname[NAMEDATALEN];
676 : Oid namespaceId;
677 : Oid relationId;
678 : Oid tablespaceId;
679 : Relation rel;
680 : TupleDesc descriptor;
681 : List *inheritOids;
682 : List *old_constraints;
683 : List *old_notnulls;
684 : List *rawDefaults;
5448 tgl 685 ECB : List *cookedDefaults;
686 : List *nncols;
687 : Datum reloptions;
688 : ListCell *listptr;
689 : AttrNumber attnum;
690 : bool partitioned;
691 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
692 : Oid ofTypeId;
693 : ObjectAddress address;
694 : LOCKMODE parentLockmode;
1495 andres 695 GIC 62952 : const char *accessMethod = NULL;
696 62952 : Oid accessMethodId = InvalidOid;
697 :
698 : /*
699 : * Truncate relname to appropriate length (probably a waste of time, as
700 : * parser should have done this already).
701 : */
972 peter 702 62952 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
703 :
704 : /*
705 : * Check consistency of arguments
706 : */
4382 bruce 707 62952 : if (stmt->oncommit != ONCOMMIT_NOOP
4500 rhaas 708 86 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
7203 tgl 709 CBC 6 : ereport(ERROR,
7203 tgl 710 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
711 : errmsg("ON COMMIT can only be used on temporary tables")));
712 :
2314 rhaas 713 GIC 62946 : if (stmt->partspec != NULL)
714 : {
715 2178 : if (relkind != RELKIND_RELATION)
2314 rhaas 716 LBC 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
717 :
2314 rhaas 718 GIC 2178 : relkind = RELKIND_PARTITIONED_TABLE;
1445 alvherre 719 2178 : partitioned = true;
720 : }
1445 alvherre 721 ECB : else
1445 alvherre 722 CBC 60768 : partitioned = false;
2314 rhaas 723 ECB :
724 : /*
725 : * Look up the namespace in which we are supposed to create the relation,
726 : * check we have permission to create there, lock it against concurrent
4101 727 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
728 : * namespace is selected.
4298 729 : */
4101 rhaas 730 EUB : namespaceId =
4101 rhaas 731 GIC 62946 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
4298 rhaas 732 ECB :
4869 tgl 733 : /*
734 : * Security check: disallow creating temp tables from security-restricted
735 : * code. This is needed because calling code might not expect untrusted
736 : * tables to appear in pg_temp at the front of its search path.
737 : */
4500 rhaas 738 GIC 62946 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
739 1439 : && InSecurityRestrictedOperation())
4869 tgl 740 UIC 0 : ereport(ERROR,
741 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
742 : errmsg("cannot create temporary table within security-restricted operation")));
743 :
744 : /*
1574 alvherre 745 ECB : * Determine the lockmode to use when scanning parents. A self-exclusive
746 : * lock is needed here.
747 : *
748 : * For regular inheritance, if two backends attempt to add children to the
749 : * same parent simultaneously, and that parent has no pre-existing
750 : * children, then both will attempt to update the parent's relhassubclass
751 : * field, leading to a "tuple concurrently updated" error. Also, this
752 : * interlocks against a concurrent ANALYZE on the parent table, which
753 : * might otherwise be attempting to clear the parent's relhassubclass
1574 alvherre 754 EUB : * field, if its previous children were recently dropped.
755 : *
756 : * If the child table is a partition, then we instead grab an exclusive
757 : * lock on the parent because its partition descriptor will be changed by
758 : * addition of the new partition.
759 : */
1574 alvherre 760 GIC 62946 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
761 : ShareUpdateExclusiveLock);
762 :
763 : /* Determine the list of OIDs of the parents. */
764 62946 : inheritOids = NIL;
765 67430 : foreach(listptr, stmt->inhRelations)
766 : {
767 4484 : RangeVar *rv = (RangeVar *) lfirst(listptr);
768 : Oid parentOid;
769 :
770 4484 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
771 :
772 : /*
773 : * Reject duplications in the list of parents.
1574 alvherre 774 ECB : */
1574 alvherre 775 GIC 4484 : if (list_member_oid(inheritOids, parentOid))
1574 alvherre 776 UIC 0 : ereport(ERROR,
777 : (errcode(ERRCODE_DUPLICATE_TABLE),
1574 alvherre 778 ECB : errmsg("relation \"%s\" would be inherited from more than once",
779 : get_rel_name(parentOid))));
780 :
1574 alvherre 781 CBC 4484 : inheritOids = lappend_oid(inheritOids, parentOid);
782 : }
783 :
6869 tgl 784 ECB : /*
785 : * Select tablespace to use: an explicitly indicated one, or (in the case
786 : * of a partitioned table) the parent's, if it has one.
787 : */
6869 tgl 788 GIC 62946 : if (stmt->tablespacename)
6869 tgl 789 ECB : {
4630 rhaas 790 GBC 52 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
791 :
1445 alvherre 792 GIC 49 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
793 3 : ereport(ERROR,
794 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1445 alvherre 795 ECB : errmsg("cannot specify default tablespace for partitioned relations")));
796 : }
1574 alvherre 797 GIC 62894 : else if (stmt->partbound)
798 : {
799 : /*
800 : * For partitions, when no other tablespace is specified, we default
801 : * the tablespace to the parent partitioned table's.
1574 alvherre 802 ECB : */
1574 alvherre 803 GIC 3608 : Assert(list_length(inheritOids) == 1);
1445 alvherre 804 CBC 3608 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
805 : }
6729 tgl 806 ECB : else
1402 alvherre 807 CBC 59286 : tablespaceId = InvalidOid;
808 :
809 : /* still nothing? use the default */
1402 alvherre 810 GIC 62940 : if (!OidIsValid(tablespaceId))
1445 alvherre 811 CBC 62883 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
812 : partitioned);
813 :
814 : /* Check permissions except when using database's default */
5540 tgl 815 GIC 62937 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
816 : {
6729 tgl 817 ECB : AclResult aclresult;
818 :
147 peter 819 GNC 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
820 : ACL_CREATE);
6869 tgl 821 CBC 66 : if (aclresult != ACLCHECK_OK)
1954 peter_e 822 GIC 3 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
6729 tgl 823 3 : get_tablespace_name(tablespaceId));
6869 tgl 824 ECB : }
825 :
826 : /* In all cases disallow placing user relations in pg_global */
4809 tgl 827 GIC 62934 : if (tablespaceId == GLOBALTABLESPACE_OID)
828 9 : ereport(ERROR,
4809 tgl 829 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
830 : errmsg("only shared relations can be placed in pg_global tablespace")));
831 :
832 : /* Identify user ID that will own the table */
4617 tgl 833 CBC 62925 : if (!OidIsValid(ownerId))
4617 tgl 834 GIC 62843 : ownerId = GetUserId();
4617 tgl 835 ECB :
5789 836 : /*
837 : * Parse and validate reloptions, if any.
838 : */
5179 alvherre 839 GIC 62925 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
840 : true, false);
5789 tgl 841 ECB :
1242 michael 842 CBC 62916 : switch (relkind)
843 : {
1242 michael 844 GIC 43940 : case RELKIND_VIEW:
845 43940 : (void) view_reloptions(reloptions, true);
846 43931 : break;
1242 michael 847 CBC 2169 : case RELKIND_PARTITIONED_TABLE:
848 2169 : (void) partitioned_table_reloptions(reloptions, true);
1242 michael 849 GIC 2166 : break;
850 16807 : default:
851 16807 : (void) heap_reloptions(relkind, reloptions, true);
852 : }
5789 tgl 853 ECB :
4819 peter_e 854 GIC 62856 : if (stmt->ofTypename)
855 : {
4128 peter_e 856 ECB : AclResult aclresult;
857 :
4549 peter_e 858 CBC 43 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
4128 peter_e 859 ECB :
147 peter 860 GNC 43 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
4128 peter_e 861 CBC 43 : if (aclresult != ACLCHECK_OK)
3950 862 3 : aclcheck_error_type(aclresult, ofTypeId);
4128 peter_e 863 ECB : }
4819 864 : else
4819 peter_e 865 CBC 62813 : ofTypeId = InvalidOid;
866 :
867 : /*
6385 bruce 868 ECB : * Look up inheritance ancestors and generate relation schema, including
869 : * inherited attributes. (Note that stmt->tableElts is destructively
870 : * modified by MergeAttributes.)
871 : */
2151 rhaas 872 CBC 62769 : stmt->tableElts =
1574 alvherre 873 GIC 62853 : MergeAttributes(stmt->tableElts, inheritOids,
2151 rhaas 874 CBC 62853 : stmt->relation->relpersistence,
875 62853 : stmt->partbound != NULL,
876 : &old_constraints, &old_notnulls);
877 :
878 : /*
3260 bruce 879 ECB : * Create a tuple descriptor from the relation schema. Note that this
880 : * deals with column names, types, and in-descriptor NOT NULL flags, but
881 : * not default values, NOT NULL or CHECK constraints; we handle those
882 : * below.
883 : */
2151 rhaas 884 GIC 62769 : descriptor = BuildDescForRelation(stmt->tableElts);
885 :
886 : /*
5448 tgl 887 ECB : * Find columns with default values and prepare for insertion of the
888 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
889 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
5050 bruce 890 : * while raw defaults go into a list of RawColumnDefault structs that will
891 : * be processed by AddRelationNewConstraints. (We can't deal with raw
892 : * expressions until we can do transformExpr.)
893 : *
894 : * We can set the atthasdef flags now in the tuple descriptor; this just
895 : * saves StoreAttrDefault from having to do an immediate update of the
896 : * pg_attribute rows.
897 : */
5448 tgl 898 GIC 62757 : rawDefaults = NIL;
5448 tgl 899 CBC 62757 : cookedDefaults = NIL;
5448 tgl 900 GIC 62757 : attnum = 0;
901 :
2151 rhaas 902 549553 : foreach(listptr, stmt->tableElts)
903 : {
5448 tgl 904 486805 : ColumnDef *colDef = lfirst(listptr);
905 : Form_pg_attribute attr;
906 :
907 486805 : attnum++;
2058 andres 908 486805 : attr = TupleDescAttr(descriptor, attnum - 1);
909 :
5448 tgl 910 486805 : if (colDef->raw_default != NULL)
911 : {
912 : RawColumnDefault *rawEnt;
6797 bruce 913 ECB :
5448 tgl 914 CBC 1126 : Assert(colDef->cooked_default == NULL);
5448 tgl 915 ECB :
5448 tgl 916 GIC 1126 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
5448 tgl 917 CBC 1126 : rawEnt->attnum = attnum;
5448 tgl 918 GIC 1126 : rawEnt->raw_default = colDef->raw_default;
1838 andrew 919 CBC 1126 : rawEnt->missingMode = false;
1471 peter 920 GIC 1126 : rawEnt->generated = colDef->generated;
5448 tgl 921 1126 : rawDefaults = lappend(rawDefaults, rawEnt);
2058 andres 922 CBC 1126 : attr->atthasdef = true;
7653 tgl 923 ECB : }
5448 tgl 924 GIC 485679 : else if (colDef->cooked_default != NULL)
7653 tgl 925 ECB : {
926 : CookedConstraint *cooked;
927 :
5448 tgl 928 GIC 160 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
5448 tgl 929 CBC 160 : cooked->contype = CONSTR_DEFAULT;
2118 tgl 930 GIC 160 : cooked->conoid = InvalidOid; /* until created */
5448 tgl 931 CBC 160 : cooked->name = NULL;
932 160 : cooked->attnum = attnum;
4933 933 160 : cooked->expr = colDef->cooked_default;
4330 alvherre 934 160 : cooked->skip_validation = false;
5448 tgl 935 160 : cooked->is_local = true; /* not used for defaults */
2118 936 160 : cooked->inhcount = 0; /* ditto */
4006 alvherre 937 160 : cooked->is_no_inherit = false;
5448 tgl 938 GIC 160 : cookedDefaults = lappend(cookedDefaults, cooked);
2058 andres 939 CBC 160 : attr->atthasdef = true;
940 : }
941 :
2194 peter_e 942 GIC 486805 : if (colDef->identity)
2058 andres 943 CBC 66 : attr->attidentity = colDef->identity;
1471 peter 944 ECB :
1471 peter 945 CBC 486805 : if (colDef->generated)
946 445 : attr->attgenerated = colDef->generated;
751 rhaas 947 ECB :
682 tgl 948 CBC 486805 : if (colDef->compression)
949 41 : attr->attcompression = GetAttributeCompression(attr->atttypid,
682 tgl 950 ECB : colDef->compression);
951 :
270 peter 952 GNC 486799 : if (colDef->storage_name)
953 6 : attr->attstorage = GetAttributeStorage(attr->atttypid, colDef->storage_name);
9345 bruce 954 ECB : }
955 :
1495 andres 956 : /*
957 : * If the statement hasn't specified an access method, but we're defining
958 : * a type of relation that needs one, use the default.
959 : */
1495 andres 960 CBC 62748 : if (stmt->accessMethod != NULL)
1495 andres 961 ECB : {
1495 andres 962 GIC 57 : accessMethod = stmt->accessMethod;
1495 andres 963 ECB :
1445 alvherre 964 CBC 57 : if (partitioned)
1495 andres 965 GIC 3 : ereport(ERROR,
1495 andres 966 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
967 : errmsg("specifying a table access method is not supported on a partitioned table")));
968 : }
492 peter 969 GIC 62691 : else if (RELKIND_HAS_TABLE_AM(relkind))
1495 andres 970 CBC 15335 : accessMethod = default_table_access_method;
1495 andres 971 ECB :
972 : /* look up the access method, verify it is for a table */
1495 andres 973 GIC 62745 : if (accessMethod != NULL)
1466 974 15389 : accessMethodId = get_table_am_oid(accessMethod, false);
975 :
976 : /*
977 : * Create the relation. Inherited defaults and constraints are passed in
5050 bruce 978 ECB : * for immediate handling --- since they don't need parsing, they can be
979 : * stored immediately.
5448 tgl 980 : */
7653 tgl 981 GIC 62736 : relationId = heap_create_with_catalog(relname,
7653 tgl 982 ECB : namespaceId,
6869 983 : tablespaceId,
984 : InvalidOid,
985 : InvalidOid,
986 : ofTypeId,
4617 987 : ownerId,
1495 andres 988 : accessMethodId,
989 : descriptor,
990 : list_concat(cookedDefaults,
5448 tgl 991 : old_constraints),
7653 992 : relkind,
4500 rhaas 993 GIC 62736 : stmt->relation->relpersistence,
994 : false,
995 : false,
996 : stmt->oncommit,
997 : reloptions,
998 : true,
3820 alvherre 999 ECB : allowSystemTableMods,
1000 : false,
1001 : InvalidOid,
1002 : typaddress);
1003 :
1004 : /*
1005 : * We must bump the command counter to make the newly-created relation
1006 : * tuple visible for opening.
1007 : */
7653 tgl 1008 GIC 62724 : CommandCounterIncrement();
1009 :
1010 : /*
3260 bruce 1011 ECB : * Open the new relation and acquire exclusive lock on it. This isn't
1012 : * really necessary for locking out other backends (since they can't see
1013 : * the new rel anyway until we commit), but it keeps the lock manager from
1014 : * complaining about deadlock risks.
1015 : */
7528 tgl 1016 GIC 62724 : rel = relation_open(relationId, AccessExclusiveLock);
1017 :
1018 : /*
1019 : * Now add any newly specified column default and generation expressions
1020 : * to the new relation. These are passed to us in the form of raw
1021 : * parsetrees; we need to transform them to executable expression trees
1022 : * before they can be added. The most convenient way to do that is to
1023 : * apply the parser's transformExpr routine, but transformExpr doesn't
1024 : * work unless we have a pre-existing relation. So, the transformation has
1025 : * to be postponed to this final step of CREATE TABLE.
1471 peter 1026 ECB : *
1027 : * This needs to be before processing the partitioning clauses because
1028 : * those could refer to generated columns.
1029 : */
1471 peter 1030 GIC 62724 : if (rawDefaults)
1031 950 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1032 : true, true, false, queryString);
1033 :
1471 peter 1034 ECB : /*
1035 : * Make column generation expressions visible for use by partitioning.
1036 : */
1471 peter 1037 GIC 62673 : CommandCounterIncrement();
1038 :
1039 : /* Process and store partition bound, if any. */
2314 rhaas 1040 62673 : if (stmt->partbound)
1041 : {
1042 : PartitionBoundSpec *bound;
1043 : ParseState *pstate;
2039 1044 3581 : Oid parentId = linitial_oid(inheritOids),
1045 : defaultPartOid;
1046 : Relation parent,
1047 3581 : defaultRel = NULL;
1193 tgl 1048 ECB : ParseNamespaceItem *nsitem;
2314 rhaas 1049 :
1050 : /* Already have strong enough lock on the parent */
1539 andres 1051 GIC 3581 : parent = table_open(parentId, NoLock);
1052 :
1053 : /*
1054 : * We are going to try to validate the partition bound specification
2314 rhaas 1055 ECB : * against the partition key of parentRel, so it better have one.
1056 : */
2314 rhaas 1057 GIC 3581 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2314 rhaas 1058 CBC 6 : ereport(ERROR,
1059 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1060 : errmsg("\"%s\" is not partitioned",
1061 : RelationGetRelationName(parent))));
2314 rhaas 1062 ECB :
1063 : /*
1064 : * The partition constraint of the default partition depends on the
2039 1065 : * partition bounds of every other partition. It is possible that
1066 : * another backend might be about to execute a query on the default
1067 : * partition table, and that the query relies on previously cached
1068 : * default partition constraints. We must therefore take a table lock
1069 : * strong enough to prevent all queries on the default partition from
1070 : * proceeding until we commit and send out a shared-cache-inval notice
1071 : * that will make them update their index lists.
1072 : *
1073 : * Order of locking: The relation being added won't be visible to
1074 : * other backends until it is committed, hence here in
1075 : * DefineRelation() the order of locking the default partition and the
1076 : * relation being added does not matter. But at all other places we
1077 : * need to lock the default relation before we lock the relation being
1078 : * added or removed i.e. we should take the lock in same order at all
1079 : * the places such that lock parent, lock default partition and then
1080 : * lock the partition so as to avoid a deadlock.
1081 : */
1082 : defaultPartOid =
745 alvherre 1083 GIC 3575 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1084 : true));
2039 rhaas 1085 3575 : if (OidIsValid(defaultPartOid))
1539 andres 1086 177 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1087 :
1088 : /* Transform the bound values */
2314 rhaas 1089 3575 : pstate = make_parsestate(NULL);
1090 3575 : pstate->p_sourcetext = queryString;
1091 :
1092 : /*
1093 : * Add an nsitem containing this relation, so that transformExpr
1094 : * called on partition bound expressions is able to report errors
1095 : * using a proper context.
1096 : */
1193 tgl 1097 3575 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1098 : NULL, false, false);
1099 3575 : addNSItemToQuery(pstate, nsitem, false, true, true);
1100 :
2314 rhaas 1101 CBC 3575 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1102 :
2314 rhaas 1103 ECB : /*
1104 : * Check first that the new partition's bound is valid and does not
1105 : * overlap with any of existing partitions of the parent.
1106 : */
928 tgl 1107 CBC 3473 : check_new_partition_bound(relname, parent, bound, pstate);
2314 rhaas 1108 ECB :
1109 : /*
1110 : * If the default partition exists, its partition constraints will
1111 : * change after the addition of this new partition such that it won't
1112 : * allow any row that qualifies for this new partition. So, check that
1113 : * the existing data in the default partition satisfies the constraint
1114 : * as it will exist after adding this partition.
2039 1115 : */
2039 rhaas 1116 GIC 3416 : if (OidIsValid(defaultPartOid))
2039 rhaas 1117 ECB : {
1761 tgl 1118 GIC 162 : check_default_partition_contents(parent, defaultRel, bound);
2039 rhaas 1119 ECB : /* Keep the lock until commit. */
1539 andres 1120 GIC 153 : table_close(defaultRel, NoLock);
1121 : }
1122 :
1123 : /* Update the pg_class entry. */
2302 rhaas 1124 3407 : StorePartitionBound(rel, parent, bound);
2302 rhaas 1125 ECB :
1539 andres 1126 GIC 3407 : table_close(parent, NoLock);
1127 : }
1128 :
1129 : /* Store inheritance information for new rel. */
1677 alvherre 1130 62499 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1131 :
1132 : /*
1133 : * Process the partitioning specification (if any) and store the partition
2308 rhaas 1134 ECB : * key information into the catalog.
1135 : */
1445 alvherre 1136 CBC 62499 : if (partitioned)
1137 : {
1691 peter_e 1138 ECB : ParseState *pstate;
1139 : int partnatts;
1140 : AttrNumber partattrs[PARTITION_MAX_KEYS];
2308 rhaas 1141 : Oid partopclass[PARTITION_MAX_KEYS];
1142 : Oid partcollation[PARTITION_MAX_KEYS];
2308 rhaas 1143 CBC 2163 : List *partexprs = NIL;
1144 :
1691 peter_e 1145 GIC 2163 : pstate = make_parsestate(NULL);
1146 2163 : pstate->p_sourcetext = queryString;
1691 peter_e 1147 ECB :
2142 tgl 1148 GIC 2163 : partnatts = list_length(stmt->partspec->partParams);
1149 :
1150 : /* Protect fixed-size arrays here and in executor */
1151 2163 : if (partnatts > PARTITION_MAX_KEYS)
2142 tgl 1152 UIC 0 : ereport(ERROR,
2142 tgl 1153 ECB : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1154 : errmsg("cannot partition using more than %d columns",
1155 : PARTITION_MAX_KEYS)));
1156 :
1157 : /*
1158 : * We need to transform the raw parsetrees corresponding to partition
1159 : * expressions into executable expression trees. Like column defaults
2314 rhaas 1160 : * and CHECK constraints, we could not have done the transformation
1161 : * earlier.
1162 : */
157 alvherre 1163 GNC 2163 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
2142 tgl 1164 ECB :
1691 peter_e 1165 GIC 2148 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1166 : partattrs, &partexprs, partopclass,
157 alvherre 1167 GNC 2148 : partcollation, stmt->partspec->strategy);
2314 rhaas 1168 EUB :
157 alvherre 1169 GNC 2106 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1170 : partexprs,
1171 : partopclass, partcollation);
1172 :
1173 : /* make it all visible */
1906 alvherre 1174 GIC 2106 : CommandCounterIncrement();
1175 : }
1176 :
1177 : /*
1178 : * If we're creating a partition, create now all the indexes, triggers,
1179 : * FKs defined in the parent.
1843 alvherre 1180 ECB : *
1181 : * We can't do it earlier, because DefineIndex wants to know the partition
1182 : * key which we just stored.
1183 : */
1906 alvherre 1184 CBC 62442 : if (stmt->partbound)
1185 : {
1186 3404 : Oid parentId = linitial_oid(inheritOids);
1187 : Relation parent;
1188 : List *idxlist;
1189 : ListCell *cell;
1190 :
1906 alvherre 1191 ECB : /* Already have strong enough lock on the parent */
1539 andres 1192 GIC 3404 : parent = table_open(parentId, NoLock);
1906 alvherre 1193 3404 : idxlist = RelationGetIndexList(parent);
1194 :
1195 : /*
1196 : * For each index in the parent table, create one in the partition
1197 : */
1198 4006 : foreach(cell, idxlist)
1199 : {
1200 611 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1208 michael 1201 ECB : AttrMap *attmap;
1202 : IndexStmt *idxstmt;
1875 alvherre 1203 : Oid constraintOid;
1204 :
1383 alvherre 1205 GIC 611 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1206 : {
1207 18 : if (idxRel->rd_index->indisunique)
1208 6 : ereport(ERROR,
1383 alvherre 1209 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1210 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1211 : RelationGetRelationName(parent)),
1212 : errdetail("Table \"%s\" contains indexes that are unique.",
1213 : RelationGetRelationName(parent))));
1214 : else
1215 : {
1383 alvherre 1216 GIC 12 : index_close(idxRel, AccessShareLock);
1383 alvherre 1217 CBC 12 : continue;
1218 : }
1219 : }
1220 :
1208 michael 1221 GIC 593 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1222 : RelationGetDescr(parent),
1223 : false);
1224 : idxstmt =
1447 tgl 1225 CBC 593 : generateClonedIndexStmt(NULL, idxRel,
1208 michael 1226 ECB : attmap, &constraintOid);
1906 alvherre 1227 GIC 593 : DefineIndex(RelationGetRelid(rel),
1228 : idxstmt,
1229 : InvalidOid,
1230 : RelationGetRelid(idxRel),
1231 : constraintOid,
1232 : -1,
1233 : false, false, false, false, false);
1234 :
1906 alvherre 1235 CBC 590 : index_close(idxRel, AccessShareLock);
1906 alvherre 1236 ECB : }
1237 :
1906 alvherre 1238 GIC 3395 : list_free(idxlist);
1239 :
1843 alvherre 1240 ECB : /*
1241 : * If there are any row-level triggers, clone them to the new
1242 : * partition.
1243 : */
1843 alvherre 1244 CBC 3395 : if (parent->trigdesc != NULL)
1843 alvherre 1245 GIC 188 : CloneRowTriggersToPartition(parent, rel);
1843 alvherre 1246 ECB :
1247 : /*
1248 : * And foreign keys too. Note that because we're freshly creating the
1249 : * table, there is no need to verify these new constraints.
1250 : */
1467 alvherre 1251 GIC 3395 : CloneForeignKeyConstraints(NULL, parent, rel);
1252 :
1539 andres 1253 3395 : table_close(parent, NoLock);
2314 rhaas 1254 ECB : }
1255 :
1256 : /*
1418 tgl 1257 : * Now add any newly specified CHECK constraints to the new relation. Same
1258 : * as for defaults above, but these need to come after partitioning is set
1259 : * up.
1260 : */
1471 peter 1261 GIC 62433 : if (stmt->constraints)
1262 281 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1691 peter_e 1263 ECB : true, true, false, queryString);
8313 JanWieck 1264 :
1265 : /*
1266 : * Finally, merge the NOT NULL constraints that are directly declared with
1267 : * those that come from parent relations (making sure to count inheritance
1268 : * appropriately for each), create them, and set the attnotnull flag on
1269 : * columns that don't yet have it.
1270 : */
2 alvherre 1271 GNC 62424 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1272 : old_notnulls);
1273 65677 : foreach(listptr, nncols)
1274 3253 : set_attnotnull(NULL, rel, lfirst_int(listptr), false, NoLock);
1275 :
2959 alvherre 1276 GIC 62424 : ObjectAddressSet(address, RelationRelationId, relationId);
1277 :
1278 : /*
1279 : * Clean up. We keep lock on new relation (although it shouldn't be
1280 : * visible to anyone else anyway, until commit).
7849 tgl 1281 ECB : */
7528 tgl 1282 GIC 62424 : relation_close(rel, NoLock);
7849 tgl 1283 ECB :
2959 alvherre 1284 GIC 62424 : return address;
1285 : }
1286 :
1287 : /*
1288 : * Emit the right error or warning message for a "DROP" command issued on a
1289 : * non-existent relation
1290 : */
5412 tgl 1291 ECB : static void
3363 alvherre 1292 CBC 516 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1293 : {
1294 : const struct dropmsgstrings *rentry;
1295 :
3363 alvherre 1296 GIC 573 : if (rel->schemaname != NULL &&
1297 57 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1298 : {
1299 21 : if (!missing_ok)
1300 : {
3363 alvherre 1301 LBC 0 : ereport(ERROR,
1302 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
2118 tgl 1303 ECB : errmsg("schema \"%s\" does not exist", rel->schemaname)));
3363 alvherre 1304 : }
1305 : else
1306 : {
3363 alvherre 1307 GIC 21 : ereport(NOTICE,
1308 : (errmsg("schema \"%s\" does not exist, skipping",
1309 : rel->schemaname)));
1310 : }
1311 21 : return;
3363 alvherre 1312 ECB : }
1313 :
5412 tgl 1314 CBC 643 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1315 : {
5412 tgl 1316 GIC 643 : if (rentry->kind == rightkind)
1317 : {
1318 495 : if (!missing_ok)
1319 : {
1320 65 : ereport(ERROR,
1321 : (errcode(rentry->nonexistent_code),
3363 alvherre 1322 ECB : errmsg(rentry->nonexistent_msg, rel->relname)));
1323 : }
1324 : else
1325 : {
3363 alvherre 1326 CBC 430 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
5412 tgl 1327 430 : break;
1328 : }
5412 tgl 1329 ECB : }
1330 : }
5412 tgl 1331 EUB :
2118 tgl 1332 GIC 430 : Assert(rentry->kind != '\0'); /* Should be impossible */
1333 : }
1334 :
1335 : /*
1336 : * Emit the right error message for a "DROP" command issued on a
5412 tgl 1337 ECB : * relation of the wrong type
1338 : */
1339 : static void
5412 tgl 1340 UIC 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
5412 tgl 1341 ECB : {
1342 : const struct dropmsgstrings *rentry;
1343 : const struct dropmsgstrings *wentry;
1344 :
5412 tgl 1345 UIC 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
5412 tgl 1346 LBC 0 : if (rentry->kind == rightkind)
5412 tgl 1347 UIC 0 : break;
5412 tgl 1348 LBC 0 : Assert(rentry->kind != '\0');
1349 :
1350 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
5412 tgl 1351 UIC 0 : if (wentry->kind == wrongkind)
1352 0 : break;
1353 : /* wrongkind could be something we don't have in our table... */
1354 :
1355 0 : ereport(ERROR,
5412 tgl 1356 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1357 : errmsg(rentry->nota_msg, relname),
1358 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1359 : }
1360 :
1361 : /*
1362 : * RemoveRelations
1363 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1364 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1365 : */
1366 : void
5412 tgl 1367 GIC 7479 : RemoveRelations(DropStmt *drop)
1368 : {
1369 : ObjectAddresses *objects;
5412 tgl 1370 EUB : char relkind;
1371 : ListCell *cell;
4020 simon 1372 GIC 7479 : int flags = 0;
1373 7479 : LOCKMODE lockmode = AccessExclusiveLock;
1374 :
3784 tgl 1375 EUB : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
4020 simon 1376 GBC 7479 : if (drop->concurrent)
4020 simon 1377 EUB : {
1173 michael 1378 : /*
1379 : * Note that for temporary relations this lock may get upgraded later
1060 tgl 1380 : * on, but as no other session can access a temporary relation, this
1381 : * is actually fine.
1173 michael 1382 : */
4020 simon 1383 GIC 70 : lockmode = ShareUpdateExclusiveLock;
3784 tgl 1384 70 : Assert(drop->removeType == OBJECT_INDEX);
3784 tgl 1385 GBC 70 : if (list_length(drop->objects) != 1)
4020 simon 1386 GIC 3 : ereport(ERROR,
1387 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1388 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1389 67 : if (drop->behavior == DROP_CASCADE)
4020 simon 1390 UIC 0 : ereport(ERROR,
1391 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1392 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1393 : }
1394 :
1395 : /*
1396 : * First we identify all the relations, then we delete them in a single
5050 bruce 1397 ECB : * performMultipleDeletions() call. This is to avoid unwanted DROP
1398 : * RESTRICT errors if one of the relations depends on another.
1399 : */
1400 :
1401 : /* Determine required relkind */
5412 tgl 1402 CBC 7476 : switch (drop->removeType)
5412 tgl 1403 ECB : {
5412 tgl 1404 GIC 6494 : case OBJECT_TABLE:
1405 6494 : relkind = RELKIND_RELATION;
5412 tgl 1406 CBC 6494 : break;
1407 :
5412 tgl 1408 GIC 363 : case OBJECT_INDEX:
1409 363 : relkind = RELKIND_INDEX;
1410 363 : break;
1411 :
1412 86 : case OBJECT_SEQUENCE:
5412 tgl 1413 CBC 86 : relkind = RELKIND_SEQUENCE;
1414 86 : break;
5412 tgl 1415 ECB :
5412 tgl 1416 CBC 402 : case OBJECT_VIEW:
5412 tgl 1417 GIC 402 : relkind = RELKIND_VIEW;
1418 402 : break;
5412 tgl 1419 ECB :
3689 kgrittn 1420 GBC 57 : case OBJECT_MATVIEW:
3689 kgrittn 1421 GIC 57 : relkind = RELKIND_MATVIEW;
1422 57 : break;
1423 :
4481 rhaas 1424 74 : case OBJECT_FOREIGN_TABLE:
1425 74 : relkind = RELKIND_FOREIGN_TABLE;
1426 74 : break;
1427 :
5412 tgl 1428 UIC 0 : default:
1429 0 : elog(ERROR, "unrecognized drop object type: %d",
1430 : (int) drop->removeType);
1431 : relkind = 0; /* keep compiler quiet */
5412 tgl 1432 ECB : break;
1433 : }
1434 :
1435 : /* Lock and validate each relation; build a list of object addresses */
5412 tgl 1436 CBC 7476 : objects = new_object_addresses();
1437 :
1438 16602 : foreach(cell, drop->objects)
5412 tgl 1439 ECB : {
5412 tgl 1440 CBC 9204 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1441 : Oid relOid;
5412 tgl 1442 ECB : ObjectAddress obj;
3955 bruce 1443 : struct DropRelationCallbackState state;
5412 tgl 1444 :
1445 : /*
1446 : * These next few steps are a great deal like relation_openrv, but we
1447 : * don't bother building a relcache entry since we don't need it.
1448 : *
1449 : * Check for shared-cache-inval messages before trying to access the
1450 : * relation. This is needed to cover the case where the name
1451 : * identifies a rel that has been dropped and recreated since the
1452 : * start of our transaction: if we don't flush the old syscache entry,
1453 : * then we'll latch onto that entry and suffer an error later.
1454 : */
5412 tgl 1455 CBC 9204 : AcceptInvalidationMessages();
5412 tgl 1456 ECB :
1457 : /* Look up the appropriate relation using namespace search. */
384 tgl 1458 GBC 9204 : state.expected_relkind = relkind;
1459 18408 : state.heap_lockmode = drop->concurrent ?
384 tgl 1460 GIC 9204 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1461 : /* We must initialize these fields to show that no locks are held: */
4148 rhaas 1462 9204 : state.heapOid = InvalidOid;
2189 1463 9204 : state.partParentOid = InvalidOid;
1464 :
1836 andres 1465 9204 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
4148 rhaas 1466 ECB : RangeVarCallbackForDropRelation,
1467 : (void *) &state);
5412 tgl 1468 :
1469 : /* Not there? */
5412 tgl 1470 CBC 9194 : if (!OidIsValid(relOid))
1471 : {
3363 alvherre 1472 GIC 516 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
5412 tgl 1473 451 : continue;
1474 : }
1475 :
1476 : /*
1477 : * Decide if concurrent mode needs to be used here or not. The
1478 : * callback retrieved the rel's persistence for us.
1479 : */
1173 michael 1480 8678 : if (drop->concurrent &&
384 tgl 1481 64 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1482 : {
1173 michael 1483 55 : Assert(list_length(drop->objects) == 1 &&
1484 : drop->removeType == OBJECT_INDEX);
1173 michael 1485 CBC 55 : flags |= PERFORM_DELETION_CONCURRENTLY;
1486 : }
1487 :
950 alvherre 1488 ECB : /*
1489 : * Concurrent index drop cannot be used with partitioned indexes,
1490 : * either.
1491 : */
950 alvherre 1492 CBC 8678 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
384 tgl 1493 55 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
950 alvherre 1494 GIC 3 : ereport(ERROR,
950 alvherre 1495 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1496 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1497 : rel->relname)));
1498 :
1499 : /*
384 tgl 1500 : * If we're told to drop a partitioned index, we must acquire lock on
1501 : * all the children of its parent partitioned table before proceeding.
1502 : * Otherwise we'd try to lock the child index partitions before their
1503 : * tables, leading to potential deadlock against other sessions that
1504 : * will lock those objects in the other order.
1505 : */
384 tgl 1506 GIC 8675 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1507 26 : (void) find_all_inheritors(state.heapOid,
1508 : state.heap_lockmode,
1509 : NULL);
384 tgl 1510 ECB :
5412 1511 : /* OK, we're ready to delete this one */
5412 tgl 1512 GIC 8675 : obj.classId = RelationRelationId;
5412 tgl 1513 CBC 8675 : obj.objectId = relOid;
5412 tgl 1514 GIC 8675 : obj.objectSubId = 0;
5412 tgl 1515 ECB :
5412 tgl 1516 GIC 8675 : add_exact_object_address(&obj, objects);
1517 : }
1518 :
4020 simon 1519 7398 : performMultipleDeletions(objects, drop->behavior, flags);
1520 :
5412 tgl 1521 7330 : free_object_addresses(objects);
8484 peter_e 1522 CBC 7330 : }
8484 peter_e 1523 ECB :
4148 rhaas 1524 : /*
1525 : * Before acquiring a table lock, check whether we have sufficient rights.
1526 : * In the case of DROP INDEX, also try to lock the table before the index.
1527 : * Also, if the table to be dropped is a partition, we try to lock the parent
1528 : * first.
1529 : */
1530 : static void
4148 rhaas 1531 GIC 9317 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1532 : void *arg)
1533 : {
1534 : HeapTuple tuple;
1535 : struct DropRelationCallbackState *state;
2314 rhaas 1536 ECB : char expected_relkind;
2189 1537 : bool is_partition;
1538 : Form_pg_class classform;
1539 : LOCKMODE heap_lockmode;
1472 peter 1540 GIC 9317 : bool invalid_system_index = false;
1541 :
4148 rhaas 1542 CBC 9317 : state = (struct DropRelationCallbackState *) arg;
384 tgl 1543 9317 : heap_lockmode = state->heap_lockmode;
4148 rhaas 1544 ECB :
1545 : /*
1546 : * If we previously locked some other index's heap, and the name we're
1547 : * looking up no longer refers to that relation, release the now-useless
1548 : * lock.
1549 : */
4148 rhaas 1550 GIC 9317 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
4148 rhaas 1551 ECB : {
4020 simon 1552 LBC 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
4148 rhaas 1553 UIC 0 : state->heapOid = InvalidOid;
1554 : }
1555 :
1556 : /*
1557 : * Similarly, if we previously locked some other partition's heap, and the
1558 : * name we're looking up no longer refers to that relation, release the
1559 : * now-useless lock.
1560 : */
2189 rhaas 1561 CBC 9317 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1562 : {
2189 rhaas 1563 UIC 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1564 0 : state->partParentOid = InvalidOid;
1565 : }
1566 :
1567 : /* Didn't find a relation, so no need for locking or permission checks. */
4148 rhaas 1568 GIC 9317 : if (!OidIsValid(relOid))
1569 521 : return;
4148 rhaas 1570 ECB :
4148 rhaas 1571 GIC 8796 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
4148 rhaas 1572 CBC 8796 : if (!HeapTupleIsValid(tuple))
4148 rhaas 1573 LBC 0 : return; /* concurrently dropped, so nothing to do */
4148 rhaas 1574 GIC 8796 : classform = (Form_pg_class) GETSTRUCT(tuple);
2189 1575 8796 : is_partition = classform->relispartition;
1576 :
1577 : /* Pass back some data to save lookups in RemoveRelations */
384 tgl 1578 8796 : state->actual_relkind = classform->relkind;
1579 8796 : state->actual_relpersistence = classform->relpersistence;
384 tgl 1580 ECB :
1581 : /*
2314 rhaas 1582 EUB : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1583 : * but RemoveRelations() can only pass one relkind for a given relation.
1584 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1585 : * That means we must be careful before giving the wrong type error when
1586 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1587 : * exists with indexes.
1588 : */
2314 rhaas 1589 GIC 8796 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1590 1262 : expected_relkind = RELKIND_RELATION;
1906 alvherre 1591 CBC 7534 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1906 alvherre 1592 GIC 31 : expected_relkind = RELKIND_INDEX;
2314 rhaas 1593 EUB : else
2314 rhaas 1594 GBC 7503 : expected_relkind = classform->relkind;
1595 :
384 tgl 1596 GIC 8796 : if (state->expected_relkind != expected_relkind)
384 tgl 1597 UIC 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
384 tgl 1598 LBC 0 : state->expected_relkind);
4148 rhaas 1599 ECB :
1600 : /* Allow DROP to either table owner or schema owner */
147 peter 1601 GNC 8796 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1602 9 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
384 tgl 1603 GBC 9 : aclcheck_error(ACLCHECK_NOT_OWNER,
384 tgl 1604 CBC 9 : get_relkind_objtype(classform->relkind),
4148 rhaas 1605 9 : rel->relname);
1606 :
1607 : /*
1472 peter 1608 ECB : * Check the case of a system index that might have been invalidated by a
1609 : * failed concurrent process and allow its drop. For the time being, this
1610 : * only concerns indexes of toast relations that became invalid during a
1611 : * REINDEX CONCURRENTLY process.
1612 : */
384 tgl 1613 GIC 8787 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1614 : {
1615 : HeapTuple locTuple;
1616 : Form_pg_index indexform;
1617 : bool indisvalid;
1618 :
1472 peter 1619 LBC 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1620 0 : if (!HeapTupleIsValid(locTuple))
1472 peter 1621 ECB : {
1472 peter 1622 LBC 0 : ReleaseSysCache(tuple);
1472 peter 1623 UIC 0 : return;
1472 peter 1624 ECB : }
1625 :
1472 peter 1626 LBC 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1472 peter 1627 UBC 0 : indisvalid = indexform->indisvalid;
1628 0 : ReleaseSysCache(locTuple);
1629 :
1630 : /* Mark object as being an invalid index of system catalogs */
1472 peter 1631 LBC 0 : if (!indisvalid)
1632 0 : invalid_system_index = true;
1472 peter 1633 ECB : }
1634 :
1635 : /* In the case of an invalid index, it is fine to bypass this check */
1472 peter 1636 GIC 8787 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
4148 rhaas 1637 1 : ereport(ERROR,
1638 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1639 : errmsg("permission denied: \"%s\" is a system catalog",
1640 : rel->relname)));
1641 :
1642 8786 : ReleaseSysCache(tuple);
4148 rhaas 1643 ECB :
1644 : /*
1645 : * In DROP INDEX, attempt to acquire lock on the parent table before
1646 : * locking the index. index_drop() will need this anyway, and since
1647 : * regular queries lock tables before their indexes, we risk deadlock if
1648 : * we do it the other way around. No error if we don't find a pg_index
384 tgl 1649 EUB : * entry, though --- the relation may have been dropped. Note that this
1650 : * code will execute for either plain or partitioned indexes.
1651 : */
384 tgl 1652 GBC 8786 : if (expected_relkind == RELKIND_INDEX &&
1906 alvherre 1653 EUB : relOid != oldRelOid)
1654 : {
4148 rhaas 1655 GIC 354 : state->heapOid = IndexGetRelation(relOid, true);
4148 rhaas 1656 GBC 354 : if (OidIsValid(state->heapOid))
4020 simon 1657 354 : LockRelationOid(state->heapOid, heap_lockmode);
4148 rhaas 1658 EUB : }
1659 :
1660 : /*
2189 1661 : * Similarly, if the relation is a partition, we must acquire lock on its
1662 : * parent before locking the partition. That's because queries lock the
1663 : * parent before its partitions, so we risk deadlock if we do it the other
1664 : * way around.
1665 : */
2189 rhaas 1666 CBC 8786 : if (is_partition && relOid != oldRelOid)
2189 rhaas 1667 ECB : {
745 alvherre 1668 GIC 293 : state->partParentOid = get_partition_parent(relOid, true);
2189 rhaas 1669 293 : if (OidIsValid(state->partParentOid))
1670 293 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1671 : }
4148 rhaas 1672 ECB : }
1673 :
1674 : /*
1675 : * ExecuteTruncate
1676 : * Executes a TRUNCATE command.
1677 : *
1678 : * This is a multi-relation truncate. We first open and grab exclusive
1679 : * lock on all relations involved, checking permissions and otherwise
1680 : * verifying that the relation is OK for truncation. Note that if relations
1681 : * are foreign tables, at this stage, we have not yet checked that their
731 fujii 1682 : * foreign data in external data sources are OK for truncation. These are
1683 : * checked when foreign data are actually truncated later. In CASCADE mode,
1684 : * relations having FK references to the targeted relations are automatically
6246 tgl 1685 : * added to the group; in RESTRICT mode, we check that all FK references are
1686 : * internal to the group that's being truncated. Finally all the relations
1687 : * are truncated and reindexed.
1688 : */
1689 : void
6246 tgl 1690 GIC 641 : ExecuteTruncate(TruncateStmt *stmt)
1691 : {
6385 bruce 1692 641 : List *rels = NIL;
6246 tgl 1693 641 : List *relids = NIL;
1828 peter_e 1694 641 : List *relids_logged = NIL;
1695 : ListCell *cell;
7678 tgl 1696 ECB :
1697 : /*
6246 1698 : * Open, exclusive-lock, and check all the explicitly-specified relations
1699 : */
6246 tgl 1700 CBC 1359 : foreach(cell, stmt->relations)
1701 : {
6646 tgl 1702 GIC 742 : RangeVar *rv = lfirst(cell);
1703 : Relation rel;
2298 1704 742 : bool recurse = rv->inh;
1705 : Oid myrelid;
1564 michael 1706 742 : LOCKMODE lockmode = AccessExclusiveLock;
1707 :
1708 742 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1709 : 0, RangeVarCallbackForTruncate,
1710 : NULL);
1711 :
1712 : /* don't throw error for "TRUNCATE foo, foo" */
5200 peter_e 1713 724 : if (list_member_oid(relids, myrelid))
5380 tgl 1714 1 : continue;
1715 :
1716 : /* open the relation, we already hold a lock on it */
727 fujii 1717 723 : rel = table_open(myrelid, NoLock);
1718 :
1719 : /*
1703 michael 1720 ECB : * RangeVarGetRelidExtended() has done most checks with its callback,
1721 : * but other checks with the now-opened Relation remain.
1722 : */
1703 michael 1723 CBC 723 : truncate_check_activity(rel);
1703 michael 1724 ECB :
6246 tgl 1725 GIC 723 : rels = lappend(rels, rel);
5200 peter_e 1726 723 : relids = lappend_oid(relids, myrelid);
1727 :
1728 : /* Log this relation only if needed for logical decoding */
1828 1729 723 : if (RelationIsLogicallyLogged(rel))
1828 peter_e 1730 CBC 37 : relids_logged = lappend_oid(relids_logged, myrelid);
1731 :
5200 1732 723 : if (recurse)
1733 : {
5200 peter_e 1734 ECB : ListCell *child;
1735 : List *children;
1736 :
1564 michael 1737 GIC 692 : children = find_all_inheritors(myrelid, lockmode, NULL);
5200 peter_e 1738 ECB :
5200 peter_e 1739 GIC 2202 : foreach(child, children)
1740 : {
1741 1510 : Oid childrelid = lfirst_oid(child);
1742 :
5200 peter_e 1743 CBC 1510 : if (list_member_oid(relids, childrelid))
1744 692 : continue;
1745 :
1746 : /* find_all_inheritors already got lock */
1539 andres 1747 818 : rel = table_open(childrelid, NoLock);
1748 :
1749 : /*
1750 : * It is possible that the parent table has children that are
1751 : * temp tables of other backends. We cannot safely access
1752 : * such tables (because of buffering issues), and the best
1564 michael 1753 ECB : * thing to do is to silently ignore them. Note that this
1754 : * check is the same as one of the checks done in
1755 : * truncate_check_activity() called below, still it is kept
1756 : * here for simplicity.
1757 : */
1564 michael 1758 GIC 818 : if (RELATION_IS_OTHER_TEMP(rel))
1564 michael 1759 ECB : {
1539 andres 1760 CBC 4 : table_close(rel, lockmode);
1564 michael 1761 GIC 4 : continue;
1564 michael 1762 ECB : }
1763 :
1764 : /*
1765 : * Inherited TRUNCATE commands perform access permission
1766 : * checks on the parent table only. So we skip checking the
1060 tgl 1767 : * children's permissions and don't call
1768 : * truncate_check_perms() here.
1164 fujii 1769 : */
1703 michael 1770 GIC 814 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1703 michael 1771 CBC 814 : truncate_check_activity(rel);
1772 :
5200 peter_e 1773 814 : rels = lappend(rels, rel);
1774 814 : relids = lappend_oid(relids, childrelid);
1775 :
1776 : /* Log this relation only if needed for logical decoding */
1828 1777 814 : if (RelationIsLogicallyLogged(rel))
1828 peter_e 1778 GIC 16 : relids_logged = lappend_oid(relids_logged, childrelid);
1779 : }
1780 : }
2314 rhaas 1781 31 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1782 6 : ereport(ERROR,
1783 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1784 : errmsg("cannot truncate only a partitioned table"),
1785 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1786 : }
1787 :
712 fujii 1788 CBC 617 : ExecuteTruncateGuts(rels, relids, relids_logged,
5 rhaas 1789 GNC 617 : stmt->behavior, stmt->restart_seqs, false);
1828 peter_e 1790 ECB :
1791 : /* And close the rels */
1828 peter_e 1792 GIC 2030 : foreach(cell, rels)
1793 : {
1794 1454 : Relation rel = (Relation) lfirst(cell);
1795 :
1539 andres 1796 1454 : table_close(rel, NoLock);
1797 : }
1828 peter_e 1798 576 : }
1799 :
1828 peter_e 1800 ECB : /*
1801 : * ExecuteTruncateGuts
1802 : *
1803 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1804 : * command (see above) as well as replication subscribers that execute a
1805 : * replicated TRUNCATE action.
1806 : *
1807 : * explicit_rels is the list of Relations to truncate that the command
1808 : * specified. relids is the list of Oids corresponding to explicit_rels.
1809 : * relids_logged is the list of Oids (a subset of relids) that require
1810 : * WAL-logging. This is all a bit redundant, but the existing callers have
712 fujii 1811 : * this information handy in this form.
1828 peter_e 1812 : */
1813 : void
731 fujii 1814 GIC 634 : ExecuteTruncateGuts(List *explicit_rels,
1815 : List *relids,
1816 : List *relids_logged,
1817 : DropBehavior behavior, bool restart_seqs,
1818 : bool run_as_table_owner)
1828 peter_e 1819 ECB : {
1820 : List *rels;
1828 peter_e 1821 GIC 634 : List *seq_relids = NIL;
731 fujii 1822 634 : HTAB *ft_htab = NULL;
1828 peter_e 1823 ECB : EState *estate;
1824 : ResultRelInfo *resultRelInfos;
1825 : ResultRelInfo *resultRelInfo;
1826 : SubTransactionId mySubid;
1827 : ListCell *cell;
1828 : Oid *logrelids;
1829 :
1830 : /*
1831 : * Check the explicitly-specified relations.
1832 : *
1833 : * In CASCADE mode, suck in all referencing relations as well. This
1834 : * requires multiple iterations to find indirectly-dependent relations. At
1835 : * each phase, we need to exclusive-lock new rels before looking for their
1836 : * dependencies, else we might miss something. Also, we check each rel as
1837 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1838 : * time on a rel we have no permissions for.
1839 : */
1828 peter_e 1840 GIC 634 : rels = list_copy(explicit_rels);
1841 634 : if (behavior == DROP_CASCADE)
1842 : {
1843 : for (;;)
6246 tgl 1844 20 : {
6031 bruce 1845 ECB : List *newrelids;
1846 :
6246 tgl 1847 GIC 40 : newrelids = heap_truncate_find_FKs(relids);
1848 40 : if (newrelids == NIL)
1849 20 : break; /* nothing else to add */
1850 :
1851 67 : foreach(cell, newrelids)
6246 tgl 1852 ECB : {
6031 bruce 1853 CBC 47 : Oid relid = lfirst_oid(cell);
1854 : Relation rel;
1855 :
1539 andres 1856 GIC 47 : rel = table_open(relid, AccessExclusiveLock);
6246 tgl 1857 47 : ereport(NOTICE,
1858 : (errmsg("truncate cascades to table \"%s\"",
1859 : RelationGetRelationName(rel))));
1703 michael 1860 47 : truncate_check_rel(relid, rel->rd_rel);
1164 fujii 1861 47 : truncate_check_perms(relid, rel->rd_rel);
1703 michael 1862 47 : truncate_check_activity(rel);
6246 tgl 1863 47 : rels = lappend(rels, rel);
1864 47 : relids = lappend_oid(relids, relid);
1865 :
1866 : /* Log this relation only if needed for logical decoding */
1828 peter_e 1867 47 : if (RelationIsLogicallyLogged(rel))
1828 peter_e 1868 UIC 0 : relids_logged = lappend_oid(relids_logged, relid);
1869 : }
1870 : }
6246 tgl 1871 ECB : }
1872 :
1873 : /*
1874 : * Check foreign key references. In CASCADE mode, this should be
6031 bruce 1875 : * unnecessary since we just pulled in all the references; but as a
1876 : * cross-check, do it anyway if in an Assert-enabled build.
1877 : */
6246 tgl 1878 : #ifdef USE_ASSERT_CHECKING
6646 tgl 1879 CBC 634 : heap_truncate_check_FKs(rels, false);
6246 tgl 1880 ECB : #else
1881 : if (behavior == DROP_RESTRICT)
1882 : heap_truncate_check_FKs(rels, false);
1883 : #endif
7405 1884 :
1885 : /*
1886 : * If we are asked to restart sequences, find all the sequences, lock them
4526 1887 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1888 : * We want to do this early since it's pointless to do all the truncation
1889 : * work only to fail on sequence permissions.
1890 : */
1828 peter_e 1891 CBC 597 : if (restart_seqs)
5441 tgl 1892 ECB : {
5441 tgl 1893 CBC 26 : foreach(cell, rels)
5441 tgl 1894 ECB : {
5441 tgl 1895 CBC 13 : Relation rel = (Relation) lfirst(cell);
1357 peter 1896 GIC 13 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
1897 : ListCell *seqcell;
5441 tgl 1898 ECB :
5441 tgl 1899 GBC 31 : foreach(seqcell, seqlist)
1900 : {
5050 bruce 1901 GIC 18 : Oid seq_relid = lfirst_oid(seqcell);
1902 : Relation seq_rel;
1903 :
4526 tgl 1904 18 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1905 :
1906 : /* This check must match AlterSequence! */
147 peter 1907 GNC 18 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
1954 peter_e 1908 UIC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
5441 tgl 1909 0 : RelationGetRelationName(seq_rel));
5441 tgl 1910 ECB :
5441 tgl 1911 GIC 18 : seq_relids = lappend_oid(seq_relids, seq_relid);
1912 :
1913 18 : relation_close(seq_rel, NoLock);
1914 : }
1915 : }
1916 : }
1917 :
1918 : /* Prepare to catch AFTER triggers. */
5490 1919 597 : AfterTriggerBeginQuery();
1920 :
1921 : /*
5050 bruce 1922 ECB : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1923 : * each relation. We don't need to call ExecOpenIndices, though.
908 heikki.linnakangas 1924 : *
1925 : * We put the ResultRelInfos in the es_opened_result_relations list, even
1926 : * though we don't have a range table and don't populate the
888 michael 1927 : * es_result_relations array. That's a bit bogus, but it's enough to make
1928 : * ExecGetTriggerResultRel() find them.
1929 : */
5490 tgl 1930 CBC 597 : estate = CreateExecutorState();
1931 : resultRelInfos = (ResultRelInfo *)
1932 597 : palloc(list_length(rels) * sizeof(ResultRelInfo));
5490 tgl 1933 GIC 597 : resultRelInfo = resultRelInfos;
1934 2135 : foreach(cell, rels)
5490 tgl 1935 ECB : {
5490 tgl 1936 GIC 1538 : Relation rel = (Relation) lfirst(cell);
1937 :
5490 tgl 1938 CBC 1538 : InitResultRelInfo(resultRelInfo,
5490 tgl 1939 EUB : rel,
5050 bruce 1940 : 0, /* dummy rangetable index */
1941 : NULL,
4863 rhaas 1942 ECB : 0);
908 heikki.linnakangas 1943 GIC 1538 : estate->es_opened_result_relations =
908 heikki.linnakangas 1944 CBC 1538 : lappend(estate->es_opened_result_relations, resultRelInfo);
5490 tgl 1945 GIC 1538 : resultRelInfo++;
1946 : }
1947 :
1948 : /*
1949 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
5050 bruce 1950 ECB : * truncating (this is because one of them might throw an error). Also, if
1951 : * we were to allow them to prevent statement execution, that would need
1952 : * to be handled here.
1953 : */
5490 tgl 1954 GIC 597 : resultRelInfo = resultRelInfos;
1955 2135 : foreach(cell, rels)
1956 : {
1957 : UserContext ucxt;
1958 :
5 rhaas 1959 GNC 1538 : if (run_as_table_owner)
1960 33 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
1961 : &ucxt);
5490 tgl 1962 GIC 1538 : ExecBSTruncateTriggers(estate, resultRelInfo);
5 rhaas 1963 GNC 1538 : if (run_as_table_owner)
1964 33 : RestoreUserContext(&ucxt);
5490 tgl 1965 GIC 1538 : resultRelInfo++;
1966 : }
1967 :
7405 tgl 1968 ECB : /*
1969 : * OK, truncate each table.
1970 : */
4977 tgl 1971 CBC 597 : mySubid = GetCurrentSubTransactionId();
4977 tgl 1972 ECB :
712 fujii 1973 GIC 2135 : foreach(cell, rels)
6910 tgl 1974 ECB : {
712 fujii 1975 GIC 1538 : Relation rel = (Relation) lfirst(cell);
6646 tgl 1976 ECB :
1977 : /* Skip partitioned tables as there is nothing to do */
2229 rhaas 1978 GIC 1538 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1979 322 : continue;
1980 :
731 fujii 1981 ECB : /*
1982 : * Build the lists of foreign tables belonging to each foreign server
1983 : * and pass each list to the foreign data wrapper's callback function,
1984 : * so that each server can truncate its all foreign tables in bulk.
1985 : * Each list is saved as a single entry in a hash table that uses the
1986 : * server OID as lookup key.
1987 : */
731 fujii 1988 GIC 1216 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1989 17 : {
1990 17 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
1991 : bool found;
731 fujii 1992 ECB : ForeignTruncateInfo *ft_info;
1993 :
1994 : /* First time through, initialize hashtable for foreign tables */
731 fujii 1995 GIC 17 : if (!ft_htab)
1996 : {
731 fujii 1997 ECB : HASHCTL hctl;
1998 :
731 fujii 1999 GIC 15 : memset(&hctl, 0, sizeof(HASHCTL));
731 fujii 2000 CBC 15 : hctl.keysize = sizeof(Oid);
2001 15 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2002 15 : hctl.hcxt = CurrentMemoryContext;
731 fujii 2003 ECB :
731 fujii 2004 GIC 15 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2005 : 32, /* start small and extend */
2006 : &hctl,
2007 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2008 : }
731 fujii 2009 ECB :
2010 : /* Find or create cached entry for the foreign table */
731 fujii 2011 CBC 17 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
731 fujii 2012 GIC 17 : if (!found)
731 fujii 2013 ECB : {
731 fujii 2014 GIC 15 : ft_info->serverid = serverid;
2015 15 : ft_info->rels = NIL;
731 fujii 2016 ECB : }
2017 :
2018 : /*
2019 : * Save the foreign table in the entry of the server that the
2020 : * foreign table belongs to.
2021 : */
731 fujii 2022 GIC 17 : ft_info->rels = lappend(ft_info->rels, rel);
2023 17 : continue;
2024 : }
2025 :
6646 tgl 2026 ECB : /*
4790 bruce 2027 : * Normally, we need a transaction-safe truncation here. However, if
2028 : * the table was either created in the current (sub)transaction or has
2029 : * a new relfilenumber in the current (sub)transaction, then we can
2030 : * just truncate it in-place, because a rollback would cause the whole
2031 : * table or the current physical file to be thrown away anyway.
2032 : */
4977 tgl 2033 CBC 1199 : if (rel->rd_createSubid == mySubid ||
277 rhaas 2034 GNC 1191 : rel->rd_newRelfilelocatorSubid == mySubid)
2035 : {
2036 : /* Immediate, non-rollbackable truncation is OK */
4977 tgl 2037 CBC 24 : heap_truncate_one_rel(rel);
6646 tgl 2038 ECB : }
4977 2039 : else
2040 : {
2041 : Oid heap_relid;
2042 : Oid toast_relid;
811 michael 2043 GIC 1175 : ReindexParams reindex_params = {0};
2044 :
2045 : /*
2046 : * This effectively deletes all rows in the table, and may be done
2047 : * in a serializable transaction. In that case we must record a
2048 : * rw-conflict in to this transaction from each transaction
4323 heikki.linnakangas 2049 ECB : * holding a predicate lock on the table.
2050 : */
4323 heikki.linnakangas 2051 GIC 1175 : CheckTableForSerializableConflictIn(rel);
4323 heikki.linnakangas 2052 ECB :
4977 tgl 2053 : /*
2054 : * Need the full transaction-safe pushups.
2055 : *
2056 : * Create a new empty storage file for the relation, and assign it
2057 : * as the relfilenumber value. The old storage file is scheduled
2058 : * for deletion at commit.
2059 : */
277 rhaas 2060 GNC 1175 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
6646 tgl 2061 ECB :
4977 tgl 2062 GIC 1175 : heap_relid = RelationGetRelid(rel);
2063 :
2064 : /*
2065 : * The same for the toast table, if any.
2066 : */
1584 2067 1175 : toast_relid = rel->rd_rel->reltoastrelid;
4977 2068 1175 : if (OidIsValid(toast_relid))
2069 : {
1584 2070 700 : Relation toastrel = relation_open(toast_relid,
1584 tgl 2071 ECB : AccessExclusiveLock);
1418 2072 :
277 rhaas 2073 GNC 700 : RelationSetNewRelfilenumber(toastrel,
2074 700 : toastrel->rd_rel->relpersistence);
1539 andres 2075 CBC 700 : table_close(toastrel, NoLock);
2076 : }
2077 :
2078 : /*
2079 : * Reconstruct the indexes to match, and we're done.
2080 : */
811 michael 2081 1175 : reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,
2082 : &reindex_params);
2083 : }
2084 :
2970 alvherre 2085 GIC 1199 : pgstat_count_truncate(rel);
2086 : }
2087 :
2088 : /* Now go through the hash table, and truncate foreign tables */
731 fujii 2089 CBC 597 : if (ft_htab)
2090 : {
2091 : ForeignTruncateInfo *ft_info;
2092 : HASH_SEQ_STATUS seq;
2093 :
731 fujii 2094 GIC 15 : hash_seq_init(&seq, ft_htab);
2095 :
2096 15 : PG_TRY();
2097 : {
731 fujii 2098 CBC 26 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2099 : {
2100 15 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2101 :
2102 : /* truncate_check_rel() has checked that already */
731 fujii 2103 GIC 15 : Assert(routine->ExecForeignTruncate != NULL);
2104 :
731 fujii 2105 CBC 15 : routine->ExecForeignTruncate(ft_info->rels,
731 fujii 2106 ECB : behavior,
2107 : restart_seqs);
2108 : }
2109 : }
731 fujii 2110 GIC 4 : PG_FINALLY();
731 fujii 2111 ECB : {
731 fujii 2112 CBC 15 : hash_destroy(ft_htab);
731 fujii 2113 ECB : }
731 fujii 2114 GIC 15 : PG_END_TRY();
2115 : }
2116 :
2117 : /*
2118 : * Restart owned sequences if we were asked to.
4526 tgl 2119 ECB : */
4526 tgl 2120 GIC 611 : foreach(cell, seq_relids)
2121 : {
2122 18 : Oid seq_relid = lfirst_oid(cell);
4526 tgl 2123 ECB :
4526 tgl 2124 GIC 18 : ResetSequence(seq_relid);
2125 : }
2126 :
1828 peter_e 2127 ECB : /*
2128 : * Write a WAL record to allow this set of actions to be logically
2129 : * decoded.
2130 : *
2131 : * Assemble an array of relids so we can write a single WAL record for the
2132 : * whole action.
2133 : */
235 tgl 2134 GNC 593 : if (relids_logged != NIL)
2135 : {
1828 peter_e 2136 ECB : xl_heap_truncate xlrec;
1828 peter_e 2137 GIC 47 : int i = 0;
1828 peter_e 2138 ECB :
2139 : /* should only get here if wal_level >= logical */
1828 peter_e 2140 GIC 47 : Assert(XLogLogicalInfoActive());
1828 peter_e 2141 ECB :
1828 peter_e 2142 GIC 47 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
1809 tgl 2143 CBC 133 : foreach(cell, relids_logged)
1828 peter_e 2144 GIC 86 : logrelids[i++] = lfirst_oid(cell);
2145 :
2146 47 : xlrec.dbId = MyDatabaseId;
2147 47 : xlrec.nrelids = list_length(relids_logged);
1828 peter_e 2148 CBC 47 : xlrec.flags = 0;
1828 peter_e 2149 GIC 47 : if (behavior == DROP_CASCADE)
1828 peter_e 2150 CBC 1 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
1828 peter_e 2151 GIC 47 : if (restart_seqs)
1828 peter_e 2152 CBC 3 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2153 :
1828 peter_e 2154 GIC 47 : XLogBeginInsert();
2155 47 : XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2156 47 : XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2157 :
1828 peter_e 2158 CBC 47 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2159 :
2160 47 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2161 : }
1828 peter_e 2162 ECB :
2163 : /*
2164 : * Process all AFTER STATEMENT TRUNCATE triggers.
2165 : */
5490 tgl 2166 GIC 593 : resultRelInfo = resultRelInfos;
2167 2127 : foreach(cell, rels)
2168 : {
2169 : UserContext ucxt;
2170 :
5 rhaas 2171 GNC 1534 : if (run_as_table_owner)
2172 33 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2173 : &ucxt);
5490 tgl 2174 GIC 1534 : ExecASTruncateTriggers(estate, resultRelInfo);
5 rhaas 2175 GNC 1534 : if (run_as_table_owner)
2176 33 : RestoreUserContext(&ucxt);
5490 tgl 2177 GIC 1534 : resultRelInfo++;
2178 : }
5490 tgl 2179 ECB :
2180 : /* Handle queued AFTER triggers */
5490 tgl 2181 GIC 593 : AfterTriggerEndQuery(estate);
5490 tgl 2182 ECB :
2183 : /* We can clean up the EState now */
5490 tgl 2184 GIC 593 : FreeExecutorState(estate);
5487 tgl 2185 ECB :
2186 : /*
1828 peter_e 2187 : * Close any rels opened by CASCADE (can't do this while EState still
2188 : * holds refs)
2189 : */
1828 peter_e 2190 GIC 593 : rels = list_difference_ptr(rels, explicit_rels);
5487 tgl 2191 CBC 640 : foreach(cell, rels)
5487 tgl 2192 ECB : {
5487 tgl 2193 CBC 47 : Relation rel = (Relation) lfirst(cell);
5487 tgl 2194 ECB :
1539 andres 2195 CBC 47 : table_close(rel, NoLock);
5487 tgl 2196 ECB : }
7653 tgl 2197 CBC 593 : }
2198 :
6246 tgl 2199 ECB : /*
1703 michael 2200 : * Check that a given relation is safe to truncate. Subroutine for
2201 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2202 : */
6246 tgl 2203 : static void
1703 michael 2204 GIC 1670 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
6246 tgl 2205 ECB : {
1703 michael 2206 GIC 1670 : char *relname = NameStr(reltuple->relname);
2207 :
2208 : /*
2209 : * Only allow truncate on regular tables, foreign tables using foreign
2210 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
731 fujii 2211 ECB : * latter are only being included here for the following checks; no
2212 : * physical truncation will occur in their case.).
2213 : */
731 fujii 2214 GIC 1670 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2215 : {
731 fujii 2216 CBC 18 : Oid serverid = GetForeignServerIdByRelId(relid);
2217 18 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2218 :
2219 18 : if (!fdwroutine->ExecForeignTruncate)
2220 1 : ereport(ERROR,
731 fujii 2221 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2222 : errmsg("cannot truncate foreign table \"%s\"",
2223 : relname)));
2224 : }
731 fujii 2225 GIC 1652 : else if (reltuple->relkind != RELKIND_RELATION &&
731 fujii 2226 CBC 330 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
6246 tgl 2227 UIC 0 : ereport(ERROR,
2228 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1703 michael 2229 ECB : errmsg("\"%s\" is not a table", relname)));
2230 :
2231 : /*
2232 : * Most system catalogs can't be truncated at all, or at least not unless
2233 : * allow_system_table_mods=on. As an exception, however, we allow
2234 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
255 rhaas 2235 : * to change its relfilenode to match the old cluster, and allowing a
2236 : * TRUNCATE command to be executed is the easiest way of doing that.
2237 : */
255 rhaas 2238 CBC 1669 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
255 rhaas 2239 GIC 7 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
6246 tgl 2240 CBC 1 : ereport(ERROR,
2241 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6246 tgl 2242 ECB : errmsg("permission denied: \"%s\" is a system catalog",
2243 : relname)));
2244 :
1233 mail 2245 GIC 1668 : InvokeObjectTruncateHook(relid);
1703 michael 2246 1668 : }
2247 :
2248 : /*
1164 fujii 2249 ECB : * Check that current user has the permission to truncate given relation.
2250 : */
2251 : static void
1164 fujii 2252 GIC 854 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2253 : {
2254 854 : char *relname = NameStr(reltuple->relname);
2255 : AclResult aclresult;
2256 :
2257 : /* Permissions checks */
2258 854 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
1164 fujii 2259 CBC 854 : if (aclresult != ACLCHECK_OK)
1164 fujii 2260 GIC 16 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
1164 fujii 2261 ECB : relname);
1164 fujii 2262 CBC 838 : }
2263 :
1703 michael 2264 ECB : /*
2265 : * Set of extra sanity checks to check if a given relation is safe to
2266 : * truncate. This is split with truncate_check_rel() as
2267 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2268 : */
2269 : static void
1703 michael 2270 CBC 1584 : truncate_check_activity(Relation rel)
1703 michael 2271 ECB : {
6246 tgl 2272 EUB : /*
2273 : * Don't allow truncate on temp tables of other backends ... their local
2274 : * buffer manager is not going to cope.
2275 : */
5122 tgl 2276 GIC 1584 : if (RELATION_IS_OTHER_TEMP(rel))
6246 tgl 2277 UIC 0 : ereport(ERROR,
2278 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2279 : errmsg("cannot truncate temporary tables of other sessions")));
2280 :
2281 : /*
2282 : * Also check for active uses of the relation in the current transaction,
5548 tgl 2283 ECB : * including open scans and pending AFTER trigger events.
5576 2284 : */
5548 tgl 2285 CBC 1584 : CheckTableNotInUse(rel, "TRUNCATE");
6246 tgl 2286 GIC 1584 : }
2287 :
2288 : /*
2289 : * storage_name
4790 bruce 2290 ECB : * returns the name corresponding to a typstorage/attstorage enum value
4927 andrew 2291 : */
2292 : static const char *
4927 andrew 2293 GIC 12 : storage_name(char c)
2294 : {
2295 12 : switch (c)
2296 : {
1131 tgl 2297 LBC 0 : case TYPSTORAGE_PLAIN:
4926 tgl 2298 UIC 0 : return "PLAIN";
1131 tgl 2299 LBC 0 : case TYPSTORAGE_EXTERNAL:
4926 tgl 2300 UIC 0 : return "EXTERNAL";
1131 tgl 2301 GIC 6 : case TYPSTORAGE_EXTENDED:
2302 6 : return "EXTENDED";
1131 tgl 2303 CBC 6 : case TYPSTORAGE_MAIN:
2304 6 : return "MAIN";
4926 tgl 2305 LBC 0 : default:
4926 tgl 2306 UIC 0 : return "???";
4927 andrew 2307 ECB : }
2308 : }
2309 :
2310 : /*----------
2311 : * MergeAttributes
2312 : * Returns new schema given initial schema and superclasses.
2313 : *
2314 : * Input arguments:
7653 tgl 2315 : * 'schema' is the column/attribute definition for the table. (It's a list
2316 : * of ColumnDef's.) It is destructively changed.
2317 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2318 : * 'relpersistence' is the persistence type of the table.
2319 : * 'is_partition' tells if the table is a partition.
2320 : *
2321 : * Output arguments:
7653 tgl 2322 EUB : * 'supconstr' receives a list of constraints belonging to the parents,
2323 : * updated as necessary to be valid for the child.
2324 : * 'nnconstraints' receives a list of CookedConstraints that corresponds to
2325 : * constraints coming from inheritance parents.
2326 : *
2327 : * Return value:
2328 : * Completed schema list.
2329 : *
2330 : * Notes:
2331 : * The order in which the attributes are inherited is very important.
7653 tgl 2332 ECB : * Intuitively, the inherited attributes should come first. If a table
2333 : * inherits from multiple parents, the order of those attributes are
2334 : * according to the order of the parents specified in CREATE TABLE.
2335 : *
2336 : * Here's an example:
2337 : *
2338 : * create table person (name text, age int4, location point);
2339 : * create table emp (salary int4, manager text) inherits(person);
2340 : * create table student (gpa float8) inherits (person);
2341 : * create table stud_emp (percent int4) inherits (emp, student);
2342 : *
2343 : * The order of the attributes of stud_emp is:
7653 tgl 2344 EUB : *
2345 : * person {1:name, 2:age, 3:location}
2346 : * / \
2347 : * {6:gpa} student emp {4:salary, 5:manager}
7653 tgl 2348 ECB : * \ /
2349 : * stud_emp {7:percent}
2350 : *
2351 : * If the same attribute name appears multiple times, then it appears
7653 tgl 2352 EUB : * in the result table in the proper location for its first appearance.
2353 : *
2354 : * Constraints (including NOT NULL constraints) for the child table
2355 : * are the union of all relevant constraints, from both the child schema
2356 : * and parent tables. In addition, in legacy inheritance, each column that
2357 : * appears in a primary key in any of the parents also gets a NOT NULL
2358 : * constraint (partitioning doesn't need this, because the PK itself gets
2359 : * inherited.)
2360 : *
2361 : * The default value for a child column is defined as:
2362 : * (1) If the child schema specifies a default, that value is used.
2363 : * (2) If neither the child nor any parent specifies a default, then
2364 : * the column will not have a default.
2365 : * (3) If conflicting defaults are inherited from different parents
2366 : * (and not overridden by the child), an error is raised.
2367 : * (4) Otherwise the inherited default is used.
2368 : *
2369 : * Note that the default-value infrastructure is used for generated
2370 : * columns' expressions too, so most of the preceding paragraph applies
2371 : * to generation expressions too. We insist that a child column be
2372 : * generated if and only if its parent(s) are, but it need not have
2373 : * the same generation expression.
2374 : *----------
2375 : */
2376 : static List *
4500 rhaas 2377 GIC 62853 : MergeAttributes(List *schema, List *supers, char relpersistence,
2378 : bool is_partition, List **supconstr, List **supnotnulls)
2379 : {
7653 tgl 2380 62853 : List *inhSchema = NIL;
2381 62853 : List *constraints = NIL;
2 alvherre 2382 GNC 62853 : List *nnconstraints = NIL;
7653 tgl 2383 GIC 62853 : bool have_bogus_defaults = false;
2384 : int child_attno;
2385 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2314 rhaas 2386 62853 : List *saved_schema = NIL;
2387 : ListCell *entry;
2388 :
2389 : /*
2390 : * Check for and reject tables with too many columns. We perform this
2391 : * check relatively early for two reasons: (a) we don't run the risk of
2392 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2393 : * okay if we're processing <= 1600 columns, but could take minutes to
2394 : * execute if the user attempts to create a table with hundreds of
2395 : * thousands of columns.
2396 : *
2397 : * Note that we also need to check that we do not exceed this figure after
2398 : * including columns from inherited relations.
2399 : */
6718 neilc 2400 62853 : if (list_length(schema) > MaxHeapAttributeNumber)
6718 neilc 2401 UIC 0 : ereport(ERROR,
2402 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2403 : errmsg("tables can have at most %d columns",
2404 : MaxHeapAttributeNumber)));
2405 :
2406 : /*
2407 : * Check for duplicate names in the explicit list of attributes.
2408 : *
2409 : * Although we might consider merging such entries in the same way that we
2410 : * handle name conflicts for inherited attributes, it seems to make more
2411 : * sense to assume such conflicts are errors.
2412 : *
2413 : * We don't use foreach() here because we have two nested loops over the
2414 : * schema list, with possible element deletions in the inner one. If we
2415 : * used foreach_delete_current() it could only fix up the state of one of
2416 : * the loops, so it seems cleaner to use looping over list indexes for
2417 : * both loops. Note that any deletion will happen beyond where the outer
2418 : * loop is, so its index never needs adjustment.
2419 : */
1364 tgl 2420 GIC 541245 : for (int coldefpos = 0; coldefpos < list_length(schema); coldefpos++)
2421 : {
2422 478404 : ColumnDef *coldef = list_nth_node(ColumnDef, schema, coldefpos);
2423 :
1613 alvherre 2424 478404 : if (!is_partition && coldef->typeName == NULL)
2425 : {
2426 : /*
2427 : * Typed table column option that does not belong to a column from
2428 : * the type. This works because the columns from the type come
2429 : * first in the list. (We omit this check for partition column
2430 : * lists; those are processed separately below.)
2431 : */
4819 peter_e 2432 CBC 3 : ereport(ERROR,
2433 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2434 : errmsg("column \"%s\" does not exist",
4819 peter_e 2435 ECB : coldef->colname)));
1613 alvherre 2436 : }
7678 tgl 2437 :
1364 2438 : /* restpos scans all entries beyond coldef; incr is in loop body */
1364 tgl 2439 GIC 6750509 : for (int restpos = coldefpos + 1; restpos < list_length(schema);)
2440 : {
1364 tgl 2441 CBC 6272117 : ColumnDef *restdef = list_nth_node(ColumnDef, schema, restpos);
2442 :
7653 tgl 2443 GIC 6272117 : if (strcmp(coldef->colname, restdef->colname) == 0)
2444 : {
4819 peter_e 2445 25 : if (coldef->is_from_type)
2446 : {
2447 : /*
2448 : * merge the column options into the column from the type
2449 : */
2450 16 : coldef->is_not_null = restdef->is_not_null;
2451 16 : coldef->raw_default = restdef->raw_default;
2452 16 : coldef->cooked_default = restdef->cooked_default;
2453 16 : coldef->constraints = restdef->constraints;
2454 16 : coldef->is_from_type = false;
1364 tgl 2455 CBC 16 : schema = list_delete_nth_cell(schema, restpos);
4819 peter_e 2456 EUB : }
2457 : else
4819 peter_e 2458 GIC 9 : ereport(ERROR,
2459 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2460 : errmsg("column \"%s\" specified more than once",
2461 : coldef->colname)));
2462 : }
2463 : else
1364 tgl 2464 6272092 : restpos++;
2465 : }
2466 : }
2467 :
2468 : /*
2469 : * In case of a partition, there are no new column definitions, only dummy
2470 : * ColumnDefs created for column constraints. Set them aside for now and
2471 : * process them at the end.
2472 : */
1613 alvherre 2473 62841 : if (is_partition)
2474 : {
1613 alvherre 2475 CBC 3602 : saved_schema = schema;
1613 alvherre 2476 GIC 3602 : schema = NIL;
1613 alvherre 2477 ECB : }
2478 :
7678 tgl 2479 : /*
2480 : * Scan the parents left-to-right, and merge their attributes to form a
2481 : * list of inherited attributes (inhSchema). Also check to see if we need
2482 : * to inherit an OID column.
2483 : */
7653 tgl 2484 GIC 62841 : child_attno = 0;
2485 67277 : foreach(entry, supers)
2486 : {
1574 alvherre 2487 CBC 4472 : Oid parent = lfirst_oid(entry);
2488 : Relation relation;
2489 : TupleDesc tupleDesc;
2490 : TupleConstr *constr;
2491 : AttrMap *newattmap;
2492 : List *inherited_defaults;
2493 : List *cols_with_defaults;
2494 : List *nnconstrs;
7653 tgl 2495 ECB : AttrNumber parent_attno;
2496 : ListCell *lc1;
961 2497 : ListCell *lc2;
2498 : Bitmapset *pkattrs;
2 alvherre 2499 GNC 4472 : Bitmapset *nncols = NULL;
2500 :
2501 :
1574 alvherre 2502 ECB : /* caller already got lock */
1539 andres 2503 GIC 4472 : relation = table_open(parent, NoLock);
2314 rhaas 2504 ECB :
2505 : /*
2506 : * Check for active uses of the parent partitioned table in the
2507 : * current transaction, such as being used in some manner by an
2508 : * enclosing command.
1616 michael 2509 : */
1616 michael 2510 CBC 4472 : if (is_partition)
2511 3602 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
1616 michael 2512 ECB :
2314 rhaas 2513 : /*
2308 2514 : * We do not allow partitioned tables and partitions to participate in
2515 : * regular inheritance.
2516 : */
2314 rhaas 2517 CBC 4469 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2314 rhaas 2518 GIC 3596 : !is_partition)
2519 3 : ereport(ERROR,
2520 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2521 : errmsg("cannot inherit from partitioned table \"%s\"",
2522 : RelationGetRelationName(relation))));
2314 rhaas 2523 CBC 4466 : if (relation->rd_rel->relispartition && !is_partition)
2314 rhaas 2524 GIC 3 : ereport(ERROR,
2525 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2526 : errmsg("cannot inherit from partition \"%s\"",
2527 : RelationGetRelationName(relation))));
2528 :
2940 tgl 2529 4463 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2314 rhaas 2530 3603 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2531 3593 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
7203 tgl 2532 LBC 0 : ereport(ERROR,
2533 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2940 tgl 2534 ECB : errmsg("inherited relation \"%s\" is not a table or foreign table",
1574 alvherre 2535 : RelationGetRelationName(relation))));
2536 :
2537 : /*
2538 : * If the parent is permanent, so must be all of its partitions. Note
2539 : * that inheritance allows that case.
2540 : */
1754 michael 2541 GIC 4463 : if (is_partition &&
2542 3599 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
1754 michael 2543 ECB : relpersistence == RELPERSISTENCE_TEMP)
1754 michael 2544 CBC 3 : ereport(ERROR,
2545 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1754 michael 2546 ECB : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2547 : RelationGetRelationName(relation))));
2548 :
2549 : /* Permanent rels cannot inherit from temporary ones */
3765 tgl 2550 GIC 4460 : if (relpersistence != RELPERSISTENCE_TEMP &&
2551 4289 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
7203 2552 12 : ereport(ERROR,
2553 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2554 : errmsg(!is_partition
2555 : ? "cannot inherit from temporary relation \"%s\""
2556 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2557 : RelationGetRelationName(relation))));
7678 tgl 2558 ECB :
2559 : /* If existing rel is temp, it must belong to this session */
3765 tgl 2560 GIC 4448 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2561 147 : !relation->rd_islocaltemp)
3765 tgl 2562 LBC 0 : ereport(ERROR,
2563 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2564 : errmsg(!is_partition
2565 : ? "cannot inherit from temporary relation of another session"
2566 : : "cannot create as partition of temporary relation of another session")));
2567 :
2568 : /*
7653 tgl 2569 ECB : * We should have an UNDER permission flag for this, but for now,
2570 : * demand that creator of a child table own the parent.
2571 : */
147 peter 2572 GNC 4448 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
1954 peter_e 2573 UIC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
7652 tgl 2574 0 : RelationGetRelationName(relation));
2575 :
7653 tgl 2576 CBC 4448 : tupleDesc = RelationGetDescr(relation);
2577 4448 : constr = tupleDesc->constr;
7678 tgl 2578 ECB :
2579 : /*
2580 : * newattmap->attnums[] will contain the child-table attribute numbers
2581 : * for the attributes of this parent table. (They are not the same
1208 michael 2582 : * for parents after the first one, nor if we have dropped columns.)
7653 tgl 2583 : */
1208 michael 2584 GIC 4448 : newattmap = make_attrmap(tupleDesc->natts);
2585 :
2586 : /* We can't process inherited defaults until newattmap is complete. */
961 tgl 2587 4448 : inherited_defaults = cols_with_defaults = NIL;
961 tgl 2588 ECB :
2589 : /*
2590 : * All columns that are part of the parent's primary key need to be
2591 : * NOT NULL; if partition just the attnotnull bit, otherwise a full
2592 : * constraint (if they don't have one already). Also, we request
2593 : * attnotnull on columns that have a NOT NULL constraint that's not
2594 : * marked NO INHERIT.
2595 : */
2 alvherre 2596 GNC 4448 : pkattrs = RelationGetIndexAttrBitmap(relation,
2597 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
2598 4448 : nnconstrs = RelationGetNotNullConstraints(relation, true);
2599 4749 : foreach(lc1, nnconstrs)
2600 301 : nncols = bms_add_member(nncols,
2601 301 : ((CookedConstraint *) lfirst(lc1))->attnum);
2602 :
7653 tgl 2603 CBC 13432 : for (parent_attno = 1; parent_attno <= tupleDesc->natts;
2604 8984 : parent_attno++)
7653 tgl 2605 EUB : {
2058 andres 2606 GIC 8996 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2607 : parent_attno - 1);
7653 tgl 2608 8996 : char *attributeName = NameStr(attribute->attname);
2609 : int exist_attno;
2610 : ColumnDef *def;
2611 :
2612 : /*
2613 : * Ignore dropped columns in the parent.
7555 tgl 2614 ECB : */
7555 tgl 2615 CBC 8996 : if (attribute->attisdropped)
1208 michael 2616 GIC 96 : continue; /* leave newattmap->attnums entry as zero */
7555 tgl 2617 ECB :
2618 : /*
2619 : * Does it conflict with some previously inherited column?
2620 : */
7653 tgl 2621 GIC 8900 : exist_attno = findAttrByName(attributeName, inhSchema);
2622 8900 : if (exist_attno > 0)
7653 tgl 2623 ECB : {
5624 bruce 2624 : Oid defTypeId;
2625 : int32 deftypmod;
2626 : Oid defCollId;
2627 :
2628 : /*
2629 : * Yes, try to merge the two column definitions.
2630 : */
7203 tgl 2631 GIC 112 : ereport(NOTICE,
7136 peter_e 2632 ECB : (errmsg("merging multiple inherited definitions of column \"%s\"",
7203 tgl 2633 : attributeName)));
6892 neilc 2634 GBC 112 : def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2635 :
2636 : /*
2637 : * Must have the same type and typmod
2638 : */
4414 tgl 2639 GIC 112 : typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
5944 2640 112 : if (defTypeId != attribute->atttypid ||
2641 112 : deftypmod != attribute->atttypmod)
7203 tgl 2642 UIC 0 : ereport(ERROR,
2643 : (errcode(ERRCODE_DATATYPE_MISMATCH),
2644 : errmsg("inherited column \"%s\" has a type conflict",
2645 : attributeName),
2646 : errdetail("%s versus %s",
2647 : format_type_with_typemod(defTypeId,
2661 tgl 2648 ECB : deftypmod),
2118 tgl 2649 EUB : format_type_with_typemod(attribute->atttypid,
2650 : attribute->atttypmod))));
2651 :
2652 : /*
2653 : * Must have the same collation
2654 : */
4414 tgl 2655 GIC 112 : defCollId = GetColumnDefCollation(NULL, def, defTypeId);
4443 peter_e 2656 CBC 112 : if (defCollId != attribute->attcollation)
4443 peter_e 2657 LBC 0 : ereport(ERROR,
2658 : (errcode(ERRCODE_COLLATION_MISMATCH),
2659 : errmsg("inherited column \"%s\" has a collation conflict",
2660 : attributeName),
2661 : errdetail("\"%s\" versus \"%s\"",
2662 : get_collation_name(defCollId),
2663 : get_collation_name(attribute->attcollation))));
4927 andrew 2664 ECB :
2665 : /*
2666 : * Copy/check storage parameter
2667 : */
4927 andrew 2668 GIC 112 : if (def->storage == 0)
4927 andrew 2669 LBC 0 : def->storage = attribute->attstorage;
4927 andrew 2670 GIC 112 : else if (def->storage != attribute->attstorage)
2671 3 : ereport(ERROR,
2672 : (errcode(ERRCODE_DATATYPE_MISMATCH),
2673 : errmsg("inherited column \"%s\" has a storage parameter conflict",
2674 : attributeName),
2675 : errdetail("%s versus %s",
2676 : storage_name(def->storage),
2677 : storage_name(attribute->attstorage))));
4927 andrew 2678 ECB :
2679 : /*
2680 : * Copy/check compression parameter
2681 : */
751 rhaas 2682 CBC 109 : if (CompressionMethodIsValid(attribute->attcompression))
751 rhaas 2683 ECB : {
2684 : const char *compression =
697 tgl 2685 CBC 3 : GetCompressionMethodName(attribute->attcompression);
2686 :
751 rhaas 2687 3 : if (def->compression == NULL)
751 rhaas 2688 LBC 0 : def->compression = pstrdup(compression);
751 rhaas 2689 GIC 3 : else if (strcmp(def->compression, compression) != 0)
751 rhaas 2690 CBC 3 : ereport(ERROR,
2691 : (errcode(ERRCODE_DATATYPE_MISMATCH),
751 rhaas 2692 ECB : errmsg("column \"%s\" has a compression method conflict",
2693 : attributeName),
2694 : errdetail("%s versus %s", def->compression, compression)));
2695 : }
2696 :
2697 : /*
2698 : * In regular inheritance, columns in the parent's primary key
2699 : * get an extra CHECK (NOT NULL) constraint. Partitioning
2700 : * doesn't need this, because the PK itself is going to be
2701 : * cloned to the partition.
2702 : */
2 alvherre 2703 GNC 212 : if (!is_partition &&
2704 106 : bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2705 : pkattrs))
2706 : {
2707 : CookedConstraint *nn;
2708 :
2 alvherre 2709 UNC 0 : nn = palloc(sizeof(CookedConstraint));
2710 0 : nn->contype = CONSTR_NOTNULL;
2711 0 : nn->conoid = InvalidOid;
2712 0 : nn->name = NULL;
2713 0 : nn->attnum = exist_attno;
2714 0 : nn->expr = NULL;
2715 0 : nn->skip_validation = false;
2716 0 : nn->is_local = false;
2717 0 : nn->inhcount = 1;
2718 0 : nn->is_no_inherit = false;
2719 :
2720 0 : nnconstraints = lappend(nnconstraints, nn);
2721 : }
2722 :
2723 : /*
2724 : * mark attnotnull if parent has it and it's not NO INHERIT
2725 : */
2 alvherre 2726 GNC 202 : if (bms_is_member(parent_attno, nncols) ||
2727 96 : bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2728 : pkattrs))
2729 10 : def->is_not_null = true;
2730 :
2731 : /*
2732 : * Check for GENERATED conflicts
2733 : */
1471 peter 2734 GIC 106 : if (def->generated != attribute->attgenerated)
1471 peter 2735 CBC 6 : ereport(ERROR,
1471 peter 2736 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
2737 : errmsg("inherited column \"%s\" has a generation conflict",
2738 : attributeName)));
2739 :
2740 : /*
2741 : * Default and other constraints are handled below
2742 : */
2743 :
32 peter 2744 GNC 100 : def->inhcount++;
12 2745 100 : if (def->inhcount < 0)
12 peter 2746 UNC 0 : ereport(ERROR,
2747 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2748 : errmsg("too many inheritance parents"));
2749 :
32 peter 2750 GNC 100 : newattmap->attnums[parent_attno - 1] = exist_attno;
2751 : }
2752 : else
2753 : {
2754 : /*
2755 : * No, create a new inherited column
2756 : */
7653 tgl 2757 CBC 8788 : def = makeNode(ColumnDef);
7653 tgl 2758 GIC 8788 : def->colname = pstrdup(attributeName);
5015 peter_e 2759 8788 : def->typeName = makeTypeNameFromOid(attribute->atttypid,
4414 tgl 2760 ECB : attribute->atttypmod);
7504 tgl 2761 GIC 8788 : def->inhcount = 1;
2762 8788 : def->is_local = false;
2763 : /* mark attnotnull if parent has it and it's not NO INHERIT */
2 alvherre 2764 GNC 17285 : if (bms_is_member(parent_attno, nncols) ||
2765 8497 : bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2766 : pkattrs))
2767 831 : def->is_not_null = true;
4414 tgl 2768 GIC 8788 : def->is_from_type = false;
4926 tgl 2769 CBC 8788 : def->storage = attribute->attstorage;
7653 2770 8788 : def->raw_default = NULL;
2771 8788 : def->cooked_default = NULL;
1471 peter 2772 GBC 8788 : def->generated = attribute->attgenerated;
4414 tgl 2773 GIC 8788 : def->collClause = NULL;
2774 8788 : def->collOid = attribute->attcollation;
7653 2775 8788 : def->constraints = NIL;
3426 2776 8788 : def->location = -1;
751 rhaas 2777 8788 : if (CompressionMethodIsValid(attribute->attcompression))
686 tgl 2778 9 : def->compression =
2779 9 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2780 : else
751 rhaas 2781 8779 : def->compression = NULL;
7653 tgl 2782 8788 : inhSchema = lappend(inhSchema, def);
1208 michael 2783 8788 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2784 :
2785 : /*
2786 : * In regular inheritance, columns in the parent's primary key
2787 : * get an extra NOT NULL constraint. Partitioning doesn't
2788 : * need this, because the PK itself is going to be cloned to
2789 : * the partition.
2790 : */
2 alvherre 2791 GNC 10386 : if (!is_partition &&
2792 1598 : bms_is_member(parent_attno -
2793 : FirstLowInvalidHeapAttributeNumber,
2794 : pkattrs))
2795 : {
2796 : CookedConstraint *nn;
2797 :
2798 95 : nn = palloc(sizeof(CookedConstraint));
2799 95 : nn->contype = CONSTR_NOTNULL;
2800 95 : nn->conoid = InvalidOid;
2801 95 : nn->name = NULL;
2802 95 : nn->attnum = newattmap->attnums[parent_attno - 1];
2803 95 : nn->expr = NULL;
2804 95 : nn->skip_validation = false;
2805 95 : nn->is_local = false;
2806 95 : nn->inhcount = 1;
2807 95 : nn->is_no_inherit = false;
2808 :
2809 95 : nnconstraints = lappend(nnconstraints, nn);
2810 : }
2811 : }
7678 tgl 2812 ECB :
7653 2813 : /*
2814 : * Locate default/generation expression if any
2815 : */
7653 tgl 2816 GIC 8888 : if (attribute->atthasdef)
2817 : {
4933 2818 260 : Node *this_default = NULL;
2819 :
2820 : /* Find default in constraint structure */
733 2821 260 : if (constr != NULL)
2822 : {
2823 260 : AttrDefault *attrdef = constr->defval;
2824 :
733 tgl 2825 CBC 284 : for (int i = 0; i < constr->num_defval; i++)
7653 tgl 2826 EUB : {
733 tgl 2827 CBC 284 : if (attrdef[i].adnum == parent_attno)
733 tgl 2828 ECB : {
733 tgl 2829 GIC 260 : this_default = stringToNode(attrdef[i].adbin);
2830 260 : break;
2831 : }
2832 : }
2833 : }
2834 260 : if (this_default == NULL)
733 tgl 2835 UIC 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2836 : parent_attno, RelationGetRelationName(relation));
2837 :
2838 : /*
961 tgl 2839 ECB : * If it's a GENERATED default, it might contain Vars that
2840 : * need to be mapped to the inherited column(s)' new numbers.
2841 : * We can't do that till newattmap is ready, so just remember
2842 : * all the inherited default expressions for the moment.
2843 : */
961 tgl 2844 CBC 260 : inherited_defaults = lappend(inherited_defaults, this_default);
961 tgl 2845 GBC 260 : cols_with_defaults = lappend(cols_with_defaults, def);
961 tgl 2846 ECB : }
2847 : }
2848 :
2849 : /*
2850 : * Now process any inherited default expressions, adjusting attnos
2851 : * using the completed newattmap map.
2852 : */
961 tgl 2853 GIC 4696 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2854 : {
2855 260 : Node *this_default = (Node *) lfirst(lc1);
2856 260 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2857 : bool found_whole_row;
2858 :
2859 : /* Adjust Vars to match new table's column numbering */
961 tgl 2860 CBC 260 : this_default = map_variable_attnos(this_default,
961 tgl 2861 ECB : 1, 0,
2862 : newattmap,
2863 : InvalidOid, &found_whole_row);
2864 :
2865 : /*
961 tgl 2866 EUB : * For the moment we have to reject whole-row variables. We could
2867 : * convert them, if we knew the new table's rowtype OID, but that
2868 : * hasn't been assigned yet. (A variable could only appear in a
2869 : * generation expression, so the error message is correct.)
2870 : */
961 tgl 2871 GBC 260 : if (found_whole_row)
961 tgl 2872 UBC 0 : ereport(ERROR,
961 tgl 2873 EUB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2874 : errmsg("cannot convert whole-row table reference"),
2875 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2876 : def->colname,
2877 : RelationGetRelationName(relation))));
2878 :
2879 : /*
2880 : * If we already had a default from some prior parent, check to
2881 : * see if they are the same. If so, no problem; if not, mark the
2882 : * column as having a bogus default. Below, we will complain if
961 tgl 2883 ECB : * the bogus default isn't overridden by the child schema.
2884 : */
961 tgl 2885 GIC 260 : Assert(def->raw_default == NULL);
961 tgl 2886 CBC 260 : if (def->cooked_default == NULL)
961 tgl 2887 GIC 245 : def->cooked_default = this_default;
2888 15 : else if (!equal(def->cooked_default, this_default))
2889 : {
2890 12 : def->cooked_default = &bogus_marker;
961 tgl 2891 CBC 12 : have_bogus_defaults = true;
7653 tgl 2892 ECB : }
2893 : }
2894 :
2895 : /*
2896 : * Now copy the CHECK constraints of this parent, adjusting attnos
2897 : * using the completed newattmap map. Identically named constraints
2898 : * are merged if possible, else we throw error.
2899 : */
7653 tgl 2900 GIC 4436 : if (constr && constr->num_check > 0)
7653 tgl 2901 ECB : {
7653 tgl 2902 CBC 98 : ConstrCheck *check = constr->check;
7653 tgl 2903 EUB : int i;
2904 :
7653 tgl 2905 GIC 211 : for (i = 0; i < constr->num_check; i++)
2906 : {
5448 tgl 2907 CBC 113 : char *name = check[i].ccname;
2908 : Node *expr;
2909 : bool found_whole_row;
2910 :
2911 : /* ignore if the constraint is non-inheritable */
4006 alvherre 2912 GIC 113 : if (check[i].ccnoinherit)
4143 2913 21 : continue;
4143 alvherre 2914 ECB :
3935 tgl 2915 : /* Adjust Vars to match new table's column numbering */
3935 tgl 2916 CBC 92 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2917 : 1, 0,
1208 michael 2918 ECB : newattmap,
2075 rhaas 2919 : InvalidOid, &found_whole_row);
2920 :
3935 tgl 2921 : /*
3602 bruce 2922 : * For the moment we have to reject whole-row variables. We
2923 : * could convert them, if we knew the new table's rowtype OID,
2924 : * but that hasn't been assigned yet.
3935 tgl 2925 : */
3935 tgl 2926 CBC 92 : if (found_whole_row)
3935 tgl 2927 LBC 0 : ereport(ERROR,
3935 tgl 2928 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2118 2929 : errmsg("cannot convert whole-row table reference"),
3935 2930 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2931 : name,
2932 : RelationGetRelationName(relation))));
5448 2933 :
2934 : /* check for duplicate */
5448 tgl 2935 CBC 92 : if (!MergeCheckConstraint(constraints, name, expr))
5448 tgl 2936 ECB : {
2937 : /* nope, this is a new one */
2938 : CookedConstraint *cooked;
2939 :
5448 tgl 2940 CBC 86 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
5448 tgl 2941 GIC 86 : cooked->contype = CONSTR_CHECK;
2118 2942 86 : cooked->conoid = InvalidOid; /* until created */
5448 2943 86 : cooked->name = pstrdup(name);
5050 bruce 2944 86 : cooked->attnum = 0; /* not used for constraints */
5448 tgl 2945 86 : cooked->expr = expr;
4330 alvherre 2946 86 : cooked->skip_validation = false;
5448 tgl 2947 86 : cooked->is_local = false;
5448 tgl 2948 CBC 86 : cooked->inhcount = 1;
4006 alvherre 2949 86 : cooked->is_no_inherit = false;
5448 tgl 2950 GIC 86 : constraints = lappend(constraints, cooked);
2951 : }
2952 : }
2953 : }
2954 :
2955 : /*
2956 : * Also copy the NOT NULL constraints from this parent. The
2957 : * attnotnull markings were already installed above.
2958 : */
2 alvherre 2959 GNC 4737 : foreach(lc1, nnconstrs)
2960 : {
2961 301 : CookedConstraint *nn = lfirst(lc1);
2962 :
2963 301 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2964 :
2965 301 : nnconstraints = lappend(nnconstraints, nn);
2966 : }
2967 :
1208 michael 2968 CBC 4436 : free_attrmap(newattmap);
8484 peter_e 2969 ECB :
7653 tgl 2970 : /*
2314 rhaas 2971 : * Close the parent rel, but keep our lock on it until xact commit.
2972 : * That will prevent someone else from deleting or ALTERing the parent
2973 : * before the child is committed.
7653 tgl 2974 : */
1539 andres 2975 CBC 4436 : table_close(relation, NoLock);
7653 tgl 2976 ECB : }
7828 2977 :
2978 : /*
7653 2979 : * If we had no inherited attributes, the result schema is just the
2980 : * explicitly declared columns. Otherwise, we need to merge the declared
2981 : * columns into the inherited schema list. Although, we never have any
2982 : * explicitly declared columns if the table is a partition.
2983 : */
7653 tgl 2984 GIC 62805 : if (inhSchema != NIL)
2985 : {
2878 bruce 2986 CBC 4318 : int schema_attno = 0;
2987 :
7653 tgl 2988 4698 : foreach(entry, schema)
2989 : {
7653 tgl 2990 GIC 404 : ColumnDef *newdef = lfirst(entry);
7653 tgl 2991 CBC 404 : char *attributeName = newdef->colname;
2992 : int exist_attno;
7828 tgl 2993 ECB :
3140 bruce 2994 GIC 404 : schema_attno++;
3140 bruce 2995 ECB :
2996 : /*
7653 tgl 2997 : * Does it conflict with some previously inherited column?
2998 : */
7653 tgl 2999 CBC 404 : exist_attno = findAttrByName(attributeName, inhSchema);
3000 404 : if (exist_attno > 0)
3001 : {
3002 : ColumnDef *def;
3003 : Oid defTypeId,
5624 bruce 3004 ECB : newTypeId;
5624 bruce 3005 EUB : int32 deftypmod,
3006 : newtypmod;
3007 : Oid defcollid,
3008 : newcollid;
3009 :
3010 : /*
3011 : * Partitions have only one parent and have no column
3012 : * definitions of their own, so conflict should never occur.
3013 : */
2314 rhaas 3014 CBC 125 : Assert(!is_partition);
2314 rhaas 3015 ECB :
3016 : /*
3017 : * Yes, try to merge the two column definitions.
3018 : */
2878 bruce 3019 GIC 125 : if (exist_attno == schema_attno)
3140 3020 114 : ereport(NOTICE,
3021 : (errmsg("merging column \"%s\" with inherited definition",
2118 tgl 3022 ECB : attributeName)));
3023 : else
3140 bruce 3024 CBC 11 : ereport(NOTICE,
2878 bruce 3025 ECB : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3026 : errdetail("User-specified column moved to the position of the inherited column.")));
6892 neilc 3027 GIC 125 : def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
3028 :
3029 : /*
3030 : * Must have the same type and typmod
3031 : */
4414 tgl 3032 125 : typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
4414 tgl 3033 CBC 125 : typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
5944 tgl 3034 GIC 125 : if (defTypeId != newTypeId || deftypmod != newtypmod)
7203 3035 6 : ereport(ERROR,
3036 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3037 : errmsg("column \"%s\" has a type conflict",
3038 : attributeName),
3039 : errdetail("%s versus %s",
3040 : format_type_with_typemod(defTypeId,
3041 : deftypmod),
3042 : format_type_with_typemod(newTypeId,
3043 : newtypmod))));
3044 :
3045 : /*
3046 : * Must have the same collation
3047 : */
4414 tgl 3048 CBC 119 : defcollid = GetColumnDefCollation(NULL, def, defTypeId);
4414 tgl 3049 GBC 119 : newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
4443 peter_e 3050 GIC 119 : if (defcollid != newcollid)
3051 3 : ereport(ERROR,
3052 : (errcode(ERRCODE_COLLATION_MISMATCH),
3053 : errmsg("column \"%s\" has a collation conflict",
3054 : attributeName),
3055 : errdetail("\"%s\" versus \"%s\"",
3056 : get_collation_name(defcollid),
3057 : get_collation_name(newcollid))));
3058 :
3059 : /*
3060 : * Identity is never inherited. The new column can have an
3061 : * identity definition, so we always just take that one.
2194 peter_e 3062 ECB : */
2194 peter_e 3063 CBC 116 : def->identity = newdef->identity;
2194 peter_e 3064 ECB :
3065 : /*
3066 : * Copy storage parameter
3067 : */
4927 andrew 3068 GIC 116 : if (def->storage == 0)
4927 andrew 3069 LBC 0 : def->storage = newdef->storage;
4927 andrew 3070 CBC 116 : else if (newdef->storage != 0 && def->storage != newdef->storage)
4927 andrew 3071 GIC 3 : ereport(ERROR,
3072 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3073 : errmsg("column \"%s\" has a storage parameter conflict",
3074 : attributeName),
3075 : errdetail("%s versus %s",
3076 : storage_name(def->storage),
3077 : storage_name(newdef->storage))));
3078 :
3079 : /*
3080 : * Copy compression parameter
3081 : */
751 rhaas 3082 113 : if (def->compression == NULL)
751 rhaas 3083 CBC 110 : def->compression = newdef->compression;
751 rhaas 3084 GIC 3 : else if (newdef->compression != NULL)
3085 : {
751 rhaas 3086 CBC 3 : if (strcmp(def->compression, newdef->compression) != 0)
751 rhaas 3087 GIC 3 : ereport(ERROR,
751 rhaas 3088 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
3089 : errmsg("column \"%s\" has a compression method conflict",
3090 : attributeName),
3091 : errdetail("%s versus %s", def->compression, newdef->compression)));
3092 : }
3093 :
3094 : /*
3095 : * Merge of NOT NULL constraints = OR 'em together
3096 : */
7653 tgl 3097 CBC 110 : def->is_not_null |= newdef->is_not_null;
3098 :
3099 : /*
3100 : * Check for conflicts related to generated columns.
3101 : *
3102 : * If the parent column is generated, the child column will be
3103 : * made a generated column if it isn't already. If it is a
3104 : * generated column, we'll take its generation expression in
3105 : * preference to the parent's. We must check that the child
3106 : * column doesn't specify a default value or identity, which
3107 : * matches the rules for a single column in parse_util.c.
3108 : *
3109 : * Conversely, if the parent column is not generated, the
3110 : * child column can't be either. (We used to allow that, but
3111 : * it results in being able to override the generation
3112 : * expression via UPDATEs through the parent.)
3113 : */
1068 peter 3114 GIC 110 : if (def->generated)
3115 : {
3116 13 : if (newdef->raw_default && !newdef->generated)
3117 3 : ereport(ERROR,
1068 peter 3118 ECB : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3119 : errmsg("column \"%s\" inherits from generated column but specifies default",
3120 : def->colname)));
1068 peter 3121 CBC 10 : if (newdef->identity)
3122 3 : ereport(ERROR,
1068 peter 3123 ECB : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3124 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3125 : def->colname)));
3126 : }
3127 : else
3128 : {
1068 peter 3129 GIC 97 : if (newdef->generated)
88 tgl 3130 GNC 3 : ereport(ERROR,
3131 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3132 : errmsg("child column \"%s\" specifies generation expression",
3133 : def->colname),
3134 : errhint("A child table column cannot be generated unless its parent column is.")));
3135 : }
1068 peter 3136 ECB :
3137 : /*
3138 : * If new def has a default, override previous default
3139 : */
7653 tgl 3140 CBC 101 : if (newdef->raw_default != NULL)
3141 : {
3142 9 : def->raw_default = newdef->raw_default;
7653 tgl 3143 GIC 9 : def->cooked_default = newdef->cooked_default;
7653 tgl 3144 ECB : }
3145 :
3146 : /* Mark the column as locally defined */
32 peter 3147 GNC 101 : def->is_local = true;
3148 : }
3149 : else
7653 tgl 3150 ECB : {
3151 : /*
3152 : * No, attach new column to result schema
3153 : */
7653 tgl 3154 GIC 279 : inhSchema = lappend(inhSchema, newdef);
3155 : }
3156 : }
7653 tgl 3157 ECB :
7653 tgl 3158 GIC 4294 : schema = inhSchema;
3159 :
3160 : /*
3161 : * Check that we haven't exceeded the legal # of columns after merging
3162 : * in inherited columns.
3163 : */
6718 neilc 3164 4294 : if (list_length(schema) > MaxHeapAttributeNumber)
6718 neilc 3165 UIC 0 : ereport(ERROR,
6718 neilc 3166 ECB : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3167 : errmsg("tables can have at most %d columns",
3168 : MaxHeapAttributeNumber)));
3169 : }
8484 peter_e 3170 :
3171 : /*
2314 rhaas 3172 : * Now that we have the column definition list for a partition, we can
2308 3173 : * check whether the columns referenced in the column constraint specs
3174 : * actually exist.
2314 3175 : */
1613 alvherre 3176 GIC 62781 : if (is_partition)
3177 : {
3178 3681 : foreach(entry, saved_schema)
3179 : {
1613 alvherre 3180 CBC 100 : ColumnDef *restdef = lfirst(entry);
3181 100 : bool found = false;
3182 : ListCell *l;
3183 :
1613 alvherre 3184 GIC 372 : foreach(l, schema)
3185 : {
3186 278 : ColumnDef *coldef = lfirst(l);
3187 :
2314 rhaas 3188 278 : if (strcmp(coldef->colname, restdef->colname) == 0)
3189 : {
1613 alvherre 3190 100 : found = true;
3191 100 : coldef->is_not_null |= restdef->is_not_null;
3192 :
3193 : /*
3194 : * Check for conflicts related to generated columns.
3195 : *
3196 : * Same rules as above: generated-ness has to match the
3197 : * parent, but the contents of the generation expression
3198 : * can be different.
3199 : */
52 tgl 3200 GNC 100 : if (coldef->generated)
3201 : {
3202 49 : if (restdef->raw_default && !restdef->generated)
3203 3 : ereport(ERROR,
3204 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3205 : errmsg("column \"%s\" inherits from generated column but specifies default",
3206 : restdef->colname)));
3207 46 : if (restdef->identity)
52 tgl 3208 UNC 0 : ereport(ERROR,
3209 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3210 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3211 : restdef->colname)));
3212 : }
3213 : else
3214 : {
52 tgl 3215 GNC 51 : if (restdef->generated)
3216 3 : ereport(ERROR,
3217 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3218 : errmsg("child column \"%s\" specifies generation expression",
3219 : restdef->colname),
3220 : errhint("A child table column cannot be generated unless its parent column is.")));
3221 : }
3222 :
3223 : /*
3224 : * Override the parent's default value for this column
1613 alvherre 3225 ECB : * (coldef->cooked_default) with the partition's local
3226 : * definition (restdef->raw_default), if there's one. It
3227 : * should be physically impossible to get a cooked default
3228 : * in the local definition or a raw default in the
3229 : * inherited definition, but make sure they're nulls, for
3230 : * future-proofing.
2314 rhaas 3231 : */
1613 alvherre 3232 GIC 94 : Assert(restdef->cooked_default == NULL);
3233 94 : Assert(coldef->raw_default == NULL);
3234 94 : if (restdef->raw_default)
2172 rhaas 3235 ECB : {
2172 rhaas 3236 GIC 58 : coldef->raw_default = restdef->raw_default;
1613 alvherre 3237 58 : coldef->cooked_default = NULL;
2172 rhaas 3238 ECB : }
3239 : }
3240 : }
3241 :
3242 : /* complain for constraints on columns not in parent */
1613 alvherre 3243 CBC 94 : if (!found)
1613 alvherre 3244 LBC 0 : ereport(ERROR,
1613 alvherre 3245 ECB : (errcode(ERRCODE_UNDEFINED_COLUMN),
3246 : errmsg("column \"%s\" does not exist",
3247 : restdef->colname)));
3248 : }
3249 : }
3250 :
3251 : /*
3252 : * If we found any conflicting parent default values, check to make sure
3253 : * they were overridden by the child.
3254 : */
7653 tgl 3255 GIC 62775 : if (have_bogus_defaults)
3256 : {
3257 27 : foreach(entry, schema)
3258 : {
7653 tgl 3259 CBC 21 : ColumnDef *def = lfirst(entry);
8397 bruce 3260 ECB :
4933 tgl 3261 CBC 21 : if (def->cooked_default == &bogus_marker)
1068 peter 3262 ECB : {
1068 peter 3263 GIC 6 : if (def->generated)
3264 3 : ereport(ERROR,
3265 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3266 : errmsg("column \"%s\" inherits conflicting generation expressions",
3267 : def->colname),
3268 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3269 : else
3270 3 : ereport(ERROR,
3271 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3272 : errmsg("column \"%s\" inherits conflicting default values",
3273 : def->colname),
3274 : errhint("To resolve the conflict, specify a default explicitly.")));
1068 peter 3275 ECB : }
3276 : }
3277 : }
3278 :
7653 tgl 3279 GIC 62769 : *supconstr = constraints;
2 alvherre 3280 GNC 62769 : *supnotnulls = nnconstraints;
3281 :
7653 tgl 3282 CBC 62769 : return schema;
7653 tgl 3283 EUB : }
8397 bruce 3284 ECB :
6024 tgl 3285 :
3286 : /*
3287 : * MergeCheckConstraint
3288 : * Try to merge an inherited CHECK constraint with previous ones
3289 : *
3290 : * If we inherit identically-named constraints from multiple parents, we must
3291 : * merge them, or throw an error if they don't have identical definitions.
3292 : *
3293 : * constraints is a list of CookedConstraint structs for previous constraints.
3294 : *
3295 : * Returns true if merged (constraint is a duplicate), or false if it's
5448 3296 : * got a so-far-unique name, or throws error if conflict.
7653 3297 : */
5448 3298 : static bool
5448 tgl 3299 GIC 92 : MergeCheckConstraint(List *constraints, char *name, Node *expr)
6130 bruce 3300 ECB : {
5448 tgl 3301 : ListCell *lc;
3302 :
5448 tgl 3303 GIC 107 : foreach(lc, constraints)
3304 : {
3305 21 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3306 :
3307 21 : Assert(ccon->contype == CONSTR_CHECK);
3308 :
3309 : /* Non-matching names never conflict */
3310 21 : if (strcmp(ccon->name, name) != 0)
6130 bruce 3311 CBC 15 : continue;
3312 :
5448 tgl 3313 GIC 6 : if (equal(expr, ccon->expr))
3314 : {
3315 : /* OK to merge */
3316 6 : ccon->inhcount++;
12 peter 3317 GNC 6 : if (ccon->inhcount < 0)
12 peter 3318 UNC 0 : ereport(ERROR,
3319 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3320 : errmsg("too many inheritance parents"));
5448 tgl 3321 GIC 6 : return true;
3322 : }
3323 :
6024 tgl 3324 UIC 0 : ereport(ERROR,
3325 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3326 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3327 : name)));
3328 : }
3329 :
5448 tgl 3330 GIC 86 : return false;
3331 : }
6130 bruce 3332 ECB :
3333 : /*
3334 : * RelationGetNotNullConstraints -- get list of NOT NULL constraints
3335 : *
3336 : * Caller can request cooked constraints, or raw.
3337 : *
3338 : * This is seldom needed, so we just scan pg_constraint each time.
3339 : *
3340 : * XXX This is only used to create derived tables, so NO INHERIT constraints
3341 : * are always skipped.
3342 : */
3343 : List *
2 alvherre 3344 GNC 4761 : RelationGetNotNullConstraints(Relation relation, bool cooked)
3345 : {
3346 4761 : List *notnulls = NIL;
3347 : Relation constrRel;
3348 : HeapTuple htup;
3349 : SysScanDesc conscan;
3350 : ScanKeyData skey;
3351 :
3352 4761 : constrRel = table_open(ConstraintRelationId, AccessShareLock);
3353 4761 : ScanKeyInit(&skey,
3354 : Anum_pg_constraint_conrelid,
3355 : BTEqualStrategyNumber, F_OIDEQ,
3356 : ObjectIdGetDatum(RelationGetRelid(relation)));
3357 4761 : conscan = systable_beginscan(constrRel, ConstraintRelidTypidNameIndexId, true,
3358 : NULL, 1, &skey);
3359 :
3360 6297 : while (HeapTupleIsValid(htup = systable_getnext(conscan)))
3361 : {
3362 1536 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(htup);
3363 : AttrNumber colnum;
3364 :
3365 1536 : if (conForm->contype != CONSTRAINT_NOTNULL)
3366 1149 : continue;
3367 387 : if (conForm->connoinherit)
3368 9 : continue;
3369 :
3370 378 : colnum = extractNotNullColumn(htup);
3371 :
3372 378 : if (cooked)
3373 : {
3374 : CookedConstraint *cooked;
3375 :
3376 301 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
3377 :
3378 301 : cooked->contype = CONSTR_NOTNULL;
3379 301 : cooked->name = pstrdup(NameStr(conForm->conname));
3380 301 : cooked->attnum = colnum;
3381 301 : cooked->expr = NULL;
3382 301 : cooked->skip_validation = false;
3383 301 : cooked->is_local = true;
3384 301 : cooked->inhcount = 0;
3385 301 : cooked->is_no_inherit = conForm->connoinherit;
3386 :
3387 301 : notnulls = lappend(notnulls, cooked);
3388 : }
3389 : else
3390 : {
3391 : Constraint *constr;
3392 :
3393 77 : constr = makeNode(Constraint);
3394 77 : constr->contype = CONSTR_NOTNULL;
3395 77 : constr->conname = pstrdup(NameStr(conForm->conname));
3396 77 : constr->deferrable = false;
3397 77 : constr->initdeferred = false;
3398 77 : constr->location = -1;
3399 77 : constr->colname = get_attname(RelationGetRelid(relation),
3400 : colnum, false);
3401 77 : constr->skip_validation = false;
3402 77 : constr->initially_valid = true;
3403 77 : notnulls = lappend(notnulls, constr);
3404 : }
3405 : }
3406 :
3407 4761 : systable_endscan(conscan);
3408 4761 : table_close(constrRel, AccessShareLock);
3409 :
3410 4761 : return notnulls;
3411 : }
3412 :
8007 tgl 3413 ECB : /*
7653 3414 : * StoreCatalogInheritance
3415 : * Updates the system catalogs with proper inheritance information.
3416 : *
3417 : * supers is a list of the OIDs of the new relation's direct ancestors.
8007 3418 : */
7653 3419 : static void
2225 simon 3420 GIC 62499 : StoreCatalogInheritance(Oid relationId, List *supers,
3421 : bool child_is_partition)
3422 : {
3423 : Relation relation;
3424 : int32 seqNumber;
3425 : ListCell *entry;
7681 tgl 3426 ECB :
7668 bruce 3427 : /*
3428 : * sanity checks
3429 : */
163 peter 3430 GNC 62499 : Assert(OidIsValid(relationId));
3431 :
7653 tgl 3432 GIC 62499 : if (supers == NIL)
3433 58385 : return;
3434 :
3435 : /*
3436 : * Store INHERITS information in pg_inherits using direct ancestors only.
6385 bruce 3437 ECB : * Also enter dependencies on the direct ancestors, and make sure they are
3438 : * marked with relhassubclass = true.
7118 tgl 3439 : *
6347 bruce 3440 : * (Once upon a time, both direct and indirect ancestors were found here
3441 : * and then entered into pg_ipl. Since that catalog doesn't exist
3442 : * anymore, there's no need to look for indirect ancestors.)
3443 : */
1539 andres 3444 CBC 4114 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3445 :
7653 tgl 3446 GIC 4114 : seqNumber = 1;
3447 8322 : foreach(entry, supers)
3448 : {
6022 3449 4208 : Oid parentOid = lfirst_oid(entry);
3450 :
2225 simon 3451 CBC 4208 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3452 : child_is_partition);
6125 neilc 3453 GIC 4208 : seqNumber++;
3454 : }
6125 bruce 3455 ECB :
1539 andres 3456 GIC 4114 : table_close(relation, RowExclusiveLock);
3457 : }
3458 :
3459 : /*
3460 : * Make catalog entries showing relationId as being an inheritance child
6022 tgl 3461 ECB : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
6022 tgl 3462 EUB : */
3463 : static void
6125 neilc 3464 GIC 5238 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3465 : int32 seqNumber, Relation inhRelation,
3466 : bool child_is_partition)
3467 : {
3468 : ObjectAddress childobject,
3469 : parentobject;
3470 :
3471 : /* store the pg_inherits row */
1906 alvherre 3472 5238 : StoreSingleInheritance(relationId, parentOid, seqNumber);
8007 tgl 3473 ECB :
3474 : /*
6125 neilc 3475 : * Store a dependency too
3476 : */
6125 neilc 3477 CBC 5238 : parentobject.classId = RelationRelationId;
3478 5238 : parentobject.objectId = parentOid;
6125 neilc 3479 GIC 5238 : parentobject.objectSubId = 0;
3480 5238 : childobject.classId = RelationRelationId;
6125 neilc 3481 CBC 5238 : childobject.objectId = relationId;
6125 neilc 3482 GIC 5238 : childobject.objectSubId = 0;
7576 tgl 3483 ECB :
2126 rhaas 3484 GIC 5238 : recordDependencyOn(&childobject, &parentobject,
2126 rhaas 3485 ECB : child_dependency_type(child_is_partition));
3486 :
3675 3487 : /*
3488 : * Post creation hook of this inheritance. Since object_access_hook
3489 : * doesn't take multiple object identifiers, we relay oid of parent
3490 : * relation using auxiliary_id argument.
3491 : */
3675 rhaas 3492 GIC 5238 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3493 : relationId, 0,
3494 : parentOid, false);
3495 :
3496 : /*
6125 neilc 3497 ECB : * Mark the parent as having subclasses.
3498 : */
4237 tgl 3499 CBC 5238 : SetRelationHasSubclass(parentOid, true);
7653 3500 5238 : }
3501 :
3502 : /*
3503 : * Look for an existing schema entry with the given name.
7653 tgl 3504 ECB : *
7653 tgl 3505 EUB : * Returns the index (starting with 1) if attribute already exists in schema,
3506 : * 0 if it doesn't.
3507 : */
3508 : static int
7653 tgl 3509 GIC 9304 : findAttrByName(const char *attributeName, List *schema)
3510 : {
3511 : ListCell *s;
6892 neilc 3512 CBC 9304 : int i = 1;
7653 tgl 3513 ECB :
7653 tgl 3514 GIC 16605 : foreach(s, schema)
3515 : {
3516 7538 : ColumnDef *def = lfirst(s);
3517 :
3518 7538 : if (strcmp(attributeName, def->colname) == 0)
3519 237 : return i;
3520 :
6892 neilc 3521 7301 : i++;
3522 : }
7653 tgl 3523 9067 : return 0;
3524 : }
3525 :
3526 :
3527 : /*
3528 : * SetRelationHasSubclass
4237 tgl 3529 ECB : * Set the value of the relation's relhassubclass field in pg_class.
3530 : *
3531 : * NOTE: caller must be holding an appropriate lock on the relation.
3532 : * ShareUpdateExclusiveLock is sufficient.
3533 : *
3534 : * NOTE: an important side-effect of this operation is that an SI invalidation
3535 : * message is sent out to all backends --- including me --- causing plans
3536 : * referencing the relation to be rebuilt with the new list of children.
3537 : * This must happen even if we find that no change is needed in the pg_class
3538 : * row.
3539 : */
3540 : void
4237 tgl 3541 GBC 6544 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3542 : {
3543 : Relation relationRelation;
3544 : HeapTuple tuple;
3545 : Form_pg_class classtuple;
3546 :
3547 : /*
3548 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3549 : */
1539 andres 3550 GIC 6544 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
4802 rhaas 3551 6544 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
7653 tgl 3552 CBC 6544 : if (!HeapTupleIsValid(tuple))
7203 tgl 3553 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
7118 tgl 3554 CBC 6544 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3555 :
3556 6544 : if (classtuple->relhassubclass != relhassubclass)
3557 : {
3558 3248 : classtuple->relhassubclass = relhassubclass;
2259 alvherre 3559 GIC 3248 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
7118 tgl 3560 ECB : }
3561 : else
3562 : {
3563 : /* no need to change tuple, but force relcache rebuild anyway */
6998 tgl 3564 GIC 3296 : CacheInvalidateRelcacheByTuple(tuple);
3565 : }
3566 :
7653 tgl 3567 CBC 6544 : heap_freetuple(tuple);
1539 andres 3568 GIC 6544 : table_close(relationRelation, RowExclusiveLock);
7653 tgl 3569 6544 : }
3570 :
3571 : /*
3572 : * CheckRelationTableSpaceMove
3573 : * Check if relation can be moved to new tablespace.
3574 : *
3575 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
802 michael 3576 ECB : *
800 3577 : * Returns true if the relation can be moved to the new tablespace; raises
3578 : * an error if it is not possible to do the move; returns false if the move
3579 : * would have no effect.
3580 : */
3581 : bool
802 michael 3582 GIC 215 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3583 : {
3584 : Oid oldTableSpaceId;
3585 :
3586 : /*
3587 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3588 : * stored as 0.
3589 : */
3590 215 : oldTableSpaceId = rel->rd_rel->reltablespace;
3591 215 : if (newTableSpaceId == oldTableSpaceId ||
3592 211 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3593 5 : return false;
3594 :
3595 : /*
802 michael 3596 ECB : * We cannot support moving mapped relations into different tablespaces.
3597 : * (In particular this eliminates all shared catalogs.)
3598 : */
802 michael 3599 GIC 210 : if (RelationIsMapped(rel))
802 michael 3600 LBC 0 : ereport(ERROR,
3601 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
802 michael 3602 ECB : errmsg("cannot move system relation \"%s\"",
3603 : RelationGetRelationName(rel))));
3604 :
3605 : /* Cannot move a non-shared relation into pg_global */
802 michael 3606 GIC 210 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
802 michael 3607 CBC 6 : ereport(ERROR,
802 michael 3608 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3609 : errmsg("only shared relations can be placed in pg_global tablespace")));
3610 :
3611 : /*
3612 : * Do not allow moving temp tables of other backends ... their local
3613 : * buffer manager is not going to cope.
3614 : */
802 michael 3615 GBC 204 : if (RELATION_IS_OTHER_TEMP(rel))
802 michael 3616 UIC 0 : ereport(ERROR,
3617 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
802 michael 3618 ECB : errmsg("cannot move temporary tables of other sessions")));
3619 :
802 michael 3620 GIC 204 : return true;
802 michael 3621 EUB : }
3622 :
3623 : /*
3624 : * SetRelationTableSpace
3625 : * Set new reltablespace and relfilenumber in pg_class entry.
3626 : *
802 michael 3627 ECB : * newTableSpaceId is the new tablespace for the relation, and
3628 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3629 : * InvalidRelFileNumber, this field is not updated.
3630 : *
3631 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3632 : *
3633 : * The caller of this routine had better check if a relation can be
3634 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3635 : * first, and is responsible for making the change visible with
3636 : * CommandCounterIncrement().
3637 : */
3638 : void
802 michael 3639 GIC 102 : SetRelationTableSpace(Relation rel,
3640 : Oid newTableSpaceId,
3641 : RelFileNumber newRelFilenumber)
3642 : {
802 michael 3643 ECB : Relation pg_class;
3644 : HeapTuple tuple;
3645 : Form_pg_class rd_rel;
802 michael 3646 GIC 102 : Oid reloid = RelationGetRelid(rel);
3647 :
3648 102 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
802 michael 3649 ECB :
3650 : /* Get a modifiable copy of the relation's pg_class row. */
802 michael 3651 GIC 102 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3652 :
3653 102 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
802 michael 3654 CBC 102 : if (!HeapTupleIsValid(tuple))
802 michael 3655 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
802 michael 3656 GIC 102 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
802 michael 3657 ECB :
3658 : /* Update the pg_class row. */
802 michael 3659 CBC 204 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
802 michael 3660 GIC 102 : InvalidOid : newTableSpaceId;
277 rhaas 3661 GNC 102 : if (RelFileNumberIsValid(newRelFilenumber))
3662 80 : rd_rel->relfilenode = newRelFilenumber;
802 michael 3663 CBC 102 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
802 michael 3664 ECB :
3665 : /*
3666 : * Record dependency on tablespace. This is only required for relations
3667 : * that have no physical storage.
3668 : */
802 michael 3669 CBC 102 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
802 michael 3670 GIC 15 : changeDependencyOnTablespace(RelationRelationId, reloid,
3671 : rd_rel->reltablespace);
3672 :
802 michael 3673 CBC 102 : heap_freetuple(tuple);
802 michael 3674 GIC 102 : table_close(pg_class, RowExclusiveLock);
802 michael 3675 CBC 102 : }
802 michael 3676 ECB :
8478 peter_e 3677 : /*
4133 rhaas 3678 : * renameatt_check - basic sanity checks before attribute rename
8478 peter_e 3679 : */
4520 3680 : static void
4133 rhaas 3681 CBC 479 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
8484 peter_e 3682 ECB : {
4133 rhaas 3683 GIC 479 : char relkind = classform->relkind;
7681 tgl 3684 ECB :
4133 rhaas 3685 GIC 479 : if (classform->reloftype && !recursing)
4819 peter_e 3686 3 : ereport(ERROR,
3687 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3688 : errmsg("cannot rename column of typed table")));
3689 :
4768 rhaas 3690 ECB : /*
3691 : * Renaming the columns of sequences or toast tables doesn't actually
3692 : * break anything from the system's point of view, since internal
4660 bruce 3693 : * references are by attnum. But it doesn't seem right to allow users to
3694 : * change names that are hardcoded into the system, hence the following
4768 rhaas 3695 : * restriction.
3696 : */
4768 rhaas 3697 GIC 476 : if (relkind != RELKIND_RELATION &&
4768 rhaas 3698 CBC 42 : relkind != RELKIND_VIEW &&
3689 kgrittn 3699 42 : relkind != RELKIND_MATVIEW &&
4768 rhaas 3700 18 : relkind != RELKIND_COMPOSITE_TYPE &&
4481 rhaas 3701 GIC 18 : relkind != RELKIND_INDEX &&
1906 alvherre 3702 18 : relkind != RELKIND_PARTITIONED_INDEX &&
2314 rhaas 3703 UIC 0 : relkind != RELKIND_FOREIGN_TABLE &&
2314 rhaas 3704 ECB : relkind != RELKIND_PARTITIONED_TABLE)
4768 rhaas 3705 LBC 0 : ereport(ERROR,
3706 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
640 peter 3707 ECB : errmsg("cannot rename columns of relation \"%s\"",
3708 : NameStr(classform->relname)),
3709 : errdetail_relkind_not_supported(relkind)));
3710 :
3711 : /*
3712 : * permissions checking. only the owner of a class can change its schema.
3713 : */
147 peter 3714 GNC 476 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
1954 peter_e 3715 UIC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
4133 rhaas 3716 0 : NameStr(classform->relname));
3419 rhaas 3717 CBC 476 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
7203 tgl 3718 GIC 1 : ereport(ERROR,
3719 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3720 : errmsg("permission denied: \"%s\" is a system catalog",
3721 : NameStr(classform->relname))));
4133 rhaas 3722 475 : }
3723 :
3724 : /*
3725 : * renameatt_internal - workhorse for renameatt
3726 : *
2959 alvherre 3727 ECB : * Return value is the attribute number in the 'myrelid' relation.
3728 : */
3729 : static AttrNumber
4133 rhaas 3730 CBC 267 : renameatt_internal(Oid myrelid,
3731 : const char *oldattname,
3732 : const char *newattname,
3733 : bool recurse,
3734 : bool recursing,
3735 : int expected_parents,
3736 : DropBehavior behavior)
3737 : {
3738 : Relation targetrelation;
3739 : Relation attrelation;
3740 : HeapTuple atttup;
3602 bruce 3741 ECB : Form_pg_attribute attform;
3742 : AttrNumber attnum;
4133 rhaas 3743 :
3744 : /*
3745 : * Grab an exclusive lock on the target table, which we will NOT release
3746 : * until end of transaction.
3747 : */
4133 rhaas 3748 CBC 267 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4133 rhaas 3749 GIC 267 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
7849 tgl 3750 ECB :
3751 : /*
3752 : * if the 'recurse' flag is set then we are supposed to rename this
7653 3753 : * attribute in all classes that inherit from 'relname' (as well as in
3754 : * 'relname').
3755 : *
3756 : * any permissions or problems with duplicate attributes will cause the
3757 : * whole transaction to abort, which is what we want -- all or nothing.
3758 : */
7653 tgl 3759 GIC 267 : if (recurse)
3760 : {
4790 bruce 3761 ECB : List *child_oids,
3762 : *child_numparents;
3763 : ListCell *lo,
3764 : *li;
3765 :
3766 : /*
3767 : * we need the number of parents for each child so that the recursive
3768 : * calls to renameatt() can determine whether there are any parents
4815 rhaas 3769 : * outside the inheritance hierarchy being processed.
3770 : */
4815 rhaas 3771 GIC 115 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3772 : &child_numparents);
3773 :
7849 tgl 3774 ECB : /*
6385 bruce 3775 : * find_all_inheritors does the recursive search of the inheritance
3776 : * hierarchy, so all we have to do is process all of the relids in the
3777 : * list that it returns.
7849 tgl 3778 : */
4815 rhaas 3779 CBC 349 : forboth(lo, child_oids, li, child_numparents)
3780 : {
3781 249 : Oid childrelid = lfirst_oid(lo);
4815 rhaas 3782 GIC 249 : int numparents = lfirst_int(li);
3783 :
7527 tgl 3784 249 : if (childrelid == myrelid)
7849 3785 115 : continue;
3786 : /* note we need not recurse again */
4520 peter_e 3787 134 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3788 : }
7849 tgl 3789 ECB : }
3790 : else
3791 : {
3792 : /*
3793 : * If we are told not to recurse, there had better not be any child
3794 : * tables; else the rename would put them out of step.
3795 : *
4815 rhaas 3796 : * expected_parents will only be 0 if we are not already recursing.
7527 tgl 3797 : */
4815 rhaas 3798 GIC 170 : if (expected_parents == 0 &&
711 alvherre 3799 18 : find_inheritance_children(myrelid, NoLock) != NIL)
7203 tgl 3800 6 : ereport(ERROR,
3801 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3802 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3803 : oldattname)));
3804 : }
3805 :
4520 peter_e 3806 ECB : /* rename attributes in typed tables of composite type */
4520 peter_e 3807 GIC 246 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3808 : {
4520 peter_e 3809 ECB : List *child_oids;
3810 : ListCell *lo;
3811 :
4520 peter_e 3812 GIC 12 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
2118 tgl 3813 CBC 12 : RelationGetRelationName(targetrelation),
3814 : behavior);
4520 peter_e 3815 ECB :
4520 peter_e 3816 CBC 12 : foreach(lo, child_oids)
4520 peter_e 3817 GIC 3 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
4520 peter_e 3818 ECB : }
3819 :
1539 andres 3820 CBC 243 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3821 :
7527 tgl 3822 GIC 243 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
7653 3823 243 : if (!HeapTupleIsValid(atttup))
7203 3824 12 : ereport(ERROR,
3825 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3826 : errmsg("column \"%s\" does not exist",
3827 : oldattname)));
7527 3828 231 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3829 :
7256 3830 231 : attnum = attform->attnum;
6913 3831 231 : if (attnum <= 0)
7203 tgl 3832 UIC 0 : ereport(ERROR,
3833 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3834 : errmsg("cannot rename system column \"%s\"",
3835 : oldattname)));
3836 :
3837 : /*
3260 bruce 3838 ECB : * if the attribute is inherited, forbid the renaming. if this is a
3839 : * top-level call to renameatt(), then expected_parents will be 0, so the
3840 : * effect of this code will be to prohibit the renaming if the attribute
3841 : * is inherited at all. if this is a recursive call to renameatt(),
3842 : * expected_parents will be the number of parents the current relation has
3843 : * within the inheritance hierarchy being processed, so we'll prohibit the
3844 : * renaming only if there are additional parents from elsewhere.
3845 : */
4815 rhaas 3846 GIC 231 : if (attform->attinhcount > expected_parents)
7203 tgl 3847 CBC 15 : ereport(ERROR,
7203 tgl 3848 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7136 peter_e 3849 : errmsg("cannot rename inherited column \"%s\"",
7203 tgl 3850 EUB : oldattname)));
7527 tgl 3851 ECB :
3852 : /* new name should not already exist */
2811 andrew 3853 CBC 216 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3854 :
4855 tgl 3855 ECB : /* apply the update */
7527 tgl 3856 CBC 210 : namestrcpy(&(attform->attname), newattname);
3857 :
2259 alvherre 3858 GIC 210 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3859 :
3675 rhaas 3860 210 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3675 rhaas 3861 ECB :
7653 tgl 3862 GIC 210 : heap_freetuple(atttup);
3863 :
1539 andres 3864 CBC 210 : table_close(attrelation, RowExclusiveLock);
7678 tgl 3865 ECB :
2118 tgl 3866 CBC 210 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3867 :
2959 alvherre 3868 GIC 210 : return attnum;
3869 : }
3870 :
3871 : /*
3872 : * Perform permissions and integrity checks before acquiring a relation lock.
3873 : */
3874 : static void
4133 rhaas 3875 191 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3876 : void *arg)
3877 : {
3878 : HeapTuple tuple;
3955 bruce 3879 ECB : Form_pg_class form;
3880 :
4133 rhaas 3881 GIC 191 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3882 191 : if (!HeapTupleIsValid(tuple))
3955 bruce 3883 18 : return; /* concurrently dropped */
4133 rhaas 3884 173 : form = (Form_pg_class) GETSTRUCT(tuple);
3885 173 : renameatt_check(relid, form, false);
3886 169 : ReleaseSysCache(tuple);
4133 rhaas 3887 ECB : }
5499 tgl 3888 :
4520 peter_e 3889 : /*
2881 heikki.linnakangas 3890 : * renameatt - changes the name of an attribute in a relation
3891 : *
3892 : * The returned ObjectAddress is that of the renamed column.
3893 : */
3894 : ObjectAddress
4133 rhaas 3895 GIC 149 : renameatt(RenameStmt *stmt)
4520 peter_e 3896 ECB : {
4133 rhaas 3897 EUB : Oid relid;
3898 : AttrNumber attnum;
3899 : ObjectAddress address;
3900 :
3901 : /* lock level taken here should match renameatt_internal */
4133 rhaas 3902 GIC 149 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1836 andres 3903 CBC 149 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4133 rhaas 3904 ECB : RangeVarCallbackForRenameAttribute,
3905 : NULL);
3906 :
4094 simon 3907 GIC 142 : if (!OidIsValid(relid))
3908 : {
3909 12 : ereport(NOTICE,
3910 : (errmsg("relation \"%s\" does not exist, skipping",
3911 : stmt->relation->relname)));
2959 alvherre 3912 CBC 12 : return InvalidObjectAddress;
4094 simon 3913 EUB : }
3914 :
3915 : attnum =
2959 alvherre 3916 GIC 130 : renameatt_internal(relid,
2118 tgl 3917 CBC 130 : stmt->subname, /* old att name */
2118 tgl 3918 GIC 130 : stmt->newname, /* new att name */
2298 3919 130 : stmt->relation->inh, /* recursive? */
3920 : false, /* recursing? */
3921 : 0, /* expected inhcount */
3922 : stmt->behavior);
3923 :
2959 alvherre 3924 88 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3925 :
3926 88 : return address;
3927 : }
3928 :
3929 : /*
3930 : * same logic as renameatt_internal
3931 : */
3932 : static ObjectAddress
4047 peter_e 3933 42 : rename_constraint_internal(Oid myrelid,
3934 : Oid mytypid,
3935 : const char *oldconname,
4047 peter_e 3936 ECB : const char *newconname,
3937 : bool recurse,
3938 : bool recursing,
3939 : int expected_parents)
3940 : {
4023 peter_e 3941 GIC 42 : Relation targetrelation = NULL;
3942 : Oid constraintOid;
3955 bruce 3943 ECB : HeapTuple tuple;
3944 : Form_pg_constraint con;
2959 alvherre 3945 : ObjectAddress address;
3946 :
163 peter 3947 GNC 42 : Assert(!myrelid || !mytypid);
4023 peter_e 3948 ECB :
4023 peter_e 3949 GIC 42 : if (mytypid)
4023 peter_e 3950 ECB : {
4023 peter_e 3951 CBC 3 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4023 peter_e 3952 EUB : }
4023 peter_e 3953 ECB : else
3954 : {
4023 peter_e 3955 GIC 39 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3955 bruce 3956 ECB :
3957 : /*
3958 : * don't tell it whether we're recursing; we allow changing typed
3959 : * tables here
3960 : */
4023 peter_e 3961 GIC 39 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3962 :
3963 39 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3964 : }
3965 :
4047 peter_e 3966 CBC 42 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3967 42 : if (!HeapTupleIsValid(tuple))
4047 peter_e 3968 UIC 0 : elog(ERROR, "cache lookup failed for constraint %u",
3969 : constraintOid);
4047 peter_e 3970 CBC 42 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4047 peter_e 3971 ECB :
2 alvherre 3972 GNC 42 : if (myrelid &&
3973 39 : (con->contype == CONSTRAINT_CHECK ||
3974 9 : con->contype == CONSTRAINT_NOTNULL) &&
3975 30 : !con->connoinherit)
3976 : {
4047 peter_e 3977 GIC 24 : if (recurse)
3978 : {
3979 : List *child_oids,
3980 : *child_numparents;
4047 peter_e 3981 ECB : ListCell *lo,
3982 : *li;
3983 :
4047 peter_e 3984 GIC 15 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4047 peter_e 3985 ECB : &child_numparents);
3986 :
4047 peter_e 3987 GIC 36 : forboth(lo, child_oids, li, child_numparents)
3988 : {
3989 21 : Oid childrelid = lfirst_oid(lo);
3990 21 : int numparents = lfirst_int(li);
3991 :
3992 21 : if (childrelid == myrelid)
3993 15 : continue;
3994 :
4023 3995 6 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3996 : }
4047 peter_e 3997 ECB : }
3998 : else
3999 : {
4047 peter_e 4000 CBC 12 : if (expected_parents == 0 &&
711 alvherre 4001 3 : find_inheritance_children(myrelid, NoLock) != NIL)
4047 peter_e 4002 3 : ereport(ERROR,
4047 peter_e 4003 EUB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4004 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4005 : oldconname)));
4006 : }
4007 :
4047 peter_e 4008 GIC 21 : if (con->coninhcount > expected_parents)
4009 3 : ereport(ERROR,
4010 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4011 : errmsg("cannot rename inherited constraint \"%s\"",
4012 : oldconname)));
4013 : }
4047 peter_e 4014 ECB :
4047 peter_e 4015 GBC 36 : if (con->conindid
4016 9 : && (con->contype == CONSTRAINT_PRIMARY
4047 peter_e 4017 CBC 3 : || con->contype == CONSTRAINT_UNIQUE
4047 peter_e 4018 LBC 0 : || con->contype == CONSTRAINT_EXCLUSION))
4019 : /* rename the index; this renames the constraint as well */
1627 peter_e 4020 GIC 9 : RenameRelationInternal(con->conindid, newconname, false, true);
4021 : else
4047 peter_e 4022 CBC 27 : RenameConstraintById(constraintOid, newconname);
4023 :
2959 alvherre 4024 GIC 36 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4025 :
4047 peter_e 4026 36 : ReleaseSysCache(tuple);
4027 :
4023 4028 36 : if (targetrelation)
4029 : {
1574 michael 4030 ECB : /*
4031 : * Invalidate relcache so as others can see the new constraint name.
4032 : */
1574 michael 4033 GIC 33 : CacheInvalidateRelcache(targetrelation);
4034 :
4035 33 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4036 : }
4037 :
2959 alvherre 4038 36 : return address;
4039 : }
4040 :
4041 : ObjectAddress
4047 peter_e 4042 39 : RenameConstraint(RenameStmt *stmt)
4043 : {
4023 4044 39 : Oid relid = InvalidOid;
4045 39 : Oid typid = InvalidOid;
4046 :
3029 alvherre 4047 39 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4023 peter_e 4048 ECB : {
4049 : Relation rel;
4050 : HeapTuple tup;
4051 :
2339 peter_e 4052 GIC 3 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
1539 andres 4053 3 : rel = table_open(TypeRelationId, RowExclusiveLock);
4023 peter_e 4054 3 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4055 3 : if (!HeapTupleIsValid(tup))
4023 peter_e 4056 UIC 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4023 peter_e 4057 GIC 3 : checkDomainOwner(tup);
4058 3 : ReleaseSysCache(tup);
1539 andres 4059 CBC 3 : table_close(rel, NoLock);
4060 : }
4061 : else
4062 : {
4063 : /* lock level taken here should match rename_constraint_internal */
4023 peter_e 4064 GIC 36 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1836 andres 4065 36 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4066 : RangeVarCallbackForRenameAttribute,
4067 : NULL);
2938 bruce 4068 36 : if (!OidIsValid(relid))
4069 : {
4070 3 : ereport(NOTICE,
2938 bruce 4071 ECB : (errmsg("relation \"%s\" does not exist, skipping",
4072 : stmt->relation->relname)));
2938 bruce 4073 GIC 3 : return InvalidObjectAddress;
4074 : }
4075 : }
4076 :
4077 : return
3759 rhaas 4078 36 : rename_constraint_internal(relid, typid,
3759 rhaas 4079 CBC 36 : stmt->subname,
3759 rhaas 4080 GIC 36 : stmt->newname,
2298 tgl 4081 CBC 69 : (stmt->relation &&
2118 4082 33 : stmt->relation->inh), /* recursive? */
4083 : false, /* recursing? */
3759 rhaas 4084 ECB : 0 /* expected inhcount */ );
4047 peter_e 4085 : }
4086 :
4133 rhaas 4087 : /*
4088 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4089 : * RENAME
4090 : */
4091 : ObjectAddress
4133 rhaas 4092 GIC 255 : RenameRelation(RenameStmt *stmt)
4093 : {
537 alvherre 4094 255 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4095 : Oid relid;
4096 : ObjectAddress address;
4097 :
5499 tgl 4098 ECB : /*
3330 rhaas 4099 : * Grab an exclusive lock on the target table, index, sequence, view,
4100 : * materialized view, or foreign table, which we will NOT release until
4101 : * end of transaction.
4102 : *
4103 : * Lock level used here should match RenameRelationInternal, to avoid lock
4104 : * escalation. However, because ALTER INDEX can be used with any relation
4105 : * type, we mustn't believe without verification.
4106 : */
537 alvherre 4107 : for (;;)
4094 simon 4108 GIC 6 : {
4109 : LOCKMODE lockmode;
4110 : char relkind;
4111 : bool obj_is_index;
537 alvherre 4112 ECB :
537 alvherre 4113 CBC 261 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4114 :
537 alvherre 4115 GIC 261 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
537 alvherre 4116 CBC 261 : stmt->missing_ok ? RVR_MISSING_OK : 0,
537 alvherre 4117 ECB : RangeVarCallbackForAlterRelation,
4118 : (void *) stmt);
4119 :
537 alvherre 4120 CBC 236 : if (!OidIsValid(relid))
4121 : {
4122 9 : ereport(NOTICE,
537 alvherre 4123 ECB : (errmsg("relation \"%s\" does not exist, skipping",
4124 : stmt->relation->relname)));
537 alvherre 4125 GIC 9 : return InvalidObjectAddress;
4126 : }
4127 :
537 alvherre 4128 ECB : /*
4129 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4130 : * to rename a table), but we might've used the wrong lock level. If
4131 : * that happens, retry with the correct lock level. We don't bother
537 alvherre 4132 EUB : * if we already acquired AccessExclusiveLock with an index, however.
4133 : */
537 alvherre 4134 GIC 227 : relkind = get_rel_relkind(relid);
4135 227 : obj_is_index = (relkind == RELKIND_INDEX ||
4136 : relkind == RELKIND_PARTITIONED_INDEX);
4137 227 : if (obj_is_index || is_index_stmt == obj_is_index)
4138 : break;
4139 :
4140 6 : UnlockRelationOid(relid, lockmode);
4141 6 : is_index_stmt = obj_is_index;
4142 : }
4143 :
4144 : /* Do the work */
4145 221 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
3759 rhaas 4146 ECB :
2959 alvherre 4147 CBC 215 : ObjectAddressSet(address, RelationRelationId, relid);
4148 :
2959 alvherre 4149 GIC 215 : return address;
4150 : }
4151 :
4152 : /*
5499 tgl 4153 ECB : * RenameRelationInternal - change the name of a relation
4154 : */
4155 : void
1627 peter_e 4156 CBC 575 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4157 : {
5499 tgl 4158 ECB : Relation targetrelation;
4159 : Relation relrelation; /* for RELATION relation */
4160 : HeapTuple reltup;
4161 : Form_pg_class relform;
4133 rhaas 4162 : Oid namespaceId;
4163 :
5499 tgl 4164 : /*
4165 : * Grab a lock on the target relation, which we will NOT release until end
1627 peter_e 4166 : * of transaction. We need at least a self-exclusive lock so that
4167 : * concurrent DDL doesn't overwrite the rename if they start updating
4168 : * while still seeing the old version. The lock also guards against
4169 : * triggering relcache reloads in concurrent sessions, which might not
4170 : * handle this information changing under them. For indexes, we can use a
4171 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4172 : * specially.
4173 : */
1627 peter_e 4174 GIC 575 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4133 rhaas 4175 CBC 575 : namespaceId = RelationGetNamespace(targetrelation);
4176 :
4177 : /*
4178 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4179 : */
1539 andres 4180 GIC 575 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
7984 bruce 4181 ECB :
4802 rhaas 4182 CBC 575 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
2118 tgl 4183 575 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
7203 tgl 4184 LBC 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
5811 tgl 4185 CBC 575 : relform = (Form_pg_class) GETSTRUCT(reltup);
7681 tgl 4186 ECB :
7653 tgl 4187 GIC 575 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
7203 4188 6 : ereport(ERROR,
4189 : (errcode(ERRCODE_DUPLICATE_TABLE),
4190 : errmsg("relation \"%s\" already exists",
4191 : newrelname)));
4192 :
4193 : /*
4194 : * RenameRelation is careful not to believe the caller's idea of the
537 alvherre 4195 ECB : * relation kind being handled. We don't have to worry about this, but
4196 : * let's not be totally oblivious to it. We can process an index as
4197 : * not-an-index, but not the other way around.
4198 : */
537 alvherre 4199 GIC 569 : Assert(!is_index ||
4200 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4201 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
537 alvherre 4202 ECB :
7984 bruce 4203 : /*
4204 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4205 : * because it's a copy...)
4206 : */
5811 tgl 4207 CBC 569 : namestrcpy(&(relform->relname), newrelname);
4208 :
2259 alvherre 4209 569 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4210 :
3675 rhaas 4211 GIC 569 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
3675 rhaas 4212 ECB : InvalidOid, is_internal);
4213 :
7653 tgl 4214 GIC 569 : heap_freetuple(reltup);
1539 andres 4215 569 : table_close(relrelation, RowExclusiveLock);
7984 bruce 4216 ECB :
4217 : /*
7653 tgl 4218 : * Also rename the associated type, if any.
7984 bruce 4219 : */
5811 tgl 4220 GIC 569 : if (OidIsValid(targetrelation->rd_rel->reltype))
5499 4221 62 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4222 : newrelname, namespaceId);
4223 :
5561 tgl 4224 ECB : /*
4225 : * Also rename the associated constraint, if any.
4226 : */
1906 alvherre 4227 GIC 569 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4228 308 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4229 : {
5561 tgl 4230 270 : Oid constraintId = get_index_constraint(myrelid);
4231 :
4232 270 : if (OidIsValid(constraintId))
5561 tgl 4233 CBC 18 : RenameConstraintById(constraintId, newrelname);
4234 : }
4235 :
4236 : /*
4237 : * Close rel, but keep lock!
4238 : */
7653 tgl 4239 GIC 569 : relation_close(targetrelation, NoLock);
592 akapila 4240 569 : }
592 akapila 4241 ECB :
4242 : /*
4243 : * ResetRelRewrite - reset relrewrite
4244 : */
4245 : void
592 akapila 4246 GIC 170 : ResetRelRewrite(Oid myrelid)
592 akapila 4247 ECB : {
4248 : Relation relrelation; /* for RELATION relation */
4249 : HeapTuple reltup;
4250 : Form_pg_class relform;
4251 :
4252 : /*
4253 : * Find relation's pg_class tuple.
4254 : */
592 akapila 4255 CBC 170 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4256 :
592 akapila 4257 GIC 170 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4258 170 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
592 akapila 4259 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
592 akapila 4260 GIC 170 : relform = (Form_pg_class) GETSTRUCT(reltup);
592 akapila 4261 ECB :
4262 : /*
4263 : * Update pg_class tuple.
4264 : */
592 akapila 4265 GIC 170 : relform->relrewrite = InvalidOid;
592 akapila 4266 ECB :
592 akapila 4267 CBC 170 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
592 akapila 4268 EUB :
592 akapila 4269 GIC 170 : heap_freetuple(reltup);
592 akapila 4270 CBC 170 : table_close(relrelation, RowExclusiveLock);
7653 tgl 4271 GIC 170 : }
7984 bruce 4272 ECB :
5548 tgl 4273 : /*
4274 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4275 : * any open reference to the target table besides the one just acquired by
4276 : * the calling command; this implies there's an open cursor or active plan.
4638 simon 4277 : * We need this check because our lock doesn't protect us against stomping
4278 : * on our own foot, only other people's feet!
4279 : *
4280 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4281 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4282 : * possibly be relaxed to only error out for certain types of alterations.
4283 : * But the use-case for allowing any of these things is not obvious, so we
5548 tgl 4284 : * won't work hard at it for now.
4285 : *
4286 : * We also reject these commands if there are any pending AFTER trigger events
4287 : * for the rel. This is certainly necessary for the rewriting variants of
4288 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
3260 bruce 4289 : * events would try to fetch the wrong tuples. It might be overly cautious
5548 tgl 4290 : * in other cases, but again it seems better to err on the side of paranoia.
4291 : *
4292 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4293 : * we are worried about active indexscans on the index. The trigger-event
4294 : * check can be skipped, since we are doing no damage to the parent table.
4295 : *
4296 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4297 : */
4298 : void
5548 tgl 4299 GIC 90091 : CheckTableNotInUse(Relation rel, const char *stmt)
5548 tgl 4300 ECB : {
4301 : int expected_refcnt;
4302 :
5548 tgl 4303 GIC 90091 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4304 90091 : if (rel->rd_refcnt != expected_refcnt)
4305 12 : ereport(ERROR,
4306 : (errcode(ERRCODE_OBJECT_IN_USE),
4307 : /* translator: first %s is a SQL command, eg ALTER TABLE */
1356 alvherre 4308 ECB : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
5548 tgl 4309 : stmt, RelationGetRelationName(rel))));
4310 :
5548 tgl 4311 GIC 90079 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
1906 alvherre 4312 154916 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
5548 tgl 4313 77014 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4314 9 : ereport(ERROR,
5548 tgl 4315 ECB : (errcode(ERRCODE_OBJECT_IN_USE),
5050 bruce 4316 : /* translator: first %s is a SQL command, eg ALTER TABLE */
1356 alvherre 4317 : errmsg("cannot %s \"%s\" because it has pending trigger events",
5548 tgl 4318 EUB : stmt, RelationGetRelationName(rel))));
5548 tgl 4319 GIC 90070 : }
5548 tgl 4320 ECB :
4321 : /*
4111 rhaas 4322 : * AlterTableLookupRelation
4323 : * Look up, and lock, the OID for the relation named by an alter table
4324 : * statement.
4325 : */
4326 : Oid
4111 rhaas 4327 GIC 44953 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4111 rhaas 4328 ECB : {
1836 andres 4329 GIC 89864 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4330 44953 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4331 : RangeVarCallbackForAlterRelation,
4332 : (void *) stmt);
4111 rhaas 4333 ECB : }
4334 :
6913 tgl 4335 : /*
4336 : * AlterTable
4337 : * Execute ALTER TABLE, which can be a list of subcommands
4338 : *
4339 : * ALTER TABLE is performed in three phases:
4340 : * 1. Examine subcommands and perform pre-transformation checking.
4341 : * 2. Validate and transform subcommands, and update system catalogs.
4342 : * 3. Scan table(s) to check new constraints, and optionally recopy
4343 : * the data into new table(s).
4344 : * Phase 3 is not performed unless one or more of the subcommands requires
3260 bruce 4345 : * it. The intention of this design is to allow multiple independent
4346 : * updates of the table schema to be performed with only one pass over the
6913 tgl 4347 : * data.
4348 : *
4349 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4350 : * each table to be affected (there may be multiple affected tables if the
4351 : * commands traverse a table inheritance hierarchy). Also we do preliminary
1180 4352 : * validation of the subcommands. Because earlier subcommands may change
4353 : * the catalog state seen by later commands, there are limits to what can
4354 : * be done in this phase. Generally, this phase acquires table locks,
4355 : * checks permissions and relkind, and recurses to find child tables.
6913 tgl 4356 EUB : *
248 alvherre 4357 ECB : * ATRewriteCatalogs performs phase 2 for each affected table.
6913 tgl 4358 : * Certain subcommands need to be performed before others to avoid
4359 : * unnecessary conflicts; for example, DROP COLUMN should come before
4360 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4361 : * lists, one for each logical "pass" of phase 2.
4362 : *
4363 : * ATRewriteTables performs phase 3 for those tables that need it.
4364 : *
248 alvherre 4365 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4366 : * since phase 1 already does it. However, for certain subcommand types
4367 : * it is only possible to determine how to recurse at phase 2 time; for
4368 : * those cases, phase 1 sets the cmd->recurse flag.
4369 : *
4370 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4371 : * the whole operation; we don't have to do anything special to clean up.
4638 simon 4372 : *
4373 : * The caller must lock the relation, with an appropriate lock level
4374 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4375 : * or higher. We pass the lock level down
4376 : * so that we can apply it recursively to inherited tables. Note that the
4111 rhaas 4377 : * lock level we want as we recurse might well be higher than required for
4638 simon 4378 : * that specific subcommand. So we pass down the overall lock requirement,
4379 : * rather than reassess it at lower levels.
1180 tgl 4380 : *
4381 : * The caller also provides a "context" which is to be passed back to
4382 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4383 : * Some of the fields therein, such as the relid, are used here as well.
4384 : */
4385 : void
1180 tgl 4386 GIC 44842 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4387 : AlterTableUtilityContext *context)
4388 : {
4389 : Relation rel;
4390 :
4111 rhaas 4391 ECB : /* Caller is required to provide an adequate lock. */
1180 tgl 4392 GIC 44842 : rel = relation_open(context->relid, NoLock);
5576 tgl 4393 ECB :
5548 tgl 4394 GIC 44842 : CheckTableNotInUse(rel, "ALTER TABLE");
4395 :
1180 4396 44833 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
5576 4397 43514 : }
4398 :
4399 : /*
4400 : * AlterTableInternal
4401 : *
4402 : * ALTER TABLE with target specified by OID
4403 : *
4404 : * We do not reject if the relation is already open, because it's quite
4405 : * likely that one or more layers of caller have it open. That means it
4406 : * is unsafe to use this entry point for alterations that could break
5576 tgl 4407 ECB : * existing query plans. On the assumption it's not used for such, we
4408 : * don't have to reject pending AFTER triggers, either.
4409 : *
4410 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4411 : * used for any subcommand types that require parse transformation or
1180 4412 : * could generate subcommands that have to be passed to ProcessUtility.
4413 : */
8315 JanWieck 4414 : void
6913 tgl 4415 CBC 139 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4416 : {
4417 : Relation rel;
4382 bruce 4418 GIC 139 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
5910 tgl 4419 ECB :
4638 simon 4420 GIC 139 : rel = relation_open(relid, lockmode);
4638 simon 4421 ECB :
2890 alvherre 4422 GIC 139 : EventTriggerAlterTableRelid(relid);
4423 :
1180 tgl 4424 CBC 139 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4638 simon 4425 GIC 139 : }
4426 :
4427 : /*
4428 : * AlterTableGetLockLevel
4429 : *
4430 : * Sets the overall lock level required for the supplied list of subcommands.
4431 : * Policy for doing this set according to needs of AlterTable(), see
4432 : * comments there for overall explanation.
4638 simon 4433 ECB : *
4434 : * Function is called before and after parsing, so it must give same
4435 : * answer each time it is called. Some subcommands are transformed
4436 : * into other subcommand types, so the transform must never be made to a
4437 : * lower lock level than previously assigned. All transforms are noted below.
4438 : *
4439 : * Since this is called before we lock the table we cannot use table metadata
4440 : * to influence the type of lock we acquire.
4441 : *
4442 : * There should be no lockmodes hardcoded into the subcommand functions. All
4443 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4444 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4445 : * and does not travel through this section of code and cannot be combined with
4446 : * any of the subcommands given here.
4447 : *
1029 andres 4448 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4449 : * so any changes that might affect SELECTs running on standbys need to use
4450 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4451 : * have a solution for that also.
4452 : *
4453 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4454 : * that takes a lock less than AccessExclusiveLock can change object definitions
3290 simon 4455 : * while pg_dump is running. Be careful to check that the appropriate data is
4456 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4457 : * otherwise we might end up with an inconsistent dump that can't restore.
4458 : */
4459 : LOCKMODE
4638 simon 4460 GIC 45092 : AlterTableGetLockLevel(List *cmds)
4461 : {
4462 : /*
4463 : * This only works if we read catalog tables using MVCC snapshots.
4464 : */
4465 : ListCell *lcmd;
4382 bruce 4466 45092 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4467 :
4638 simon 4468 90928 : foreach(lcmd, cmds)
4469 : {
4470 45836 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4382 bruce 4471 45836 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4472 :
4638 simon 4473 CBC 45836 : switch (cmd->subtype)
4638 simon 4474 ECB : {
4475 : /*
4476 : * These subcommands rewrite the heap, so require full locks.
4477 : */
4382 bruce 4478 GIC 1490 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4382 bruce 4479 ECB : * to SELECT */
4480 : case AT_SetAccessMethod: /* must rewrite heap */
2118 tgl 4481 : case AT_SetTableSpace: /* must rewrite heap */
4638 simon 4482 : case AT_AlterColumnType: /* must rewrite heap */
3290 simon 4483 GBC 1490 : cmd_lockmode = AccessExclusiveLock;
3290 simon 4484 CBC 1490 : break;
4485 :
3290 simon 4486 ECB : /*
3260 bruce 4487 : * These subcommands may require addition of toast tables. If
4488 : * we add a toast table to a table currently being scanned, we
4489 : * might miss data added to the new toast table by concurrent
4490 : * insert transactions.
4491 : */
2118 tgl 4492 GIC 104 : case AT_SetStorage: /* may add toast tables, see
4493 : * ATRewriteCatalogs() */
3290 simon 4494 104 : cmd_lockmode = AccessExclusiveLock;
4495 104 : break;
4496 :
4497 : /*
3290 simon 4498 ECB : * Removing constraints can affect SELECTs that have been
4499 : * optimized assuming the constraint holds true. See also
4500 : * CloneFkReferenced.
4501 : */
2118 tgl 4502 GIC 393 : case AT_DropConstraint: /* as DROP INDEX */
4503 : case AT_DropNotNull: /* may change some SQL plans */
3290 simon 4504 393 : cmd_lockmode = AccessExclusiveLock;
4505 393 : break;
3290 simon 4506 ECB :
4507 : /*
4508 : * Subcommands that may be visible to concurrent SELECTs
4509 : */
2118 tgl 4510 CBC 814 : case AT_DropColumn: /* change visible to SELECT */
4511 : case AT_AddColumnToView: /* CREATE VIEW */
4512 : case AT_DropOids: /* used to equiv to DropColumn */
4638 simon 4513 ECB : case AT_EnableAlwaysRule: /* may change SELECT rules */
4514 : case AT_EnableReplicaRule: /* may change SELECT rules */
4515 : case AT_EnableRule: /* may change SELECT rules */
4516 : case AT_DisableRule: /* may change SELECT rules */
3290 simon 4517 GIC 814 : cmd_lockmode = AccessExclusiveLock;
4518 814 : break;
3290 simon 4519 ECB :
4520 : /*
4521 : * Changing owner may remove implicit SELECT privileges
4522 : */
2118 tgl 4523 GIC 861 : case AT_ChangeOwner: /* change visible to SELECT */
3290 simon 4524 861 : cmd_lockmode = AccessExclusiveLock;
4525 861 : break;
3290 simon 4526 ECB :
4527 : /*
4528 : * Changing foreign table options may affect optimization.
4529 : */
4481 rhaas 4530 GIC 117 : case AT_GenericOptions:
4265 rhaas 4531 ECB : case AT_AlterColumnGenericOptions:
4638 simon 4532 CBC 117 : cmd_lockmode = AccessExclusiveLock;
4638 simon 4533 GIC 117 : break;
4534 :
4535 : /*
4536 : * These subcommands affect write operations only.
4537 : */
4638 simon 4538 CBC 168 : case AT_EnableTrig:
4638 simon 4539 ECB : case AT_EnableAlwaysTrig:
4540 : case AT_EnableReplicaTrig:
4541 : case AT_EnableTrigAll:
4542 : case AT_EnableTrigUser:
4543 : case AT_DisableTrig:
4544 : case AT_DisableTrigAll:
4545 : case AT_DisableTrigUser:
2926 simon 4546 GIC 168 : cmd_lockmode = ShareRowExclusiveLock;
4547 168 : break;
4548 :
4549 : /*
4550 : * These subcommands affect write operations only. XXX
4551 : * Theoretically, these could be ShareRowExclusiveLock.
4552 : */
4553 4135 : case AT_ColumnDefault:
961 tgl 4554 ECB : case AT_CookedColumnDefault:
4555 : case AT_AlterConstraint:
4382 bruce 4556 : case AT_AddIndex: /* from ADD CONSTRAINT */
4457 tgl 4557 : case AT_AddIndexConstraint:
3439 rhaas 4558 EUB : case AT_ReplicaIdentity:
3290 simon 4559 ECB : case AT_SetNotNull:
4560 : case AT_SetAttNotNull:
4561 : case AT_EnableRowSecurity:
4562 : case AT_DisableRowSecurity:
4563 : case AT_ForceRowSecurity:
4564 : case AT_NoForceRowSecurity:
2194 peter_e 4565 : case AT_AddIdentity:
4566 : case AT_DropIdentity:
4567 : case AT_SetIdentity:
4568 : case AT_DropExpression:
751 rhaas 4569 : case AT_SetCompression:
3290 simon 4570 CBC 4135 : cmd_lockmode = AccessExclusiveLock;
4638 4571 4135 : break;
4572 :
4638 simon 4573 GIC 35237 : case AT_AddConstraint:
4574 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4575 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4576 35237 : if (IsA(cmd->def, Constraint))
4577 : {
4578 35237 : Constraint *con = (Constraint *) cmd->def;
4579 :
4580 35237 : switch (con->contype)
4581 : {
4582 33818 : case CONSTR_EXCLUSION:
4583 : case CONSTR_PRIMARY:
4584 : case CONSTR_UNIQUE:
4585 :
4586 : /*
4587 : * Cases essentially the same as CREATE INDEX. We
4588 : * could reduce the lock strength to ShareLock if
4589 : * we can work out how to allow concurrent catalog
4590 : * updates. XXX Might be set down to
4591 : * ShareRowExclusiveLock but requires further
4592 : * analysis.
4593 : */
3290 4594 33818 : cmd_lockmode = AccessExclusiveLock;
4638 4595 33818 : break;
4596 1000 : case CONSTR_FOREIGN:
4597 :
4638 simon 4598 ECB : /*
4599 : * We add triggers to both tables when we add a
4600 : * Foreign Key, so the lock level must be at least
4601 : * as strong as CREATE TRIGGER.
4602 : */
2926 simon 4603 CBC 1000 : cmd_lockmode = ShareRowExclusiveLock;
4638 4604 1000 : break;
4605 :
4638 simon 4606 GIC 419 : default:
3290 4607 419 : cmd_lockmode = AccessExclusiveLock;
4608 : }
4609 : }
4638 simon 4610 CBC 35237 : break;
4638 simon 4611 ECB :
4382 bruce 4612 : /*
4613 : * These subcommands affect inheritance behaviour. Queries
4614 : * started before us will continue to see the old inheritance
4615 : * behaviour, while queries started after we commit will see
4616 : * new behaviour. No need to prevent reads or writes to the
4617 : * subtable while we hook it up though. Changing the TupDesc
3260 4618 : * may be a problem, so keep highest lock.
4619 : */
4638 simon 4620 GIC 165 : case AT_AddInherit:
4621 : case AT_DropInherit:
3290 4622 165 : cmd_lockmode = AccessExclusiveLock;
4638 4623 165 : break;
4624 :
4625 : /*
4372 rhaas 4626 ECB : * These subcommands affect implicit row type conversion. They
4627 : * have affects similar to CREATE/DROP CAST on queries. don't
3260 bruce 4628 : * provide for invalidating parse trees as a result of such
4629 : * changes, so we keep these at AccessExclusiveLock.
4630 : */
4372 rhaas 4631 GIC 36 : case AT_AddOf:
4632 : case AT_DropOf:
3290 simon 4633 36 : cmd_lockmode = AccessExclusiveLock;
4634 36 : break;
4635 :
4636 : /*
4637 : * Only used by CREATE OR REPLACE VIEW which must conflict
4638 : * with an SELECTs currently using the view.
4639 : */
4640 97 : case AT_ReplaceRelOptions:
4641 97 : cmd_lockmode = AccessExclusiveLock;
4642 97 : break;
4643 :
4644 : /*
4645 : * These subcommands affect general strategies for performance
4646 : * and maintenance, though don't change the semantic results
4647 : * from normal data reads and writes. Delaying an ALTER TABLE
4648 : * behind currently active writes only delays the point where
4649 : * the new strategy begins to take effect, so there is no
4650 : * benefit in waiting. In this case the minimum restriction
4651 : * applies: we don't currently allow concurrent catalog
4652 : * updates.
4653 : */
2118 tgl 4654 117 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4655 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4656 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4657 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4658 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4638 simon 4659 117 : cmd_lockmode = ShareUpdateExclusiveLock;
4660 117 : break;
4661 :
3152 alvherre 4662 35 : case AT_SetLogged:
4663 : case AT_SetUnLogged:
4664 35 : cmd_lockmode = AccessExclusiveLock;
4665 35 : break;
4666 :
2118 tgl 4667 194 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
3290 simon 4668 194 : cmd_lockmode = ShareUpdateExclusiveLock;
4669 194 : break;
4670 :
4671 : /*
4672 : * Rel options are more complex than first appears. Options
4673 : * are set here for tables, views and indexes; for historical
4674 : * reasons these can all be used with ALTER TABLE, so we can't
4675 : * decide between them using the basic grammar.
4676 : */
2118 tgl 4677 367 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4678 : * getTables() */
4679 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4680 : * getTables() */
2795 simon 4681 367 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
3290 4682 367 : break;
4683 :
2314 rhaas 4684 1241 : case AT_AttachPartition:
1494 rhaas 4685 CBC 1241 : cmd_lockmode = ShareUpdateExclusiveLock;
1494 rhaas 4686 GIC 1241 : break;
4687 :
2314 4688 258 : case AT_DetachPartition:
745 alvherre 4689 258 : if (((PartitionCmd *) cmd->def)->concurrent)
4690 79 : cmd_lockmode = ShareUpdateExclusiveLock;
745 alvherre 4691 ECB : else
745 alvherre 4692 GIC 179 : cmd_lockmode = AccessExclusiveLock;
745 alvherre 4693 CBC 258 : break;
4694 :
4695 7 : case AT_DetachPartitionFinalize:
4696 7 : cmd_lockmode = ShareUpdateExclusiveLock;
2314 rhaas 4697 GIC 7 : break;
4698 :
1447 tgl 4699 UIC 0 : case AT_CheckNotNull:
4700 :
4701 : /*
4702 : * This only examines the table's schema; but lock must be
4703 : * strong enough to prevent concurrent DROP NOT NULL.
4704 : */
4705 0 : cmd_lockmode = AccessShareLock;
4706 0 : break;
4707 :
4382 bruce 4708 0 : default: /* oops */
4638 simon 4709 0 : elog(ERROR, "unrecognized alter table type: %d",
4710 : (int) cmd->subtype);
4711 : break;
4712 : }
4713 :
4638 simon 4714 ECB : /*
4715 : * Take the greatest lockmode from any subcommand
4716 : */
4638 simon 4717 CBC 45836 : if (cmd_lockmode > lockmode)
4638 simon 4718 GIC 43180 : lockmode = cmd_lockmode;
4638 simon 4719 ECB : }
4720 :
4638 simon 4721 CBC 45092 : return lockmode;
4722 : }
8315 JanWieck 4723 ECB :
3044 simon 4724 : /*
4725 : * ATController provides top level control over the phases.
4726 : *
4727 : * parsetree is passed in to allow it to be passed to event triggers
4728 : * when requested.
4729 : */
4730 : static void
3044 simon 4731 GIC 44972 : ATController(AlterTableStmt *parsetree,
4732 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4733 : AlterTableUtilityContext *context)
4734 : {
6913 tgl 4735 44972 : List *wqueue = NIL;
4736 : ListCell *lcmd;
4737 :
4738 : /* Phase 1: preliminary examination of commands, create work queue */
4739 90549 : foreach(lcmd, cmds)
4740 : {
4741 45713 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4742 :
1180 4743 45713 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4744 : }
4745 :
4746 : /* Close the relation, but keep lock until commit */
6913 4747 44836 : relation_close(rel, NoLock);
4748 :
4749 : /* Phase 2: update system catalogs */
1180 4750 44836 : ATRewriteCatalogs(&wqueue, lockmode, context);
4751 :
4752 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4753 43800 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
6913 4754 43653 : }
4755 :
4756 : /*
4757 : * ATPrepCmd
4758 : *
6869 tgl 4759 ECB : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4760 : * recursion and permission checks.
4761 : *
4762 : * Caller must have acquired appropriate lock type on relation already.
4763 : * This lock should be held until commit.
4764 : */
6913 4765 : static void
6913 tgl 4766 GIC 45921 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
1180 tgl 4767 ECB : bool recurse, bool recursing, LOCKMODE lockmode,
4768 : AlterTableUtilityContext *context)
6913 4769 : {
4770 : AlteredTableInfo *tab;
3571 simon 4771 GIC 45921 : int pass = AT_PASS_UNSET;
6913 tgl 4772 ECB :
4773 : /* Find or create work queue entry for this table */
6913 tgl 4774 GIC 45921 : tab = ATGetQueueEntry(wqueue, rel);
4775 :
4776 : /*
745 alvherre 4777 ECB : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4778 : * partitions that are pending detach.
4779 : */
745 alvherre 4780 GIC 45921 : if (rel->rd_rel->relispartition &&
4781 1124 : cmd->subtype != AT_DetachPartitionFinalize &&
745 alvherre 4782 CBC 562 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4783 1 : ereport(ERROR,
4784 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4785 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4786 : RelationGetRelationName(rel)),
4787 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4788 :
4789 : /*
4790 : * Copy the original subcommand for each table, so we can scribble on it.
248 alvherre 4791 ECB : * This avoids conflicts when different child tables need to make
4792 : * different parse transformations (for example, the same column may have
4793 : * different column numbers in different children).
7653 tgl 4794 : */
6913 tgl 4795 GIC 45920 : cmd = copyObject(cmd);
4796 :
4797 : /*
4798 : * Do permissions and relkind checking, recursion to child tables if
4799 : * needed, and any additional phase-1 processing needed. (But beware of
4800 : * adding any processing that looks at table details that another
1180 tgl 4801 ECB : * subcommand could change. In some cases we reject multiple subcommands
4802 : * that could try to change the same state in contrary ways.)
8313 JanWieck 4803 : */
6913 tgl 4804 CBC 45920 : switch (cmd->subtype)
4805 : {
6913 tgl 4806 GIC 911 : case AT_AddColumn: /* ADD COLUMN */
640 peter 4807 911 : ATSimplePermissions(cmd->subtype, rel,
4808 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
2963 alvherre 4809 CBC 911 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4810 : lockmode, context);
4811 : /* Recursion occurs during execution phase */
6913 tgl 4812 GIC 905 : pass = AT_PASS_ADD_COL;
4813 905 : break;
2118 4814 12 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
640 peter 4815 12 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
2963 alvherre 4816 CBC 12 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
1180 tgl 4817 ECB : lockmode, context);
4818 : /* Recursion occurs during execution phase */
5237 bruce 4819 GIC 12 : pass = AT_PASS_ADD_COL;
4820 12 : break;
6913 tgl 4821 266 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
6797 bruce 4822 ECB :
6913 tgl 4823 : /*
6385 bruce 4824 : * We allow defaults on views so that INSERT into a view can have
4825 : * default-ish behavior. This works because the rewriter
4826 : * substitutes default values into INSERTs before it expands
4827 : * rules.
4828 : */
640 peter 4829 CBC 266 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
1180 tgl 4830 GIC 266 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
6913 tgl 4831 ECB : /* No command-specific prep needed */
1180 tgl 4832 CBC 266 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
6913 tgl 4833 GIC 266 : break;
961 4834 28 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4835 : /* This is currently used only in CREATE TABLE */
4836 : /* (so the permission check really isn't necessary) */
640 peter 4837 CBC 28 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4838 : /* This command never recurses */
961 tgl 4839 GIC 28 : pass = AT_PASS_ADD_OTHERCONSTR;
4840 28 : break;
2194 peter_e 4841 51 : case AT_AddIdentity:
640 peter 4842 51 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4843 : /* This command never recurses */
1180 tgl 4844 51 : pass = AT_PASS_ADD_OTHERCONSTR;
2194 peter_e 4845 CBC 51 : break;
4846 19 : case AT_SetIdentity:
640 peter 4847 GIC 19 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4848 : /* This command never recurses */
4849 : /* This should run after AddIdentity, so do it in MISC pass */
1180 tgl 4850 19 : pass = AT_PASS_MISC;
2194 peter_e 4851 19 : break;
1356 alvherre 4852 CBC 19 : case AT_DropIdentity:
640 peter 4853 GIC 19 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4854 : /* This command never recurses */
1356 alvherre 4855 19 : pass = AT_PASS_DROP;
4856 19 : break;
6913 tgl 4857 110 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
640 peter 4858 110 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4859 : /* Set up recursion for phase 2; no other prep needed */
2 alvherre 4860 GNC 107 : if (recurse)
4861 101 : cmd->recurse = true;
6913 tgl 4862 GIC 107 : pass = AT_PASS_DROP;
4863 107 : break;
4864 162 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
640 peter 4865 162 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4866 : /* Need command-specific recursion decision */
2 alvherre 4867 GNC 159 : if (recurse)
4868 150 : cmd->recurse = true;
4869 159 : pass = AT_PASS_COL_ATTRS;
4870 159 : break;
4871 3155 : case AT_SetAttNotNull: /* set pg_attribute.attnotnull without adding
4872 : * a constraint */
4873 3155 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4874 : /* Need command-specific recursion decision */
4875 3155 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
1447 tgl 4876 GIC 3155 : pass = AT_PASS_COL_ATTRS;
1447 tgl 4877 CBC 3155 : break;
1447 tgl 4878 LBC 0 : case AT_CheckNotNull: /* check column is already marked NOT NULL */
640 peter 4879 UIC 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
1180 tgl 4880 LBC 0 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4881 : /* No command-specific prep needed */
1447 tgl 4882 UIC 0 : pass = AT_PASS_COL_ATTRS;
6913 tgl 4883 LBC 0 : break;
1180 tgl 4884 GIC 22 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
640 peter 4885 CBC 22 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
1180 tgl 4886 GIC 22 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
887 peter 4887 CBC 22 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
1181 peter 4888 GIC 16 : pass = AT_PASS_DROP;
1181 peter 4889 CBC 16 : break;
4998 tgl 4890 GIC 82 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
640 peter 4891 82 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
1180 tgl 4892 82 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4893 : /* No command-specific prep needed */
4637 simon 4894 82 : pass = AT_PASS_MISC;
6913 tgl 4895 82 : break;
4825 rhaas 4896 22 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4897 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
537 michael 4898 22 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4899 : /* This command never recurses */
4637 simon 4900 16 : pass = AT_PASS_MISC;
4998 tgl 4901 CBC 16 : break;
4902 115 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
640 peter 4903 115 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
1180 tgl 4904 GIC 115 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4905 : /* No command-specific prep needed */
4637 simon 4906 115 : pass = AT_PASS_MISC;
6913 tgl 4907 115 : break;
697 4908 33 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
640 peter 4909 33 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
751 rhaas 4910 ECB : /* This command never recurses */
4911 : /* No command-specific prep needed */
751 rhaas 4912 GIC 33 : pass = AT_PASS_MISC;
751 rhaas 4913 CBC 33 : break;
6913 tgl 4914 781 : case AT_DropColumn: /* DROP COLUMN */
640 peter 4915 GIC 781 : ATSimplePermissions(cmd->subtype, rel,
4916 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
1180 tgl 4917 CBC 778 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4918 : lockmode, context);
4919 : /* Recursion occurs during execution phase */
6913 tgl 4920 GIC 772 : pass = AT_PASS_DROP;
4921 772 : break;
6913 tgl 4922 UIC 0 : case AT_AddIndex: /* ADD INDEX */
640 peter 4923 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4924 : /* This command never recurses */
4925 : /* No command-specific prep needed */
6913 tgl 4926 0 : pass = AT_PASS_ADD_INDEX;
6913 tgl 4927 LBC 0 : break;
6913 tgl 4928 GIC 35224 : case AT_AddConstraint: /* ADD CONSTRAINT */
640 peter 4929 CBC 35224 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5448 tgl 4930 ECB : /* Recursion occurs during execution phase */
4931 : /* No command-specific prep needed except saving recurse flag */
5448 tgl 4932 GIC 35224 : if (recurse)
118 alvherre 4933 GNC 35072 : cmd->recurse = true;
6913 tgl 4934 GIC 35224 : pass = AT_PASS_ADD_CONSTR;
4935 35224 : break;
2118 tgl 4936 UIC 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
640 peter 4937 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4457 tgl 4938 ECB : /* This command never recurses */
4939 : /* No command-specific prep needed */
1180 tgl 4940 LBC 0 : pass = AT_PASS_ADD_INDEXCONSTR;
4457 4941 0 : break;
2118 tgl 4942 GIC 264 : case AT_DropConstraint: /* DROP CONSTRAINT */
640 peter 4943 264 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
1356 alvherre 4944 264 : ATCheckPartitionsNotInUse(rel, lockmode);
4945 : /* Other recursion occurs during execution phase */
4946 : /* No command-specific prep needed except saving recurse flag */
5448 tgl 4947 CBC 261 : if (recurse)
118 alvherre 4948 GNC 249 : cmd->recurse = true;
6913 tgl 4949 CBC 261 : pass = AT_PASS_DROP;
6913 tgl 4950 GIC 261 : break;
2118 4951 553 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
640 peter 4952 553 : ATSimplePermissions(cmd->subtype, rel,
4953 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4954 : /* See comments for ATPrepAlterColumnType */
1180 tgl 4955 553 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4956 : AT_PASS_UNSET, context);
4957 550 : Assert(cmd != NULL);
4958 : /* Performs own recursion */
4959 550 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4960 : lockmode, context);
6913 tgl 4961 CBC 478 : pass = AT_PASS_ALTER_TYPE;
6913 tgl 4962 GIC 478 : break;
4265 rhaas 4963 82 : case AT_AlterColumnGenericOptions:
640 peter 4964 82 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
4965 : /* This command never recurses */
4265 rhaas 4966 ECB : /* No command-specific prep needed */
4265 rhaas 4967 CBC 82 : pass = AT_PASS_MISC;
4265 rhaas 4968 GIC 82 : break;
6913 tgl 4969 CBC 849 : case AT_ChangeOwner: /* ALTER OWNER */
4970 : /* This command never recurses */
6913 tgl 4971 ECB : /* No command-specific prep needed */
6913 tgl 4972 CBC 849 : pass = AT_PASS_MISC;
6913 tgl 4973 GIC 849 : break;
6797 bruce 4974 CBC 32 : case AT_ClusterOn: /* CLUSTER ON */
6885 bruce 4975 ECB : case AT_DropCluster: /* SET WITHOUT CLUSTER */
640 peter 4976 CBC 32 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4977 : /* These commands never recurse */
4978 : /* No command-specific prep needed */
6913 tgl 4979 GIC 32 : pass = AT_PASS_MISC;
4980 32 : break;
3152 alvherre 4981 16 : case AT_SetLogged: /* SET LOGGED */
367 peter 4982 16 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
1180 tgl 4983 16 : if (tab->chgPersistence)
1180 tgl 4984 LBC 0 : ereport(ERROR,
4985 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4986 : errmsg("cannot change persistence setting twice")));
3149 alvherre 4987 GIC 16 : tab->chgPersistence = ATPrepChangePersistence(rel, true);
3149 alvherre 4988 ECB : /* force rewrite if necessary; see comment in ATRewriteTables */
3149 alvherre 4989 CBC 13 : if (tab->chgPersistence)
4990 : {
3044 simon 4991 10 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3149 alvherre 4992 10 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
3149 alvherre 4993 ECB : }
3152 alvherre 4994 GIC 13 : pass = AT_PASS_MISC;
3152 alvherre 4995 CBC 13 : break;
4996 19 : case AT_SetUnLogged: /* SET UNLOGGED */
367 peter 4997 19 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
1180 tgl 4998 GIC 19 : if (tab->chgPersistence)
1180 tgl 4999 LBC 0 : ereport(ERROR,
1180 tgl 5000 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5001 : errmsg("cannot change persistence setting twice")));
3149 alvherre 5002 CBC 19 : tab->chgPersistence = ATPrepChangePersistence(rel, false);
3149 alvherre 5003 ECB : /* force rewrite if necessary; see comment in ATRewriteTables */
3149 alvherre 5004 CBC 16 : if (tab->chgPersistence)
5005 : {
3044 simon 5006 GBC 13 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3149 alvherre 5007 GIC 13 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
5008 : }
3152 5009 16 : pass = AT_PASS_MISC;
5010 16 : break;
6797 bruce 5011 3 : case AT_DropOids: /* SET WITHOUT OIDS */
640 peter 5012 GBC 3 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6913 tgl 5013 3 : pass = AT_PASS_DROP;
6913 tgl 5014 GIC 3 : break;
620 michael 5015 GBC 24 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5016 24 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5017 :
5018 : /* partitioned tables don't have an access method */
620 michael 5019 GIC 24 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5020 3 : ereport(ERROR,
5021 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5022 : errmsg("cannot change access method of a partitioned table")));
5023 :
620 michael 5024 ECB : /* check if another access method change was already requested */
620 michael 5025 CBC 21 : if (OidIsValid(tab->newAccessMethod))
620 michael 5026 GIC 6 : ereport(ERROR,
5027 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
620 michael 5028 ECB : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5029 :
620 michael 5030 GIC 15 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5031 15 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5032 15 : break;
6846 tgl 5033 79 : case AT_SetTableSpace: /* SET TABLESPACE */
640 peter 5034 79 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
5035 : ATT_PARTITIONED_INDEX);
5036 : /* This command never recurses */
4638 simon 5037 79 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
6797 bruce 5038 CBC 79 : pass = AT_PASS_MISC; /* doesn't actually matter */
6846 tgl 5039 GIC 79 : break;
6031 bruce 5040 464 : case AT_SetRelOptions: /* SET (...) */
5041 : case AT_ResetRelOptions: /* RESET (...) */
2118 tgl 5042 ECB : case AT_ReplaceRelOptions: /* reset them all, then set just these */
640 peter 5043 GIC 464 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
5044 : /* This command never recurses */
5045 : /* No command-specific prep needed */
6125 bruce 5046 CBC 464 : pass = AT_PASS_MISC;
6125 bruce 5047 GIC 464 : break;
4643 peter_e 5048 CBC 143 : case AT_AddInherit: /* INHERIT */
640 peter 5049 GIC 143 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4643 peter_e 5050 ECB : /* This command never recurses */
4643 peter_e 5051 GIC 143 : ATPrepAddInherit(rel);
5052 134 : pass = AT_PASS_MISC;
5053 134 : break;
2940 tgl 5054 CBC 22 : case AT_DropInherit: /* NO INHERIT */
640 peter 5055 GIC 22 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5056 : /* This command never recurses */
2940 tgl 5057 ECB : /* No command-specific prep needed */
2940 tgl 5058 GIC 22 : pass = AT_PASS_MISC;
5059 22 : break;
2118 tgl 5060 CBC 36 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
640 peter 5061 36 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5062 : /* Recursion occurs during execution phase */
3571 simon 5063 GIC 33 : pass = AT_PASS_MISC;
5064 33 : break;
2118 tgl 5065 194 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
640 peter 5066 194 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5067 : /* Recursion occurs during execution phase */
5068 : /* No command-specific prep needed except saving recurse flag */
4330 alvherre 5069 194 : if (recurse)
118 alvherre 5070 GNC 194 : cmd->recurse = true;
4330 alvherre 5071 GIC 194 : pass = AT_PASS_MISC;
5072 194 : break;
2118 tgl 5073 CBC 198 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
640 peter 5074 GIC 198 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
3439 rhaas 5075 198 : pass = AT_PASS_MISC;
5076 : /* This command never recurses */
5077 : /* No command-specific prep needed */
3439 rhaas 5078 CBC 198 : break;
6438 tgl 5079 GIC 168 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5080 : case AT_EnableAlwaysTrig:
5865 JanWieck 5081 ECB : case AT_EnableReplicaTrig:
5082 : case AT_EnableTrigAll:
5083 : case AT_EnableTrigUser:
5084 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5085 : case AT_DisableTrigAll:
5086 : case AT_DisableTrigUser:
640 peter 5087 CBC 168 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
248 alvherre 5088 ECB : /* Set up recursion for phase 2; no other prep needed */
248 alvherre 5089 CBC 168 : if (recurse)
5090 154 : cmd->recurse = true;
3304 noah 5091 GIC 168 : pass = AT_PASS_MISC;
5092 168 : break;
5865 JanWieck 5093 239 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5094 : case AT_EnableAlwaysRule:
5095 : case AT_EnableReplicaRule:
5096 : case AT_DisableRule:
5097 : case AT_AddOf: /* OF */
5098 : case AT_DropOf: /* NOT OF */
5099 : case AT_EnableRowSecurity:
5100 : case AT_DisableRowSecurity:
5101 : case AT_ForceRowSecurity:
2744 sfrost 5102 ECB : case AT_NoForceRowSecurity:
640 peter 5103 GIC 239 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5104 : /* These commands never recurse */
5105 : /* No command-specific prep needed */
4481 rhaas 5106 239 : pass = AT_PASS_MISC;
5107 239 : break;
5108 23 : case AT_GenericOptions:
640 peter 5109 23 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5110 : /* No command-specific prep needed */
4481 rhaas 5111 CBC 23 : pass = AT_PASS_MISC;
4481 rhaas 5112 GIC 23 : break;
2314 rhaas 5113 CBC 1235 : case AT_AttachPartition:
640 peter 5114 1235 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
5115 : /* No command-specific prep needed */
1906 alvherre 5116 1235 : pass = AT_PASS_MISC;
1906 alvherre 5117 GIC 1235 : break;
2314 rhaas 5118 258 : case AT_DetachPartition:
640 peter 5119 CBC 258 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
2314 rhaas 5120 ECB : /* No command-specific prep needed */
2314 rhaas 5121 CBC 255 : pass = AT_PASS_MISC;
5122 255 : break;
745 alvherre 5123 7 : case AT_DetachPartitionFinalize:
640 peter 5124 GIC 7 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5125 : /* No command-specific prep needed */
745 alvherre 5126 CBC 7 : pass = AT_PASS_MISC;
5127 7 : break;
6797 bruce 5128 LBC 0 : default: /* oops */
6913 tgl 5129 UIC 0 : elog(ERROR, "unrecognized alter table type: %d",
5130 : (int) cmd->subtype);
5131 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5132 : break;
5133 : }
3571 simon 5134 GIC 45779 : Assert(pass > AT_PASS_UNSET);
5135 :
6913 tgl 5136 ECB : /* Add the subcommand to the appropriate list for phase 2 */
6913 tgl 5137 CBC 45779 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
6913 tgl 5138 GIC 45779 : }
7653 tgl 5139 ECB :
6913 5140 : /*
5141 : * ATRewriteCatalogs
5142 : *
5143 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5144 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5145 : * conflicts).
5146 : */
5147 : static void
1180 tgl 5148 CBC 44836 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
1180 tgl 5149 ECB : AlterTableUtilityContext *context)
5150 : {
6913 5151 : int pass;
6892 neilc 5152 : ListCell *ltab;
6913 tgl 5153 :
5154 : /*
5155 : * We process all the tables "in parallel", one pass at a time. This is
5156 : * needed because we may have to propagate work from one table to another
6385 bruce 5157 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5158 : * re-adding of the foreign key constraint to the other table). Work can
5159 : * only be propagated into later passes, however.
6913 tgl 5160 : */
6913 tgl 5161 GIC 533639 : for (pass = 0; pass < AT_NUM_PASSES; pass++)
6913 tgl 5162 ECB : {
5163 : /* Go through each table that needs to be processed */
6913 tgl 5164 CBC 983980 : foreach(ltab, *wqueue)
7653 tgl 5165 ECB : {
6913 tgl 5166 GIC 495177 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6913 tgl 5167 CBC 495177 : List *subcmds = tab->subcmds[pass];
6892 neilc 5168 ECB : ListCell *lcmd;
7653 tgl 5169 :
6913 tgl 5170 CBC 495177 : if (subcmds == NIL)
7653 5171 395396 : continue;
7653 tgl 5172 ECB :
5173 : /*
745 alvherre 5174 : * Open the relation and store it in tab. This allows subroutines
5175 : * close and reopen, if necessary. Appropriate lock was obtained
5176 : * by phase 1, needn't get it again.
6797 bruce 5177 : */
745 alvherre 5178 CBC 99781 : tab->rel = relation_open(tab->relid, NoLock);
5179 :
6913 tgl 5180 205421 : foreach(lcmd, subcmds)
745 alvherre 5181 GIC 106676 : ATExecCmd(wqueue, tab,
629 peter 5182 CBC 106676 : lfirst_node(AlterTableCmd, lcmd),
1180 tgl 5183 ECB : lockmode, pass, context);
7463 5184 :
7463 tgl 5185 EUB : /*
6385 bruce 5186 : * After the ALTER TYPE pass, do cleanup work (this is not done in
5187 : * ATExecAlterColumnType since it should be done only once if
5188 : * multiple columns of a table are altered).
7463 tgl 5189 : */
6913 tgl 5190 GBC 98745 : if (pass == AT_PASS_ALTER_TYPE)
4638 simon 5191 CBC 430 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
7475 bruce 5192 ECB :
745 alvherre 5193 CBC 98745 : if (tab->rel)
745 alvherre 5194 ECB : {
745 alvherre 5195 CBC 98745 : relation_close(tab->rel, NoLock);
5196 98745 : tab->rel = NULL;
745 alvherre 5197 ECB : }
6913 tgl 5198 : }
5199 : }
5200 :
4379 rhaas 5201 : /* Check to see if a toast table must be added. */
6913 tgl 5202 CBC 89325 : foreach(ltab, *wqueue)
6913 tgl 5203 ECB : {
6913 tgl 5204 GIC 45525 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
7463 tgl 5205 ECB :
5206 : /*
2314 rhaas 5207 : * If the table is source table of ATTACH PARTITION command, we did
5208 : * not modify anything about it that will change its toasting
5209 : * requirement, so no need to check.
5210 : */
2314 rhaas 5211 CBC 45525 : if (((tab->relkind == RELKIND_RELATION ||
2314 rhaas 5212 GIC 2926 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
2205 tgl 5213 CBC 44623 : tab->partition_constraint == NULL) ||
3689 kgrittn 5214 1811 : tab->relkind == RELKIND_MATVIEW)
3290 simon 5215 43739 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
7653 tgl 5216 ECB : }
6913 tgl 5217 GIC 43800 : }
5218 :
6913 tgl 5219 ECB : /*
5220 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5221 : */
5222 : static void
745 alvherre 5223 GIC 106676 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
1180 tgl 5224 ECB : AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
5225 : AlterTableUtilityContext *context)
5226 : {
2937 alvherre 5227 CBC 106676 : ObjectAddress address = InvalidObjectAddress;
745 5228 106676 : Relation rel = tab->rel;
2937 alvherre 5229 EUB :
6913 tgl 5230 GBC 106676 : switch (cmd->subtype)
5231 : {
6913 tgl 5232 GIC 914 : case AT_AddColumn: /* ADD COLUMN */
2118 tgl 5233 EUB : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
1180 tgl 5234 GBC 914 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
118 alvherre 5235 GNC 914 : cmd->recurse, false,
1180 tgl 5236 ECB : lockmode, cur_pass, context);
6913 tgl 5237 CBC 857 : break;
6913 tgl 5238 GBC 266 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
2937 alvherre 5239 266 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
6913 tgl 5240 GIC 242 : break;
961 5241 28 : case AT_CookedColumnDefault: /* add a pre-cooked default */
961 tgl 5242 GBC 28 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5243 28 : break;
2194 peter_e 5244 CBC 51 : case AT_AddIdentity:
1180 tgl 5245 51 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
1180 tgl 5246 ECB : cur_pass, context);
1180 tgl 5247 GIC 48 : Assert(cmd != NULL);
2194 peter_e 5248 48 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
2194 peter_e 5249 CBC 36 : break;
5250 19 : case AT_SetIdentity:
1180 tgl 5251 19 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
1180 tgl 5252 ECB : cur_pass, context);
1180 tgl 5253 CBC 19 : Assert(cmd != NULL);
2194 peter_e 5254 19 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
2194 peter_e 5255 GIC 16 : break;
5256 19 : case AT_DropIdentity:
2194 peter_e 5257 CBC 19 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
2194 peter_e 5258 GIC 16 : break;
6913 tgl 5259 CBC 107 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
2 alvherre 5260 GNC 107 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
6913 tgl 5261 CBC 65 : break;
6913 tgl 5262 GIC 159 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
2 alvherre 5263 GNC 159 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5264 159 : cmd->recurse, false, NULL, lockmode);
5265 144 : break;
5266 28396 : case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
5267 28396 : ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
6913 tgl 5268 CBC 28387 : break;
1447 tgl 5269 LBC 0 : case AT_CheckNotNull: /* check column is already marked NOT NULL */
5270 0 : ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
1447 tgl 5271 UIC 0 : break;
1181 peter 5272 GIC 16 : case AT_DropExpression:
1181 peter 5273 CBC 16 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5274 13 : break;
4998 tgl 5275 82 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
2041 simon 5276 GIC 82 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
6913 tgl 5277 58 : break;
4825 rhaas 5278 CBC 13 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
2937 alvherre 5279 13 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
4825 rhaas 5280 13 : break;
4825 rhaas 5281 GIC 3 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
2937 alvherre 5282 CBC 3 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
4998 tgl 5283 GIC 3 : break;
5284 115 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
2937 alvherre 5285 CBC 115 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
6913 tgl 5286 109 : break;
270 peter 5287 GNC 33 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5288 33 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
751 rhaas 5289 ECB : lockmode);
751 rhaas 5290 GBC 30 : break;
6913 tgl 5291 GIC 772 : case AT_DropColumn: /* DROP COLUMN */
2937 alvherre 5292 772 : address = ATExecDropColumn(wqueue, rel, cmd->name,
118 alvherre 5293 GNC 772 : cmd->behavior, cmd->recurse, false,
1274 michael 5294 CBC 772 : cmd->missing_ok, lockmode,
1274 michael 5295 ECB : NULL);
6913 tgl 5296 CBC 688 : break;
5297 435 : case AT_AddIndex: /* ADD INDEX */
2937 alvherre 5298 435 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
2937 alvherre 5299 EUB : lockmode);
6913 tgl 5300 GIC 383 : break;
5301 199 : case AT_ReAddIndex: /* ADD INDEX */
2937 alvherre 5302 CBC 199 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5303 : lockmode);
6913 tgl 5304 199 : break;
744 tomas.vondra 5305 GIC 7 : case AT_ReAddStatistics: /* ADD STATISTICS */
744 tomas.vondra 5306 CBC 7 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
744 tomas.vondra 5307 ECB : true, lockmode);
744 tomas.vondra 5308 GIC 7 : break;
6913 tgl 5309 CBC 36768 : case AT_AddConstraint: /* ADD CONSTRAINT */
960 tgl 5310 ECB : /* Transform the command only during initial examination */
960 tgl 5311 CBC 36768 : if (cur_pass == AT_PASS_ADD_CONSTR)
5312 35209 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
118 alvherre 5313 GNC 35224 : cmd->recurse, lockmode,
960 tgl 5314 ECB : cur_pass, context);
5315 : /* Depending on constraint type, might be no more work to do now */
1180 tgl 5316 CBC 36753 : if (cmd != NULL)
5317 : address =
1180 tgl 5318 GIC 1544 : ATExecAddConstraint(wqueue, tab, rel,
1180 tgl 5319 CBC 1544 : (Constraint *) cmd->def,
118 alvherre 5320 GNC 1544 : cmd->recurse, false, lockmode);
3807 tgl 5321 CBC 36566 : break;
2118 tgl 5322 GIC 82 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5323 : address =
2937 alvherre 5324 CBC 82 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
2697 tgl 5325 ECB : true, true, lockmode);
6913 tgl 5326 CBC 76 : break;
1985 5327 7 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5328 : * constraint */
5329 : address =
5330 7 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
1985 tgl 5331 GIC 7 : ((AlterDomainStmt *) cmd->def)->def,
5332 : NULL);
1985 tgl 5333 CBC 4 : break;
2826 heikki.linnakangas 5334 27 : case AT_ReAddComment: /* Re-add existing comment */
5335 27 : address = CommentObject((CommentStmt *) cmd->def);
5336 27 : break;
2118 tgl 5337 GIC 33361 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
2937 alvherre 5338 CBC 33361 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
2937 alvherre 5339 ECB : lockmode);
4457 tgl 5340 CBC 33355 : break;
2118 5341 33 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
2937 alvherre 5342 33 : address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
3571 simon 5343 GIC 27 : break;
2118 tgl 5344 194 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
118 alvherre 5345 GNC 194 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
999 drowley 5346 ECB : false, lockmode);
4443 simon 5347 CBC 194 : break;
2118 tgl 5348 261 : case AT_DropConstraint: /* DROP CONSTRAINT */
5011 andrew 5349 GIC 261 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
118 alvherre 5350 GNC 261 : cmd->recurse, false,
4638 simon 5351 CBC 261 : cmd->missing_ok, lockmode);
6913 tgl 5352 174 : break;
2118 tgl 5353 GIC 463 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5354 : /* parse transformation was done earlier */
2937 alvherre 5355 CBC 463 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
6913 tgl 5356 445 : break;
2118 tgl 5357 GIC 82 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5358 : address =
2937 alvherre 5359 82 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5360 82 : (List *) cmd->def, lockmode);
4265 rhaas 5361 79 : break;
6913 tgl 5362 849 : case AT_ChangeOwner: /* ALTER OWNER */
6494 5363 846 : ATExecChangeOwner(RelationGetRelid(rel),
2953 alvherre 5364 CBC 849 : get_rolespec_oid(cmd->newowner, false),
5365 : false, lockmode);
6913 tgl 5366 840 : break;
5367 32 : case AT_ClusterOn: /* CLUSTER ON */
2937 alvherre 5368 32 : address = ATExecClusterOn(rel, cmd->name, lockmode);
6913 tgl 5369 29 : break;
6797 bruce 5370 9 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
4638 simon 5371 GIC 9 : ATExecDropCluster(rel, lockmode);
6885 bruce 5372 6 : break;
3152 alvherre 5373 29 : case AT_SetLogged: /* SET LOGGED */
5374 : case AT_SetUnLogged: /* SET UNLOGGED */
5375 29 : break;
6913 tgl 5376 3 : case AT_DropOids: /* SET WITHOUT OIDS */
5377 : /* nothing to do here, oid columns don't exist anymore */
5378 3 : break;
620 michael 5379 9 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
620 michael 5380 ECB : /* handled specially in Phase 3 */
620 michael 5381 GIC 9 : break;
6797 bruce 5382 79 : case AT_SetTableSpace: /* SET TABLESPACE */
1418 tgl 5383 ECB :
6846 5384 : /*
1574 alvherre 5385 : * Only do this for partitioned tables and indexes, for which this
5386 : * is just a catalog change. Other relation types which have
5387 : * storage are handled by Phase 3.
6846 tgl 5388 : */
1574 alvherre 5389 CBC 79 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5390 73 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5391 18 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5392 :
6846 tgl 5393 76 : break;
6031 bruce 5394 464 : case AT_SetRelOptions: /* SET (...) */
2118 tgl 5395 ECB : case AT_ResetRelOptions: /* RESET (...) */
5396 : case AT_ReplaceRelOptions: /* replace entire option list */
4126 rhaas 5397 GIC 464 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
6125 bruce 5398 CBC 438 : break;
5624 5399 60 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5400 60 : ATExecEnableDisableTrigger(rel, cmd->name,
248 alvherre 5401 ECB : TRIGGER_FIRES_ON_ORIGIN, false,
248 alvherre 5402 GIC 60 : cmd->recurse,
248 alvherre 5403 ECB : lockmode);
5865 JanWieck 5404 CBC 60 : break;
2118 tgl 5405 GBC 20 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5624 bruce 5406 20 : ATExecEnableDisableTrigger(rel, cmd->name,
5407 : TRIGGER_FIRES_ALWAYS, false,
248 alvherre 5408 GIC 20 : cmd->recurse,
5409 : lockmode);
5865 JanWieck 5410 20 : break;
2118 tgl 5411 CBC 8 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5624 bruce 5412 GIC 8 : ATExecEnableDisableTrigger(rel, cmd->name,
5413 : TRIGGER_FIRES_ON_REPLICA, false,
248 alvherre 5414 CBC 8 : cmd->recurse,
248 alvherre 5415 ECB : lockmode);
6438 tgl 5416 GIC 8 : break;
5417 68 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5624 bruce 5418 68 : ATExecEnableDisableTrigger(rel, cmd->name,
5419 : TRIGGER_DISABLED, false,
248 alvherre 5420 68 : cmd->recurse,
5421 : lockmode);
6438 tgl 5422 68 : break;
6438 tgl 5423 UIC 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5624 bruce 5424 0 : ATExecEnableDisableTrigger(rel, NULL,
248 alvherre 5425 ECB : TRIGGER_FIRES_ON_ORIGIN, false,
248 alvherre 5426 UIC 0 : cmd->recurse,
5427 : lockmode);
6438 tgl 5428 0 : break;
2118 tgl 5429 GIC 6 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5624 bruce 5430 6 : ATExecEnableDisableTrigger(rel, NULL,
5431 : TRIGGER_DISABLED, false,
248 alvherre 5432 6 : cmd->recurse,
5433 : lockmode);
6438 tgl 5434 6 : break;
2118 tgl 5435 UIC 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5624 bruce 5436 0 : ATExecEnableDisableTrigger(rel, NULL,
5437 : TRIGGER_FIRES_ON_ORIGIN, true,
248 alvherre 5438 LBC 0 : cmd->recurse,
5439 : lockmode);
6438 tgl 5440 UIC 0 : break;
2118 tgl 5441 CBC 6 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5624 bruce 5442 GIC 6 : ATExecEnableDisableTrigger(rel, NULL,
248 alvherre 5443 ECB : TRIGGER_DISABLED, true,
248 alvherre 5444 CBC 6 : cmd->recurse,
5445 : lockmode);
5865 JanWieck 5446 GIC 6 : break;
5865 JanWieck 5447 ECB :
5624 bruce 5448 CBC 3 : case AT_EnableRule: /* ENABLE RULE name */
5624 bruce 5449 GIC 3 : ATExecEnableDisableRule(rel, cmd->name,
5450 : RULE_FIRES_ON_ORIGIN, lockmode);
5865 JanWieck 5451 3 : break;
2118 tgl 5452 UIC 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5624 bruce 5453 0 : ATExecEnableDisableRule(rel, cmd->name,
5454 : RULE_FIRES_ALWAYS, lockmode);
5865 JanWieck 5455 LBC 0 : break;
2118 tgl 5456 GIC 3 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5624 bruce 5457 CBC 3 : ATExecEnableDisableRule(rel, cmd->name,
4638 simon 5458 ECB : RULE_FIRES_ON_REPLICA, lockmode);
5865 JanWieck 5459 CBC 3 : break;
5865 JanWieck 5460 GIC 3 : case AT_DisableRule: /* DISABLE RULE name */
5624 bruce 5461 3 : ATExecEnableDisableRule(rel, cmd->name,
5462 : RULE_DISABLED, lockmode);
6438 tgl 5463 3 : break;
5464 :
6022 5465 134 : case AT_AddInherit:
2937 alvherre 5466 134 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
6125 bruce 5467 CBC 95 : break;
6022 tgl 5468 22 : case AT_DropInherit:
2937 alvherre 5469 GIC 22 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
6125 bruce 5470 CBC 19 : break;
4372 rhaas 5471 GIC 33 : case AT_AddOf:
2937 alvherre 5472 CBC 33 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
4372 rhaas 5473 15 : break;
4372 rhaas 5474 GIC 3 : case AT_DropOf:
5475 3 : ATExecDropOf(rel, lockmode);
5476 3 : break;
3439 5477 207 : case AT_ReplicaIdentity:
5478 207 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
3439 rhaas 5479 CBC 186 : break;
3124 sfrost 5480 GIC 135 : case AT_EnableRowSecurity:
768 michael 5481 CBC 135 : ATExecSetRowSecurity(rel, true);
3124 sfrost 5482 GIC 135 : break;
5483 4 : case AT_DisableRowSecurity:
768 michael 5484 4 : ATExecSetRowSecurity(rel, false);
3124 sfrost 5485 4 : break;
2744 5486 40 : case AT_ForceRowSecurity:
5487 40 : ATExecForceNoForceRowSecurity(rel, true);
2744 sfrost 5488 CBC 40 : break;
5489 15 : case AT_NoForceRowSecurity:
5490 15 : ATExecForceNoForceRowSecurity(rel, false);
5491 15 : break;
4481 rhaas 5492 23 : case AT_GenericOptions:
4481 rhaas 5493 GIC 23 : ATExecGenericOptions(rel, (List *) cmd->def);
4481 rhaas 5494 CBC 22 : break;
2314 rhaas 5495 GIC 1235 : case AT_AttachPartition:
1180 tgl 5496 1235 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5497 : cur_pass, context);
5498 1220 : Assert(cmd != NULL);
1906 alvherre 5499 1220 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
252 michael 5500 GNC 1034 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5501 : context);
5502 : else
5503 186 : address = ATExecAttachPartitionIdx(wqueue, rel,
5504 186 : ((PartitionCmd *) cmd->def)->name);
2314 rhaas 5505 CBC 1064 : break;
2314 rhaas 5506 GIC 255 : case AT_DetachPartition:
1180 tgl 5507 CBC 255 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5508 : cur_pass, context);
5509 252 : Assert(cmd != NULL);
5510 : /* ATPrepCmd ensures it must be a table */
1906 alvherre 5511 252 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
252 michael 5512 GNC 252 : address = ATExecDetachPartition(wqueue, tab, rel,
5513 252 : ((PartitionCmd *) cmd->def)->name,
5514 252 : ((PartitionCmd *) cmd->def)->concurrent);
745 alvherre 5515 CBC 187 : break;
5516 7 : case AT_DetachPartitionFinalize:
252 michael 5517 GNC 7 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
2314 rhaas 5518 CBC 7 : break;
6797 bruce 5519 LBC 0 : default: /* oops */
6913 tgl 5520 0 : elog(ERROR, "unrecognized alter table type: %d",
6913 tgl 5521 ECB : (int) cmd->subtype);
5522 : break;
5523 : }
7653 5524 :
2890 alvherre 5525 : /*
5526 : * Report the subcommand to interested event triggers.
5527 : */
1180 tgl 5528 CBC 105640 : if (cmd)
1180 tgl 5529 GIC 70431 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
2937 alvherre 5530 ECB :
7653 tgl 5531 : /*
6385 bruce 5532 : * Bump the command counter to ensure the next subcommand in the sequence
5533 : * can see the changes so far
7653 tgl 5534 : */
6913 tgl 5535 CBC 105640 : CommandCounterIncrement();
5536 105640 : }
7653 tgl 5537 ECB :
1180 5538 : /*
5539 : * ATParseTransformCmd: perform parse transformation for one subcommand
5540 : *
5541 : * Returns the transformed subcommand tree, if there is one, else NULL.
5542 : *
5543 : * The parser may hand back additional AlterTableCmd(s) and/or other
5544 : * utility statements, either before or after the original subcommand.
5545 : * Other AlterTableCmds are scheduled into the appropriate slot of the
1180 tgl 5546 EUB : * AlteredTableInfo (they had better be for later passes than the current one).
5547 : * Utility statements that are supposed to happen before the AlterTableCmd
5548 : * are executed immediately. Those that are supposed to happen afterwards
1180 tgl 5549 ECB : * are added to the tab->afterStmts list to be done at the very end.
5550 : */
5551 : static AlterTableCmd *
1180 tgl 5552 CBC 38191 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
1180 tgl 5553 ECB : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5554 : int cur_pass, AlterTableUtilityContext *context)
5555 : {
1180 tgl 5556 CBC 38191 : AlterTableCmd *newcmd = NULL;
5557 38191 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
1180 tgl 5558 ECB : List *beforeStmts;
5559 : List *afterStmts;
5560 : ListCell *lc;
5561 :
5562 : /* Gin up an AlterTableStmt with just this subcommand and this table */
1180 tgl 5563 CBC 38191 : atstmt->relation =
5564 38191 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5565 38191 : pstrdup(RelationGetRelationName(rel)),
5566 : -1);
5567 38191 : atstmt->relation->inh = recurse;
5568 38191 : atstmt->cmds = list_make1(cmd);
1002 michael 5569 38191 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
1180 tgl 5570 38191 : atstmt->missing_ok = false;
1180 tgl 5571 ECB :
5572 : /* Transform the AlterTableStmt */
1180 tgl 5573 CBC 38191 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
1180 tgl 5574 ECB : atstmt,
5575 : context->queryString,
5576 : &beforeStmts,
5577 : &afterStmts);
5578 :
5579 : /* Execute any statements that should happen before these subcommand(s) */
1180 tgl 5580 GIC 38313 : foreach(lc, beforeStmts)
1180 tgl 5581 ECB : {
1180 tgl 5582 CBC 161 : Node *stmt = (Node *) lfirst(lc);
1180 tgl 5583 ECB :
1180 tgl 5584 GIC 161 : ProcessUtilityForAlterTable(stmt, context);
1180 tgl 5585 CBC 155 : CommandCounterIncrement();
1180 tgl 5586 ECB : }
5587 :
5588 : /* Examine the transformed subcommands and schedule them appropriately */
1180 tgl 5589 CBC 101688 : foreach(lc, atstmt->cmds)
1180 tgl 5590 ECB : {
1180 tgl 5591 GIC 63536 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5592 : int pass;
1180 tgl 5593 ECB :
5594 : /*
960 5595 : * This switch need only cover the subcommand types that can be added
5596 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5597 : * executing the subcommand immediately, as a substitute for the
5598 : * original subcommand. (Note, however, that this does cause
5599 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5600 : * which is important for index and foreign key constraints.)
5601 : *
5602 : * We assume we needn't do any phase-1 checks for added subcommands.
5603 : */
960 tgl 5604 CBC 63536 : switch (cmd2->subtype)
5605 : {
2 alvherre 5606 GNC 25241 : case AT_SetAttNotNull:
5607 25241 : ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
960 tgl 5608 CBC 25241 : pass = AT_PASS_COL_ATTRS;
5609 25241 : break;
5610 444 : case AT_AddIndex:
960 tgl 5611 ECB : /* This command never recurses */
5612 : /* No command-specific prep needed */
960 tgl 5613 GIC 444 : pass = AT_PASS_ADD_INDEX;
960 tgl 5614 CBC 444 : break;
5615 33361 : case AT_AddIndexConstraint:
960 tgl 5616 ECB : /* This command never recurses */
5617 : /* No command-specific prep needed */
960 tgl 5618 CBC 33361 : pass = AT_PASS_ADD_INDEXCONSTR;
5619 33361 : break;
960 tgl 5620 GIC 1547 : case AT_AddConstraint:
960 tgl 5621 ECB : /* Recursion occurs during execution phase */
960 tgl 5622 CBC 1547 : if (recurse)
118 alvherre 5623 GNC 1526 : cmd2->recurse = true;
960 tgl 5624 CBC 1547 : switch (castNode(Constraint, cmd2->def)->contype)
960 tgl 5625 ECB : {
960 tgl 5626 LBC 0 : case CONSTR_PRIMARY:
960 tgl 5627 ECB : case CONSTR_UNIQUE:
5628 : case CONSTR_EXCLUSION:
960 tgl 5629 LBC 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5630 0 : break;
960 tgl 5631 CBC 1547 : default:
960 tgl 5632 GIC 1547 : pass = AT_PASS_ADD_OTHERCONSTR;
960 tgl 5633 CBC 1547 : break;
960 tgl 5634 ECB : }
960 tgl 5635 CBC 1547 : break;
960 tgl 5636 LBC 0 : case AT_AlterColumnGenericOptions:
960 tgl 5637 ECB : /* This command never recurses */
5638 : /* No command-specific prep needed */
960 tgl 5639 UIC 0 : pass = AT_PASS_MISC;
960 tgl 5640 LBC 0 : break;
960 tgl 5641 CBC 2943 : default:
5642 2943 : pass = cur_pass;
5643 2943 : break;
960 tgl 5644 ECB : }
5645 :
960 tgl 5646 CBC 63536 : if (pass < cur_pass)
960 tgl 5647 ECB : {
5648 : /* Cannot schedule into a pass we already finished */
960 tgl 5649 LBC 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
960 tgl 5650 ECB : pass);
5651 : }
960 tgl 5652 CBC 63536 : else if (pass > cur_pass)
960 tgl 5653 ECB : {
5654 : /* OK, queue it up for later */
960 tgl 5655 CBC 60593 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
1180 tgl 5656 ECB : }
5657 : else
5658 : {
5659 : /*
5660 : * We should see at most one subcommand for the current pass,
5661 : * which is the transformed version of the original subcommand.
5662 : */
960 tgl 5663 CBC 2943 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
1180 tgl 5664 ECB : {
960 5665 : /* Found the transformed version of our subcommand */
960 tgl 5666 GIC 2943 : newcmd = cmd2;
1180 tgl 5667 ECB : }
960 5668 : else
960 tgl 5669 UIC 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5670 : pass);
1180 tgl 5671 ECB : }
5672 : }
5673 :
5674 : /* Queue up any after-statements to happen at the end */
1180 tgl 5675 GIC 38152 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
1180 tgl 5676 ECB :
1180 tgl 5677 GIC 38152 : return newcmd;
1180 tgl 5678 ECB : }
5679 :
6913 5680 : /*
5681 : * ATRewriteTables: ALTER TABLE phase 3
5682 : */
5683 : static void
1180 tgl 5684 CBC 43800 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
1180 tgl 5685 ECB : AlterTableUtilityContext *context)
6913 5686 : {
5687 : ListCell *ltab;
7653 5688 :
5689 : /* Go through each table that needs to be checked or rewritten */
6913 tgl 5690 CBC 89203 : foreach(ltab, *wqueue)
6913 tgl 5691 ECB : {
6913 tgl 5692 CBC 45519 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5693 :
1464 tgl 5694 ECB : /* Relations without storage may be ignored here */
1464 tgl 5695 GIC 45519 : if (!RELKIND_HAS_STORAGE(tab->relkind))
4481 rhaas 5696 CBC 2788 : continue;
4481 rhaas 5697 EUB :
4439 5698 : /*
5699 : * If we change column data types, the operation has to be propagated
1028 michael 5700 : * to tables that use this table's rowtype as a column type.
5701 : * tab->newvals will also be non-NULL in the case where we're adding a
5702 : * column with a default. We choose to forbid that case as well,
1028 michael 5703 ECB : * since composite types might eventually support defaults.
4439 rhaas 5704 : *
5705 : * (Eventually we'll probably need to check for composite type
5706 : * dependencies even when we're just scanning the table without a
5707 : * rewrite, but at the moment a composite type does not enforce any
4382 bruce 5708 : * constraints, so it's not necessary/appropriate to enforce them just
4382 bruce 5709 EUB : * during ALTER.)
4439 rhaas 5710 : */
3044 simon 5711 GIC 42731 : if (tab->newvals != NIL || tab->rewrite > 0)
4439 rhaas 5712 EUB : {
5713 : Relation rel;
5714 :
1539 andres 5715 CBC 652 : rel = table_open(tab->relid, NoLock);
4439 rhaas 5716 652 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
1539 andres 5717 GIC 646 : table_close(rel, NoLock);
4439 rhaas 5718 ECB : }
5719 :
6913 tgl 5720 : /*
5721 : * We only need to rewrite the table if at least one column needs to
620 michael 5722 : * be recomputed, or we are changing its persistence or access method.
3149 alvherre 5723 : *
5724 : * There are two reasons for requiring a rewrite when changing
5725 : * persistence: on one hand, we need to ensure that the buffers
3149 alvherre 5726 EUB : * belonging to each of the two relations are marked with or without
5727 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5728 : * and assigns a new relfilenumber, we automatically create or drop an
5729 : * init fork for the relation as appropriate.
6913 tgl 5730 ECB : */
367 peter 5731 CBC 42725 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
6913 tgl 5732 GIC 356 : {
6913 tgl 5733 ECB : /* Build a temporary relation and copy data */
4812 5734 : Relation OldHeap;
6913 5735 : Oid OIDNewHeap;
5736 : Oid NewAccessMethod;
6846 5737 : Oid NewTableSpace;
5738 : char persistence;
6913 5739 :
1539 andres 5740 CBC 372 : OldHeap = table_open(tab->relid, NoLock);
6910 tgl 5741 ECB :
5742 : /*
4790 bruce 5743 : * We don't support rewriting of system catalogs; there are too
5744 : * many corner cases and too little benefit. In particular this
5745 : * is certainly not going to work for mapped catalogs.
6910 tgl 5746 : */
4809 tgl 5747 CBC 372 : if (IsSystemRelation(OldHeap))
6910 tgl 5748 LBC 0 : ereport(ERROR,
6910 tgl 5749 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5750 : errmsg("cannot rewrite system relation \"%s\"",
5751 : RelationGetRelationName(OldHeap))));
5752 :
3407 rhaas 5753 CBC 372 : if (RelationIsUsedAsCatalogTable(OldHeap))
5754 1 : ereport(ERROR,
3407 rhaas 5755 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2118 tgl 5756 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5757 : RelationGetRelationName(OldHeap))));
3407 rhaas 5758 :
6910 tgl 5759 : /*
6385 bruce 5760 : * Don't allow rewrite on temp tables of other backends ... their
5761 : * local buffer manager is not going to cope.
6910 tgl 5762 : */
5122 tgl 5763 CBC 371 : if (RELATION_IS_OTHER_TEMP(OldHeap))
6910 tgl 5764 LBC 0 : ereport(ERROR,
6910 tgl 5765 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2118 5766 : errmsg("cannot rewrite temporary tables of other sessions")));
6910 5767 :
6846 5768 : /*
5769 : * Select destination tablespace (same as original unless user
5770 : * requested a change)
5771 : */
6846 tgl 5772 CBC 371 : if (tab->newTableSpace)
6846 tgl 5773 LBC 0 : NewTableSpace = tab->newTableSpace;
6846 tgl 5774 ECB : else
6846 tgl 5775 GIC 371 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5776 :
620 michael 5777 ECB : /*
5778 : * Select destination access method (same as original unless user
5779 : * requested a change)
5780 : */
620 michael 5781 CBC 371 : if (OidIsValid(tab->newAccessMethod))
620 michael 5782 GIC 9 : NewAccessMethod = tab->newAccessMethod;
620 michael 5783 ECB : else
620 michael 5784 GIC 362 : NewAccessMethod = OldHeap->rd_rel->relam;
620 michael 5785 ECB :
3152 alvherre 5786 : /*
5787 : * Select persistence of transient table (same as original unless
5788 : * user requested a change)
5789 : */
3149 alvherre 5790 CBC 371 : persistence = tab->chgPersistence ?
3152 5791 354 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
3152 alvherre 5792 ECB :
1539 andres 5793 GBC 371 : table_close(OldHeap, NoLock);
8179 tgl 5794 EUB :
5795 : /*
5796 : * Fire off an Event Trigger now, before actually rewriting the
5797 : * table.
5798 : *
5799 : * We don't support Event Trigger for nested commands anywhere,
5800 : * here included, and parsetree is given NULL when coming from
5801 : * AlterTableInternal.
3044 simon 5802 ECB : *
5803 : * And fire it only once.
5804 : */
3044 simon 5805 GIC 371 : if (parsetree)
2878 bruce 5806 371 : EventTriggerTableRewrite((Node *) parsetree,
5807 : tab->relid,
5808 : tab->rewrite);
3044 simon 5809 ECB :
3152 alvherre 5810 : /*
5811 : * Create transient table that will receive the modified data.
5812 : *
5813 : * Ensure it is marked correctly as logged or unlogged. We have
5814 : * to do this here so that buffers for the new relfilenumber will
5815 : * have the right persistence set, and at the same time ensure
5816 : * that the original filenumbers's buffers will get read in with
5817 : * the correct setting (i.e. the original one). Otherwise a
5818 : * rollback after the rewrite would possibly result with buffers
5819 : * for the original filenumbers having the wrong persistence
5820 : * setting.
5821 : *
5822 : * NB: This relies on swap_relation_files() also swapping the
5823 : * persistence. That wouldn't work for pg_class, but that can't be
5824 : * unlogged anyway.
5825 : */
620 michael 5826 GIC 368 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
620 michael 5827 ECB : persistence, lockmode);
5828 :
5829 : /*
5830 : * Copy the heap data into the new table with the desired
6913 tgl 5831 : * modifications, and test the current data within the table
5832 : * against new constraints generated by ALTER TABLE commands.
5833 : */
4638 simon 5834 GIC 368 : ATRewriteTable(tab, OIDNewHeap, lockmode);
5835 :
5836 : /*
5837 : * Swap the physical files of the old and new heaps, then rebuild
4462 rhaas 5838 ECB : * indexes and discard the old heap. We can use RecentXmin for
4809 tgl 5839 : * the table's new relfrozenxid because we rewrote all the tuples
5840 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5841 : * we never try to swap toast tables by content, since we have no
5842 : * interest in letting this code work on system catalogs.
6913 5843 : */
4462 rhaas 5844 CBC 359 : finish_heap_swap(tab->relid, OIDNewHeap,
3675 rhaas 5845 ECB : false, false, true,
3675 rhaas 5846 GIC 359 : !OidIsValid(tab->newTableSpace),
5847 : RecentXmin,
3067 alvherre 5848 ECB : ReadNextMultiXactId(),
5849 : persistence);
5850 :
607 michael 5851 GIC 356 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5852 : }
367 peter 5853 42353 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5854 : {
367 peter 5855 CBC 6 : if (tab->chgPersistence)
367 peter 5856 GIC 6 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
367 peter 5857 ECB : }
5858 : else
6913 tgl 5859 : {
5860 : /*
5861 : * If required, test the current data within the table against new
5862 : * constraints generated by ALTER TABLE commands, but don't
5863 : * rebuild data.
5864 : */
1488 rhaas 5865 GIC 42347 : if (tab->constraints != NIL || tab->verify_new_notnull ||
2205 tgl 5866 CBC 41184 : tab->partition_constraint != NULL)
4638 simon 5867 GIC 1998 : ATRewriteTable(tab, InvalidOid, lockmode);
5868 :
5869 : /*
5870 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5871 : * just do a block-by-block copy.
5872 : */
6846 tgl 5873 42253 : if (tab->newTableSpace)
4638 simon 5874 61 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5875 : }
5876 :
5877 : /*
5878 : * Also change persistence of owned sequences, so that it matches the
367 peter 5879 ECB : * table persistence.
5880 : */
367 peter 5881 CBC 42615 : if (tab->chgPersistence)
367 peter 5882 ECB : {
367 peter 5883 CBC 23 : List *seqlist = getOwnedSequences(tab->relid);
367 peter 5884 ECB : ListCell *lc;
5885 :
367 peter 5886 GIC 38 : foreach(lc, seqlist)
5887 : {
332 tgl 5888 CBC 15 : Oid seq_relid = lfirst_oid(lc);
367 peter 5889 ECB :
367 peter 5890 CBC 15 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5891 : }
5892 : }
6913 tgl 5893 ECB : }
8314 5894 :
7912 5895 : /*
5896 : * Foreign key constraints are checked in a final pass, since (a) it's
6385 bruce 5897 : * generally best to examine each one separately, and (b) it's at least
5898 : * theoretically possible that we have changed both relations of the
5899 : * foreign key, and we'd better have finished both rewrites before we try
5900 : * to read the tables.
7912 tgl 5901 EUB : */
6913 tgl 5902 GIC 88994 : foreach(ltab, *wqueue)
5903 : {
6797 bruce 5904 GBC 45341 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5905 45341 : Relation rel = NULL;
6797 bruce 5906 ECB : ListCell *lcon;
8304 tgl 5907 :
1464 5908 : /* Relations without storage may be ignored here too */
1464 tgl 5909 GIC 45341 : if (!RELKIND_HAS_STORAGE(tab->relkind))
1464 tgl 5910 CBC 2751 : continue;
1464 tgl 5911 EUB :
6913 tgl 5912 GIC 43348 : foreach(lcon, tab->constraints)
5913 : {
6913 tgl 5914 GBC 789 : NewConstraint *con = lfirst(lcon);
7524 tgl 5915 EUB :
6913 tgl 5916 CBC 789 : if (con->contype == CONSTR_FOREIGN)
6913 tgl 5917 ECB : {
5001 tgl 5918 CBC 466 : Constraint *fkconstraint = (Constraint *) con->qual;
5919 : Relation refrel;
5920 :
6913 5921 466 : if (rel == NULL)
5922 : {
5923 : /* Long since locked, no need for another */
1539 andres 5924 GBC 460 : rel = table_open(tab->relid, NoLock);
5925 : }
5926 :
1539 andres 5927 CBC 466 : refrel = table_open(con->refrelid, RowShareLock);
5928 :
4443 simon 5929 GIC 466 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5003 tgl 5930 ECB : con->refindid,
5931 : con->conid);
5932 :
5933 : /*
5934 : * No need to mark the constraint row as validated, we did
5935 : * that when we inserted the row earlier.
5936 : */
5937 :
1539 andres 5938 CBC 435 : table_close(refrel, NoLock);
5939 : }
5940 : }
8315 JanWieck 5941 ECB :
6913 tgl 5942 GIC 42559 : if (rel)
1539 andres 5943 429 : table_close(rel, NoLock);
6913 tgl 5944 EUB : }
5945 :
5946 : /* Finally, run any afterStmts that were queued up */
1180 tgl 5947 GIC 88944 : foreach(ltab, *wqueue)
5948 : {
5949 45291 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
1180 tgl 5950 ECB : ListCell *lc;
5951 :
1180 tgl 5952 CBC 45325 : foreach(lc, tab->afterStmts)
5953 : {
1180 tgl 5954 GIC 34 : Node *stmt = (Node *) lfirst(lc);
5955 :
5956 34 : ProcessUtilityForAlterTable(stmt, context);
5957 34 : CommandCounterIncrement();
5958 : }
1180 tgl 5959 ECB : }
6913 tgl 5960 GIC 43653 : }
5961 :
5962 : /*
5963 : * ATRewriteTable: scan or rewrite one table
5964 : *
6913 tgl 5965 ECB : * OIDNewHeap is InvalidOid if we don't need to rewrite
5966 : */
5967 : static void
4638 simon 5968 GIC 2366 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
5969 : {
6913 tgl 5970 ECB : Relation oldrel;
5971 : Relation newrel;
5972 : TupleDesc oldTupDesc;
5973 : TupleDesc newTupDesc;
6913 tgl 5974 GIC 2366 : bool needscan = false;
5975 : List *notnull_attrs;
5976 : int i;
5977 : ListCell *l;
5978 : EState *estate;
5979 : CommandId mycid;
5980 : BulkInsertState bistate;
5981 : int ti_options;
2217 andres 5982 2366 : ExprState *partqualstate = NULL;
5983 :
5984 : /*
5985 : * Open the relation(s). We have surely already locked the existing
6913 tgl 5986 ECB : * table.
5987 : */
1539 andres 5988 GIC 2366 : oldrel = table_open(tab->relid, NoLock);
6913 tgl 5989 2366 : oldTupDesc = tab->oldDesc;
2118 tgl 5990 CBC 2366 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6913 tgl 5991 ECB :
6913 tgl 5992 CBC 2366 : if (OidIsValid(OIDNewHeap))
1539 andres 5993 GIC 368 : newrel = table_open(OIDNewHeap, lockmode);
5994 : else
6913 tgl 5995 1998 : newrel = NULL;
5996 :
5997 : /*
5998 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
5999 : * is empty, so don't bother using it.
6000 : */
4904 heikki.linnakangas 6001 2366 : if (newrel)
6002 : {
6003 368 : mycid = GetCurrentCommandId(true);
6004 368 : bistate = GetBulkInsertState();
1469 andres 6005 368 : ti_options = TABLE_INSERT_SKIP_FSM;
4904 heikki.linnakangas 6006 ECB : }
6007 : else
6008 : {
6009 : /* keep compiler quiet about using these uninitialized */
4904 heikki.linnakangas 6010 GIC 1998 : mycid = 0;
6011 1998 : bistate = NULL;
1469 andres 6012 1998 : ti_options = 0;
6013 : }
6014 :
7653 tgl 6015 ECB : /*
6016 : * Generate the constraint and default execution states
6017 : */
6018 :
6913 tgl 6019 GIC 2366 : estate = CreateExecutorState();
6020 :
6021 : /* Build the needed expression execution states */
6913 tgl 6022 CBC 3200 : foreach(l, tab->constraints)
6913 tgl 6023 EUB : {
6913 tgl 6024 GIC 834 : NewConstraint *con = lfirst(l);
6025 :
6026 834 : switch (con->contype)
6027 : {
6913 tgl 6028 CBC 365 : case CONSTR_CHECK:
6029 365 : needscan = true;
2217 andres 6030 GIC 365 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6913 tgl 6031 365 : break;
6032 469 : case CONSTR_FOREIGN:
6033 : /* Nothing to do here */
6034 469 : break;
6913 tgl 6035 UIC 0 : default:
6036 0 : elog(ERROR, "unrecognized constraint type: %d",
6037 : (int) con->contype);
6913 tgl 6038 ECB : }
6913 tgl 6039 EUB : }
6040 :
6041 : /* Build expression execution states for partition check quals */
2314 rhaas 6042 GIC 2366 : if (tab->partition_constraint)
6043 : {
6044 906 : needscan = true;
2205 tgl 6045 906 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6046 : }
2314 rhaas 6047 ECB :
6913 tgl 6048 GBC 2747 : foreach(l, tab->newvals)
6049 : {
6797 bruce 6050 CBC 381 : NewColumnValue *ex = lfirst(l);
6051 :
6052 : /* expr already planned */
4310 rhaas 6053 GIC 381 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6054 : }
6055 :
6117 tgl 6056 CBC 2366 : notnull_attrs = NIL;
1488 rhaas 6057 2366 : if (newrel || tab->verify_new_notnull)
6058 : {
6117 tgl 6059 ECB : /*
6060 : * If we are rebuilding the tuples OR if we added any new but not
6061 : * verified NOT NULL constraints, check all not-null constraints. This
6062 : * is a bit of overkill but it minimizes risk of bugs, and
6063 : * heap_attisnull is a pretty cheap test anyway.
6064 : */
6117 tgl 6065 CBC 2831 : for (i = 0; i < newTupDesc->natts; i++)
6117 tgl 6066 ECB : {
2058 andres 6067 GIC 2073 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
2058 andres 6068 ECB :
2058 andres 6069 GIC 2073 : if (attr->attnotnull && !attr->attisdropped)
6117 tgl 6070 805 : notnull_attrs = lappend_int(notnull_attrs, i);
6071 : }
6072 758 : if (notnull_attrs)
6073 571 : needscan = true;
6074 : }
6075 :
5170 6076 2366 : if (newrel || needscan)
6077 : {
6078 : ExprContext *econtext;
6079 : TupleTableSlot *oldslot;
6913 tgl 6080 ECB : TupleTableSlot *newslot;
1490 andres 6081 : TableScanDesc scan;
6082 : MemoryContext oldCxt;
6385 bruce 6083 GIC 1983 : List *dropped_attrs = NIL;
6084 : ListCell *lc;
6085 : Snapshot snapshot;
6086 :
4439 rhaas 6087 1983 : if (newrel)
6088 368 : ereport(DEBUG1,
6089 : (errmsg_internal("rewriting table \"%s\"",
6090 : RelationGetRelationName(oldrel))));
6091 : else
6092 1615 : ereport(DEBUG1,
6093 : (errmsg_internal("verifying table \"%s\"",
6094 : RelationGetRelationName(oldrel))));
6095 :
4323 heikki.linnakangas 6096 1983 : if (newrel)
6097 : {
6098 : /*
6099 : * All predicate locks on the tuples or pages are about to be made
6100 : * invalid, because we move tuples around. Promote them to
4323 heikki.linnakangas 6101 ECB : * relation locks.
6102 : */
4323 heikki.linnakangas 6103 GIC 368 : TransferPredicateLocksToHeapRelation(oldrel);
6104 : }
6105 :
6913 tgl 6106 1983 : econtext = GetPerTupleExprContext(estate);
6107 :
6108 : /*
1490 andres 6109 ECB : * Create necessary tuple slots. When rewriting, two slots are needed,
6110 : * otherwise one suffices. In the case where one slot suffices, we
6111 : * need to use the new tuple descriptor, otherwise some constraints
6112 : * can't be evaluated. Note that even when the tuple layout is the
6113 : * same and no rewrite is required, the tupDescs might not be
6114 : * (consider ADD COLUMN without a default).
6115 : */
1490 andres 6116 GIC 1983 : if (tab->rewrite)
6117 : {
6118 368 : Assert(newrel != NULL);
1490 andres 6119 CBC 368 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6120 : table_slot_callbacks(oldrel));
6121 368 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6122 : table_slot_callbacks(newrel));
6123 :
6124 : /*
6125 : * Set all columns in the new slot to NULL initially, to ensure
1203 tgl 6126 ECB : * columns added as part of the rewrite are initialized to NULL.
6127 : * That is necessary as tab->newvals will not contain an
1278 andres 6128 : * expression for columns with a NULL default, e.g. when adding a
6129 : * column without a default together with a column with a default
6130 : * requiring an actual rewrite.
6131 : */
1278 andres 6132 GIC 368 : ExecStoreAllNullTuple(newslot);
6133 : }
6134 : else
6135 : {
1490 6136 1615 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6137 : table_slot_callbacks(oldrel));
6138 1615 : newslot = NULL;
6139 : }
6913 tgl 6140 ECB :
6633 neilc 6141 : /*
6142 : * Any attributes that are dropped according to the new tuple
6143 : * descriptor can be set to NULL. We precompute the list of dropped
6144 : * attributes to avoid needing to do so in the per-tuple loop.
6145 : */
6633 neilc 6146 GIC 7029 : for (i = 0; i < newTupDesc->natts; i++)
6147 : {
2058 andres 6148 CBC 5046 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6633 neilc 6149 381 : dropped_attrs = lappend_int(dropped_attrs, i);
6150 : }
6151 :
6152 : /*
6153 : * Scan through the rows, generating a new row if needed and then
6154 : * checking all the constraints.
6155 : */
3568 rhaas 6156 1983 : snapshot = RegisterSnapshot(GetLatestSnapshot());
1490 andres 6157 GIC 1983 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6913 tgl 6158 ECB :
6159 : /*
6160 : * Switch to per-tuple memory context and reset it for each tuple
6385 bruce 6161 : * produced, so we don't leak memory.
6162 : */
6633 neilc 6163 CBC 1983 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6164 :
1490 andres 6165 385085 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6166 : {
6167 : TupleTableSlot *insertslot;
6168 :
3044 simon 6169 GIC 381222 : if (tab->rewrite > 0)
6170 : {
6171 : /* Extract data from old tuple */
1490 andres 6172 48647 : slot_getallattrs(oldslot);
6173 48647 : ExecClearTuple(newslot);
6174 :
6175 : /* copy attributes */
6176 48647 : memcpy(newslot->tts_values, oldslot->tts_values,
1490 andres 6177 CBC 48647 : sizeof(Datum) * oldslot->tts_nvalid);
1490 andres 6178 GIC 48647 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
1490 andres 6179 CBC 48647 : sizeof(bool) * oldslot->tts_nvalid);
8262 tgl 6180 ECB :
6181 : /* Set dropped attributes to null in new tuple */
6385 bruce 6182 GIC 48690 : foreach(lc, dropped_attrs)
1490 andres 6183 43 : newslot->tts_isnull[lfirst_int(lc)] = true;
7836 bruce 6184 ECB :
688 tgl 6185 : /*
6186 : * Constraints and GENERATED expressions might reference the
6187 : * tableoid column, so fill tts_tableOid with the desired
6188 : * value. (We must do this each time, because it gets
6189 : * overwritten with newrel's OID during storing.)
6190 : */
688 tgl 6191 CBC 48647 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6192 :
6913 tgl 6193 ECB : /*
6194 : * Process supplied expressions to replace selected columns.
6195 : *
1187 6196 : * First, evaluate expressions whose inputs come from the old
6197 : * tuple.
6198 : */
6913 tgl 6199 CBC 48647 : econtext->ecxt_scantuple = oldslot;
6200 :
6913 tgl 6201 GIC 100279 : foreach(l, tab->newvals)
6913 tgl 6202 ECB : {
6797 bruce 6203 GIC 51638 : NewColumnValue *ex = lfirst(l);
6913 tgl 6204 ECB :
1187 tgl 6205 GIC 51638 : if (ex->is_generated)
6206 30 : continue;
6207 :
1490 andres 6208 51608 : newslot->tts_values[ex->attnum - 1]
6209 51602 : = ExecEvalExpr(ex->exprstate,
6210 : econtext,
6211 51608 : &newslot->tts_isnull[ex->attnum - 1]);
6212 : }
7664 tgl 6213 ECB :
1490 andres 6214 GIC 48641 : ExecStoreVirtualTuple(newslot);
6215 :
6216 : /*
1187 tgl 6217 ECB : * Now, evaluate any expressions whose inputs come from the
6218 : * new tuple. We assume these columns won't reference each
6219 : * other, so that there's no ordering dependency.
6220 : */
1187 tgl 6221 GIC 48641 : econtext->ecxt_scantuple = newslot;
1187 tgl 6222 ECB :
1187 tgl 6223 GIC 100273 : foreach(l, tab->newvals)
1187 tgl 6224 ECB : {
1187 tgl 6225 GIC 51632 : NewColumnValue *ex = lfirst(l);
6226 :
1187 tgl 6227 CBC 51632 : if (!ex->is_generated)
1187 tgl 6228 GIC 51602 : continue;
1187 tgl 6229 ECB :
1187 tgl 6230 GIC 30 : newslot->tts_values[ex->attnum - 1]
1187 tgl 6231 CBC 30 : = ExecEvalExpr(ex->exprstate,
1187 tgl 6232 ECB : econtext,
1187 tgl 6233 GIC 30 : &newslot->tts_isnull[ex->attnum - 1]);
6234 : }
1187 tgl 6235 ECB :
1490 andres 6236 GIC 48641 : insertslot = newslot;
6237 : }
6238 : else
6239 : {
6240 : /*
6241 : * If there's no rewrite, old and new table are guaranteed to
6242 : * have the same AM, so we can just use the old slot to verify
1418 tgl 6243 ECB : * new constraints etc.
6244 : */
1490 andres 6245 GIC 332575 : insertslot = oldslot;
6246 : }
6247 :
6248 : /* Now check any constraints on the possibly-changed tuple */
1490 andres 6249 CBC 381216 : econtext->ecxt_scantuple = insertslot;
6250 :
6117 tgl 6251 GIC 1669304 : foreach(l, notnull_attrs)
6252 : {
6031 bruce 6253 1288115 : int attn = lfirst_int(l);
6254 :
1490 andres 6255 1288115 : if (slot_attisnull(insertslot, attn + 1))
6256 : {
2058 andres 6257 CBC 27 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6258 :
6117 tgl 6259 GIC 27 : ereport(ERROR,
6260 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6261 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6262 : NameStr(attr->attname),
1167 akapila 6263 ECB : RelationGetRelationName(oldrel)),
3722 tgl 6264 : errtablecol(oldrel, attn + 1)));
2058 andres 6265 : }
6266 : }
6117 tgl 6267 :
6913 tgl 6268 CBC 385230 : foreach(l, tab->constraints)
6269 : {
6270 4074 : NewConstraint *con = lfirst(l);
6271 :
6913 tgl 6272 GIC 4074 : switch (con->contype)
6273 : {
6274 4030 : case CONSTR_CHECK:
2217 andres 6275 4030 : if (!ExecCheck(con->qualstate, econtext))
6913 tgl 6276 CBC 33 : ereport(ERROR,
6277 : (errcode(ERRCODE_CHECK_VIOLATION),
1167 akapila 6278 ECB : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6279 : con->name,
6280 : RelationGetRelationName(oldrel)),
6281 : errtableconstraint(oldrel, con->name)));
6913 tgl 6282 GIC 3997 : break;
2 alvherre 6283 GNC 44 : case CONSTR_NOTNULL:
6284 : case CONSTR_FOREIGN:
6285 : /* Nothing to do here */
6913 tgl 6286 CBC 44 : break;
6913 tgl 6287 LBC 0 : default:
6288 0 : elog(ERROR, "unrecognized constraint type: %d",
6289 : (int) con->contype);
6290 : }
6291 : }
6292 :
2217 andres 6293 GIC 381156 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6294 : {
2039 rhaas 6295 CBC 37 : if (tab->validate_default)
2039 rhaas 6296 GIC 13 : ereport(ERROR,
6297 : (errcode(ERRCODE_CHECK_VIOLATION),
1167 akapila 6298 ECB : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6299 : RelationGetRelationName(oldrel)),
1112 6300 : errtable(oldrel)));
6301 : else
2039 rhaas 6302 CBC 24 : ereport(ERROR,
6303 : (errcode(ERRCODE_CHECK_VIOLATION),
1167 akapila 6304 ECB : errmsg("partition constraint of relation \"%s\" is violated by some row",
1112 6305 : RelationGetRelationName(oldrel)),
6306 : errtable(oldrel)));
2039 rhaas 6307 : }
2314 6308 :
6309 : /* Write the tuple out to the new relation */
6913 tgl 6310 CBC 381119 : if (newrel)
1417 andres 6311 GBC 48638 : table_tuple_insert(newrel, insertslot, mycid,
1417 andres 6312 EUB : ti_options, bistate);
6313 :
6913 tgl 6314 GIC 381119 : ResetExprContext(econtext);
6315 :
6316 381119 : CHECK_FOR_INTERRUPTS();
6317 : }
7664 tgl 6318 ECB :
6633 neilc 6319 GIC 1880 : MemoryContextSwitchTo(oldCxt);
1490 andres 6320 CBC 1880 : table_endscan(scan);
3568 rhaas 6321 1880 : UnregisterSnapshot(snapshot);
6322 :
6141 tgl 6323 GIC 1880 : ExecDropSingleTupleTableSlot(oldslot);
1490 andres 6324 CBC 1880 : if (newslot)
1490 andres 6325 GIC 359 : ExecDropSingleTupleTableSlot(newslot);
7653 tgl 6326 ECB : }
6327 :
6913 tgl 6328 GIC 2263 : FreeExecutorState(estate);
7664 tgl 6329 ECB :
1539 andres 6330 GIC 2263 : table_close(oldrel, NoLock);
6913 tgl 6331 2263 : if (newrel)
4904 heikki.linnakangas 6332 ECB : {
4904 heikki.linnakangas 6333 CBC 359 : FreeBulkInsertState(bistate);
6334 :
1469 andres 6335 GIC 359 : table_finish_bulk_insert(newrel, ti_options);
6336 :
1539 6337 359 : table_close(newrel, NoLock);
6338 : }
6913 tgl 6339 2263 : }
6340 :
6913 tgl 6341 ECB : /*
6342 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6343 : */
6344 : static AlteredTableInfo *
6913 tgl 6345 CBC 48759 : ATGetQueueEntry(List **wqueue, Relation rel)
6913 tgl 6346 ECB : {
6913 tgl 6347 GIC 48759 : Oid relid = RelationGetRelid(rel);
6913 tgl 6348 ECB : AlteredTableInfo *tab;
6892 neilc 6349 : ListCell *ltab;
6350 :
6913 tgl 6351 GIC 52842 : foreach(ltab, *wqueue)
6913 tgl 6352 ECB : {
6913 tgl 6353 GIC 6083 : tab = (AlteredTableInfo *) lfirst(ltab);
6354 6083 : if (tab->relid == relid)
6355 2000 : return tab;
6356 : }
6357 :
6358 : /*
6913 tgl 6359 ECB : * Not there, so add it. Note that we make a copy of the relation's
6360 : * existing descriptor before anything interesting can happen to it.
6361 : */
6913 tgl 6362 GIC 46759 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6913 tgl 6363 CBC 46759 : tab->relid = relid;
745 alvherre 6364 46759 : tab->rel = NULL; /* set later */
6910 tgl 6365 GIC 46759 : tab->relkind = rel->rd_rel->relkind;
1838 andrew 6366 46759 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
620 michael 6367 46759 : tab->newAccessMethod = InvalidOid;
620 michael 6368 CBC 46759 : tab->newTableSpace = InvalidOid;
3152 alvherre 6369 GIC 46759 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
3149 6370 46759 : tab->chgPersistence = false;
6371 :
6913 tgl 6372 CBC 46759 : *wqueue = lappend(*wqueue, tab);
6373 :
6913 tgl 6374 GIC 46759 : return tab;
6375 : }
6376 :
6377 : static const char *
640 peter 6378 21 : alter_table_type_to_string(AlterTableType cmdtype)
640 peter 6379 ECB : {
640 peter 6380 GIC 21 : switch (cmdtype)
6381 : {
640 peter 6382 LBC 0 : case AT_AddColumn:
6383 : case AT_AddColumnToView:
640 peter 6384 UIC 0 : return "ADD COLUMN";
6385 0 : case AT_ColumnDefault:
6386 : case AT_CookedColumnDefault:
6387 0 : return "ALTER COLUMN ... SET DEFAULT";
640 peter 6388 GIC 3 : case AT_DropNotNull:
6389 3 : return "ALTER COLUMN ... DROP NOT NULL";
6390 3 : case AT_SetNotNull:
640 peter 6391 CBC 3 : return "ALTER COLUMN ... SET NOT NULL";
2 alvherre 6392 UNC 0 : case AT_SetAttNotNull:
6393 0 : return NULL; /* not real grammar */
640 peter 6394 UIC 0 : case AT_DropExpression:
640 peter 6395 LBC 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6396 0 : case AT_CheckNotNull:
633 tgl 6397 UIC 0 : return NULL; /* not real grammar */
640 peter 6398 LBC 0 : case AT_SetStatistics:
640 peter 6399 UIC 0 : return "ALTER COLUMN ... SET STATISTICS";
640 peter 6400 GIC 6 : case AT_SetOptions:
6401 6 : return "ALTER COLUMN ... SET";
640 peter 6402 UIC 0 : case AT_ResetOptions:
6403 0 : return "ALTER COLUMN ... RESET";
6404 0 : case AT_SetStorage:
6405 0 : return "ALTER COLUMN ... SET STORAGE";
6406 0 : case AT_SetCompression:
6407 0 : return "ALTER COLUMN ... SET COMPRESSION";
640 peter 6408 GIC 3 : case AT_DropColumn:
6409 3 : return "DROP COLUMN";
640 peter 6410 UIC 0 : case AT_AddIndex:
6411 : case AT_ReAddIndex:
633 tgl 6412 LBC 0 : return NULL; /* not real grammar */
640 peter 6413 UIC 0 : case AT_AddConstraint:
6414 : case AT_ReAddConstraint:
6415 : case AT_ReAddDomainConstraint:
6416 : case AT_AddIndexConstraint:
6417 0 : return "ADD CONSTRAINT";
640 peter 6418 GIC 3 : case AT_AlterConstraint:
6419 3 : return "ALTER CONSTRAINT";
640 peter 6420 UIC 0 : case AT_ValidateConstraint:
6421 0 : return "VALIDATE CONSTRAINT";
640 peter 6422 LBC 0 : case AT_DropConstraint:
640 peter 6423 UIC 0 : return "DROP CONSTRAINT";
6424 0 : case AT_ReAddComment:
633 tgl 6425 0 : return NULL; /* not real grammar */
640 peter 6426 0 : case AT_AlterColumnType:
6427 0 : return "ALTER COLUMN ... SET DATA TYPE";
6428 0 : case AT_AlterColumnGenericOptions:
640 peter 6429 LBC 0 : return "ALTER COLUMN ... OPTIONS";
6430 0 : case AT_ChangeOwner:
640 peter 6431 UIC 0 : return "OWNER TO";
6432 0 : case AT_ClusterOn:
6433 0 : return "CLUSTER ON";
6434 0 : case AT_DropCluster:
6435 0 : return "SET WITHOUT CLUSTER";
620 michael 6436 LBC 0 : case AT_SetAccessMethod:
620 michael 6437 UIC 0 : return "SET ACCESS METHOD";
640 peter 6438 LBC 0 : case AT_SetLogged:
640 peter 6439 UIC 0 : return "SET LOGGED";
6440 0 : case AT_SetUnLogged:
6441 0 : return "SET UNLOGGED";
640 peter 6442 LBC 0 : case AT_DropOids:
640 peter 6443 UIC 0 : return "SET WITHOUT OIDS";
6444 0 : case AT_SetTableSpace:
640 peter 6445 LBC 0 : return "SET TABLESPACE";
6446 0 : case AT_SetRelOptions:
640 peter 6447 UIC 0 : return "SET";
6448 0 : case AT_ResetRelOptions:
640 peter 6449 LBC 0 : return "RESET";
6450 0 : case AT_ReplaceRelOptions:
633 tgl 6451 0 : return NULL; /* not real grammar */
640 peter 6452 0 : case AT_EnableTrig:
640 peter 6453 UIC 0 : return "ENABLE TRIGGER";
6454 0 : case AT_EnableAlwaysTrig:
640 peter 6455 LBC 0 : return "ENABLE ALWAYS TRIGGER";
6456 0 : case AT_EnableReplicaTrig:
640 peter 6457 UIC 0 : return "ENABLE REPLICA TRIGGER";
6458 0 : case AT_DisableTrig:
6459 0 : return "DISABLE TRIGGER";
6460 0 : case AT_EnableTrigAll:
6461 0 : return "ENABLE TRIGGER ALL";
6462 0 : case AT_DisableTrigAll:
6463 0 : return "DISABLE TRIGGER ALL";
640 peter 6464 LBC 0 : case AT_EnableTrigUser:
640 peter 6465 UIC 0 : return "ENABLE TRIGGER USER";
6466 0 : case AT_DisableTrigUser:
6467 0 : return "DISABLE TRIGGER USER";
6468 0 : case AT_EnableRule:
6469 0 : return "ENABLE RULE";
6470 0 : case AT_EnableAlwaysRule:
6471 0 : return "ENABLE ALWAYS RULE";
640 peter 6472 LBC 0 : case AT_EnableReplicaRule:
640 peter 6473 UIC 0 : return "ENABLE REPLICA RULE";
640 peter 6474 LBC 0 : case AT_DisableRule:
640 peter 6475 UIC 0 : return "DISABLE RULE";
640 peter 6476 LBC 0 : case AT_AddInherit:
640 peter 6477 UIC 0 : return "INHERIT";
640 peter 6478 LBC 0 : case AT_DropInherit:
6479 0 : return "NO INHERIT";
640 peter 6480 UIC 0 : case AT_AddOf:
640 peter 6481 LBC 0 : return "OF";
6482 0 : case AT_DropOf:
640 peter 6483 UIC 0 : return "NOT OF";
640 peter 6484 LBC 0 : case AT_ReplicaIdentity:
640 peter 6485 UIC 0 : return "REPLICA IDENTITY";
6486 0 : case AT_EnableRowSecurity:
640 peter 6487 LBC 0 : return "ENABLE ROW SECURITY";
640 peter 6488 UIC 0 : case AT_DisableRowSecurity:
6489 0 : return "DISABLE ROW SECURITY";
6490 0 : case AT_ForceRowSecurity:
6491 0 : return "FORCE ROW SECURITY";
6492 0 : case AT_NoForceRowSecurity:
6493 0 : return "NO FORCE ROW SECURITY";
640 peter 6494 LBC 0 : case AT_GenericOptions:
640 peter 6495 UIC 0 : return "OPTIONS";
640 peter 6496 LBC 0 : case AT_AttachPartition:
640 peter 6497 UIC 0 : return "ATTACH PARTITION";
640 peter 6498 CBC 3 : case AT_DetachPartition:
640 peter 6499 GIC 3 : return "DETACH PARTITION";
640 peter 6500 LBC 0 : case AT_DetachPartitionFinalize:
6501 0 : return "DETACH PARTITION ... FINALIZE";
640 peter 6502 UIC 0 : case AT_AddIdentity:
640 peter 6503 LBC 0 : return "ALTER COLUMN ... ADD IDENTITY";
6504 0 : case AT_SetIdentity:
640 peter 6505 UIC 0 : return "ALTER COLUMN ... SET";
640 peter 6506 LBC 0 : case AT_DropIdentity:
640 peter 6507 UIC 0 : return "ALTER COLUMN ... DROP IDENTITY";
6508 0 : case AT_ReAddStatistics:
633 tgl 6509 LBC 0 : return NULL; /* not real grammar */
6510 : }
6511 :
640 peter 6512 UIC 0 : return NULL;
6513 : }
6514 :
6515 : /*
6516 : * ATSimplePermissions
6517 : *
6913 tgl 6518 ECB : * - Ensure that it is a relation (or possibly a view)
6519 : * - Ensure this user is the owner
6520 : * - Ensure that it is not a system table
6521 : */
6522 : static void
640 peter 6523 GIC 47080 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
7689 tgl 6524 ECB : {
6525 : int actual_target;
4481 rhaas 6526 :
4481 rhaas 6527 GIC 47080 : switch (rel->rd_rel->relkind)
6913 tgl 6528 ECB : {
4481 rhaas 6529 GIC 45994 : case RELKIND_RELATION:
2314 rhaas 6530 ECB : case RELKIND_PARTITIONED_TABLE:
4481 rhaas 6531 GIC 45994 : actual_target = ATT_TABLE;
4481 rhaas 6532 CBC 45994 : break;
4481 rhaas 6533 GIC 198 : case RELKIND_VIEW:
6534 198 : actual_target = ATT_VIEW;
6535 198 : break;
3689 kgrittn 6536 23 : case RELKIND_MATVIEW:
6537 23 : actual_target = ATT_MATVIEW;
6538 23 : break;
4481 rhaas 6539 113 : case RELKIND_INDEX:
6540 113 : actual_target = ATT_INDEX;
4481 rhaas 6541 CBC 113 : break;
1906 alvherre 6542 GIC 207 : case RELKIND_PARTITIONED_INDEX:
1906 alvherre 6543 CBC 207 : actual_target = ATT_PARTITIONED_INDEX;
1906 alvherre 6544 GIC 207 : break;
4481 rhaas 6545 CBC 107 : case RELKIND_COMPOSITE_TYPE:
4481 rhaas 6546 GIC 107 : actual_target = ATT_COMPOSITE_TYPE;
4481 rhaas 6547 CBC 107 : break;
6548 432 : case RELKIND_FOREIGN_TABLE:
6549 432 : actual_target = ATT_FOREIGN_TABLE;
4481 rhaas 6550 GIC 432 : break;
367 peter 6551 6 : case RELKIND_SEQUENCE:
6552 6 : actual_target = ATT_SEQUENCE;
6553 6 : break;
4481 rhaas 6554 UIC 0 : default:
4481 rhaas 6555 LBC 0 : actual_target = 0;
6556 0 : break;
6557 : }
6558 :
4481 rhaas 6559 ECB : /* Wrong target type? */
4481 rhaas 6560 GBC 47080 : if ((actual_target & allowed_targets) == 0)
640 peter 6561 EUB : {
640 peter 6562 GIC 21 : const char *action_str = alter_table_type_to_string(cmdtype);
6563 :
6564 21 : if (action_str)
6565 21 : ereport(ERROR,
640 peter 6566 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6567 : /* translator: %s is a group of some SQL keywords */
6568 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6569 : action_str, RelationGetRelationName(rel)),
6570 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6571 : else
6572 : /* internal error? */
640 peter 6573 UIC 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6574 : RelationGetRelationName(rel));
640 peter 6575 ECB : }
6576 :
6577 : /* Permissions checks */
147 peter 6578 GNC 47059 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
1954 peter_e 6579 GIC 3 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
7191 tgl 6580 3 : RelationGetRelationName(rel));
6581 :
7203 6582 47056 : if (!allowSystemTableMods && IsSystemRelation(rel))
7203 tgl 6583 LBC 0 : ereport(ERROR,
7203 tgl 6584 ECB : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6585 : errmsg("permission denied: \"%s\" is a system catalog",
6586 : RelationGetRelationName(rel))));
6913 tgl 6587 CBC 47056 : }
6588 :
6913 tgl 6589 ECB : /*
6590 : * ATSimpleRecursion
6591 : *
6592 : * Simple table recursion sufficient for most ALTER TABLE operations.
6593 : * All direct and indirect children are processed in an unspecified order.
6594 : * Note that if a child inherits from the original table via multiple
6595 : * inheritance paths, it will be visited just once.
6596 : */
6597 : static void
6913 tgl 6598 CBC 28881 : ATSimpleRecursion(List **wqueue, Relation rel,
6599 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6600 : AlterTableUtilityContext *context)
6913 tgl 6601 ECB : {
6602 : /*
935 6603 : * Propagate to children, if desired and if there are (or might be) any
6604 : * children.
6605 : */
935 tgl 6606 CBC 28881 : if (recurse && rel->rd_rel->relhassubclass)
6607 : {
6913 6608 50 : Oid relid = RelationGetRelid(rel);
6609 : ListCell *child;
6892 neilc 6610 ECB : List *children;
6611 :
4638 simon 6612 CBC 50 : children = find_all_inheritors(relid, lockmode, NULL);
6613 :
6614 : /*
6615 : * find_all_inheritors does the recursive search of the inheritance
6616 : * hierarchy, so all we have to do is process all of the relids in the
6617 : * list that it returns.
7653 tgl 6618 ECB : */
7653 tgl 6619 GIC 192 : foreach(child, children)
7664 tgl 6620 ECB : {
6892 neilc 6621 GIC 142 : Oid childrelid = lfirst_oid(child);
6622 : Relation childrel;
6623 :
6913 tgl 6624 CBC 142 : if (childrelid == relid)
7653 tgl 6625 GIC 50 : continue;
5080 tgl 6626 ECB : /* find_all_inheritors already got lock */
5080 tgl 6627 CBC 92 : childrel = relation_open(childrelid, NoLock);
5548 6628 92 : CheckTableNotInUse(childrel, "ALTER TABLE");
1180 tgl 6629 GIC 92 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6913 6630 92 : relation_close(childrel, NoLock);
6631 : }
6632 : }
6633 28881 : }
6634 :
1356 alvherre 6635 ECB : /*
6636 : * Obtain list of partitions of the given table, locking them all at the given
6637 : * lockmode and ensuring that they all pass CheckTableNotInUse.
6638 : *
6639 : * This function is a no-op if the given relation is not a partitioned table;
6640 : * in particular, nothing is done if it's a legacy inheritance parent.
6641 : */
6642 : static void
1356 alvherre 6643 CBC 264 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6644 : {
6645 264 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6646 : {
1356 alvherre 6647 ECB : List *inh;
6648 : ListCell *cell;
6649 :
1356 alvherre 6650 GIC 61 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
1356 alvherre 6651 ECB : /* first element is the parent rel; must ignore it */
923 tgl 6652 GIC 206 : for_each_from(cell, inh, 1)
1356 alvherre 6653 ECB : {
6654 : Relation childrel;
1356 alvherre 6655 EUB :
6656 : /* find_all_inheritors already got lock */
1356 alvherre 6657 GBC 148 : childrel = table_open(lfirst_oid(cell), NoLock);
6658 148 : CheckTableNotInUse(childrel, "ALTER TABLE");
1356 alvherre 6659 GIC 145 : table_close(childrel, NoLock);
1356 alvherre 6660 EUB : }
1356 alvherre 6661 CBC 58 : list_free(inh);
1356 alvherre 6662 ECB : }
1356 alvherre 6663 CBC 261 : }
1356 alvherre 6664 ECB :
4520 peter_e 6665 EUB : /*
6666 : * ATTypedTableRecursion
6667 : *
6668 : * Propagate ALTER TYPE operations to the typed tables of that type.
4372 rhaas 6669 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6670 : * recursion to inheritance children of the typed tables.
4520 peter_e 6671 : */
6672 : static void
4520 peter_e 6673 CBC 95 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
1180 tgl 6674 ECB : LOCKMODE lockmode, AlterTableUtilityContext *context)
4520 peter_e 6675 EUB : {
6676 : ListCell *child;
6677 : List *children;
6678 :
4520 peter_e 6679 GBC 95 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
4520 peter_e 6680 EUB :
4520 peter_e 6681 CBC 95 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6682 95 : RelationGetRelationName(rel),
4520 peter_e 6683 EUB : cmd->behavior);
6684 :
4520 peter_e 6685 GBC 101 : foreach(child, children)
4520 peter_e 6686 EUB : {
4520 peter_e 6687 GIC 15 : Oid childrelid = lfirst_oid(child);
6688 : Relation childrel;
6689 :
4520 peter_e 6690 GBC 15 : childrel = relation_open(childrelid, lockmode);
4520 peter_e 6691 CBC 15 : CheckTableNotInUse(childrel, "ALTER TABLE");
1180 tgl 6692 15 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
4520 peter_e 6693 GBC 15 : relation_close(childrel, NoLock);
4520 peter_e 6694 EUB : }
4520 peter_e 6695 GBC 86 : }
4520 peter_e 6696 EUB :
6881 tgl 6697 :
6698 : /*
6699 : * find_composite_type_dependencies
6700 : *
2069 6701 : * Check to see if the type "typeOid" is being used as a column in some table
6702 : * (possibly nested several levels deep in composite types, arrays, etc!).
6881 6703 : * Eventually, we'd like to propagate the check or rewrite operation
2069 6704 : * into such tables, but for now, just error out if we find any.
6881 6705 : *
2069 6706 : * Caller should provide either the associated relation of a rowtype,
6707 : * or a type name (not both) for use in the error message, if any.
6708 : *
6709 : * Note that "typeOid" is not necessarily a composite type; it could also be
6710 : * another container type such as an array or range, or a domain over one of
6711 : * these things. The name of this function is therefore somewhat historical,
6712 : * but it's not worth changing.
5812 6713 : *
6881 6714 : * We assume that functions and views depending on the type are not reasons
6715 : * to reject the ALTER. (How safe is this really?)
6716 : */
5812 6717 : void
4440 rhaas 6718 GBC 1752 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
4440 rhaas 6719 EUB : const char *origTypeName)
6881 tgl 6720 : {
6721 : Relation depRel;
6722 : ScanKeyData key[2];
6723 : SysScanDesc depScan;
6724 : HeapTuple depTup;
2069 6725 :
6726 : /* since this function recurses, it could be driven to stack overflow */
2069 tgl 6727 GBC 1752 : check_stack_depth();
6881 tgl 6728 EUB :
6729 : /*
2069 6730 : * We scan pg_depend to find those things that depend on the given type.
6731 : * (We assume we can ignore refobjsubid for a type.)
6881 6732 : */
1539 andres 6733 GBC 1752 : depRel = table_open(DependRelationId, AccessShareLock);
6881 tgl 6734 EUB :
6881 tgl 6735 GBC 1752 : ScanKeyInit(&key[0],
6881 tgl 6736 EUB : Anum_pg_depend_refclassid,
6737 : BTEqualStrategyNumber, F_OIDEQ,
6569 6738 : ObjectIdGetDatum(TypeRelationId));
6881 tgl 6739 GBC 1752 : ScanKeyInit(&key[1],
6881 tgl 6740 EUB : Anum_pg_depend_refobjid,
6741 : BTEqualStrategyNumber, F_OIDEQ,
6742 : ObjectIdGetDatum(typeOid));
6743 :
6569 tgl 6744 GBC 1752 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
3568 rhaas 6745 EUB : NULL, 2, key);
6881 tgl 6746 :
6881 tgl 6747 GBC 2720 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6881 tgl 6748 EUB : {
6881 tgl 6749 GBC 1016 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6881 tgl 6750 EUB : Relation rel;
13 6751 : TupleDesc tupleDesc;
6881 6752 : Form_pg_attribute att;
6753 :
2069 6754 : /* Check for directly dependent types */
2069 tgl 6755 GBC 1016 : if (pg_depend->classid == TypeRelationId)
2069 tgl 6756 EUB : {
6757 : /*
6758 : * This must be an array, domain, or range containing the given
6759 : * type, so recursively check for uses of this type. Note that
6760 : * any error message will mention the original type not the
6761 : * container; this is intentional.
6762 : */
2069 tgl 6763 GBC 843 : find_composite_type_dependencies(pg_depend->objid,
2069 tgl 6764 EUB : origRelation, origTypeName);
2069 tgl 6765 GBC 831 : continue;
2069 tgl 6766 EUB : }
6767 :
13 6768 : /* Else, ignore dependees that aren't relations */
13 tgl 6769 GBC 173 : if (pg_depend->classid != RelationRelationId)
6881 6770 61 : continue;
6881 tgl 6771 ECB :
6881 tgl 6772 CBC 112 : rel = relation_open(pg_depend->objid, AccessShareLock);
13 tgl 6773 GBC 112 : tupleDesc = RelationGetDescr(rel);
13 tgl 6774 EUB :
6775 : /*
6776 : * If objsubid identifies a specific column, refer to that in error
6777 : * messages. Otherwise, search to see if there's a user column of the
6778 : * type. (We assume system columns are never of interesting types.)
6779 : * The search is needed because an index containing an expression
6780 : * column of the target type will just be recorded as a whole-relation
6781 : * dependency. If we do not find a column of the type, the dependency
6782 : * must indicate that the type is transiently referenced in an index
6783 : * expression but not stored on disk, which we assume is OK, just as
6784 : * we do for references in views. (It could also be that the target
6785 : * type is embedded in some container type that is stored in an index
6786 : * column, but the previous recursion should catch such cases.)
6787 : */
13 tgl 6788 GIC 112 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6789 33 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6790 : else
6791 : {
6792 79 : att = NULL;
6793 203 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6794 : {
6795 127 : att = TupleDescAttr(tupleDesc, attno - 1);
13 tgl 6796 CBC 127 : if (att->atttypid == typeOid && !att->attisdropped)
13 tgl 6797 GIC 3 : break;
6798 124 : att = NULL;
6799 : }
13 tgl 6800 CBC 79 : if (att == NULL)
6801 : {
13 tgl 6802 ECB : /* No such column, so assume OK */
13 tgl 6803 GIC 76 : relation_close(rel, AccessShareLock);
13 tgl 6804 CBC 76 : continue;
13 tgl 6805 ECB : }
6806 : }
6881 6807 :
13 6808 : /*
6809 : * We definitely should reject if the relation has storage. If it's
6810 : * partitioned, then perhaps we don't have to reject: if there are
6811 : * partitions then we'll fail when we find one, else there is no
6812 : * stored data to worry about. However, it's possible that the type
6813 : * change would affect conclusions about whether the type is sortable
6814 : * or hashable and thus (if it's a partitioning column) break the
6815 : * partitioning rule. For now, reject for partitioned rels too.
6816 : */
13 tgl 6817 CBC 36 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
13 tgl 6818 LBC 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6881 tgl 6819 ECB : {
4394 tgl 6820 CBC 36 : if (origTypeName)
6821 15 : ereport(ERROR,
4394 tgl 6822 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4309 peter_e 6823 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
4394 tgl 6824 : origTypeName,
6825 : RelationGetRelationName(rel),
6826 : NameStr(att->attname))));
4394 tgl 6827 GBC 21 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6828 9 : ereport(ERROR,
4394 tgl 6829 EUB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6830 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6831 : RelationGetRelationName(origRelation),
6832 : RelationGetRelationName(rel),
4394 tgl 6833 ECB : NameStr(att->attname))));
4440 rhaas 6834 GIC 12 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4394 tgl 6835 CBC 3 : ereport(ERROR,
6836 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4309 peter_e 6837 ECB : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
4394 tgl 6838 : RelationGetRelationName(origRelation),
6839 : RelationGetRelationName(rel),
6840 : NameStr(att->attname))));
6841 : else
4394 tgl 6842 GIC 9 : ereport(ERROR,
6843 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6844 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6845 : RelationGetRelationName(origRelation),
4394 tgl 6846 EUB : RelationGetRelationName(rel),
6847 : NameStr(att->attname))));
6848 : }
6881 tgl 6849 UIC 0 : else if (OidIsValid(rel->rd_rel->reltype))
6850 : {
6881 tgl 6851 ECB : /*
6385 bruce 6852 : * A view or composite type itself isn't a problem, but we must
6853 : * recursively check for indirect dependencies via its rowtype.
6854 : */
6881 tgl 6855 LBC 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
4440 rhaas 6856 EUB : origRelation, origTypeName);
6857 : }
6858 :
6881 tgl 6859 UIC 0 : relation_close(rel, AccessShareLock);
6881 tgl 6860 ECB : }
6861 :
6881 tgl 6862 GIC 1704 : systable_endscan(depScan);
6863 :
6864 1704 : relation_close(depRel, AccessShareLock);
6865 1704 : }
6866 :
6867 :
6868 : /*
6869 : * find_typed_table_dependencies
6870 : *
4578 peter_e 6871 ECB : * Check to see if a composite type is being used as the type of a
6872 : * typed table. Abort if any are found and behavior is RESTRICT.
6873 : * Else return the list of tables.
6874 : */
6875 : static List *
4520 peter_e 6876 GIC 107 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6877 : {
6878 : Relation classRel;
4578 peter_e 6879 ECB : ScanKeyData key[1];
6880 : TableScanDesc scan;
6881 : HeapTuple tuple;
4520 peter_e 6882 GIC 107 : List *result = NIL;
6883 :
1539 andres 6884 107 : classRel = table_open(RelationRelationId, AccessShareLock);
4578 peter_e 6885 ECB :
4578 peter_e 6886 GIC 107 : ScanKeyInit(&key[0],
6887 : Anum_pg_class_reloftype,
6888 : BTEqualStrategyNumber, F_OIDEQ,
6889 : ObjectIdGetDatum(typeOid));
6890 :
1490 andres 6891 107 : scan = table_beginscan_catalog(classRel, 1, key);
4578 peter_e 6892 ECB :
4384 rhaas 6893 GIC 125 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
4578 peter_e 6894 ECB : {
1601 andres 6895 GIC 30 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6896 :
4520 peter_e 6897 CBC 30 : if (behavior == DROP_RESTRICT)
6898 12 : ereport(ERROR,
6899 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
4520 peter_e 6900 ECB : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6901 : typeName),
2118 tgl 6902 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
4520 peter_e 6903 : else
1601 andres 6904 GIC 18 : result = lappend_oid(result, classform->oid);
6905 : }
4578 peter_e 6906 ECB :
1490 andres 6907 GIC 95 : table_endscan(scan);
1539 6908 95 : table_close(classRel, AccessShareLock);
6909 :
4520 peter_e 6910 95 : return result;
6911 : }
6912 :
6913 :
6914 : /*
6915 : * check_of_type
4372 rhaas 6916 ECB : *
6917 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6918 : * isn't suitable, throw an error. Currently, we require that the type
6919 : * originated with CREATE TYPE AS. We could support any row type, but doing so
6920 : * would require handling a number of extra corner cases in the DDL commands.
6921 : * (Also, allowing domain-over-composite would open up a can of worms about
6922 : * whether and how the domain's constraints should apply to derived tables.)
6923 : */
6924 : void
4372 rhaas 6925 CBC 85 : check_of_type(HeapTuple typetuple)
6926 : {
4372 rhaas 6927 GIC 85 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6928 85 : bool typeOk = false;
6929 :
4372 rhaas 6930 CBC 85 : if (typ->typtype == TYPTYPE_COMPOSITE)
4372 rhaas 6931 ECB : {
6932 : Relation typeRelation;
6933 :
4372 rhaas 6934 CBC 85 : Assert(OidIsValid(typ->typrelid));
4372 rhaas 6935 GIC 85 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
4372 rhaas 6936 CBC 85 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6937 :
6938 : /*
6939 : * Close the parent rel, but keep our AccessShareLock on it until xact
6940 : * commit. That will prevent someone else from deleting or ALTERing
6941 : * the type before the typed table creation/conversion commits.
6942 : */
4372 rhaas 6943 GIC 85 : relation_close(typeRelation, NoLock);
6944 : }
6945 85 : if (!typeOk)
4372 rhaas 6946 CBC 3 : ereport(ERROR,
6947 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6948 : errmsg("type %s is not a composite type",
6949 : format_type_be(typ->oid))));
4372 rhaas 6950 GIC 82 : }
6951 :
4372 rhaas 6952 ECB :
6953 : /*
6913 tgl 6954 : * ALTER TABLE ADD COLUMN
6955 : *
6956 : * Adds an additional attribute to a relation making the assumption that
6957 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
5769 6958 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6959 : * AlterTableCmd's.
4389 rhaas 6960 : *
6961 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6962 : * have to decide at runtime whether to recurse or not depending on whether we
6963 : * actually add a column or merely merge with an existing column. (We can't
6964 : * check this in a static pre-pass because it won't handle multiple inheritance
6965 : * situations correctly.)
6913 tgl 6966 : */
6967 : static void
4520 peter_e 6968 CBC 923 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6969 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
6970 : AlterTableUtilityContext *context)
6971 : {
4520 peter_e 6972 GIC 923 : if (rel->rd_rel->reloftype && !recursing)
4643 6973 3 : ereport(ERROR,
6974 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6975 : errmsg("cannot add column to typed table")));
6976 :
4578 6977 920 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
1180 tgl 6978 29 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
6979 :
2963 alvherre 6980 917 : if (recurse && !is_view)
118 alvherre 6981 GNC 867 : cmd->recurse = true;
6913 tgl 6982 GIC 917 : }
6983 :
6984 : /*
6985 : * Add a column to a table. The return value is the address of the
6986 : * new column in the parent relation.
6987 : *
6988 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
6989 : * copy (but that happens only after we check for IF NOT EXISTS).
6990 : */
2937 alvherre 6991 ECB : static ObjectAddress
4389 rhaas 6992 GIC 1205 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
6993 : AlterTableCmd **cmd, bool recurse, bool recursing,
6994 : LOCKMODE lockmode, int cur_pass,
6995 : AlterTableUtilityContext *context)
6996 : {
6913 tgl 6997 1205 : Oid myrelid = RelationGetRelid(rel);
1180 6998 1205 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
1180 tgl 6999 CBC 1205 : bool if_not_exists = (*cmd)->missing_ok;
7000 : Relation pgclass,
7001 : attrdesc;
7002 : HeapTuple reltup;
7003 : FormData_pg_attribute attribute;
7004 : int newattnum;
5237 bruce 7005 ECB : char relkind;
7006 : HeapTuple typeTuple;
6649 tgl 7007 : Oid typeOid;
7008 : int32 typmod;
7009 : Oid collOid;
7010 : Form_pg_type tform;
6913 7011 : Expr *defval;
7012 : List *children;
7013 : ListCell *child;
7014 : AlterTableCmd *childcmd;
7015 : AclResult aclresult;
2937 alvherre 7016 : ObjectAddress address;
7017 : TupleDesc tupdesc;
982 michael 7018 GIC 1205 : FormData_pg_attribute *aattr[] = {&attribute};
4389 rhaas 7019 ECB :
7020 : /* At top level, permission check was done in ATPrepCmd, else do it */
4389 rhaas 7021 CBC 1205 : if (recursing)
640 peter 7022 GIC 291 : ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7023 :
2314 rhaas 7024 1205 : if (rel->rd_rel->relispartition && !recursing)
7025 6 : ereport(ERROR,
7026 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2314 rhaas 7027 ECB : errmsg("cannot add column to a partition")));
7028 :
1539 andres 7029 GIC 1199 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7030 :
7031 : /*
7032 : * Are we adding the column to a recursion child? If so, check whether to
7033 : * merge with an existing definition for the column. If we do merge, we
7034 : * must not recurse. Children will already have the column, and recursing
4382 bruce 7035 ECB : * into them would mess up attinhcount.
7036 : */
6913 tgl 7037 CBC 1199 : if (colDef->inhcount > 0)
7038 : {
7039 : HeapTuple tuple;
7040 :
6913 tgl 7041 ECB : /* Does child already have a column by this name? */
6913 tgl 7042 CBC 291 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
6913 tgl 7043 GIC 291 : if (HeapTupleIsValid(tuple))
6913 tgl 7044 ECB : {
6913 tgl 7045 CBC 12 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7046 : Oid ctypeId;
7047 : int32 ctypmod;
7048 : Oid ccollid;
7049 :
7050 : /* Child column must match on type, typmod, and collation */
4414 tgl 7051 GIC 12 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
5944 7052 12 : if (ctypeId != childatt->atttypid ||
7053 12 : ctypmod != childatt->atttypmod)
6913 tgl 7054 UIC 0 : ereport(ERROR,
7055 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7056 : errmsg("child table \"%s\" has different type for column \"%s\"",
7057 : RelationGetRelationName(rel), colDef->colname)));
4414 tgl 7058 GIC 12 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
4443 peter_e 7059 12 : if (ccollid != childatt->attcollation)
4443 peter_e 7060 LBC 0 : ereport(ERROR,
4443 peter_e 7061 ECB : (errcode(ERRCODE_COLLATION_MISMATCH),
7062 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7063 : RelationGetRelationName(rel), colDef->colname),
7064 : errdetail("\"%s\" versus \"%s\"",
7065 : get_collation_name(ccollid),
7066 : get_collation_name(childatt->attcollation))));
7664 tgl 7067 :
6913 7068 : /* Bump the existing child att's inhcount */
6913 tgl 7069 CBC 12 : childatt->attinhcount++;
12 peter 7070 GNC 12 : if (childatt->attinhcount < 0)
12 peter 7071 UNC 0 : ereport(ERROR,
7072 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7073 : errmsg("too many inheritance parents"));
2259 alvherre 7074 CBC 12 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7075 :
6913 tgl 7076 12 : heap_freetuple(tuple);
7077 :
7078 : /* Inform the user about the merge */
7079 12 : ereport(NOTICE,
2118 tgl 7080 ECB : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7081 : colDef->colname, RelationGetRelationName(rel))));
7082 :
1539 andres 7083 GIC 12 : table_close(attrdesc, RowExclusiveLock);
2937 alvherre 7084 12 : return InvalidObjectAddress;
7085 : }
7086 : }
7087 :
7088 : /* skip if the name already exists and if_not_exists is true */
1180 tgl 7089 1187 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7090 : {
7091 27 : table_close(attrdesc, RowExclusiveLock);
7092 27 : return InvalidObjectAddress;
1180 tgl 7093 ECB : }
7664 tgl 7094 EUB :
7095 : /*
1180 tgl 7096 ECB : * Okay, we need to add the column, so go ahead and do parse
7097 : * transformation. This can result in queueing up, or even immediately
7098 : * executing, subsidiary operations (such as creation of unique indexes);
7099 : * so we mustn't do it until we have made the if_not_exists check.
7100 : *
7101 : * When recursing, the command was already transformed and we needn't do
7102 : * so again. Also, if context isn't given we can't transform. (That
7103 : * currently happens only for AT_AddColumnToView; we expect that view.c
7104 : * passed us a ColumnDef that doesn't need work.)
7105 : */
1180 tgl 7106 GIC 1145 : if (context != NULL && !recursing)
7107 : {
7108 854 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7109 : cur_pass, context);
1180 tgl 7110 CBC 854 : Assert(*cmd != NULL);
7111 854 : colDef = castNode(ColumnDef, (*cmd)->def);
7112 : }
7113 :
7114 : /*
7115 : * Cannot add identity column if table has children, because identity does
7116 : * not inherit. (Adding column and identity separately will work.)
7117 : */
2194 peter_e 7118 1145 : if (colDef->identity &&
2194 peter_e 7119 GIC 15 : recurse &&
711 alvherre 7120 15 : find_inheritance_children(myrelid, NoLock) != NIL)
2194 peter_e 7121 3 : ereport(ERROR,
7122 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7123 : errmsg("cannot recursively add identity column to table that has child tables")));
7124 :
1180 tgl 7125 GBC 1142 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7126 :
1180 tgl 7127 GIC 1142 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7128 1142 : if (!HeapTupleIsValid(reltup))
1180 tgl 7129 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
1180 tgl 7130 GIC 1142 : relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7664 tgl 7131 EUB :
7132 : /* Determine the new attribute's number */
1601 andres 7133 GIC 1142 : newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7134 1142 : if (newattnum > MaxHeapAttributeNumber)
1601 andres 7135 UBC 0 : ereport(ERROR,
7136 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7137 : errmsg("tables can have at most %d columns",
1601 andres 7138 ECB : MaxHeapAttributeNumber)));
7139 :
4414 tgl 7140 CBC 1142 : typeTuple = typenameType(NULL, colDef->typeName, &typmod);
6913 7141 1142 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
1601 andres 7142 GIC 1142 : typeOid = tform->oid;
7143 :
147 peter 7144 GNC 1142 : aclresult = object_aclcheck(TypeRelationId, typeOid, GetUserId(), ACL_USAGE);
4128 peter_e 7145 GIC 1142 : if (aclresult != ACLCHECK_OK)
3950 7146 6 : aclcheck_error_type(aclresult, typeOid);
7147 :
4414 tgl 7148 1136 : collOid = GetColumnDefCollation(NULL, colDef, typeOid);
7149 :
7150 : /* make sure datatype is legal for a column */
4395 7151 1136 : CheckAttributeType(colDef->colname, typeOid, collOid,
4395 tgl 7152 CBC 1136 : list_make1_oid(rel->rd_rel->reltype),
7153 : 0);
7154 :
7155 : /*
7156 : * Construct new attribute's pg_attribute entry. (Variable-length fields
7157 : * are handled by InsertPgAttributeTuples().)
499 peter 7158 ECB : */
5259 alvherre 7159 GIC 1121 : attribute.attrelid = myrelid;
5259 alvherre 7160 CBC 1121 : namestrcpy(&(attribute.attname), colDef->colname);
5259 alvherre 7161 GIC 1121 : attribute.atttypid = typeOid;
5170 tgl 7162 CBC 1121 : attribute.attstattarget = (newattnum > 0) ? -1 : 0;
5259 alvherre 7163 GIC 1121 : attribute.attlen = tform->typlen;
5228 tgl 7164 1121 : attribute.attnum = newattnum;
12 peter 7165 GNC 1121 : if (list_length(colDef->typeName->arrayBounds) > PG_INT16_MAX)
12 peter 7166 UNC 0 : ereport(ERROR,
7167 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7168 : errmsg("too many array dimensions"));
5015 peter_e 7169 GIC 1121 : attribute.attndims = list_length(colDef->typeName->arrayBounds);
686 tgl 7170 1121 : attribute.atttypmod = typmod;
686 tgl 7171 CBC 1121 : attribute.attbyval = tform->typbyval;
5259 alvherre 7172 GIC 1121 : attribute.attalign = tform->typalign;
270 peter 7173 GNC 1121 : if (colDef->storage_name)
270 peter 7174 UNC 0 : attribute.attstorage = GetAttributeStorage(typeOid, colDef->storage_name);
7175 : else
270 peter 7176 GNC 1121 : attribute.attstorage = tform->typstorage;
682 tgl 7177 GIC 1121 : attribute.attcompression = GetAttributeCompression(typeOid,
682 tgl 7178 ECB : colDef->compression);
5259 alvherre 7179 GIC 1121 : attribute.attnotnull = colDef->is_not_null;
5259 alvherre 7180 CBC 1121 : attribute.atthasdef = false;
1838 andrew 7181 1121 : attribute.atthasmissing = false;
2194 peter_e 7182 GIC 1121 : attribute.attidentity = colDef->identity;
1471 peter 7183 1121 : attribute.attgenerated = colDef->generated;
5259 alvherre 7184 1121 : attribute.attisdropped = false;
7185 1121 : attribute.attislocal = colDef->is_local;
7186 1121 : attribute.attinhcount = colDef->inhcount;
4443 peter_e 7187 CBC 1121 : attribute.attcollation = collOid;
7188 :
6913 tgl 7189 GIC 1121 : ReleaseSysCache(typeTuple);
6913 tgl 7190 ECB :
982 michael 7191 CBC 1121 : tupdesc = CreateTupleDesc(lengthof(aattr), (FormData_pg_attribute **) &aattr);
7192 :
7193 1121 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7194 :
1539 andres 7195 GIC 1121 : table_close(attrdesc, RowExclusiveLock);
7196 :
7197 : /*
7198 : * Update pg_class tuple as appropriate
7199 : */
1601 7200 1121 : ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7201 :
2259 alvherre 7202 1121 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7203 :
6913 tgl 7204 1121 : heap_freetuple(reltup);
7205 :
7206 : /* Post creation hook for new attribute */
3686 rhaas 7207 1121 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
4518 rhaas 7208 ECB :
1539 andres 7209 GIC 1121 : table_close(pgclass, RowExclusiveLock);
6913 tgl 7210 ECB :
7211 : /* Make the attribute's catalog entry visible */
6913 tgl 7212 GIC 1121 : CommandCounterIncrement();
7653 tgl 7213 ECB :
7214 : /*
7215 : * Store the DEFAULT, if any, in the catalogs
7216 : */
6913 tgl 7217 CBC 1121 : if (colDef->raw_default)
7664 tgl 7218 ECB : {
7653 7219 : RawColumnDefault *rawEnt;
7220 :
7653 tgl 7221 GIC 334 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
5259 alvherre 7222 334 : rawEnt->attnum = attribute.attnum;
6913 tgl 7223 334 : rawEnt->raw_default = copyObject(colDef->raw_default);
7224 :
7225 : /*
1838 andrew 7226 ECB : * Attempt to skip a complete table rewrite by storing the specified
7227 : * DEFAULT value outside of the heap. This may be disabled inside
7228 : * AddRelationNewConstraints if the optimization cannot be applied.
7229 : */
1471 peter 7230 GIC 334 : rawEnt->missingMode = (!colDef->generated);
7231 :
7232 334 : rawEnt->generated = colDef->generated;
1838 andrew 7233 ECB :
7234 : /*
7235 : * This function is intended for CREATE TABLE, so it processes a
7236 : * _list_ of defaults, but we just do one.
7237 : */
3675 rhaas 7238 GIC 334 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7239 : false, true, false, NULL);
7240 :
7241 : /* Make the additional catalog changes visible */
6913 tgl 7242 328 : CommandCounterIncrement();
7243 :
7244 : /*
7245 : * Did the request for a missing value work? If not we'll have to do a
7246 : * rewrite
7247 : */
1838 andrew 7248 328 : if (!rawEnt->missingMode)
7249 48 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7250 : }
7664 tgl 7251 ECB :
7252 : /*
7253 : * Tell Phase 3 to fill in the default expression, if there is one.
7254 : *
6347 bruce 7255 : * If there is no default, Phase 3 doesn't have to do anything, because
7256 : * that effectively means that the default is NULL. The heap tuple access
7257 : * routines always check for attnum > # of attributes in tuple, and return
7258 : * NULL if so, so without any modification of the tuple data we will get
7259 : * the effect of NULL values in the new column.
6913 tgl 7260 : *
6385 bruce 7261 : * An exception occurs when the new column is of a domain type: the domain
7262 : * might have a NOT NULL constraint, or a check constraint that indirectly
7263 : * rejects nulls. If there are any domain constraints then we construct
7264 : * an explicit NULL default value that will be passed through
7265 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7266 : * rewriting the table which we really don't have to do, but the present
7267 : * design of domain processing doesn't offer any simple way of checking
7268 : * the constraints more directly.)
7269 : *
7270 : * Note: we use build_column_default, and not just the cooked default
7271 : * returned by AddRelationNewConstraints, so that the right thing happens
7272 : * when a datatype's default applies.
7273 : *
7274 : * Note: it might seem that this should happen at the end of Phase 2, so
1174 tgl 7275 : * that the effects of subsequent subcommands can be taken into account.
7276 : * It's intentional that we do it now, though. The new column should be
7277 : * filled according to what is said in the ADD COLUMN subcommand, so that
7278 : * the effects are the same as if this subcommand had been run by itself
7279 : * and the later subcommands had been issued in new ALTER TABLE commands.
7280 : *
7281 : * We can skip this entirely for relations without storage, since Phase 3
7282 : * is certainly not going to touch them. System attributes don't have
7283 : * interesting defaults, either.
7284 : */
1174 tgl 7285 GIC 1115 : if (RELKIND_HAS_STORAGE(relkind) && attribute.attnum > 0)
7286 : {
7287 : /*
7288 : * For an identity column, we can't use build_column_default(),
7289 : * because the sequence ownership isn't set yet. So do it manually.
7290 : */
1892 peter_e 7291 945 : if (colDef->identity)
7292 : {
7293 12 : NextValueExpr *nve = makeNode(NextValueExpr);
7294 :
7295 12 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7296 12 : nve->typeId = typeOid;
7297 :
7298 12 : defval = (Expr *) nve;
7299 :
7300 : /* must do a rewrite for identity columns */
1838 andrew 7301 CBC 12 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7302 : }
7303 : else
1892 peter_e 7304 933 : defval = (Expr *) build_column_default(rel, attribute.attnum);
6649 tgl 7305 ECB :
2961 tgl 7306 GIC 945 : if (!defval && DomainHasConstraints(typeOid))
5237 bruce 7307 ECB : {
7308 : Oid baseTypeId;
7309 : int32 baseTypeMod;
7310 : Oid baseTypeColl;
7311 :
5237 bruce 7312 CBC 3 : baseTypeMod = typmod;
5237 bruce 7313 GIC 3 : baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
4398 tgl 7314 3 : baseTypeColl = get_typcollation(baseTypeId);
7315 3 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
5237 bruce 7316 3 : defval = (Expr *) coerce_to_target_type(NULL,
7317 : (Node *) defval,
7318 : baseTypeId,
7319 : typeOid,
5237 bruce 7320 ECB : typmod,
7321 : COERCION_ASSIGNMENT,
7322 : COERCE_IMPLICIT_CAST,
7323 : -1);
5050 bruce 7324 GIC 3 : if (defval == NULL) /* should not happen */
5237 bruce 7325 LBC 0 : elog(ERROR, "failed to coerce base type to domain");
5237 bruce 7326 ECB : }
7327 :
5237 bruce 7328 CBC 945 : if (defval)
7329 : {
7330 : NewColumnValue *newval;
7331 :
5237 bruce 7332 GIC 286 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7333 286 : newval->attnum = attribute.attnum;
4310 rhaas 7334 CBC 286 : newval->expr = expression_planner(defval);
1187 tgl 7335 286 : newval->is_generated = (colDef->generated != '\0');
7664 tgl 7336 ECB :
5237 bruce 7337 GBC 286 : tab->newvals = lappend(tab->newvals, newval);
7338 : }
7339 :
1838 andrew 7340 GIC 945 : if (DomainHasConstraints(typeOid))
1838 andrew 7341 CBC 6 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
1838 andrew 7342 ECB :
1838 andrew 7343 GBC 945 : if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing)
7344 : {
7345 : /*
7346 : * If the new column is NOT NULL, and there is no missing value,
7347 : * tell Phase 3 it needs to check for NULLs.
7348 : */
1488 rhaas 7349 GIC 725 : tab->verify_new_notnull |= colDef->is_not_null;
7350 : }
7351 : }
5463 tgl 7352 ECB :
7653 7353 : /*
6877 tgl 7354 EUB : * Add needed dependency entries for the new column.
7355 : */
4370 tgl 7356 GIC 1115 : add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
4370 tgl 7357 CBC 1115 : add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
7358 :
4389 rhaas 7359 ECB : /*
7360 : * Propagate to children as appropriate. Unlike most other ALTER
7361 : * routines, we have to do this one level of recursion at a time; we can't
7362 : * use find_all_inheritors to do it in one pass.
7363 : */
7364 : children =
711 alvherre 7365 GIC 1115 : find_inheritance_children(RelationGetRelid(rel), lockmode);
4389 rhaas 7366 ECB :
7367 : /*
7368 : * If we are told not to recurse, there had better not be any child
7369 : * tables; else the addition would put them out of step.
7370 : */
4389 rhaas 7371 GIC 1115 : if (children && !recurse)
4389 rhaas 7372 CBC 6 : ereport(ERROR,
7373 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4389 rhaas 7374 ECB : errmsg("column must be added to child tables too")));
7375 :
7376 : /* Children should see column as singly inherited */
4389 rhaas 7377 GIC 1109 : if (!recursing)
7378 : {
1180 tgl 7379 830 : childcmd = copyObject(*cmd);
7380 830 : colDef = castNode(ColumnDef, childcmd->def);
4389 rhaas 7381 830 : colDef->inhcount = 1;
7382 830 : colDef->is_local = false;
7383 : }
7384 : else
1180 tgl 7385 279 : childcmd = *cmd; /* no need to copy again */
7386 :
4389 rhaas 7387 1400 : foreach(child, children)
7388 : {
4389 rhaas 7389 CBC 291 : Oid childrelid = lfirst_oid(child);
7390 : Relation childrel;
4389 rhaas 7391 ECB : AlteredTableInfo *childtab;
7392 :
7393 : /* find_inheritance_children already got lock */
1539 andres 7394 CBC 291 : childrel = table_open(childrelid, NoLock);
4389 rhaas 7395 GIC 291 : CheckTableNotInUse(childrel, "ALTER TABLE");
7396 :
7397 : /* Find or create work queue entry for this table */
7398 291 : childtab = ATGetQueueEntry(wqueue, childrel);
7399 :
7400 : /* Recurse to child; return value is ignored */
4389 rhaas 7401 CBC 291 : ATExecAddColumn(wqueue, childtab, childrel,
1180 tgl 7402 ECB : &childcmd, recurse, true,
7403 : lockmode, cur_pass, context);
4389 rhaas 7404 :
1539 andres 7405 GIC 291 : table_close(childrel, NoLock);
7406 : }
7407 :
2937 alvherre 7408 CBC 1109 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
2937 alvherre 7409 GIC 1109 : return address;
6913 tgl 7410 ECB : }
7664 7411 :
4091 rhaas 7412 EUB : /*
4091 rhaas 7413 ECB : * If a new or renamed column will collide with the name of an existing
7414 : * column and if_not_exists is false then error out, else do nothing.
7415 : */
2811 andrew 7416 : static bool
2811 andrew 7417 CBC 1403 : check_for_column_name_collision(Relation rel, const char *colname,
2811 andrew 7418 EUB : bool if_not_exists)
7419 : {
7420 : HeapTuple attTuple;
7421 : int attnum;
7422 :
4091 rhaas 7423 ECB : /*
7424 : * this test is deliberately not attisdropped-aware, since if one tries to
7425 : * add a column matching a dropped column name, it's gonna fail anyway.
7426 : */
4091 rhaas 7427 CBC 1403 : attTuple = SearchSysCache2(ATTNAME,
4091 rhaas 7428 ECB : ObjectIdGetDatum(RelationGetRelid(rel)),
7429 : PointerGetDatum(colname));
4091 rhaas 7430 GIC 1403 : if (!HeapTupleIsValid(attTuple))
2811 andrew 7431 CBC 1355 : return true;
7432 :
3955 bruce 7433 GIC 48 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
4091 rhaas 7434 CBC 48 : ReleaseSysCache(attTuple);
4091 rhaas 7435 ECB :
7436 : /*
7437 : * We throw a different error message for conflicts with system column
7438 : * names, since they are normally not shown and the user might otherwise
7439 : * be confused about the reason for the conflict.
7440 : */
3955 bruce 7441 GIC 48 : if (attnum <= 0)
3955 bruce 7442 CBC 6 : ereport(ERROR,
3955 bruce 7443 ECB : (errcode(ERRCODE_DUPLICATE_COLUMN),
2118 tgl 7444 : errmsg("column name \"%s\" conflicts with a system column name",
7445 : colname)));
3955 bruce 7446 : else
2811 andrew 7447 : {
2811 andrew 7448 CBC 42 : if (if_not_exists)
2811 andrew 7449 EUB : {
2811 andrew 7450 GIC 27 : ereport(NOTICE,
7451 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2811 andrew 7452 ECB : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7453 : colname, RelationGetRelationName(rel))));
2811 andrew 7454 CBC 27 : return false;
2811 andrew 7455 ECB : }
7456 :
3955 bruce 7457 GBC 15 : ereport(ERROR,
7458 : (errcode(ERRCODE_DUPLICATE_COLUMN),
3955 bruce 7459 ECB : errmsg("column \"%s\" of relation \"%s\" already exists",
7460 : colname, RelationGetRelationName(rel))));
7461 : }
2811 andrew 7462 :
7463 : return true;
4091 rhaas 7464 : }
7465 :
6913 tgl 7466 : /*
7467 : * Install a column's dependency on its datatype.
7468 : */
7469 : static void
4370 tgl 7470 CBC 1560 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7471 : {
6913 tgl 7472 ECB : ObjectAddress myself,
7473 : referenced;
7474 :
6569 tgl 7475 GIC 1560 : myself.classId = RelationRelationId;
6913 tgl 7476 CBC 1560 : myself.objectId = relid;
6913 tgl 7477 GIC 1560 : myself.objectSubId = attnum;
6569 tgl 7478 CBC 1560 : referenced.classId = TypeRelationId;
6913 tgl 7479 GIC 1560 : referenced.objectId = typid;
7480 1560 : referenced.objectSubId = 0;
7481 1560 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
4370 7482 1560 : }
4370 tgl 7483 ECB :
7484 : /*
7485 : * Install a column's dependency on its collation.
7486 : */
7487 : static void
4370 tgl 7488 GIC 1560 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7489 : {
4370 tgl 7490 ECB : ObjectAddress myself,
7491 : referenced;
4439 peter_e 7492 :
7493 : /* We know the default collation is pinned, so don't bother recording it */
4370 tgl 7494 GIC 1560 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
4439 peter_e 7495 ECB : {
4370 tgl 7496 GIC 9 : myself.classId = RelationRelationId;
7497 9 : myself.objectId = relid;
7498 9 : myself.objectSubId = attnum;
4439 peter_e 7499 9 : referenced.classId = CollationRelationId;
4439 peter_e 7500 CBC 9 : referenced.objectId = collid;
4439 peter_e 7501 GIC 9 : referenced.objectSubId = 0;
7502 9 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7503 : }
6913 tgl 7504 CBC 1560 : }
6913 tgl 7505 ECB :
7506 : /*
7507 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7508 : *
1447 7509 : * Return the address of the modified column. If the column was already
7510 : * nullable, InvalidObjectAddress is returned.
7511 : */
7512 : static ObjectAddress
2 alvherre 7513 GNC 107 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7514 : LOCKMODE lockmode)
7515 : {
7516 : HeapTuple tuple;
7517 : HeapTuple conTup;
7518 : Form_pg_attribute attTup;
7519 : AttrNumber attnum;
7520 : Relation attr_rel;
7521 : ObjectAddress address;
7522 :
7523 : /*
7524 : * lookup the attribute
7525 : */
1539 andres 7526 GIC 107 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7527 :
6913 tgl 7528 107 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7529 107 : if (!HeapTupleIsValid(tuple))
7530 9 : ereport(ERROR,
7531 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7532 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7533 : colName, RelationGetRelationName(rel))));
1629 peter_e 7534 98 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7535 98 : attnum = attTup->attnum;
2 alvherre 7536 GNC 98 : ObjectAddressSubSet(address, RelationRelationId,
7537 : RelationGetRelid(rel), attnum);
7538 :
7539 : /* If the column is already nullable there's nothing to do. */
7540 98 : if (!attTup->attnotnull)
7541 : {
7542 3 : table_close(attr_rel, RowExclusiveLock);
7543 3 : return InvalidObjectAddress;
7544 : }
7545 :
7546 : /* Prevent them from altering a system attribute */
6913 tgl 7547 GIC 95 : if (attnum <= 0)
6913 tgl 7548 UIC 0 : ereport(ERROR,
7549 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7550 : errmsg("cannot alter system column \"%s\"",
7551 : colName)));
7552 :
1629 peter_e 7553 GIC 95 : if (attTup->attidentity)
2194 7554 3 : ereport(ERROR,
2194 peter_e 7555 ECB : (errcode(ERRCODE_SYNTAX_ERROR),
7556 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7557 : colName, RelationGetRelationName(rel))));
7558 :
7559 : /*
7560 : * It's not OK to remove a constraint only for the parent and leave it in
7561 : * the children, so disallow that.
7562 : */
2 alvherre 7563 GNC 92 : if (!recurse)
7564 : {
7565 6 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2 alvherre 7566 ECB : {
7567 : PartitionDesc partdesc;
7568 :
2 alvherre 7569 GNC 6 : partdesc = RelationGetPartitionDesc(rel, true);
7570 :
7571 6 : if (partdesc->nparts > 0)
7572 3 : ereport(ERROR,
7573 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7574 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7575 : errhint("Do not specify the ONLY keyword."));
7576 : }
2 alvherre 7577 UNC 0 : else if (rel->rd_rel->relhassubclass &&
7578 0 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
7579 : {
7580 0 : ereport(ERROR,
7581 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7582 : errmsg("NOT NULL constraint on column \"%s\" must be removed in child tables too",
7583 : colName),
7584 : errhint("Do not specify the ONLY keyword."));
7585 : }
7664 tgl 7586 ECB : }
7587 :
7588 : /*
7589 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7590 : */
2314 rhaas 7591 GIC 89 : if (rel->rd_rel->relispartition)
7592 : {
2 alvherre 7593 GNC 9 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7594 9 : Relation parent = table_open(parentId, AccessShareLock);
7595 9 : TupleDesc tupDesc = RelationGetDescr(parent);
7596 : AttrNumber parent_attnum;
7597 :
2314 rhaas 7598 GIC 9 : parent_attnum = get_attnum(parentId, colName);
2058 andres 7599 9 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
2314 rhaas 7600 9 : ereport(ERROR,
2314 rhaas 7601 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2118 tgl 7602 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7603 : colName)));
1539 andres 7604 UIC 0 : table_close(parent, AccessShareLock);
7605 : }
7606 :
7607 : /*
7608 : * Find the constraint that makes this column NOT NULL.
7609 : */
2 alvherre 7610 GNC 80 : conTup = findNotNullConstraint(rel, colName);
7611 80 : if (conTup == NULL)
7612 : {
7613 : Bitmapset *pkcols;
7614 :
7615 : /*
7616 : * There's no NOT NULL constraint, so throw an error. If the column
7617 : * is in a primary key, we can throw a specific error. Otherwise,
7618 : * this is unexpected.
7619 : */
7620 3 : pkcols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
7621 3 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
7622 : pkcols))
7623 3 : ereport(ERROR,
7624 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7625 : errmsg("column \"%s\" is in a primary key", colName));
7626 :
7627 : /* this shouldn't happen */
2 alvherre 7628 UNC 0 : elog(ERROR, "no NOT NULL constraint found to drop");
7629 : }
7630 :
2 alvherre 7631 GNC 77 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7632 : false, NULL, lockmode);
7633 :
7634 62 : heap_freetuple(conTup);
3675 rhaas 7635 ECB :
1539 andres 7636 CBC 62 : table_close(attr_rel, RowExclusiveLock);
2937 alvherre 7637 ECB :
2937 alvherre 7638 CBC 62 : return address;
7639 : }
7640 :
6913 tgl 7641 ECB : /*
7642 : * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7643 : * to verify it; recurses to apply the same to children.
7644 : *
7645 : * When called to alter an existing table, 'wqueue' must be given so that we can
7646 : * queue a check that existing tuples pass the constraint. When called from
7647 : * table creation, 'wqueue' should be passed as NULL.
7648 : */
2314 rhaas 7649 : static void
2 alvherre 7650 GNC 32180 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
7651 : LOCKMODE lockmode)
7652 : {
7653 : HeapTuple tuple;
7654 : Form_pg_attribute attForm;
7655 : List *children;
7656 : ListCell *lc;
7657 :
7658 32180 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7659 32180 : if (!HeapTupleIsValid(tuple))
2 alvherre 7660 UNC 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7661 : attnum, RelationGetRelid(rel));
2 alvherre 7662 GNC 32180 : attForm = (Form_pg_attribute) GETSTRUCT(tuple);
7663 32180 : if (!attForm->attnotnull)
7664 : {
7665 : Relation attr_rel;
7666 :
7667 556 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7668 :
7669 556 : attForm->attnotnull = true;
7670 556 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7671 :
7672 556 : table_close(attr_rel, RowExclusiveLock);
7673 :
7674 : /*
7675 : * And set up for existing values to be checked, unless another constraint
7676 : * already proves this.
7677 : */
7678 556 : if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
7679 : {
7680 : AlteredTableInfo *tab;
7681 :
7682 528 : tab = ATGetQueueEntry(wqueue, rel);
7683 528 : tab->verify_new_notnull = true;
7684 : }
7685 : }
7686 :
7687 : /* if no recursion is desired, we're done */
7688 32180 : if (!recurse)
2 alvherre 7689 GIC 31839 : return;
7690 :
2 alvherre 7691 GNC 341 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7692 386 : foreach(lc, children)
2175 sfrost 7693 ECB : {
2 alvherre 7694 GNC 45 : Oid childrelid = lfirst_oid(lc);
7695 : Relation childrel;
7696 : AttrNumber childattno;
7697 :
7698 : /* find_inheritance_children already got lock */
7699 45 : childrel = table_open(childrelid, NoLock);
7700 45 : CheckTableNotInUse(childrel, "ALTER TABLE");
7701 :
7702 45 : childattno = get_attnum(RelationGetRelid(childrel),
7703 45 : get_attname(RelationGetRelid(rel), attnum,
7704 : false));
7705 45 : set_attnotnull(wqueue, childrel, childattno,
7706 : recurse, lockmode);
7707 45 : table_close(childrel, NoLock);
2175 sfrost 7708 ECB : }
7709 : }
7710 :
1877 peter_e 7711 : /*
7712 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7713 : *
7714 : * Add a NOT NULL constraint to a single table and its children. Returns
7715 : * the address of the constraint added to the parent relation, if one gets
7716 : * added, or InvalidObjectAddress otherwise.
7717 : *
7718 : * We must recurse to child tables during execution, rather than using
7719 : * ALTER TABLE's normal prep-time recursion.
7720 : */
7721 : static ObjectAddress
2 alvherre 7722 GNC 221 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7723 : bool recurse, bool recursing, List **readyRels,
7724 : LOCKMODE lockmode)
7725 : {
7726 : HeapTuple tuple;
7727 : Relation constr_rel;
7728 : ScanKeyData skey;
7729 : SysScanDesc conscan;
7730 : AttrNumber attnum;
7731 : ObjectAddress address;
7732 : Constraint *constraint;
7733 : CookedConstraint *ccon;
7734 : List *cooked;
7735 221 : List *ready = NIL;
7736 :
7737 : /*
7738 : * In cases of multiple inheritance, we might visit the same child more
7739 : * than once. In the topmost call, set up a list that we fill with all
7740 : * visited relations, to skip those.
7741 : */
7742 221 : if (readyRels == NULL)
7743 159 : readyRels = &ready;
7744 221 : if (list_member_oid(*readyRels, RelationGetRelid(rel)))
7745 3 : return InvalidObjectAddress;
7746 218 : *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
7747 :
7748 : /* At top level, permission check was done in ATPrepCmd, else do it */
7749 218 : if (recursing)
7750 : {
7751 59 : ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7752 59 : Assert(conName != NULL);
7753 : }
7754 :
7755 218 : attnum = get_attnum(RelationGetRelid(rel), colName);
7756 218 : if (attnum == InvalidAttrNumber)
7203 tgl 7757 CBC 9 : ereport(ERROR,
7203 tgl 7758 ECB : (errcode(ERRCODE_UNDEFINED_COLUMN),
6913 7759 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7760 : colName, RelationGetRelationName(rel))));
7653 7761 :
6913 7762 : /* Prevent them from altering a system attribute */
6913 tgl 7763 GIC 209 : if (attnum <= 0)
7203 tgl 7764 UIC 0 : ereport(ERROR,
7765 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7766 : errmsg("cannot alter system column \"%s\"",
7767 : colName)));
7522 bruce 7768 ECB :
7769 : /* See if there's already a constraint */
2 alvherre 7770 GNC 209 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7771 209 : ScanKeyInit(&skey,
7772 : Anum_pg_constraint_conrelid,
7773 : BTEqualStrategyNumber, F_OIDEQ,
7774 : ObjectIdGetDatum(RelationGetRelid(rel)));
7775 209 : conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
7776 : NULL, 1, &skey);
7777 :
7778 401 : while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
7779 : {
7780 202 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7781 202 : bool changed = false;
7782 : HeapTuple copytup;
7783 :
7784 202 : if (conForm->contype != CONSTRAINT_NOTNULL)
7785 97 : continue;
7786 :
7787 105 : if (extractNotNullColumn(tuple) != attnum)
7788 95 : continue;
7789 :
7790 10 : copytup = heap_copytuple(tuple);
7791 10 : conForm = (Form_pg_constraint) GETSTRUCT(copytup);
7653 tgl 7792 ECB :
1488 rhaas 7793 : /*
7794 : * If we find an appropriate constraint, we're almost done, but just
7795 : * need to change some properties on it: if we're recursing, increment
7796 : * coninhcount; if not, set conislocal if not already set.
7797 : */
2 alvherre 7798 GNC 10 : if (recursing)
7799 : {
7800 3 : conForm->coninhcount++;
7801 3 : changed = true;
7802 : }
7803 7 : else if (!conForm->conislocal)
7804 : {
2 alvherre 7805 UNC 0 : conForm->conislocal = true;
7806 0 : changed = true;
7807 : }
7808 :
2 alvherre 7809 GNC 10 : if (changed)
7810 : {
7811 3 : CatalogTupleUpdate(constr_rel, ©tup->t_self, copytup);
7812 3 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7813 : }
7814 :
7815 10 : systable_endscan(conscan);
7816 10 : table_close(constr_rel, RowExclusiveLock);
7817 :
7818 10 : if (changed)
7819 3 : return address;
7820 : else
7821 7 : return InvalidObjectAddress;
6913 tgl 7822 ECB : }
7823 :
2 alvherre 7824 GNC 199 : systable_endscan(conscan);
7825 199 : table_close(constr_rel, RowExclusiveLock);
7826 :
7827 : /*
7828 : * If we're asked not to recurse, and children exist, raise an error.
7829 : */
7830 208 : if (!recurse &&
7831 9 : find_inheritance_children(RelationGetRelid(rel),
7832 : NoLock) != NIL)
7833 : {
7834 6 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7835 3 : ereport(ERROR,
7836 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7837 : errmsg("cannot add constraint to only the partitioned table when partitions exist"),
7838 : errhint("Do not specify the ONLY keyword."));
7839 : else
7840 3 : ereport(ERROR,
7841 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7842 : errmsg("cannot add constraint only to table with inheritance children"),
7843 : errhint("Do not specify the ONLY keyword."));
7844 : }
7845 :
7846 : /*
7847 : * No constraint exists; we must add one. First determine a name to use,
7848 : * if we haven't already.
7849 : */
7850 193 : if (!recursing)
7851 : {
7852 137 : Assert(conName == NULL);
7853 137 : conName = ChooseConstraintName(RelationGetRelationName(rel),
7854 : colName, "not_null",
7855 137 : RelationGetNamespace(rel),
7856 : NIL);
7857 : }
7858 193 : constraint = makeNode(Constraint);
7859 193 : constraint->contype = CONSTR_NOTNULL;
7860 193 : constraint->conname = conName;
7861 193 : constraint->deferrable = false;
7862 193 : constraint->initdeferred = false;
7863 193 : constraint->location = -1;
7864 193 : constraint->colname = colName;
7865 193 : constraint->skip_validation = false;
7866 193 : constraint->initially_valid = true;
7867 :
7868 : /* and do it */
7869 193 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7870 193 : false, !recursing, false, NULL);
7871 193 : ccon = linitial(cooked);
7872 193 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7873 :
7874 : /*
7875 : * Mark pg_attribute.attnotnull for the column. Tell that function not to
7876 : * recurse, because we're going to do it here.
7877 : */
7878 193 : set_attnotnull(wqueue, rel, attnum, false, lockmode);
7879 :
7880 : /*
7881 : * Recurse to propagate the constraint to children that don't have one.
7882 : */
7883 193 : if (recurse)
7884 : {
7885 : List *children;
7886 : ListCell *lc;
7887 :
7888 190 : children = find_inheritance_children(RelationGetRelid(rel),
7889 : lockmode);
7890 :
7891 252 : foreach(lc, children)
7892 : {
7893 : Relation childrel;
7894 :
7895 62 : childrel = table_open(lfirst_oid(lc), NoLock);
7896 :
7897 62 : ATExecSetNotNull(wqueue, childrel,
7898 : conName, colName, recurse, true,
7899 : readyRels, lockmode);
7900 :
7901 62 : table_close(childrel, NoLock);
7902 : }
7903 : }
7904 :
2937 alvherre 7905 GIC 193 : return address;
7906 : }
7907 :
7908 : /*
7909 : * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
7910 : *
7911 : * This doesn't exist in the grammar; it's used when creating a
7912 : * primary key and the column is not already marked attnotnull.
7913 : */
7914 : static void
2 alvherre 7915 GNC 28396 : ATExecSetAttNotNull(List **wqueue, Relation rel,
7916 : const char *colName, LOCKMODE lockmode)
7917 : {
7918 : AttrNumber attnum;
7919 :
7920 28396 : attnum = get_attnum(RelationGetRelid(rel), colName);
7921 28396 : if (attnum == InvalidAttrNumber) /* XXX should not happen .. elog? */
7922 9 : ereport(ERROR,
7923 : errcode(ERRCODE_UNDEFINED_COLUMN),
7924 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7925 : colName, RelationGetRelationName(rel)));
7926 :
7927 28387 : set_attnotnull(wqueue, rel, attnum, false, lockmode);
7928 28387 : }
7929 :
7930 : /*
1447 tgl 7931 ECB : * ALTER TABLE ALTER COLUMN CHECK NOT NULL
7932 : *
7933 : * This doesn't exist in the grammar, but we generate AT_CheckNotNull
7934 : * commands against the partitions of a partitioned table if the user
7935 : * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
7936 : * or tries to create a primary key on it (which internally creates
7937 : * AT_SetNotNull on the partitioned table). Such a command doesn't
7938 : * allow us to actually modify any partition, but we want to let it
7939 : * go through if the partitions are already properly marked.
7940 : *
7941 : * In future, this might need to adjust the child table's state, likely
7942 : * by incrementing an inheritance count for the attnotnull constraint.
7943 : * For now we need only check for the presence of the flag.
7944 : */
7945 : static void
1447 tgl 7946 UIC 0 : ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
1447 tgl 7947 ECB : const char *colName, LOCKMODE lockmode)
7948 : {
7949 : HeapTuple tuple;
7950 :
1447 tgl 7951 UIC 0 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
1447 tgl 7952 ECB :
1447 tgl 7953 UBC 0 : if (!HeapTupleIsValid(tuple))
1447 tgl 7954 UIC 0 : ereport(ERROR,
7955 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7956 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7957 : colName, RelationGetRelationName(rel))));
1447 tgl 7958 ECB :
1447 tgl 7959 LBC 0 : if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
1447 tgl 7960 UIC 0 : ereport(ERROR,
7961 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7962 : errmsg("constraint must be added to child tables too"),
7963 : errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
7964 : colName, RelationGetRelationName(rel)),
7965 : errhint("Do not specify the ONLY keyword.")));
7966 :
7967 0 : ReleaseSysCache(tuple);
1447 tgl 7968 LBC 0 : }
7969 :
1488 rhaas 7970 ECB : /*
7971 : * NotNullImpliedByRelConstraints
7972 : * Does rel's existing constraints imply NOT NULL for the given attribute?
7973 : */
7974 : static bool
1488 rhaas 7975 GIC 553 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
1488 rhaas 7976 ECB : {
1488 rhaas 7977 CBC 553 : NullTest *nnulltest = makeNode(NullTest);
7978 :
1488 rhaas 7979 GIC 1106 : nnulltest->arg = (Expr *) makeVar(1,
7980 553 : attr->attnum,
7981 : attr->atttypid,
1488 rhaas 7982 EUB : attr->atttypmod,
7983 : attr->attcollation,
7984 : 0);
1488 rhaas 7985 GBC 553 : nnulltest->nulltesttype = IS_NOT_NULL;
7986 :
7987 : /*
7988 : * argisrow = false is correct even for a composite column, because
7989 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7990 : * case, just IS DISTINCT FROM NULL.
7991 : */
1488 rhaas 7992 GIC 553 : nnulltest->argisrow = false;
7993 553 : nnulltest->location = -1;
7994 :
7995 553 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
1488 rhaas 7996 ECB : {
1488 rhaas 7997 GIC 25 : ereport(DEBUG1,
781 peter 7998 ECB : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
697 tgl 7999 : RelationGetRelationName(rel), NameStr(attr->attname))));
1488 rhaas 8000 CBC 25 : return true;
8001 : }
8002 :
8003 528 : return false;
1488 rhaas 8004 ECB : }
8005 :
8006 : /*
8007 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8008 : *
2937 alvherre 8009 EUB : * Return the address of the affected column.
8010 : */
8011 : static ObjectAddress
6913 tgl 8012 GIC 266 : ATExecColumnDefault(Relation rel, const char *colName,
8013 : Node *newDefault, LOCKMODE lockmode)
8014 : {
1629 peter_e 8015 CBC 266 : TupleDesc tupdesc = RelationGetDescr(rel);
6913 tgl 8016 ECB : AttrNumber attnum;
8017 : ObjectAddress address;
8018 :
8019 : /*
8020 : * get the number of the attribute
8021 : */
6913 tgl 8022 GIC 266 : attnum = get_attnum(RelationGetRelid(rel), colName);
8023 266 : if (attnum == InvalidAttrNumber)
8024 15 : ereport(ERROR,
6913 tgl 8025 ECB : (errcode(ERRCODE_UNDEFINED_COLUMN),
6797 bruce 8026 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8027 : colName, RelationGetRelationName(rel))));
7360 8028 :
8029 : /* Prevent them from altering a system attribute */
6913 tgl 8030 GIC 251 : if (attnum <= 0)
7203 tgl 8031 UIC 0 : ereport(ERROR,
8032 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6913 tgl 8033 EUB : errmsg("cannot alter system column \"%s\"",
8034 : colName)));
8035 :
1629 peter_e 8036 CBC 251 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
2194 peter_e 8037 GIC 3 : ereport(ERROR,
8038 : (errcode(ERRCODE_SYNTAX_ERROR),
2118 tgl 8039 ECB : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8040 : colName, RelationGetRelationName(rel)),
2194 peter_e 8041 : newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
8042 :
1471 peter 8043 CBC 248 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
1471 peter 8044 GIC 3 : ereport(ERROR,
8045 : (errcode(ERRCODE_SYNTAX_ERROR),
8046 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8047 : colName, RelationGetRelationName(rel)),
8048 : newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
8049 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.")));
8050 :
8051 : /*
8052 : * Remove any old default for the column. We use RESTRICT here for
8053 : * safety, but at present we do not expect anything to depend on the
8054 : * default.
4091 rhaas 8055 ECB : *
8056 : * We treat removing the existing default as an internal operation when it
8057 : * is preparatory to adding a new default, but as a user-initiated
8058 : * operation when the user asked for a drop.
8059 : */
4091 rhaas 8060 GIC 245 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8061 : newDefault != NULL);
8062 :
6913 tgl 8063 CBC 245 : if (newDefault)
7360 bruce 8064 ECB : {
6913 tgl 8065 EUB : /* SET DEFAULT */
8066 : RawColumnDefault *rawEnt;
6913 tgl 8067 ECB :
6913 tgl 8068 CBC 158 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
6913 tgl 8069 GIC 158 : rawEnt->attnum = attnum;
8070 158 : rawEnt->raw_default = newDefault;
1838 andrew 8071 158 : rawEnt->missingMode = false;
1471 peter 8072 CBC 158 : rawEnt->generated = '\0';
8073 :
6913 tgl 8074 ECB : /*
8075 : * This function is intended for CREATE TABLE, so it processes a
8076 : * _list_ of defaults, but we just do one.
8077 : */
3675 rhaas 8078 GIC 158 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8079 : false, true, false, NULL);
8080 : }
8081 :
2937 alvherre 8082 242 : ObjectAddressSubSet(address, RelationRelationId,
2937 alvherre 8083 ECB : RelationGetRelid(rel), attnum);
2937 alvherre 8084 GIC 242 : return address;
8085 : }
8086 :
961 tgl 8087 ECB : /*
8088 : * Add a pre-cooked default expression.
8089 : *
8090 : * Return the address of the affected column.
8091 : */
8092 : static ObjectAddress
961 tgl 8093 CBC 28 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
961 tgl 8094 ECB : Node *newDefault)
8095 : {
8096 : ObjectAddress address;
8097 :
8098 : /* We assume no checking is required */
8099 :
8100 : /*
8101 : * Remove any old default for the column. We use RESTRICT here for
8102 : * safety, but at present we do not expect anything to depend on the
8103 : * default. (In ordinary cases, there could not be a default in place
8104 : * anyway, but it's possible when combining LIKE with inheritance.)
8105 : */
961 tgl 8106 GIC 28 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
961 tgl 8107 ECB : true);
8108 :
961 tgl 8109 GIC 28 : (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
961 tgl 8110 ECB :
961 tgl 8111 GIC 28 : ObjectAddressSubSet(address, RelationRelationId,
961 tgl 8112 ECB : RelationGetRelid(rel), attnum);
961 tgl 8113 GIC 28 : return address;
8114 : }
8115 :
8116 : /*
8117 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8118 : *
8119 : * Return the address of the affected column.
8120 : */
8121 : static ObjectAddress
2194 peter_e 8122 48 : ATExecAddIdentity(Relation rel, const char *colName,
8123 : Node *def, LOCKMODE lockmode)
8124 : {
8125 : Relation attrelation;
8126 : HeapTuple tuple;
2194 peter_e 8127 ECB : Form_pg_attribute attTup;
8128 : AttrNumber attnum;
8129 : ObjectAddress address;
2194 peter_e 8130 GIC 48 : ColumnDef *cdef = castNode(ColumnDef, def);
8131 :
1539 andres 8132 48 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8133 :
2194 peter_e 8134 48 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8135 48 : if (!HeapTupleIsValid(tuple))
2194 peter_e 8136 UIC 0 : ereport(ERROR,
8137 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8138 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8139 : colName, RelationGetRelationName(rel))));
2194 peter_e 8140 CBC 48 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
2194 peter_e 8141 GIC 48 : attnum = attTup->attnum;
8142 :
8143 : /* Can't alter a system attribute */
8144 48 : if (attnum <= 0)
2194 peter_e 8145 UIC 0 : ereport(ERROR,
8146 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2194 peter_e 8147 ECB : errmsg("cannot alter system column \"%s\"",
8148 : colName)));
8149 :
8150 : /*
8151 : * Creating a column as identity implies NOT NULL, so adding the identity
8152 : * to an existing column that is not NOT NULL would create a state that
8153 : * cannot be reproduced without contortions.
8154 : */
2194 peter_e 8155 GIC 48 : if (!attTup->attnotnull)
2194 peter_e 8156 CBC 3 : ereport(ERROR,
2194 peter_e 8157 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8158 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8159 : colName, RelationGetRelationName(rel))));
8160 :
2194 peter_e 8161 CBC 45 : if (attTup->attidentity)
8162 6 : ereport(ERROR,
8163 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8164 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8165 : colName, RelationGetRelationName(rel))));
8166 :
2194 peter_e 8167 GIC 39 : if (attTup->atthasdef)
2194 peter_e 8168 CBC 3 : ereport(ERROR,
2194 peter_e 8169 EUB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8170 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8171 : colName, RelationGetRelationName(rel))));
8172 :
2194 peter_e 8173 GIC 36 : attTup->attidentity = cdef->identity;
8174 36 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
2194 peter_e 8175 ECB :
2194 peter_e 8176 CBC 36 : InvokeObjectPostAlterHook(RelationRelationId,
8177 : RelationGetRelid(rel),
8178 : attTup->attnum);
2194 peter_e 8179 GIC 36 : ObjectAddressSubSet(address, RelationRelationId,
2194 peter_e 8180 ECB : RelationGetRelid(rel), attnum);
2194 peter_e 8181 GIC 36 : heap_freetuple(tuple);
8182 :
1539 andres 8183 CBC 36 : table_close(attrelation, RowExclusiveLock);
8184 :
2194 peter_e 8185 36 : return address;
2194 peter_e 8186 ECB : }
8187 :
8188 : /*
8189 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8190 : *
8191 : * Return the address of the affected column.
8192 : */
8193 : static ObjectAddress
2194 peter_e 8194 GIC 19 : ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
2194 peter_e 8195 ECB : {
8196 : ListCell *option;
2153 bruce 8197 GIC 19 : DefElem *generatedEl = NULL;
8198 : HeapTuple tuple;
8199 : Form_pg_attribute attTup;
8200 : AttrNumber attnum;
8201 : Relation attrelation;
8202 : ObjectAddress address;
2194 peter_e 8203 ECB :
2194 peter_e 8204 GIC 32 : foreach(option, castNode(List, def))
2194 peter_e 8205 ECB : {
2153 bruce 8206 CBC 13 : DefElem *defel = lfirst_node(DefElem, option);
8207 :
2194 peter_e 8208 13 : if (strcmp(defel->defname, "generated") == 0)
8209 : {
2194 peter_e 8210 GBC 13 : if (generatedEl)
2194 peter_e 8211 UBC 0 : ereport(ERROR,
8212 : (errcode(ERRCODE_SYNTAX_ERROR),
8213 : errmsg("conflicting or redundant options")));
2194 peter_e 8214 CBC 13 : generatedEl = defel;
8215 : }
2194 peter_e 8216 ECB : else
2194 peter_e 8217 LBC 0 : elog(ERROR, "option \"%s\" not recognized",
8218 : defel->defname);
8219 : }
2194 peter_e 8220 ECB :
8221 : /*
8222 : * Even if there is nothing to change here, we run all the checks. There
8223 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8224 : * there.
8225 : */
8226 :
1539 andres 8227 GIC 19 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
2194 peter_e 8228 19 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
2194 peter_e 8229 CBC 19 : if (!HeapTupleIsValid(tuple))
2194 peter_e 8230 LBC 0 : ereport(ERROR,
8231 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8232 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8233 : colName, RelationGetRelationName(rel))));
8234 :
2194 peter_e 8235 CBC 19 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8236 19 : attnum = attTup->attnum;
8237 :
2194 peter_e 8238 GIC 19 : if (attnum <= 0)
2194 peter_e 8239 LBC 0 : ereport(ERROR,
2194 peter_e 8240 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8241 : errmsg("cannot alter system column \"%s\"",
8242 : colName)));
8243 :
2194 peter_e 8244 GIC 19 : if (!attTup->attidentity)
2194 peter_e 8245 CBC 3 : ereport(ERROR,
8246 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8247 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8248 : colName, RelationGetRelationName(rel))));
8249 :
2194 peter_e 8250 GIC 16 : if (generatedEl)
8251 : {
8252 13 : attTup->attidentity = defGetInt32(generatedEl);
8253 13 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8254 :
2194 peter_e 8255 CBC 13 : InvokeObjectPostAlterHook(RelationRelationId,
8256 : RelationGetRelid(rel),
2194 peter_e 8257 ECB : attTup->attnum);
2194 peter_e 8258 CBC 13 : ObjectAddressSubSet(address, RelationRelationId,
8259 : RelationGetRelid(rel), attnum);
2194 peter_e 8260 ECB : }
8261 : else
2194 peter_e 8262 GIC 3 : address = InvalidObjectAddress;
2194 peter_e 8263 ECB :
2194 peter_e 8264 CBC 16 : heap_freetuple(tuple);
1539 andres 8265 16 : table_close(attrelation, RowExclusiveLock);
2194 peter_e 8266 ECB :
2194 peter_e 8267 CBC 16 : return address;
2194 peter_e 8268 ECB : }
8269 :
8270 : /*
8271 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8272 : *
8273 : * Return the address of the affected column.
8274 : */
8275 : static ObjectAddress
2194 peter_e 8276 CBC 19 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
2194 peter_e 8277 ECB : {
8278 : HeapTuple tuple;
8279 : Form_pg_attribute attTup;
8280 : AttrNumber attnum;
8281 : Relation attrelation;
8282 : ObjectAddress address;
8283 : Oid seqid;
8284 : ObjectAddress seqaddress;
8285 :
1539 andres 8286 GIC 19 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
2194 peter_e 8287 19 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
2194 peter_e 8288 CBC 19 : if (!HeapTupleIsValid(tuple))
2194 peter_e 8289 UIC 0 : ereport(ERROR,
8290 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8291 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8292 : colName, RelationGetRelationName(rel))));
2194 peter_e 8293 ECB :
2194 peter_e 8294 GIC 19 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8295 19 : attnum = attTup->attnum;
2194 peter_e 8296 ECB :
2194 peter_e 8297 GIC 19 : if (attnum <= 0)
2194 peter_e 8298 UIC 0 : ereport(ERROR,
8299 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2194 peter_e 8300 ECB : errmsg("cannot alter system column \"%s\"",
8301 : colName)));
8302 :
2194 peter_e 8303 GIC 19 : if (!attTup->attidentity)
8304 : {
8305 6 : if (!missing_ok)
2194 peter_e 8306 CBC 3 : ereport(ERROR,
8307 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8308 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8309 : colName, RelationGetRelationName(rel))));
2194 peter_e 8310 ECB : else
8311 : {
2194 peter_e 8312 GIC 3 : ereport(NOTICE,
8313 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8314 : colName, RelationGetRelationName(rel))));
8315 3 : heap_freetuple(tuple);
1539 andres 8316 3 : table_close(attrelation, RowExclusiveLock);
2194 peter_e 8317 3 : return InvalidObjectAddress;
8318 : }
8319 : }
2194 peter_e 8320 ECB :
2194 peter_e 8321 GIC 13 : attTup->attidentity = '\0';
8322 13 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8323 :
8324 13 : InvokeObjectPostAlterHook(RelationRelationId,
2194 peter_e 8325 ECB : RelationGetRelid(rel),
8326 : attTup->attnum);
2194 peter_e 8327 CBC 13 : ObjectAddressSubSet(address, RelationRelationId,
8328 : RelationGetRelid(rel), attnum);
2194 peter_e 8329 GIC 13 : heap_freetuple(tuple);
8330 :
1539 andres 8331 13 : table_close(attrelation, RowExclusiveLock);
2194 peter_e 8332 ECB :
8333 : /* drop the internal sequence */
1357 peter 8334 GIC 13 : seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
2194 peter_e 8335 13 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8336 : RelationRelationId, DEPENDENCY_INTERNAL);
8337 13 : CommandCounterIncrement();
8338 13 : seqaddress.classId = RelationRelationId;
8339 13 : seqaddress.objectId = seqid;
8340 13 : seqaddress.objectSubId = 0;
8341 13 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8342 :
8343 13 : return address;
8344 : }
8345 :
8346 : /*
8347 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8348 : */
8349 : static void
887 peter 8350 22 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
1181 peter 8351 EUB : {
8352 : /*
8353 : * Reject ONLY if there are child tables. We could implement this, but it
8354 : * is a bit complicated. GENERATED clauses must be attached to the column
8355 : * definition and cannot be added later like DEFAULT, so if a child table
887 8356 : * has a generation expression that the parent does not have, the child
8357 : * column will necessarily be an attlocal column. So to implement ONLY
8358 : * here, we'd need extra code to update attislocal of the direct child
8359 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8360 : * resulting state can be properly dumped and restored.
8361 : */
887 peter 8362 GIC 28 : if (!recurse &&
711 alvherre 8363 6 : find_inheritance_children(RelationGetRelid(rel), lockmode))
887 peter 8364 GBC 3 : ereport(ERROR,
887 peter 8365 EUB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8366 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8367 :
8368 : /*
8369 : * Cannot drop generation expression from inherited columns.
8370 : */
1181 peter 8371 GIC 19 : if (!recursing)
1181 peter 8372 EUB : {
8373 : HeapTuple tuple;
8374 : Form_pg_attribute attTup;
8375 :
1181 peter 8376 GIC 16 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8377 16 : if (!HeapTupleIsValid(tuple))
1181 peter 8378 UIC 0 : ereport(ERROR,
8379 : (errcode(ERRCODE_UNDEFINED_COLUMN),
1181 peter 8380 ECB : errmsg("column \"%s\" of relation \"%s\" does not exist",
8381 : cmd->name, RelationGetRelationName(rel))));
8382 :
1181 peter 8383 GIC 16 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
1181 peter 8384 ECB :
1181 peter 8385 CBC 16 : if (attTup->attinhcount > 0)
1181 peter 8386 GIC 3 : ereport(ERROR,
8387 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8388 : errmsg("cannot drop generation expression from inherited column")));
8389 : }
1181 peter 8390 CBC 16 : }
8391 :
8392 : /*
8393 : * Return the address of the affected column.
8394 : */
8395 : static ObjectAddress
1181 peter 8396 GIC 16 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
1181 peter 8397 ECB : {
8398 : HeapTuple tuple;
8399 : Form_pg_attribute attTup;
8400 : AttrNumber attnum;
8401 : Relation attrelation;
384 tgl 8402 : Oid attrdefoid;
8403 : ObjectAddress address;
8404 :
1181 peter 8405 CBC 16 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
1181 peter 8406 GIC 16 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8407 16 : if (!HeapTupleIsValid(tuple))
1181 peter 8408 LBC 0 : ereport(ERROR,
8409 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8410 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8411 : colName, RelationGetRelationName(rel))));
8412 :
1181 peter 8413 GIC 16 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8414 16 : attnum = attTup->attnum;
8415 :
8416 16 : if (attnum <= 0)
1181 peter 8417 LBC 0 : ereport(ERROR,
8418 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8419 : errmsg("cannot alter system column \"%s\"",
1181 peter 8420 ECB : colName)));
8421 :
1181 peter 8422 GIC 16 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8423 : {
8424 6 : if (!missing_ok)
8425 3 : ereport(ERROR,
8426 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1181 peter 8427 ECB : errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8428 : colName, RelationGetRelationName(rel))));
8429 : else
8430 : {
1181 peter 8431 GIC 3 : ereport(NOTICE,
8432 : (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8433 : colName, RelationGetRelationName(rel))));
8434 3 : heap_freetuple(tuple);
1181 peter 8435 CBC 3 : table_close(attrelation, RowExclusiveLock);
1181 peter 8436 GBC 3 : return InvalidObjectAddress;
8437 : }
8438 : }
8439 :
8440 : /*
384 tgl 8441 ECB : * Mark the column as no longer generated. (The atthasdef flag needs to
8442 : * get cleared too, but RemoveAttrDefault will handle that.)
8443 : */
1181 peter 8444 GIC 10 : attTup->attgenerated = '\0';
8445 10 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8446 :
8447 10 : InvokeObjectPostAlterHook(RelationRelationId,
1181 peter 8448 ECB : RelationGetRelid(rel),
384 tgl 8449 : attnum);
1181 peter 8450 GIC 10 : heap_freetuple(tuple);
8451 :
8452 10 : table_close(attrelation, RowExclusiveLock);
8453 :
8454 : /*
8455 : * Drop the dependency records of the GENERATED expression, in particular
8456 : * its INTERNAL dependency on the column, which would otherwise cause
8457 : * dependency.c to refuse to perform the deletion.
8458 : */
384 tgl 8459 10 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8460 10 : if (!OidIsValid(attrdefoid))
384 tgl 8461 UIC 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8462 : RelationGetRelid(rel), attnum);
384 tgl 8463 GIC 10 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8464 :
384 tgl 8465 ECB : /* Make above changes visible */
384 tgl 8466 GIC 10 : CommandCounterIncrement();
8467 :
384 tgl 8468 ECB : /*
8469 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8470 : * safety, but at present we do not expect anything to depend on the
8471 : * default.
8472 : */
384 tgl 8473 CBC 10 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
384 tgl 8474 ECB : false, false);
1181 peter 8475 :
384 tgl 8476 CBC 10 : ObjectAddressSubSet(address, RelationRelationId,
384 tgl 8477 ECB : RelationGetRelid(rel), attnum);
1181 peter 8478 GIC 10 : return address;
8479 : }
8480 :
8481 : /*
8482 : * ALTER TABLE ALTER COLUMN SET STATISTICS
1215 peter 8483 ECB : *
8484 : * Return value is the address of the modified column
8485 : */
8486 : static ObjectAddress
1215 peter 8487 CBC 82 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8488 : {
1215 peter 8489 ECB : int newtarget;
8490 : Relation attrelation;
8491 : HeapTuple tuple;
8492 : Form_pg_attribute attrtuple;
8493 : AttrNumber attnum;
8494 : ObjectAddress address;
8495 :
8496 : /*
8497 : * We allow referencing columns by numbers only for indexes, since table
1991 tgl 8498 : * column numbers could contain gaps if columns are later dropped.
8499 : */
1906 alvherre 8500 GIC 82 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8501 50 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8502 : !colName)
2041 simon 8503 UIC 0 : ereport(ERROR,
8504 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8505 : errmsg("cannot refer to non-index column by number")));
8506 :
6913 tgl 8507 GIC 82 : Assert(IsA(newValue, Integer));
8508 82 : newtarget = intVal(newValue);
8509 :
8510 : /*
6913 tgl 8511 ECB : * Limit target to a sane range
8512 : */
6913 tgl 8513 GIC 82 : if (newtarget < -1)
6913 tgl 8514 ECB : {
7203 tgl 8515 UIC 0 : ereport(ERROR,
6913 tgl 8516 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8517 : errmsg("statistics target %d is too low",
8518 : newtarget)));
8519 : }
5230 tgl 8520 GIC 82 : else if (newtarget > 10000)
8521 : {
5230 tgl 8522 UIC 0 : newtarget = 10000;
6913 8523 0 : ereport(WARNING,
8524 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8525 : errmsg("lowering statistics target to %d",
8526 : newtarget)));
6913 tgl 8527 ECB : }
8528 :
1539 andres 8529 GIC 82 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8530 :
2041 simon 8531 82 : if (colName)
8532 : {
8533 50 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8534 :
2041 simon 8535 CBC 50 : if (!HeapTupleIsValid(tuple))
2041 simon 8536 GIC 6 : ereport(ERROR,
2041 simon 8537 ECB : (errcode(ERRCODE_UNDEFINED_COLUMN),
8538 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8539 : colName, RelationGetRelationName(rel))));
8540 : }
2041 simon 8541 EUB : else
8542 : {
2041 simon 8543 GIC 32 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
8544 :
2041 simon 8545 CBC 32 : if (!HeapTupleIsValid(tuple))
8546 6 : ereport(ERROR,
8547 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8548 : errmsg("column number %d of relation \"%s\" does not exist",
2041 simon 8549 ECB : colNum, RelationGetRelationName(rel))));
2041 simon 8550 EUB : }
8551 :
6913 tgl 8552 GIC 70 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8553 :
2937 alvherre 8554 70 : attnum = attrtuple->attnum;
8555 70 : if (attnum <= 0)
7203 tgl 8556 UIC 0 : ereport(ERROR,
8557 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8558 : errmsg("cannot alter system column \"%s\"",
8559 : colName)));
7527 tgl 8560 ECB :
1728 alvherre 8561 CBC 70 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
1728 alvherre 8562 GIC 44 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8563 : {
8564 26 : if (attnum > rel->rd_index->indnkeyatts)
8565 3 : ereport(ERROR,
1728 alvherre 8566 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8567 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8568 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
1728 alvherre 8569 GIC 23 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8570 9 : ereport(ERROR,
8571 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1728 alvherre 8572 ECB : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8573 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8574 : errhint("Alter statistics on table column instead.")));
8575 : }
8576 :
6913 tgl 8577 GIC 58 : attrtuple->attstattarget = newtarget;
6956 tgl 8578 ECB :
2259 alvherre 8579 CBC 58 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8580 :
3675 rhaas 8581 58 : InvokeObjectPostAlterHook(RelationRelationId,
8582 : RelationGetRelid(rel),
8583 : attrtuple->attnum);
2937 alvherre 8584 58 : ObjectAddressSubSet(address, RelationRelationId,
8585 : RelationGetRelid(rel), attnum);
6913 tgl 8586 58 : heap_freetuple(tuple);
8587 :
1539 andres 8588 58 : table_close(attrelation, RowExclusiveLock);
8589 :
2937 alvherre 8590 58 : return address;
8591 : }
8592 :
8593 : /*
8594 : * Return value is the address of the modified column
8595 : */
8596 : static ObjectAddress
4825 rhaas 8597 GIC 16 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8598 : bool isReset, LOCKMODE lockmode)
4998 tgl 8599 ECB : {
8600 : Relation attrelation;
8601 : HeapTuple tuple,
4825 rhaas 8602 : newtuple;
8603 : Form_pg_attribute attrtuple;
8604 : AttrNumber attnum;
8605 : Datum datum,
8606 : newOptions;
8607 : bool isnull;
8608 : ObjectAddress address;
8609 : Datum repl_val[Natts_pg_attribute];
8610 : bool repl_null[Natts_pg_attribute];
8611 : bool repl_repl[Natts_pg_attribute];
8612 :
1539 andres 8613 CBC 16 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8614 :
4825 rhaas 8615 16 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
4998 tgl 8616 EUB :
4998 tgl 8617 GIC 16 : if (!HeapTupleIsValid(tuple))
4998 tgl 8618 UIC 0 : ereport(ERROR,
4998 tgl 8619 ECB : (errcode(ERRCODE_UNDEFINED_COLUMN),
8620 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8621 : colName, RelationGetRelationName(rel))));
4998 tgl 8622 GBC 16 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8623 :
2937 alvherre 8624 GIC 16 : attnum = attrtuple->attnum;
8625 16 : if (attnum <= 0)
4998 tgl 8626 UIC 0 : ereport(ERROR,
8627 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8628 : errmsg("cannot alter system column \"%s\"",
8629 : colName)));
8630 :
8631 : /* Generate new proposed attoptions (text array) */
4825 rhaas 8632 CBC 16 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
4790 bruce 8633 ECB : &isnull);
4825 rhaas 8634 CBC 16 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
2264 andres 8635 GBC 16 : castNode(List, options), NULL, NULL,
8636 : false, isReset);
8637 : /* Validate new options */
4825 rhaas 8638 GIC 16 : (void) attribute_reloptions(newOptions, true);
8639 :
4825 rhaas 8640 ECB : /* Build new tuple. */
4825 rhaas 8641 CBC 16 : memset(repl_null, false, sizeof(repl_null));
4825 rhaas 8642 GIC 16 : memset(repl_repl, false, sizeof(repl_repl));
4825 rhaas 8643 CBC 16 : if (newOptions != (Datum) 0)
4825 rhaas 8644 GBC 16 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8645 : else
4825 rhaas 8646 UIC 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
4825 rhaas 8647 GIC 16 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8648 16 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
4825 rhaas 8649 ECB : repl_val, repl_null, repl_repl);
8650 :
8651 : /* Update system catalog. */
2259 alvherre 8652 GIC 16 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8653 :
3675 rhaas 8654 16 : InvokeObjectPostAlterHook(RelationRelationId,
3675 rhaas 8655 ECB : RelationGetRelid(rel),
8656 : attrtuple->attnum);
2937 alvherre 8657 CBC 16 : ObjectAddressSubSet(address, RelationRelationId,
2937 alvherre 8658 ECB : RelationGetRelid(rel), attnum);
8659 :
4825 rhaas 8660 CBC 16 : heap_freetuple(newtuple);
8661 :
3675 rhaas 8662 GIC 16 : ReleaseSysCache(tuple);
3675 rhaas 8663 ECB :
1539 andres 8664 GIC 16 : table_close(attrelation, RowExclusiveLock);
8665 :
2937 alvherre 8666 16 : return address;
6913 tgl 8667 ECB : }
8668 :
751 rhaas 8669 : /*
8670 : * Helper function for ATExecSetStorage and ATExecSetCompression
8671 : *
682 tgl 8672 : * Set the attstorage and/or attcompression fields for index columns
8673 : * associated with the specified table column.
8674 : */
8675 : static void
751 rhaas 8676 GIC 139 : SetIndexStorageProperties(Relation rel, Relation attrelation,
8677 : AttrNumber attnum,
8678 : bool setstorage, char newstorage,
8679 : bool setcompression, char newcompression,
8680 : LOCKMODE lockmode)
751 rhaas 8681 ECB : {
8682 : ListCell *lc;
8683 :
751 rhaas 8684 GIC 174 : foreach(lc, RelationGetIndexList(rel))
8685 : {
8686 35 : Oid indexoid = lfirst_oid(lc);
8687 : Relation indrel;
8688 35 : AttrNumber indattnum = 0;
8689 : HeapTuple tuple;
8690 :
751 rhaas 8691 CBC 35 : indrel = index_open(indexoid, lockmode);
751 rhaas 8692 ECB :
751 rhaas 8693 CBC 59 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
751 rhaas 8694 EUB : {
751 rhaas 8695 GIC 38 : if (indrel->rd_index->indkey.values[i] == attnum)
8696 : {
8697 14 : indattnum = i + 1;
8698 14 : break;
751 rhaas 8699 ECB : }
8700 : }
8701 :
751 rhaas 8702 CBC 35 : if (indattnum == 0)
751 rhaas 8703 EUB : {
751 rhaas 8704 GIC 21 : index_close(indrel, lockmode);
8705 21 : continue;
8706 : }
8707 :
751 rhaas 8708 CBC 14 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8709 :
8710 14 : if (HeapTupleIsValid(tuple))
751 rhaas 8711 ECB : {
682 tgl 8712 GIC 14 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8713 :
8714 14 : if (setstorage)
751 rhaas 8715 11 : attrtuple->attstorage = newstorage;
8716 :
682 tgl 8717 CBC 14 : if (setcompression)
682 tgl 8718 GIC 3 : attrtuple->attcompression = newcompression;
8719 :
751 rhaas 8720 CBC 14 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
751 rhaas 8721 ECB :
751 rhaas 8722 CBC 14 : InvokeObjectPostAlterHook(RelationRelationId,
8723 : RelationGetRelid(rel),
8724 : attrtuple->attnum);
8725 :
8726 14 : heap_freetuple(tuple);
751 rhaas 8727 ECB : }
8728 :
751 rhaas 8729 CBC 14 : index_close(indrel, lockmode);
8730 : }
751 rhaas 8731 GIC 139 : }
748 rhaas 8732 ECB :
8733 : /*
6913 tgl 8734 : * ALTER TABLE ALTER COLUMN SET STORAGE
8735 : *
2937 alvherre 8736 : * Return value is the address of the modified column
8737 : */
8738 : static ObjectAddress
4638 simon 8739 CBC 115 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6913 tgl 8740 ECB : {
8741 : Relation attrelation;
8742 : HeapTuple tuple;
8743 : Form_pg_attribute attrtuple;
2937 alvherre 8744 : AttrNumber attnum;
8745 : ObjectAddress address;
7527 tgl 8746 :
1539 andres 8747 CBC 115 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8748 :
6913 tgl 8749 GIC 115 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8750 :
8751 115 : if (!HeapTupleIsValid(tuple))
8752 6 : ereport(ERROR,
8753 : (errcode(ERRCODE_UNDEFINED_COLUMN),
6797 bruce 8754 ECB : errmsg("column \"%s\" of relation \"%s\" does not exist",
8755 : colName, RelationGetRelationName(rel))));
6913 tgl 8756 GIC 109 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8757 :
2937 alvherre 8758 109 : attnum = attrtuple->attnum;
2937 alvherre 8759 CBC 109 : if (attnum <= 0)
6913 tgl 8760 LBC 0 : ereport(ERROR,
6913 tgl 8761 EUB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8762 : errmsg("cannot alter system column \"%s\"",
8763 : colName)));
8764 :
270 peter 8765 GNC 109 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8766 :
2259 alvherre 8767 GIC 109 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8768 :
3675 rhaas 8769 CBC 109 : InvokeObjectPostAlterHook(RelationRelationId,
8770 : RelationGetRelid(rel),
8771 : attrtuple->attnum);
8772 :
8773 : /*
8774 : * Apply the change to indexes as well (only for simple index columns,
8775 : * matching behavior of index.c ConstructTupleDescriptor()).
1095 peter 8776 ECB : */
751 rhaas 8777 CBC 109 : SetIndexStorageProperties(rel, attrelation, attnum,
270 peter 8778 GNC 109 : true, attrtuple->attstorage,
682 tgl 8779 EUB : false, 0,
8780 : lockmode);
8781 :
270 peter 8782 GNC 109 : heap_freetuple(tuple);
8783 :
1539 andres 8784 GIC 109 : table_close(attrelation, RowExclusiveLock);
8785 :
2937 alvherre 8786 CBC 109 : ObjectAddressSubSet(address, RelationRelationId,
2937 alvherre 8787 ECB : RelationGetRelid(rel), attnum);
2937 alvherre 8788 GIC 109 : return address;
6913 tgl 8789 ECB : }
6913 tgl 8790 EUB :
8791 :
8792 : /*
8793 : * ALTER TABLE DROP COLUMN
8794 : *
6913 tgl 8795 ECB : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8796 : * because we have to decide at runtime whether to recurse or not depending
3260 bruce 8797 : * on whether attinhcount goes to zero or not. (We can't check this in a
6913 tgl 8798 : * static pre-pass because it won't handle multiple inheritance situations
8799 : * correctly.)
8800 : */
8801 : static void
4520 peter_e 8802 GIC 778 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8803 : AlterTableCmd *cmd, LOCKMODE lockmode,
1180 tgl 8804 ECB : AlterTableUtilityContext *context)
8805 : {
4520 peter_e 8806 GIC 778 : if (rel->rd_rel->reloftype && !recursing)
4643 peter_e 8807 CBC 3 : ereport(ERROR,
4643 peter_e 8808 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8809 : errmsg("cannot drop column from typed table")));
8810 :
4578 peter_e 8811 GIC 775 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
1180 tgl 8812 41 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8813 :
4643 peter_e 8814 772 : if (recurse)
118 alvherre 8815 GNC 638 : cmd->recurse = true;
4643 peter_e 8816 GIC 772 : }
4643 peter_e 8817 ECB :
2937 alvherre 8818 : /*
8819 : * Drops column 'colName' from relation 'rel' and returns the address of the
1274 michael 8820 : * dropped column. The column is also dropped (or marked as no longer
8821 : * inherited from relation) from the relation's inheritance children, if any.
8822 : *
8823 : * In the recursive invocations for inheritance child relations, instead of
8824 : * dropping the column directly (if to be dropped at all), its object address
8825 : * is added to 'addrs', which must be non-NULL in such invocations. All
8826 : * columns are dropped at the same time after all the children have been
8827 : * checked recursively.
8828 : */
8829 : static ObjectAddress
5170 tgl 8830 GIC 1050 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
8831 : DropBehavior behavior,
5011 andrew 8832 ECB : bool recurse, bool recursing,
1274 michael 8833 : bool missing_ok, LOCKMODE lockmode,
1274 michael 8834 EUB : ObjectAddresses *addrs)
8835 : {
6913 tgl 8836 ECB : HeapTuple tuple;
8837 : Form_pg_attribute targetatt;
8838 : AttrNumber attnum;
8839 : List *children;
8840 : ObjectAddress object;
8841 : bool is_expr;
8842 :
8843 : /* At top level, permission check was done in ATPrepCmd, else do it */
6913 tgl 8844 GIC 1050 : if (recursing)
640 peter 8845 278 : ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6913 tgl 8846 ECB :
8847 : /* Initialize addrs on the first invocation */
1274 michael 8848 GIC 1050 : Assert(!recursing || addrs != NULL);
1274 michael 8849 CBC 1050 : if (!recursing)
1274 michael 8850 GIC 772 : addrs = new_object_addresses();
1274 michael 8851 ECB :
8852 : /*
8853 : * get the number of the attribute
8854 : */
6913 tgl 8855 GIC 1050 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
4790 bruce 8856 1050 : if (!HeapTupleIsValid(tuple))
8857 : {
8858 27 : if (!missing_ok)
8859 : {
5011 andrew 8860 CBC 18 : ereport(ERROR,
8861 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8862 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8863 : colName, RelationGetRelationName(rel))));
8864 : }
8865 : else
8866 : {
5011 andrew 8867 GIC 9 : ereport(NOTICE,
8868 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8869 : colName, RelationGetRelationName(rel))));
2937 alvherre 8870 9 : return InvalidObjectAddress;
8871 : }
8872 : }
6913 tgl 8873 CBC 1023 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
6913 tgl 8874 ECB :
6913 tgl 8875 GIC 1023 : attnum = targetatt->attnum;
6913 tgl 8876 EUB :
8877 : /* Can't drop a system attribute */
1601 andres 8878 GIC 1023 : if (attnum <= 0)
6913 tgl 8879 3 : ereport(ERROR,
6913 tgl 8880 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8881 : errmsg("cannot drop system column \"%s\"",
8882 : colName)));
8883 :
8884 : /*
8885 : * Don't drop inherited columns, unless recursing (presumably from a drop
1357 8886 : * of the parent column)
8887 : */
6913 tgl 8888 GBC 1020 : if (targetatt->attinhcount > 0 && !recursing)
6913 tgl 8889 GIC 24 : ereport(ERROR,
8890 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8891 : errmsg("cannot drop inherited column \"%s\"",
8892 : colName)));
6913 tgl 8893 ECB :
8894 : /*
1357 tgl 8895 EUB : * Don't drop columns used in the partition key, either. (If we let this
8896 : * go through, the key column's dependencies would cause a cascaded drop
8897 : * of the whole table, which is surely not what the user expected.)
8898 : */
1921 rhaas 8899 GIC 996 : if (has_partition_attrs(rel,
8900 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
8901 : &is_expr))
1357 tgl 8902 CBC 15 : ereport(ERROR,
8903 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1357 tgl 8904 ECB : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
8905 : colName, RelationGetRelationName(rel))));
2314 rhaas 8906 :
6913 tgl 8907 GIC 981 : ReleaseSysCache(tuple);
7527 tgl 8908 ECB :
7555 8909 : /*
8910 : * Propagate to children as appropriate. Unlike most other ALTER
8911 : * routines, we have to do this one level of recursion at a time; we can't
8912 : * use find_all_inheritors to do it in one pass.
8913 : */
8914 : children =
711 alvherre 8915 GIC 981 : find_inheritance_children(RelationGetRelid(rel), lockmode);
6913 tgl 8916 ECB :
6913 tgl 8917 GIC 981 : if (children)
7555 tgl 8918 ECB : {
7504 8919 : Relation attr_rel;
8920 : ListCell *child;
8921 :
8922 : /*
8923 : * In case of a partitioned table, the column must be dropped from the
8924 : * partitions as well.
2314 rhaas 8925 : */
2314 rhaas 8926 GIC 151 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
2314 rhaas 8927 CBC 3 : ereport(ERROR,
2314 rhaas 8928 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2175 sfrost 8929 EUB : errmsg("cannot drop column from only the partitioned table when partitions exist"),
8930 : errhint("Do not specify the ONLY keyword.")));
8931 :
1539 andres 8932 GIC 148 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7555 tgl 8933 441 : foreach(child, children)
7555 tgl 8934 ECB : {
6892 neilc 8935 CBC 296 : Oid childrelid = lfirst_oid(child);
8936 : Relation childrel;
7504 tgl 8937 ECB : Form_pg_attribute childatt;
7555 8938 :
8939 : /* find_inheritance_children already got lock */
1539 andres 8940 GIC 296 : childrel = table_open(childrelid, NoLock);
5548 tgl 8941 296 : CheckTableNotInUse(childrel, "ALTER TABLE");
7504 tgl 8942 ECB :
7504 tgl 8943 CBC 296 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
2118 tgl 8944 GIC 296 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
7203 tgl 8945 UIC 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
8946 : colName, childrelid);
7504 tgl 8947 GIC 296 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
8948 :
2118 8949 296 : if (childatt->attinhcount <= 0) /* shouldn't happen */
7203 tgl 8950 LBC 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
8951 : childrelid, colName);
7504 tgl 8952 ECB :
6913 tgl 8953 GIC 296 : if (recurse)
7504 tgl 8954 ECB : {
8955 : /*
8956 : * If the child column has other definition sources, just
6385 bruce 8957 : * decrement its inheritance count; if not, recurse to delete
8958 : * it.
6913 tgl 8959 : */
6913 tgl 8960 GIC 284 : if (childatt->attinhcount == 1 && !childatt->attislocal)
6913 tgl 8961 ECB : {
8962 : /* Time to delete this child column, too */
5170 tgl 8963 CBC 278 : ATExecDropColumn(wqueue, childrel, colName,
8964 : behavior, true, true,
8965 : false, lockmode, addrs);
8966 : }
8967 : else
8968 : {
8969 : /* Child column must survive my deletion */
6913 8970 6 : childatt->attinhcount--;
8971 :
2259 alvherre 8972 GIC 6 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8973 :
8974 : /* Make update visible */
6913 tgl 8975 6 : CommandCounterIncrement();
8976 : }
8977 : }
8978 : else
8979 : {
8980 : /*
8981 : * If we were told to drop ONLY in this table (no recursion),
8982 : * we need to mark the inheritors' attributes as locally
8983 : * defined rather than inherited.
8984 : */
7504 8985 12 : childatt->attinhcount--;
6913 tgl 8986 CBC 12 : childatt->attislocal = true;
8987 :
2259 alvherre 8988 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8989 :
6913 tgl 8990 ECB : /* Make update visible */
6913 tgl 8991 GBC 12 : CommandCounterIncrement();
8992 : }
8993 :
7504 tgl 8994 GIC 293 : heap_freetuple(tuple);
7504 tgl 8995 ECB :
1539 andres 8996 GIC 293 : table_close(childrel, NoLock);
7555 tgl 8997 ECB : }
1539 andres 8998 CBC 145 : table_close(attr_rel, RowExclusiveLock);
7555 tgl 8999 EUB : }
9000 :
9001 : /* Add object to delete */
6569 tgl 9002 GIC 975 : object.classId = RelationRelationId;
6913 9003 975 : object.objectId = RelationGetRelid(rel);
7555 9004 975 : object.objectSubId = attnum;
1274 michael 9005 CBC 975 : add_exact_object_address(&object, addrs);
9006 :
9007 975 : if (!recursing)
1274 michael 9008 ECB : {
9009 : /* Recursion has ended, drop everything that was collected */
1274 michael 9010 GIC 700 : performMultipleDeletions(addrs, behavior, 0);
1274 michael 9011 CBC 679 : free_object_addresses(addrs);
9012 : }
9013 :
2937 alvherre 9014 954 : return object;
7664 tgl 9015 ECB : }
9016 :
6913 9017 : /*
9018 : * ALTER TABLE ADD INDEX
6913 tgl 9019 EUB : *
5769 tgl 9020 ECB : * There is no such command in the grammar, but parse_utilcmd.c converts
9021 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9022 : * us schedule creation of the index at the appropriate time during ALTER.
9023 : *
9024 : * Return value is the address of the new index.
6913 9025 : */
9026 : static ObjectAddress
6913 tgl 9027 CBC 634 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9028 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9029 : {
6797 bruce 9030 ECB : bool check_rights;
9031 : bool skip_build;
9032 : bool quiet;
2959 alvherre 9033 : ObjectAddress address;
9034 :
6913 tgl 9035 CBC 634 : Assert(IsA(stmt, IndexStmt));
3919 tgl 9036 GIC 634 : Assert(!stmt->concurrent);
6913 tgl 9037 ECB :
9038 : /* The IndexStmt has already been through transformIndexStmt */
2968 tgl 9039 CBC 634 : Assert(stmt->transformed);
9040 :
9041 : /* suppress schema rights check when rebuilding existing index */
6913 tgl 9042 GIC 634 : check_rights = !is_rebuild;
9043 : /* skip index build if phase 3 will do it or we're reusing an old one */
277 rhaas 9044 GNC 634 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9045 : /* suppress notices when rebuilding existing index */
6913 tgl 9046 GIC 634 : quiet = is_rebuild;
9047 :
2959 alvherre 9048 634 : address = DefineIndex(RelationGetRelid(rel),
2959 alvherre 9049 ECB : stmt,
9050 : InvalidOid, /* no predefined OID */
9051 : InvalidOid, /* no parent index */
9052 : InvalidOid, /* no parent constraint */
9053 : -1, /* total_parts unknown */
9054 : true, /* is_alter_table */
9055 : check_rights,
9056 : false, /* check_not_in_use - we did it already */
9057 : skip_build,
9058 : quiet);
9059 :
4283 rhaas 9060 : /*
9061 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9062 : * new index instead of building from scratch. Restore associated fields.
9063 : * This may store InvalidSubTransactionId in both fields, in which case
9064 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
1100 noah 9065 : * this after the CCI that made catalog rows visible to any rebuild. The
9066 : * DROP of the old edition of this index will have scheduled the storage
9067 : * for deletion at commit, so cancel that pending deletion.
9068 : */
277 rhaas 9069 GNC 582 : if (RelFileNumberIsValid(stmt->oldNumber))
9070 : {
2959 alvherre 9071 CBC 36 : Relation irel = index_open(address.objectId, NoLock);
3955 bruce 9072 ECB :
1100 noah 9073 GIC 36 : irel->rd_createSubid = stmt->oldCreateSubid;
277 rhaas 9074 GNC 36 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9075 36 : RelationPreserveStorage(irel->rd_locator, true);
4283 rhaas 9076 CBC 36 : index_close(irel, NoLock);
9077 : }
2937 alvherre 9078 ECB :
2937 alvherre 9079 CBC 582 : return address;
9080 : }
9081 :
744 tomas.vondra 9082 ECB : /*
9083 : * ALTER TABLE ADD STATISTICS
9084 : *
9085 : * This is no such command in the grammar, but we use this internally to add
9086 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9087 : * column type change.
9088 : */
9089 : static ObjectAddress
744 tomas.vondra 9090 GIC 7 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
744 tomas.vondra 9091 ECB : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9092 : {
9093 : ObjectAddress address;
9094 :
744 tomas.vondra 9095 GIC 7 : Assert(IsA(stmt, CreateStatsStmt));
744 tomas.vondra 9096 ECB :
9097 : /* The CreateStatsStmt has already been through transformStatsStmt */
744 tomas.vondra 9098 GIC 7 : Assert(stmt->transformed);
9099 :
744 tomas.vondra 9100 CBC 7 : address = CreateStatistics(stmt);
9101 :
744 tomas.vondra 9102 GIC 7 : return address;
744 tomas.vondra 9103 ECB : }
9104 :
4457 tgl 9105 : /*
9106 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9107 : *
9108 : * Returns the address of the new constraint.
9109 : */
9110 : static ObjectAddress
4457 tgl 9111 GIC 33361 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9112 : IndexStmt *stmt, LOCKMODE lockmode)
4457 tgl 9113 ECB : {
4457 tgl 9114 GIC 33361 : Oid index_oid = stmt->indexOid;
9115 : Relation indexRel;
9116 : char *indexName;
9117 : IndexInfo *indexInfo;
9118 : char *constraintName;
9119 : char constraintType;
9120 : ObjectAddress address;
1972 alvherre 9121 ECB : bits16 flags;
9122 :
4457 tgl 9123 CBC 33361 : Assert(IsA(stmt, IndexStmt));
4457 tgl 9124 GIC 33361 : Assert(OidIsValid(index_oid));
4457 tgl 9125 CBC 33361 : Assert(stmt->isconstraint);
4457 tgl 9126 ECB :
9127 : /*
9128 : * Doing this on partitioned tables is not a simple feature to implement,
9129 : * so let's punt for now.
1875 alvherre 9130 : */
1875 alvherre 9131 GIC 33361 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1875 alvherre 9132 CBC 3 : ereport(ERROR,
1875 alvherre 9133 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1875 alvherre 9134 EUB : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9135 :
4457 tgl 9136 GIC 33358 : indexRel = index_open(index_oid, AccessShareLock);
9137 :
9138 33358 : indexName = pstrdup(RelationGetRelationName(indexRel));
4457 tgl 9139 ECB :
4457 tgl 9140 GIC 33358 : indexInfo = BuildIndexInfo(indexRel);
4457 tgl 9141 ECB :
9142 : /* this should have been checked at parse time */
4457 tgl 9143 CBC 33358 : if (!indexInfo->ii_Unique)
4457 tgl 9144 UIC 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9145 :
9146 : /*
9147 : * Determine name to assign to constraint. We require a constraint to
9148 : * have the same name as the underlying index; therefore, use the index's
9149 : * existing name as the default constraint name, and if the user
9150 : * explicitly gives some other name for the constraint, rename the index
4382 bruce 9151 ECB : * to match.
4457 tgl 9152 : */
4457 tgl 9153 GIC 33358 : constraintName = stmt->idxname;
9154 33358 : if (constraintName == NULL)
9155 33351 : constraintName = indexName;
4457 tgl 9156 CBC 7 : else if (strcmp(constraintName, indexName) != 0)
9157 : {
9158 4 : ereport(NOTICE,
9159 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
4457 tgl 9160 ECB : indexName, constraintName)));
1627 peter_e 9161 GIC 4 : RenameRelationInternal(index_oid, constraintName, false, true);
4457 tgl 9162 ECB : }
9163 :
9164 : /* Extra checks needed if making primary key */
4457 tgl 9165 GIC 33358 : if (stmt->primary)
1646 alvherre 9166 18810 : index_check_primary_key(rel, indexInfo, true, stmt);
9167 :
9168 : /* Note we currently don't support EXCLUSION constraints here */
4457 tgl 9169 33355 : if (stmt->primary)
9170 18807 : constraintType = CONSTRAINT_PRIMARY;
9171 : else
9172 14548 : constraintType = CONSTRAINT_UNIQUE;
9173 :
9174 : /* Create the catalog entries for the constraint */
1972 alvherre 9175 33355 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
1972 alvherre 9176 ECB : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
1972 alvherre 9177 GIC 66710 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9178 33355 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9179 33355 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
1972 alvherre 9180 ECB :
2937 alvherre 9181 CBC 33355 : address = index_constraint_create(rel,
9182 : index_oid,
9183 : InvalidOid,
9184 : indexInfo,
2937 alvherre 9185 ECB : constraintName,
9186 : constraintType,
9187 : flags,
9188 : allowSystemTableMods,
2878 bruce 9189 : false); /* is_internal */
4457 tgl 9190 :
4457 tgl 9191 GIC 33355 : index_close(indexRel, NoLock);
9192 :
2937 alvherre 9193 33355 : return address;
9194 : }
9195 :
9196 : /*
9197 : * ALTER TABLE ADD CONSTRAINT
9198 : *
9199 : * Return value is the address of the new constraint; if no constraint was
9200 : * added, InvalidObjectAddress is returned.
9201 : */
9202 : static ObjectAddress
5448 tgl 9203 1626 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
3807 tgl 9204 ECB : Constraint *newConstraint, bool recurse, bool is_readd,
9205 : LOCKMODE lockmode)
9206 : {
2937 alvherre 9207 GIC 1626 : ObjectAddress address = InvalidObjectAddress;
9208 :
5001 tgl 9209 1626 : Assert(IsA(newConstraint, Constraint));
9210 :
9211 : /*
9212 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9213 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9214 : * parse_utilcmd.c).
9215 : */
9216 1626 : switch (newConstraint->contype)
9217 : {
5001 tgl 9218 CBC 593 : case CONSTR_CHECK:
9219 : case CONSTR_NOTNULL:
2937 alvherre 9220 ECB : address =
2937 alvherre 9221 GIC 593 : ATAddCheckConstraint(wqueue, tab, rel,
9222 : newConstraint, recurse, false, is_readd,
2937 alvherre 9223 ECB : lockmode);
5001 tgl 9224 CBC 560 : break;
7504 tgl 9225 ECB :
5001 tgl 9226 GIC 1033 : case CONSTR_FOREIGN:
9227 :
9228 : /*
9229 : * Assign or validate constraint name
5001 tgl 9230 ECB : */
5001 tgl 9231 CBC 1033 : if (newConstraint->conname)
9232 : {
9233 392 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9234 : RelationGetRelid(rel),
9235 392 : newConstraint->conname))
5001 tgl 9236 UIC 0 : ereport(ERROR,
9237 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9238 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9239 : newConstraint->conname,
9240 : RelationGetRelationName(rel))));
9241 : }
5001 tgl 9242 ECB : else
5001 tgl 9243 GIC 641 : newConstraint->conname =
9244 641 : ChooseConstraintName(RelationGetRelationName(rel),
1488 peter 9245 CBC 641 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9246 : "fkey",
5001 tgl 9247 GIC 641 : RelationGetNamespace(rel),
5001 tgl 9248 ECB : NIL);
9249 :
1831 alvherre 9250 CBC 1033 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9251 : newConstraint,
9252 : recurse, false,
2937 alvherre 9253 ECB : lockmode);
5001 tgl 9254 CBC 873 : break;
9255 :
6913 tgl 9256 UIC 0 : default:
5001 9257 0 : elog(ERROR, "unrecognized constraint type: %d",
9258 : (int) newConstraint->contype);
9259 : }
9260 :
2937 alvherre 9261 GIC 1433 : return address;
9262 : }
7653 tgl 9263 ECB :
1488 peter 9264 : /*
9265 : * Generate the column-name portion of the constraint name for a new foreign
9266 : * key given the list of column names that reference the referenced
9267 : * table. This will be passed to ChooseConstraintName along with the parent
9268 : * table name and the "fkey" suffix.
9269 : *
9270 : * We know that less than NAMEDATALEN characters will actually be used, so we
9271 : * can truncate the result once we've generated that many.
9272 : *
9273 : * XXX see also ChooseExtendedStatisticNameAddition and
9274 : * ChooseIndexNameAddition.
9275 : */
9276 : static char *
1488 peter 9277 CBC 973 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9278 : {
9279 : char buf[NAMEDATALEN * 2];
1488 peter 9280 GIC 973 : int buflen = 0;
9281 : ListCell *lc;
1488 peter 9282 ECB :
1488 peter 9283 GIC 973 : buf[0] = '\0';
9284 2145 : foreach(lc, colnames)
9285 : {
9286 1172 : const char *name = strVal(lfirst(lc));
9287 :
9288 1172 : if (buflen > 0)
9289 199 : buf[buflen++] = '_'; /* insert _ between names */
1488 peter 9290 ECB :
9291 : /*
9292 : * At this point we have buflen <= NAMEDATALEN. name should be less
9293 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9294 : */
1488 peter 9295 GIC 1172 : strlcpy(buf + buflen, name, NAMEDATALEN);
9296 1172 : buflen += strlen(buf + buflen);
9297 1172 : if (buflen >= NAMEDATALEN)
1488 peter 9298 UIC 0 : break;
9299 : }
1488 peter 9300 GIC 973 : return pstrdup(buf);
1488 peter 9301 ECB : }
9302 :
9303 : /*
9304 : * Add a check or NOT NULL constraint to a single table and its children.
9305 : * Returns the address of the constraint added to the parent relation,
9306 : * if one gets added, or InvalidObjectAddress otherwise.
5448 tgl 9307 : *
9308 : * Subroutine for ATExecAddConstraint.
9309 : *
9310 : * We must recurse to child tables during execution, rather than using
9311 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9312 : * constraints *must* be given the same name, else they won't be seen as
9313 : * related later. If the user didn't explicitly specify a name, then
9314 : * AddRelationNewConstraints would normally assign different names to the
9315 : * child constraints. To fix that, we must capture the name assigned at
9316 : * the parent table and pass that down.
9317 : */
2937 alvherre 9318 : static ObjectAddress
5448 tgl 9319 CBC 876 : ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
4638 simon 9320 EUB : Constraint *constr, bool recurse, bool recursing,
9321 : bool is_readd, LOCKMODE lockmode)
5448 tgl 9322 ECB : {
9323 : List *newcons;
9324 : ListCell *lcon;
5448 tgl 9325 EUB : List *children;
9326 : ListCell *child;
2937 alvherre 9327 GIC 876 : ObjectAddress address = InvalidObjectAddress;
5448 tgl 9328 ECB :
9329 : /* At top level, permission check was done in ATPrepCmd, else do it */
5448 tgl 9330 GIC 876 : if (recursing)
640 peter 9331 216 : ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9332 :
9333 : /*
9334 : * Call AddRelationNewConstraints to do the work, making sure it works on
5050 bruce 9335 ECB : * a copy of the Constraint so transformExpr can't modify the original. It
9336 : * returns a list of cooked constraints.
9337 : *
5448 tgl 9338 : * If the constraint ends up getting merged with a pre-existing one, it's
9339 : * omitted from the returned list, which is what we want: we do not need
9340 : * to do any validation work. That can only happen at child tables,
9341 : * though, since we disallow merging at the top level.
9342 : */
5448 tgl 9343 GIC 876 : newcons = AddRelationNewConstraints(rel, NIL,
9344 876 : list_make1(copyObject(constr)),
108 michael 9345 GNC 876 : recursing || is_readd, /* allow_merge */
2118 tgl 9346 GIC 876 : !recursing, /* is_local */
1691 peter_e 9347 ECB : is_readd, /* is_internal */
1681 tgl 9348 GIC 876 : NULL); /* queryString not available
9349 : * here */
5448 tgl 9350 ECB :
9351 : /* we don't expect more than one constraint here */
2937 alvherre 9352 GIC 846 : Assert(list_length(newcons) <= 1);
9353 :
9354 : /* Add each to-be-validated constraint to Phase 3's queue */
5448 tgl 9355 1663 : foreach(lcon, newcons)
9356 : {
9357 817 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9358 :
2 alvherre 9359 GNC 817 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
4330 alvherre 9360 ECB : {
9361 : NewConstraint *newcon;
9362 :
4330 alvherre 9363 CBC 392 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
4330 alvherre 9364 GIC 392 : newcon->name = ccon->name;
9365 392 : newcon->contype = ccon->contype;
2217 andres 9366 CBC 392 : newcon->qual = ccon->expr;
9367 :
4330 alvherre 9368 GIC 392 : tab->constraints = lappend(tab->constraints, newcon);
4330 alvherre 9369 ECB : }
9370 :
5448 tgl 9371 : /* Save the actually assigned name if it was defaulted */
5001 tgl 9372 GIC 817 : if (constr->conname == NULL)
5001 tgl 9373 CBC 243 : constr->conname = ccon->name;
9374 :
9375 : /*
9376 : * If adding a NOT NULL constraint, set the pg_attribute flag and
9377 : * tell phase 3 to verify existing rows, if needed.
9378 : */
2 alvherre 9379 GNC 817 : if (constr->contype == CONSTR_NOTNULL)
9380 237 : set_attnotnull(wqueue, rel, ccon->attnum,
9381 237 : !ccon->is_no_inherit, lockmode);
9382 :
2937 alvherre 9383 GIC 817 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9384 : }
5448 tgl 9385 ECB :
9386 : /* At this point we must have a locked-down name to use */
5001 tgl 9387 CBC 846 : Assert(constr->conname != NULL);
5448 tgl 9388 ECB :
9389 : /* Advance command counter in case same table is visited multiple times */
5448 tgl 9390 CBC 846 : CommandCounterIncrement();
9391 :
9392 : /*
4632 rhaas 9393 ECB : * If the constraint got merged with an existing constraint, we're done.
4382 bruce 9394 : * We mustn't recurse to child tables in this case, because they've
9395 : * already got the constraint, and visiting them again would lead to an
9396 : * incorrect value for coninhcount.
4632 rhaas 9397 : */
4632 rhaas 9398 GIC 846 : if (newcons == NIL)
2937 alvherre 9399 29 : return address;
9400 :
9401 : /*
9402 : * If adding a NO INHERIT constraint, no need to find our children.
9403 : */
2697 tgl 9404 817 : if (constr->is_no_inherit)
2937 alvherre 9405 33 : return address;
9406 :
9407 : /*
9408 : * Propagate to children as appropriate. Unlike most other ALTER
9409 : * routines, we have to do this one level of recursion at a time; we can't
5448 tgl 9410 ECB : * use find_all_inheritors to do it in one pass.
9411 : */
9412 : children =
711 alvherre 9413 GIC 784 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9414 :
9415 : /*
9416 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9417 : * constraint creation only if there are no children currently. Error out
4006 alvherre 9418 ECB : * otherwise.
9419 : */
4006 alvherre 9420 GIC 784 : if (!recurse && children != NIL)
9421 3 : ereport(ERROR,
4006 alvherre 9422 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9423 : errmsg("constraint must be added to child tables too")));
9424 :
5448 tgl 9425 CBC 994 : foreach(child, children)
9426 : {
9427 216 : Oid childrelid = lfirst_oid(child);
9428 : Relation childrel;
5448 tgl 9429 ECB : AlteredTableInfo *childtab;
9430 :
5080 9431 : /* find_inheritance_children already got lock */
1539 andres 9432 GIC 216 : childrel = table_open(childrelid, NoLock);
5448 tgl 9433 216 : CheckTableNotInUse(childrel, "ALTER TABLE");
9434 :
9435 : /* Find or create work queue entry for this table */
9436 216 : childtab = ATGetQueueEntry(wqueue, childrel);
9437 :
9438 : /* Recurse to child */
9439 216 : ATAddCheckConstraint(wqueue, childtab, childrel,
9440 : constr, recurse, true, is_readd, lockmode);
9441 :
1539 andres 9442 213 : table_close(childrel, NoLock);
9443 : }
9444 :
2937 alvherre 9445 778 : return address;
9446 : }
9447 :
9448 : /*
9449 : * Add a foreign-key constraint to a single table; return the new constraint's
9450 : * address.
9451 : *
3260 bruce 9452 ECB : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9453 : * lock on the rel, and have done appropriate validity checks for it.
5190 tgl 9454 : * We do permissions checks here, however.
9455 : *
1467 alvherre 9456 : * When the referenced or referencing tables (or both) are partitioned,
9457 : * multiple pg_constraint rows are required -- one for each partitioned table
9458 : * and each partition on each side (fortunately, not one for every combination
9459 : * thereof). We also need action triggers on each leaf partition on the
9460 : * referenced side, and check triggers on each leaf partition on the
9461 : * referencing side.
7504 tgl 9462 : */
9463 : static ObjectAddress
1831 alvherre 9464 GIC 1033 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9465 : Constraint *fkconstraint,
9466 : bool recurse, bool recursing, LOCKMODE lockmode)
9467 : {
9468 : Relation pkrel;
267 peter 9469 GNC 1033 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9470 1033 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9471 1033 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9472 1033 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9473 1033 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9474 1033 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9475 1033 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9476 1033 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9477 1033 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
6913 tgl 9478 ECB : int i;
9479 : int numfks,
9480 : numpks,
487 peter 9481 : numfkdelsetcols;
9482 : Oid indexOid;
4059 alvherre 9483 : bool old_check_ok;
9484 : ObjectAddress address;
4059 alvherre 9485 CBC 1033 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9486 :
9487 : /*
9488 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9489 : * delete rows out from under us.
9490 : */
3338 rhaas 9491 GIC 1033 : if (OidIsValid(fkconstraint->old_pktable_oid))
1539 andres 9492 30 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9493 : else
1539 andres 9494 CBC 1003 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9495 :
9496 : /*
5050 bruce 9497 ECB : * Validity checks (permission checks wait till we have the column
9498 : * numbers)
9499 : */
1831 alvherre 9500 GIC 1033 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9501 : {
9502 145 : if (!recurse)
9503 3 : ereport(ERROR,
9504 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9505 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
1644 michael 9506 ECB : RelationGetRelationName(rel),
1831 alvherre 9507 : RelationGetRelationName(pkrel))));
1831 alvherre 9508 CBC 142 : if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
1831 alvherre 9509 GIC 3 : ereport(ERROR,
9510 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9511 : errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9512 : RelationGetRelationName(rel),
9513 : RelationGetRelationName(pkrel)),
1831 alvherre 9514 ECB : errdetail("This feature is not yet supported on partitioned tables.")));
9515 : }
9516 :
1467 alvherre 9517 GIC 1027 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9518 128 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
6913 tgl 9519 LBC 0 : ereport(ERROR,
9520 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6913 tgl 9521 ECB : errmsg("referenced relation \"%s\" is not a table",
9522 : RelationGetRelationName(pkrel))));
7664 9523 :
6913 tgl 9524 GIC 1027 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
7203 9525 1 : ereport(ERROR,
7203 tgl 9526 ECB : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
7191 tgl 9527 EUB : errmsg("permission denied: \"%s\" is a system catalog",
9528 : RelationGetRelationName(pkrel))));
9529 :
9530 : /*
9531 : * References from permanent or unlogged tables to temp tables, and from
9532 : * permanent tables to unlogged tables, are disallowed because the
9533 : * referenced data can vanish out from under us. References from temp
9534 : * tables to any other table type are also disallowed, because other
9535 : * backends might need to run the RI triggers on the perm table, but they
4484 rhaas 9536 ECB : * can't reliably see tuples in the local buffers of other backends.
7142 tgl 9537 : */
4500 rhaas 9538 CBC 1026 : switch (rel->rd_rel->relpersistence)
7142 tgl 9539 ECB : {
4500 rhaas 9540 GIC 871 : case RELPERSISTENCE_PERMANENT:
748 bruce 9541 CBC 871 : if (!RelationIsPermanent(pkrel))
4500 rhaas 9542 UIC 0 : ereport(ERROR,
9543 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4500 rhaas 9544 ECB : errmsg("constraints on permanent tables may reference only permanent tables")));
4500 rhaas 9545 GIC 871 : break;
4484 9546 16 : case RELPERSISTENCE_UNLOGGED:
748 bruce 9547 16 : if (!RelationIsPermanent(pkrel)
4484 rhaas 9548 CBC 16 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
4484 rhaas 9549 LBC 0 : ereport(ERROR,
9550 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9551 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
4484 rhaas 9552 CBC 16 : break;
4500 9553 139 : case RELPERSISTENCE_TEMP:
4500 rhaas 9554 GIC 139 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
4500 rhaas 9555 LBC 0 : ereport(ERROR,
9556 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9557 : errmsg("constraints on temporary tables may reference only temporary tables")));
3765 tgl 9558 CBC 139 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
3765 tgl 9559 UIC 0 : ereport(ERROR,
3765 tgl 9560 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9561 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
4500 rhaas 9562 CBC 139 : break;
9563 : }
7504 tgl 9564 ECB :
9565 : /*
9566 : * Look up the referencing attributes to make sure they exist, and record
9567 : * their attnums and type OIDs.
9568 : */
7504 tgl 9569 GIC 1026 : numfks = transformColumnNameList(RelationGetRelid(rel),
9570 : fkconstraint->fk_attrs,
9571 : fkattnum, fktypoid);
9572 :
487 peter 9573 1011 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9574 : fkconstraint->fk_del_set_cols,
9575 : fkdelsetcols, NULL);
487 peter 9576 CBC 1008 : validateFkOnDeleteSetColumns(numfks, fkattnum,
9577 : numfkdelsetcols, fkdelsetcols,
9578 : fkconstraint->fk_del_set_cols);
9579 :
7504 tgl 9580 ECB : /*
9581 : * If the attribute list for the referenced table was omitted, lookup the
6385 bruce 9582 : * definition of the primary key and use it. Otherwise, validate the
9583 : * supplied attribute list. In either case, discover the index OID and
9584 : * index opclasses, and the attnums and type OIDs of the attributes.
9585 : */
7504 tgl 9586 GIC 1005 : if (fkconstraint->pk_attrs == NIL)
9587 : {
9588 458 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
7504 tgl 9589 ECB : &fkconstraint->pk_attrs,
9590 : pkattnum, pktypoid,
6966 9591 : opclasses);
9592 : }
9593 : else
7504 9594 : {
7504 tgl 9595 GIC 547 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
9596 : fkconstraint->pk_attrs,
7504 tgl 9597 ECB : pkattnum, pktypoid);
9598 : /* Look for an index matching the column list */
6966 tgl 9599 CBC 532 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9600 : opclasses);
9601 : }
9602 :
9603 : /*
5190 tgl 9604 ECB : * Now we can check permissions.
9605 : */
5190 tgl 9606 CBC 984 : checkFkeyPermissions(pkrel, pkattnum, numpks);
9607 :
1471 peter 9608 ECB : /*
1471 peter 9609 EUB : * Check some things for generated columns.
9610 : */
1471 peter 9611 GIC 2220 : for (i = 0; i < numfks; i++)
9612 : {
9613 1242 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9614 :
9615 1242 : if (attgenerated)
1471 peter 9616 ECB : {
9617 : /*
9618 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
9619 : */
1471 peter 9620 CBC 9 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
1471 peter 9621 GIC 9 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9622 9 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
1471 peter 9623 CBC 3 : ereport(ERROR,
9624 : (errcode(ERRCODE_SYNTAX_ERROR),
9625 : errmsg("invalid %s action for foreign key constraint containing generated column",
9626 : "ON UPDATE")));
9627 6 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
1471 peter 9628 GIC 3 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
1471 peter 9629 GBC 3 : ereport(ERROR,
1471 peter 9630 EUB : (errcode(ERRCODE_SYNTAX_ERROR),
9631 : errmsg("invalid %s action for foreign key constraint containing generated column",
9632 : "ON DELETE")));
9633 : }
1471 peter 9634 ECB : }
9635 :
9636 : /*
9637 : * Look up the equality operators to use in the constraint.
9638 : *
9639 : * Note that we have to be careful about the difference between the actual
9640 : * PK column type and the opclass' declared input type, which might be
9641 : * only binary-compatible with it. The declared opcintype is the right
9642 : * thing to probe pg_amop with.
9643 : */
7504 tgl 9644 GIC 978 : if (numfks != numpks)
7203 tgl 9645 UIC 0 : ereport(ERROR,
9646 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9647 : errmsg("number of referencing and referenced columns for foreign key disagree")));
9648 :
9649 : /*
4059 alvherre 9650 ECB : * On the strength of a previous constraint, we might avoid scanning
9651 : * tables to validate this one. See below.
9652 : */
4059 alvherre 9653 CBC 978 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
4059 alvherre 9654 GIC 978 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9655 :
7504 tgl 9656 CBC 2037 : for (i = 0; i < numpks; i++)
7504 tgl 9657 ECB : {
5896 tgl 9658 GIC 1164 : Oid pktype = pktypoid[i];
5896 tgl 9659 CBC 1164 : Oid fktype = fktypoid[i];
9660 : Oid fktyped;
5898 tgl 9661 ECB : HeapTuple cla_ht;
9662 : Form_pg_opclass cla_tup;
9663 : Oid amid;
9664 : Oid opfamily;
9665 : Oid opcintype;
9666 : Oid pfeqop;
9667 : Oid ppeqop;
9668 : Oid ffeqop;
9669 : int16 eqstrategy;
4059 alvherre 9670 : Oid pfeqop_right;
5898 tgl 9671 EUB :
9672 : /* We need several fields out of the pg_opclass entry */
4802 rhaas 9673 CBC 1164 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
5898 tgl 9674 GIC 1164 : if (!HeapTupleIsValid(cla_ht))
5898 tgl 9675 UIC 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
5898 tgl 9676 GIC 1164 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9677 1164 : amid = cla_tup->opcmethod;
9678 1164 : opfamily = cla_tup->opcfamily;
5896 9679 1164 : opcintype = cla_tup->opcintype;
5898 9680 1164 : ReleaseSysCache(cla_ht);
9681 :
9682 : /*
9683 : * Check it's a btree; currently this can never fail since no other
9684 : * index AMs support unique indexes. If we ever did have other types
9685 : * of unique indexes, we'd need a way to determine which operator
9686 : * strategy number is equality. (Is it reasonable to insist that
9687 : * every such index AM use btree's number for equality?)
9688 : */
9689 1164 : if (amid != BTREE_AM_OID)
5898 tgl 9690 UIC 0 : elog(ERROR, "only b-tree indexes are supported for foreign keys");
5898 tgl 9691 GIC 1164 : eqstrategy = BTEqualStrategyNumber;
5898 tgl 9692 ECB :
9693 : /*
9694 : * There had better be a primary equality operator for the index.
9695 : * We'll use it for PK = PK comparisons.
9696 : */
5896 tgl 9697 GIC 1164 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
9698 : eqstrategy);
9699 :
5898 tgl 9700 CBC 1164 : if (!OidIsValid(ppeqop))
5898 tgl 9701 UIC 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
9702 : eqstrategy, opcintype, opcintype, opfamily);
5898 tgl 9703 ECB :
9704 : /*
9705 : * Are there equality operators that take exactly the FK type? Assume
9706 : * we should look through any domain here.
9707 : */
5896 tgl 9708 GIC 1164 : fktyped = getBaseType(fktype);
9709 :
9710 1164 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
9711 : eqstrategy);
9712 1164 : if (OidIsValid(pfeqop))
9713 : {
4059 alvherre 9714 1035 : pfeqop_right = fktyped;
5896 tgl 9715 1035 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
5896 tgl 9716 ECB : eqstrategy);
4059 alvherre 9717 : }
5896 tgl 9718 : else
4059 alvherre 9719 : {
9720 : /* keep compiler quiet */
4059 alvherre 9721 CBC 129 : pfeqop_right = InvalidOid;
4059 alvherre 9722 GIC 129 : ffeqop = InvalidOid;
9723 : }
9724 :
5898 tgl 9725 CBC 1164 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9726 : {
9727 : /*
5624 bruce 9728 ECB : * Otherwise, look for an implicit cast from the FK type to the
9729 : * opcintype, and if found, use the primary equality operator.
5438 tgl 9730 : * This is a bit tricky because opcintype might be a polymorphic
9731 : * type such as ANYARRAY or ANYENUM; so what we have to test is
9732 : * whether the two actual column types can be concurrently cast to
9733 : * that type. (Otherwise, we'd fail to reject combinations such
9734 : * as int[] and point[].)
9735 : */
5624 bruce 9736 : Oid input_typeids[2];
9737 : Oid target_typeids[2];
5896 tgl 9738 :
5896 tgl 9739 CBC 129 : input_typeids[0] = pktype;
5896 tgl 9740 GIC 129 : input_typeids[1] = fktype;
5896 tgl 9741 CBC 129 : target_typeids[0] = opcintype;
5896 tgl 9742 GIC 129 : target_typeids[1] = opcintype;
9743 129 : if (can_coerce_type(2, input_typeids, target_typeids,
9744 : COERCION_IMPLICIT))
4059 alvherre 9745 ECB : {
5898 tgl 9746 CBC 24 : pfeqop = ffeqop = ppeqop;
4059 alvherre 9747 GIC 24 : pfeqop_right = opcintype;
9748 : }
9749 : }
9750 :
5898 tgl 9751 1164 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
6966 tgl 9752 CBC 105 : ereport(ERROR,
5898 tgl 9753 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
1467 alvherre 9754 : errmsg("foreign key constraint \"%s\" cannot be implemented",
9755 : fkconstraint->conname),
6966 tgl 9756 : errdetail("Key columns \"%s\" and \"%s\" "
9757 : "are of incompatible types: %s and %s.",
9758 : strVal(list_nth(fkconstraint->fk_attrs, i)),
9759 : strVal(list_nth(fkconstraint->pk_attrs, i)),
5896 9760 : format_type_be(fktype),
9761 : format_type_be(pktype))));
9762 :
4059 alvherre 9763 CBC 1059 : if (old_check_ok)
9764 : {
9765 : /*
9766 : * When a pfeqop changes, revalidate the constraint. We could
9767 : * permit intra-opfamily changes, but that adds subtle complexity
9768 : * without any concrete benefit for core types. We need not
9769 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9770 : */
9771 3 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
1364 tgl 9772 3 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
9773 : old_pfeqop_item);
9774 : }
4059 alvherre 9775 GIC 1059 : if (old_check_ok)
9776 : {
4059 alvherre 9777 ECB : Oid old_fktype;
9778 : Oid new_fktype;
9779 : CoercionPathType old_pathtype;
9780 : CoercionPathType new_pathtype;
9781 : Oid old_castfunc;
9782 : Oid new_castfunc;
2058 andres 9783 GIC 3 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
9784 : fkattnum[i] - 1);
9785 :
4059 alvherre 9786 ECB : /*
9787 : * Identify coercion pathways from each of the old and new FK-side
9788 : * column types to the right (foreign) operand type of the pfeqop.
9789 : * We may assume that pg_constraint.conkey is not changing.
9790 : */
2058 andres 9791 GIC 3 : old_fktype = attr->atttypid;
4059 alvherre 9792 3 : new_fktype = fktype;
4059 alvherre 9793 CBC 3 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
4059 alvherre 9794 ECB : &old_castfunc);
4059 alvherre 9795 GIC 3 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
9796 : &new_castfunc);
9797 :
4059 alvherre 9798 ECB : /*
9799 : * Upon a change to the cast from the FK column to its pfeqop
3260 bruce 9800 : * operand, revalidate the constraint. For this evaluation, a
9801 : * binary coercion cast is equivalent to no cast at all. While
9802 : * type implementors should design implicit casts with an eye
9803 : * toward consistency of operations like equality, we cannot
9804 : * assume here that they have done so.
4059 alvherre 9805 : *
9806 : * A function with a polymorphic argument could change behavior
9807 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9808 : * when the cast destination is polymorphic, we only avoid
9809 : * revalidation if the input type has not changed at all. Given
9810 : * just the core data types and operator classes, this requirement
9811 : * prevents no would-be optimizations.
9812 : *
9813 : * If the cast converts from a base type to a domain thereon, then
9814 : * that domain type must be the opcintype of the unique index.
9815 : * Necessarily, the primary key column must then be of the domain
9816 : * type. Since the constraint was previously valid, all values on
9817 : * the foreign side necessarily exist on the primary side and in
3260 bruce 9818 : * turn conform to the domain. Consequently, we need not treat
9819 : * domains specially here.
9820 : *
9821 : * Since we require that all collations share the same notion of
9822 : * equality (which they do, because texteq reduces to bitwise
9823 : * equality), we don't compare collation here.
9824 : *
9825 : * We need not directly consider the PK type. It's necessarily
9826 : * binary coercible to the opcintype of the unique index column,
9827 : * and ri_triggers.c will only deal with PK datums in terms of
9828 : * that opcintype. Changing the opcintype also changes pfeqop.
9829 : */
4059 alvherre 9830 GIC 3 : old_check_ok = (new_pathtype == old_pathtype &&
9831 6 : new_castfunc == old_castfunc &&
9832 3 : (!IsPolymorphicType(pfeqop_right) ||
9833 : new_fktype == old_fktype));
9834 : }
9835 :
5898 tgl 9836 1059 : pfeqoperators[i] = pfeqop;
5898 tgl 9837 CBC 1059 : ppeqoperators[i] = ppeqop;
5898 tgl 9838 GIC 1059 : ffeqoperators[i] = ffeqop;
9839 : }
9840 :
9841 : /*
1467 alvherre 9842 ECB : * Create all the constraint and trigger objects, recursing to partitions
9843 : * as necessary. First handle the referenced side.
9844 : */
1467 alvherre 9845 CBC 873 : address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
1467 alvherre 9846 ECB : indexOid,
9847 : InvalidOid, /* no parent constraint */
9848 : numfks,
9849 : pkattnum,
9850 : fkattnum,
9851 : pfeqoperators,
9852 : ppeqoperators,
9853 : ffeqoperators,
9854 : numfkdelsetcols,
9855 : fkdelsetcols,
9856 : old_check_ok,
9857 : InvalidOid, InvalidOid);
9858 :
9859 : /* Now handle the referencing side. */
1467 alvherre 9860 GIC 873 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
9861 : indexOid,
9862 : address.objectId,
9863 : numfks,
1467 alvherre 9864 ECB : pkattnum,
9865 : fkattnum,
9866 : pfeqoperators,
9867 : ppeqoperators,
9868 : ffeqoperators,
9869 : numfkdelsetcols,
9870 : fkdelsetcols,
9871 : old_check_ok,
9872 : lockmode,
459 9873 : InvalidOid, InvalidOid);
9874 :
1467 9875 : /*
9876 : * Done. Close pk table, but keep lock until we've committed.
9877 : */
1467 alvherre 9878 GIC 873 : table_close(pkrel, NoLock);
9879 :
9880 873 : return address;
1467 alvherre 9881 ECB : }
9882 :
9883 : /*
9884 : * validateFkOnDeleteSetColumns
9885 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
9886 : * column lists are valid.
9887 : */
9888 : void
487 peter 9889 GIC 1008 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
487 peter 9890 ECB : int numfksetcols, const int16 *fksetcolsattnums,
9891 : List *fksetcols)
487 peter 9892 EUB : {
487 peter 9893 GIC 1020 : for (int i = 0; i < numfksetcols; i++)
9894 : {
332 tgl 9895 15 : int16 setcol_attnum = fksetcolsattnums[i];
9896 15 : bool seen = false;
487 peter 9897 ECB :
487 peter 9898 CBC 27 : for (int j = 0; j < numfks; j++)
9899 : {
487 peter 9900 GIC 24 : if (fkattnums[j] == setcol_attnum)
9901 : {
9902 12 : seen = true;
9903 12 : break;
9904 : }
9905 : }
9906 :
9907 15 : if (!seen)
9908 : {
332 tgl 9909 3 : char *col = strVal(list_nth(fksetcols, i));
9910 :
487 peter 9911 CBC 3 : ereport(ERROR,
9912 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
487 peter 9913 ECB : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
9914 : }
487 peter 9915 EUB : }
487 peter 9916 GIC 1005 : }
9917 :
1467 alvherre 9918 ECB : /*
9919 : * addFkRecurseReferenced
9920 : * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
9921 : * side of the constraint
1467 alvherre 9922 EUB : *
9923 : * Create pg_constraint rows for the referenced side of the constraint,
9924 : * referencing the parent of the referencing side; also create action triggers
1467 alvherre 9925 ECB : * on leaf partitions. If the table is partitioned, recurse to handle each
9926 : * partition.
9927 : *
1467 alvherre 9928 EUB : * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9929 : * of an ALTER TABLE sequence.
9930 : * fkconstraint is the constraint being added.
1467 alvherre 9931 ECB : * rel is the root referencing relation.
1467 alvherre 9932 EUB : * pkrel is the referenced relation; might be a partition, if recursing.
9933 : * indexOid is the OID of the index (on pkrel) implementing this constraint.
9934 : * parentConstr is the OID of a parent constraint; InvalidOid if this is a
1467 alvherre 9935 ECB : * top-level constraint.
9936 : * numfks is the number of columns in the foreign key
9937 : * pkattnum is the attnum array of referenced attributes.
9938 : * fkattnum is the attnum array of referencing attributes.
9939 : * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
9940 : * (...) clause
9941 : * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
123 peter 9942 : * NULL/DEFAULT clause
9943 : * pf/pp/ffeqoperators are OID array of operators between columns.
9944 : * old_check_ok signals that this constraint replaces an existing one that
9945 : * was already validated (thus this one doesn't need validation).
459 alvherre 9946 : * parentDelTrigger and parentUpdTrigger, when being recursively called on
9947 : * a partition, are the OIDs of the parent action triggers for DELETE and
9948 : * UPDATE respectively.
1467 9949 : */
9950 : static ObjectAddress
1467 alvherre 9951 GIC 1202 : addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
9952 : Relation pkrel, Oid indexOid, Oid parentConstr,
9953 : int numfks,
9954 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
9955 : Oid *ppeqoperators, Oid *ffeqoperators,
9956 : int numfkdelsetcols, int16 *fkdelsetcols,
9957 : bool old_check_ok,
9958 : Oid parentDelTrigger, Oid parentUpdTrigger)
1467 alvherre 9959 ECB : {
9960 : ObjectAddress address;
9961 : Oid constrOid;
9962 : char *conname;
9963 : bool conislocal;
9964 : int coninhcount;
9965 : bool connoinherit;
9966 : Oid deleteTriggerOid,
9967 : updateTriggerOid;
9968 :
9969 : /*
9970 : * Verify relkind for each referenced partition. At the top level, this
9971 : * is redundant with a previous check, but we need it when recursing.
1536 9972 : */
1467 alvherre 9973 GIC 1202 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9974 178 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1467 alvherre 9975 UIC 0 : ereport(ERROR,
9976 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9977 : errmsg("referenced relation \"%s\" is not a table",
9978 : RelationGetRelationName(pkrel))));
1467 alvherre 9979 ECB :
9980 : /*
9981 : * Caller supplies us with a constraint name; however, it may be used in
9982 : * this partition, so come up with a different one in that case.
9983 : */
1467 alvherre 9984 CBC 1202 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9985 : RelationGetRelid(rel),
9986 1202 : fkconstraint->conname))
1467 alvherre 9987 GIC 329 : conname = ChooseConstraintName(RelationGetRelationName(rel),
1467 alvherre 9988 CBC 329 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
9989 : "fkey",
1467 alvherre 9990 GIC 329 : RelationGetNamespace(rel), NIL);
9991 : else
9992 873 : conname = fkconstraint->conname;
1467 alvherre 9993 ECB :
1467 alvherre 9994 CBC 1202 : if (OidIsValid(parentConstr))
1467 alvherre 9995 ECB : {
1467 alvherre 9996 CBC 329 : conislocal = false;
1467 alvherre 9997 GIC 329 : coninhcount = 1;
9998 329 : connoinherit = false;
9999 : }
1467 alvherre 10000 ECB : else
10001 : {
1467 alvherre 10002 CBC 873 : conislocal = true;
1467 alvherre 10003 GIC 873 : coninhcount = 0;
10004 :
10005 : /*
10006 : * always inherit for partitioned tables, never for legacy inheritance
10007 : */
10008 873 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10009 : }
10010 :
10011 : /*
10012 : * Record the FK constraint in pg_constraint.
10013 : */
10014 1202 : constrOid = CreateConstraintEntry(conname,
7504 tgl 10015 1202 : RelationGetNamespace(rel),
10016 : CONSTRAINT_FOREIGN,
7504 tgl 10017 CBC 1202 : fkconstraint->deferrable,
7504 tgl 10018 GBC 1202 : fkconstraint->initdeferred,
4401 simon 10019 GIC 1202 : fkconstraint->initially_valid,
10020 : parentConstr,
10021 : RelationGetRelid(rel),
10022 : fkattnum,
10023 : numfks,
10024 : numfks,
10025 : InvalidOid, /* not a domain constraint */
5003 tgl 10026 ECB : indexOid,
7504 10027 : RelationGetRelid(pkrel),
10028 : pkattnum,
5898 10029 : pfeqoperators,
10030 : ppeqoperators,
10031 : ffeqoperators,
1467 alvherre 10032 : numfks,
7504 tgl 10033 GIC 1202 : fkconstraint->fk_upd_action,
10034 1202 : fkconstraint->fk_del_action,
10035 : fkdelsetcols,
10036 : numfkdelsetcols,
10037 1202 : fkconstraint->fk_matchtype,
10038 : NULL, /* no exclusion constraint */
10039 : NULL, /* no check constraint */
10040 : NULL,
10041 : conislocal, /* islocal */
10042 : coninhcount, /* inhcount */
10043 : connoinherit, /* conNoInherit */
10044 : false); /* is_internal */
10045 :
2937 alvherre 10046 CBC 1202 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
7504 tgl 10047 ECB :
7504 tgl 10048 EUB : /*
1467 alvherre 10049 ECB : * Mark the child constraint as part of the parent constraint; it must not
10050 : * be dropped on its own. (This constraint is deleted when the partition
10051 : * is detached, but a special check needs to occur that the partition
10052 : * contains no referenced values.)
7504 tgl 10053 : */
1467 alvherre 10054 GIC 1202 : if (OidIsValid(parentConstr))
10055 : {
10056 : ObjectAddress referenced;
10057 :
10058 329 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10059 329 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10060 : }
10061 :
1467 alvherre 10062 ECB : /* make new constraint visible, in case we add more */
1467 alvherre 10063 GBC 1202 : CommandCounterIncrement();
7504 tgl 10064 ECB :
10065 : /*
10066 : * Create the action triggers that enforce the constraint.
10067 : */
459 alvherre 10068 GIC 1202 : createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10069 : fkconstraint,
459 alvherre 10070 ECB : constrOid, indexOid,
10071 : parentDelTrigger, parentUpdTrigger,
10072 : &deleteTriggerOid, &updateTriggerOid);
5898 tgl 10073 :
1831 alvherre 10074 EUB : /*
10075 : * If the referenced table is partitioned, recurse on ourselves to handle
10076 : * each partition. We need one pg_constraint row created for each
10077 : * partition in addition to the pg_constraint row for the parent table.
10078 : */
1467 alvherre 10079 GIC 1202 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10080 : {
717 alvherre 10081 CBC 178 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10082 :
1467 10083 447 : for (int i = 0; i < pd->nparts; i++)
10084 : {
1467 alvherre 10085 ECB : Relation partRel;
10086 : AttrMap *map;
10087 : AttrNumber *mapped_pkattnum;
10088 : Oid partIndexId;
10089 :
1467 alvherre 10090 GIC 269 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10091 :
10092 : /*
10093 : * Map the attribute numbers in the referenced side of the FK
1467 alvherre 10094 ECB : * definition to match the partition's column layout.
10095 : */
1208 michael 10096 GIC 269 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10097 : RelationGetDescr(pkrel),
10098 : false);
1467 alvherre 10099 CBC 269 : if (map)
10100 : {
1467 alvherre 10101 GIC 23 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10102 46 : for (int j = 0; j < numfks; j++)
1208 michael 10103 23 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10104 : }
10105 : else
1467 alvherre 10106 246 : mapped_pkattnum = pkattnum;
10107 :
10108 : /* do the deed */
10109 269 : partIndexId = index_get_partition(partRel, indexOid);
10110 269 : if (!OidIsValid(partIndexId))
1467 alvherre 10111 UIC 0 : elog(ERROR, "index for %u not found in partition %s",
10112 : indexOid, RelationGetRelationName(partRel));
1467 alvherre 10113 CBC 269 : addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
1467 alvherre 10114 ECB : partIndexId, constrOid, numfks,
10115 : mapped_pkattnum, fkattnum,
10116 : pfeqoperators, ppeqoperators, ffeqoperators,
487 peter 10117 : numfkdelsetcols, fkdelsetcols,
10118 : old_check_ok,
10119 : deleteTriggerOid, updateTriggerOid);
1467 alvherre 10120 :
10121 : /* Done -- clean up (but keep the lock) */
1467 alvherre 10122 GIC 269 : table_close(partRel, NoLock);
10123 269 : if (map)
10124 : {
1467 alvherre 10125 CBC 23 : pfree(mapped_pkattnum);
1208 michael 10126 23 : free_attrmap(map);
10127 : }
10128 : }
10129 : }
10130 :
1467 alvherre 10131 GIC 1202 : return address;
10132 : }
10133 :
10134 : /*
10135 : * addFkRecurseReferencing
10136 : * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
1467 alvherre 10137 ECB : *
10138 : * If the referencing relation is a plain relation, create the necessary check
10139 : * triggers that implement the constraint, and set up for Phase 3 constraint
10140 : * verification. If the referencing relation is a partitioned table, then
10141 : * we create a pg_constraint row for it and recurse on this routine for each
10142 : * partition.
10143 : *
10144 : * We assume that the referenced relation is locked against concurrent
10145 : * deletions. If it's a partitioned relation, every partition must be so
10146 : * locked.
10147 : *
10148 : * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10149 : * of an ALTER TABLE sequence.
10150 : * fkconstraint is the constraint being added.
10151 : * rel is the referencing relation; might be a partition, if recursing.
10152 : * pkrel is the root referenced relation.
10153 : * indexOid is the OID of the index (on pkrel) implementing this constraint.
10154 : * parentConstr is the OID of the parent constraint (there is always one).
10155 : * numfks is the number of columns in the foreign key
10156 : * pkattnum is the attnum array of referenced attributes.
10157 : * fkattnum is the attnum array of referencing attributes.
10158 : * pf/pp/ffeqoperators are OID array of operators between columns.
10159 : * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10160 : * (...) clause
10161 : * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10162 : * NULL/DEFAULT clause
10163 : * old_check_ok signals that this constraint replaces an existing one that
10164 : * was already validated (thus this one doesn't need validation).
10165 : * lockmode is the lockmode to acquire on partitions when recursing.
459 10166 : * parentInsTrigger and parentUpdTrigger, when being recursively called on
10167 : * a partition, are the OIDs of the parent check triggers for INSERT and
10168 : * UPDATE respectively.
1467 10169 : */
10170 : static void
1467 alvherre 10171 GIC 1224 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10172 : Relation pkrel, Oid indexOid, Oid parentConstr,
10173 : int numfks, int16 *pkattnum, int16 *fkattnum,
10174 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10175 : int numfkdelsetcols, int16 *fkdelsetcols,
10176 : bool old_check_ok, LOCKMODE lockmode,
10177 : Oid parentInsTrigger, Oid parentUpdTrigger)
10178 : {
10179 : Oid insertTriggerOid,
10180 : updateTriggerOid;
10181 :
163 peter 10182 GNC 1224 : Assert(OidIsValid(parentConstr));
10183 :
1467 alvherre 10184 GIC 1224 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1467 alvherre 10185 UIC 0 : ereport(ERROR,
10186 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10187 : errmsg("foreign key constraints are not supported on foreign tables")));
10188 :
10189 : /*
10190 : * Add the check triggers to it and, if necessary, schedule it to be
10191 : * checked in Phase 3.
10192 : *
10193 : * If the relation is partitioned, drill down to do it to its partitions.
10194 : */
459 alvherre 10195 GIC 1224 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10196 : RelationGetRelid(pkrel),
10197 : fkconstraint,
10198 : parentConstr,
10199 : indexOid,
10200 : parentInsTrigger, parentUpdTrigger,
10201 : &insertTriggerOid, &updateTriggerOid);
10202 :
1467 10203 1224 : if (rel->rd_rel->relkind == RELKIND_RELATION)
1467 alvherre 10204 ECB : {
10205 : /*
10206 : * Tell Phase 3 to check that the constraint is satisfied by existing
10207 : * rows. We can skip this during table creation, when requested
10208 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10209 : * and when we're recreating a constraint following a SET DATA TYPE
10210 : * operation that did not impugn its validity.
10211 : */
1467 alvherre 10212 CBC 1029 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10213 : {
10214 : NewConstraint *newcon;
10215 : AlteredTableInfo *tab;
10216 :
1467 alvherre 10217 GIC 320 : tab = ATGetQueueEntry(wqueue, rel);
10218 :
1542 alvherre 10219 CBC 320 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
1467 alvherre 10220 GIC 320 : newcon->name = get_constraint_name(parentConstr);
1542 10221 320 : newcon->contype = CONSTR_FOREIGN;
1467 10222 320 : newcon->refrelid = RelationGetRelid(pkrel);
10223 320 : newcon->refindid = indexOid;
10224 320 : newcon->conid = parentConstr;
1542 10225 320 : newcon->qual = (Node *) fkconstraint;
10226 :
1467 10227 320 : tab->constraints = lappend(tab->constraints, newcon);
10228 : }
10229 : }
10230 195 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10231 : {
717 10232 195 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10233 : Relation trigrel;
459 alvherre 10234 ECB :
10235 : /*
10236 : * Triggers of the foreign keys will be manipulated a bunch of times
10237 : * in the loop below. To avoid repeatedly opening/closing the trigger
10238 : * catalog relation, we open it here and pass it to the subroutines
10239 : * called below.
10240 : */
459 alvherre 10241 GIC 195 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10242 :
10243 : /*
10244 : * Recurse to take appropriate action on each partition; either we
10245 : * find an existing constraint to reparent to ours, or we create a new
10246 : * one.
10247 : */
1467 10248 377 : for (int i = 0; i < pd->nparts; i++)
10249 : {
10250 182 : Oid partitionId = pd->oids[i];
10251 182 : Relation partition = table_open(partitionId, lockmode);
1467 alvherre 10252 ECB : List *partFKs;
10253 : AttrMap *attmap;
10254 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10255 : bool attached;
10256 : char *conname;
10257 : Oid constrOid;
10258 : ObjectAddress address,
10259 : referenced;
10260 : ListCell *cell;
10261 :
1467 alvherre 10262 GIC 182 : CheckTableNotInUse(partition, "ALTER TABLE");
1467 alvherre 10263 ECB :
1208 michael 10264 GIC 182 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10265 : RelationGetDescr(rel),
10266 : false);
1467 alvherre 10267 466 : for (int j = 0; j < numfks; j++)
1208 michael 10268 CBC 284 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10269 :
1467 alvherre 10270 ECB : /* Check whether an existing constraint can be repurposed */
1467 alvherre 10271 CBC 182 : partFKs = copyObject(RelationGetFKeyList(partition));
1467 alvherre 10272 GIC 182 : attached = false;
1467 alvherre 10273 CBC 191 : foreach(cell, partFKs)
10274 : {
1467 alvherre 10275 ECB : ForeignKeyCacheInfo *fk;
10276 :
1467 alvherre 10277 CBC 15 : fk = lfirst_node(ForeignKeyCacheInfo, cell);
10278 15 : if (tryAttachPartitionForeignKey(fk,
10279 : partitionId,
10280 : parentConstr,
10281 : numfks,
1467 alvherre 10282 ECB : mapped_fkattnum,
10283 : pkattnum,
459 10284 : pfeqoperators,
10285 : insertTriggerOid,
10286 : updateTriggerOid,
10287 : trigrel))
10288 : {
1467 alvherre 10289 GIC 6 : attached = true;
10290 6 : break;
1467 alvherre 10291 ECB : }
10292 : }
1467 alvherre 10293 GIC 182 : if (attached)
10294 : {
10295 6 : table_close(partition, NoLock);
10296 6 : continue;
10297 : }
10298 :
10299 : /*
10300 : * No luck finding a good constraint to reuse; create our own.
10301 : */
10302 176 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10303 : RelationGetRelid(partition),
10304 176 : fkconstraint->conname))
1467 alvherre 10305 UIC 0 : conname = ChooseConstraintName(RelationGetRelationName(partition),
10306 0 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10307 : "fkey",
10308 0 : RelationGetNamespace(partition), NIL);
10309 : else
1467 alvherre 10310 GIC 176 : conname = fkconstraint->conname;
10311 : constrOid =
10312 176 : CreateConstraintEntry(conname,
10313 176 : RelationGetNamespace(partition),
10314 : CONSTRAINT_FOREIGN,
10315 176 : fkconstraint->deferrable,
10316 176 : fkconstraint->initdeferred,
10317 176 : fkconstraint->initially_valid,
10318 : parentConstr,
10319 : partitionId,
10320 : mapped_fkattnum,
10321 : numfks,
10322 : numfks,
10323 : InvalidOid,
10324 : indexOid,
10325 : RelationGetRelid(pkrel),
1467 alvherre 10326 ECB : pkattnum,
10327 : pfeqoperators,
10328 : ppeqoperators,
10329 : ffeqoperators,
10330 : numfks,
1467 alvherre 10331 GIC 176 : fkconstraint->fk_upd_action,
10332 176 : fkconstraint->fk_del_action,
10333 : fkdelsetcols,
10334 : numfkdelsetcols,
10335 176 : fkconstraint->fk_matchtype,
10336 : NULL,
10337 : NULL,
10338 : NULL,
10339 : false,
10340 : 1,
10341 : false,
10342 : false);
10343 :
10344 : /*
10345 : * Give this constraint partition-type dependencies on the parent
10346 : * constraint as well as the table.
10347 : */
1467 alvherre 10348 CBC 176 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10349 176 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
1467 alvherre 10350 GBC 176 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
1467 alvherre 10351 GIC 176 : ObjectAddressSet(referenced, RelationRelationId, partitionId);
10352 176 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10353 :
10354 : /* Make all this visible before recursing */
10355 176 : CommandCounterIncrement();
10356 :
10357 : /* call ourselves to finalize the creation and we're done */
10358 176 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
1467 alvherre 10359 ECB : indexOid,
10360 : constrOid,
10361 : numfks,
10362 : pkattnum,
10363 : mapped_fkattnum,
10364 : pfeqoperators,
10365 : ppeqoperators,
10366 : ffeqoperators,
487 peter 10367 : numfkdelsetcols,
10368 : fkdelsetcols,
1467 alvherre 10369 : old_check_ok,
10370 : lockmode,
459 10371 : insertTriggerOid,
10372 : updateTriggerOid);
1467 10373 :
1467 alvherre 10374 GIC 176 : table_close(partition, NoLock);
10375 : }
10376 :
459 alvherre 10377 CBC 195 : table_close(trigrel, RowExclusiveLock);
1467 alvherre 10378 ECB : }
7504 tgl 10379 GIC 1224 : }
10380 :
10381 : /*
10382 : * CloneForeignKeyConstraints
1542 alvherre 10383 ECB : * Clone foreign keys from a partitioned table to a newly acquired
10384 : * partition.
10385 : *
10386 : * partitionRel is a partition of parentRel, so we can be certain that it has
10387 : * the same columns with the same datatypes. The columns may be in different
10388 : * order, though.
10389 : *
1467 10390 : * wqueue must be passed to set up phase 3 constraint checking, unless the
10391 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
10392 : * PARTITION OF).
1542 10393 : */
10394 : static void
1467 alvherre 10395 GIC 4312 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10396 : Relation partitionRel)
10397 : {
10398 : /* This only works for declarative partitioning */
10399 4312 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10400 :
10401 : /*
10402 : * Clone constraints for which the parent is on the referenced side.
10403 : */
10404 4312 : CloneFkReferenced(parentRel, partitionRel);
10405 :
10406 : /*
10407 : * Now clone constraints where the parent is on the referencing side.
1467 alvherre 10408 ECB : */
1467 alvherre 10409 CBC 4312 : CloneFkReferencing(wqueue, parentRel, partitionRel);
1467 alvherre 10410 GIC 4312 : }
10411 :
1467 alvherre 10412 ECB : /*
10413 : * CloneFkReferenced
10414 : * Subroutine for CloneForeignKeyConstraints
10415 : *
10416 : * Find all the FKs that have the parent relation on the referenced side;
10417 : * clone those constraints to the given partition. This is to be called
10418 : * when the partition is being created or attached.
10419 : *
10420 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
184 10421 : *
10422 : * This recurses to partitions, if the relation being attached is partitioned.
10423 : * Recursion is done by calling addFkRecurseReferenced.
10424 : */
10425 : static void
1467 alvherre 10426 GIC 4312 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
10427 : {
10428 : Relation pg_constraint;
1208 michael 10429 ECB : AttrMap *attmap;
10430 : ListCell *cell;
10431 : SysScanDesc scan;
10432 : ScanKeyData key[2];
1542 alvherre 10433 : HeapTuple tuple;
1542 alvherre 10434 CBC 4312 : List *clone = NIL;
10435 : Relation trigrel;
10436 :
10437 : /*
1157 alvherre 10438 ECB : * Search for any constraints where this partition's parent is in the
10439 : * referenced side. However, we must not clone any constraint whose
10440 : * parent constraint is also going to be cloned, to avoid duplicates. So
10441 : * do it in two steps: first construct the list of constraints to clone,
10442 : * then go over that list cloning those whose parents are not in the list.
10443 : * (We must not rely on the parent being seen first, since the catalog
10444 : * scan could return children first.)
10445 : */
1539 andres 10446 GIC 4312 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
1467 alvherre 10447 4312 : ScanKeyInit(&key[0],
10448 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10449 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10450 4312 : ScanKeyInit(&key[1],
10451 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
10452 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10453 : /* This is a seqscan, as we don't have a usable index ... */
1467 alvherre 10454 CBC 4312 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
10455 : NULL, 2, key);
1542 10456 4462 : while ((tuple = systable_getnext(scan)) != NULL)
10457 : {
1467 10458 150 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10459 :
1467 alvherre 10460 GIC 150 : clone = lappend_oid(clone, constrForm->oid);
10461 : }
1542 10462 4312 : systable_endscan(scan);
1467 10463 4312 : table_close(pg_constraint, RowShareLock);
10464 :
459 alvherre 10465 ECB : /*
10466 : * Triggers of the foreign keys will be manipulated a bunch of times in
10467 : * the loop below. To avoid repeatedly opening/closing the trigger
10468 : * catalog relation, we open it here and pass it to the subroutines called
10469 : * below.
10470 : */
459 alvherre 10471 CBC 4312 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10472 :
1208 michael 10473 GIC 4312 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10474 : RelationGetDescr(parentRel),
10475 : false);
1467 alvherre 10476 4462 : foreach(cell, clone)
1467 alvherre 10477 ECB : {
1467 alvherre 10478 CBC 150 : Oid constrOid = lfirst_oid(cell);
1467 alvherre 10479 ECB : Form_pg_constraint constrForm;
10480 : Relation fkRel;
10481 : Oid indexOid;
10482 : Oid partIndexId;
10483 : int numfks;
10484 : AttrNumber conkey[INDEX_MAX_KEYS];
10485 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10486 : AttrNumber confkey[INDEX_MAX_KEYS];
1467 alvherre 10487 EUB : Oid conpfeqop[INDEX_MAX_KEYS];
10488 : Oid conppeqop[INDEX_MAX_KEYS];
1467 alvherre 10489 ECB : Oid conffeqop[INDEX_MAX_KEYS];
10490 : int numfkdelsetcols;
10491 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10492 : Constraint *fkconstraint;
10493 : Oid deleteTriggerOid,
10494 : updateTriggerOid;
10495 :
1467 alvherre 10496 GIC 150 : tuple = SearchSysCache1(CONSTROID, constrOid);
10497 150 : if (!HeapTupleIsValid(tuple))
1467 alvherre 10498 LBC 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
1467 alvherre 10499 CBC 150 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10500 :
1157 alvherre 10501 ECB : /*
10502 : * As explained above: don't try to clone a constraint for which we're
10503 : * going to clone the parent.
10504 : */
1157 alvherre 10505 GIC 150 : if (list_member_oid(clone, constrForm->conparentid))
10506 : {
1157 alvherre 10507 CBC 63 : ReleaseSysCache(tuple);
184 alvherre 10508 GIC 90 : continue;
10509 : }
10510 :
10511 : /*
10512 : * Don't clone self-referencing foreign keys, which can be in the
10513 : * partitioned table or in the partition-to-be.
10514 : */
10515 87 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10516 66 : constrForm->conrelid == RelationGetRelid(partitionRel))
10517 : {
10518 27 : ReleaseSysCache(tuple);
1157 10519 27 : continue;
10520 : }
10521 :
10522 : /*
10523 : * Because we're only expanding the key space at the referenced side,
10524 : * we don't need to prevent any operation in the referencing table, so
10525 : * AccessShareLock suffices (assumes that dropping the constraint
10526 : * acquires AEL).
10527 : */
1467 10528 60 : fkRel = table_open(constrForm->conrelid, AccessShareLock);
10529 :
10530 60 : indexOid = constrForm->conindid;
10531 60 : DeconstructFkConstraintRow(tuple,
10532 : &numfks,
10533 : conkey,
10534 : confkey,
10535 : conpfeqop,
10536 : conppeqop,
10537 : conffeqop,
10538 : &numfkdelsetcols,
10539 : confdelsetcols);
10540 :
10541 120 : for (int i = 0; i < numfks; i++)
1208 michael 10542 60 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
10543 :
1467 alvherre 10544 60 : fkconstraint = makeNode(Constraint);
157 10545 60 : fkconstraint->contype = CONSTRAINT_FOREIGN;
1467 10546 60 : fkconstraint->conname = NameStr(constrForm->conname);
1467 alvherre 10547 CBC 60 : fkconstraint->deferrable = constrForm->condeferrable;
1467 alvherre 10548 GIC 60 : fkconstraint->initdeferred = constrForm->condeferred;
157 10549 60 : fkconstraint->location = -1;
10550 60 : fkconstraint->pktable = NULL;
10551 : /* ->fk_attrs determined below */
10552 60 : fkconstraint->pk_attrs = NIL;
1467 10553 60 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
157 10554 60 : fkconstraint->fk_upd_action = constrForm->confupdtype;
10555 60 : fkconstraint->fk_del_action = constrForm->confdeltype;
10556 60 : fkconstraint->fk_del_set_cols = NIL;
10557 60 : fkconstraint->old_conpfeqop = NIL;
157 alvherre 10558 CBC 60 : fkconstraint->old_pktable_oid = InvalidOid;
157 alvherre 10559 GIC 60 : fkconstraint->skip_validation = false;
157 alvherre 10560 CBC 60 : fkconstraint->initially_valid = true;
1467 alvherre 10561 EUB :
10562 : /* set up colnames that are used to generate the constraint name */
1467 alvherre 10563 GIC 120 : for (int i = 0; i < numfks; i++)
10564 : {
10565 : Form_pg_attribute att;
10566 :
10567 60 : att = TupleDescAttr(RelationGetDescr(fkRel),
10568 : conkey[i] - 1);
10569 60 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10570 60 : makeString(NameStr(att->attname)));
1467 alvherre 10571 ECB : }
10572 :
10573 : /*
10574 : * Add the new foreign key constraint pointing to the new partition.
10575 : * Because this new partition appears in the referenced side of the
10576 : * constraint, we don't need to set up for Phase 3 check.
10577 : */
1467 alvherre 10578 GIC 60 : partIndexId = index_get_partition(partitionRel, indexOid);
1467 alvherre 10579 CBC 60 : if (!OidIsValid(partIndexId))
1467 alvherre 10580 UIC 0 : elog(ERROR, "index for %u not found in partition %s",
10581 : indexOid, RelationGetRelationName(partitionRel));
10582 :
10583 : /*
10584 : * Get the "action" triggers belonging to the constraint to pass as
10585 : * parent OIDs for similar triggers that will be created on the
10586 : * partition in addFkRecurseReferenced().
10587 : */
459 alvherre 10588 CBC 60 : GetForeignKeyActionTriggers(trigrel, constrOid,
10589 : constrForm->confrelid, constrForm->conrelid,
10590 : &deleteTriggerOid, &updateTriggerOid);
10591 :
1467 alvherre 10592 GIC 60 : addFkRecurseReferenced(NULL,
1467 alvherre 10593 ECB : fkconstraint,
10594 : fkRel,
10595 : partitionRel,
10596 : partIndexId,
10597 : constrOid,
10598 : numfks,
10599 : mapped_confkey,
10600 : conkey,
10601 : conpfeqop,
10602 : conppeqop,
10603 : conffeqop,
10604 : numfkdelsetcols,
10605 : confdelsetcols,
459 10606 : true,
10607 : deleteTriggerOid,
10608 : updateTriggerOid);
10609 :
1467 alvherre 10610 GIC 60 : table_close(fkRel, NoLock);
10611 60 : ReleaseSysCache(tuple);
10612 : }
10613 :
459 10614 4312 : table_close(trigrel, RowExclusiveLock);
1542 10615 4312 : }
10616 :
1542 alvherre 10617 ECB : /*
10618 : * CloneFkReferencing
10619 : * Subroutine for CloneForeignKeyConstraints
10620 : *
10621 : * For each FK constraint of the parent relation in the given list, find an
10622 : * equivalent constraint in its partition relation that can be reparented;
10623 : * if one cannot be found, create a new constraint in the partition as its
1467 10624 : * child.
10625 : *
10626 : * If wqueue is given, it is used to set up phase-3 verification for each
10627 : * cloned constraint; if omitted, we assume that such verification is not
10628 : * needed (example: the partition is being created anew).
10629 : */
10630 : static void
1467 alvherre 10631 GIC 4312 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
10632 : {
10633 : AttrMap *attmap;
10634 : List *partFKs;
10635 4312 : List *clone = NIL;
10636 : ListCell *cell;
10637 : Relation trigrel;
1542 alvherre 10638 ECB :
10639 : /* obtain a list of constraints that we need to clone */
1467 alvherre 10640 CBC 4720 : foreach(cell, RelationGetFKeyList(parentRel))
10641 : {
1467 alvherre 10642 GIC 408 : ForeignKeyCacheInfo *fk = lfirst(cell);
1467 alvherre 10643 ECB :
1467 alvherre 10644 CBC 408 : clone = lappend_oid(clone, fk->conoid);
10645 : }
10646 :
1467 alvherre 10647 ECB : /*
10648 : * Silently do nothing if there's nothing to do. In particular, this
10649 : * avoids throwing a spurious error for foreign tables.
10650 : */
1467 alvherre 10651 GIC 4312 : if (clone == NIL)
10652 4116 : return;
1467 alvherre 10653 ECB :
1467 alvherre 10654 CBC 196 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1467 alvherre 10655 UIC 0 : ereport(ERROR,
10656 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10657 : errmsg("foreign key constraints are not supported on foreign tables")));
10658 :
10659 : /*
10660 : * Triggers of the foreign keys will be manipulated a bunch of times in
10661 : * the loop below. To avoid repeatedly opening/closing the trigger
10662 : * catalog relation, we open it here and pass it to the subroutines called
10663 : * below.
10664 : */
459 alvherre 10665 CBC 196 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
459 alvherre 10666 ECB :
10667 : /*
10668 : * The constraint key may differ, if the columns in the partition are
1542 10669 : * different. This map is used to convert them.
10670 : */
1208 michael 10671 CBC 196 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
10672 : RelationGetDescr(parentRel),
10673 : false);
10674 :
1542 alvherre 10675 GIC 196 : partFKs = copyObject(RelationGetFKeyList(partRel));
10676 :
10677 604 : foreach(cell, clone)
10678 : {
1542 alvherre 10679 CBC 408 : Oid parentConstrOid = lfirst_oid(cell);
10680 : Form_pg_constraint constrForm;
1467 alvherre 10681 ECB : Relation pkrel;
1542 alvherre 10682 EUB : HeapTuple tuple;
10683 : int numfks;
10684 : AttrNumber conkey[INDEX_MAX_KEYS];
10685 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
10686 : AttrNumber confkey[INDEX_MAX_KEYS];
1542 alvherre 10687 ECB : Oid conpfeqop[INDEX_MAX_KEYS];
10688 : Oid conppeqop[INDEX_MAX_KEYS];
10689 : Oid conffeqop[INDEX_MAX_KEYS];
487 peter 10690 : int numfkdelsetcols;
10691 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
1542 alvherre 10692 : Constraint *fkconstraint;
1467 10693 : bool attached;
10694 : Oid indexOid;
10695 : Oid constrOid;
10696 : ObjectAddress address,
10697 : referenced;
10698 : ListCell *lc;
10699 : Oid insertTriggerOid,
10700 : updateTriggerOid;
10701 :
1542 alvherre 10702 GIC 408 : tuple = SearchSysCache1(CONSTROID, parentConstrOid);
1435 tgl 10703 408 : if (!HeapTupleIsValid(tuple))
1542 alvherre 10704 UIC 0 : elog(ERROR, "cache lookup failed for constraint %u",
10705 : parentConstrOid);
1542 alvherre 10706 GIC 408 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10707 :
1467 alvherre 10708 ECB : /* Don't clone constraints whose parents are being cloned */
1467 alvherre 10709 CBC 408 : if (list_member_oid(clone, constrForm->conparentid))
10710 : {
1542 alvherre 10711 GIC 200 : ReleaseSysCache(tuple);
1542 alvherre 10712 CBC 233 : continue;
10713 : }
10714 :
10715 : /*
10716 : * Need to prevent concurrent deletions. If pkrel is a partitioned
10717 : * relation, that means to lock all partitions.
10718 : */
1467 alvherre 10719 GIC 208 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
10720 208 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10721 86 : (void) find_all_inheritors(RelationGetRelid(pkrel),
10722 : ShareRowExclusiveLock, NULL);
10723 :
1542 10724 208 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
487 peter 10725 ECB : conpfeqop, conppeqop, conffeqop,
10726 : &numfkdelsetcols, confdelsetcols);
1467 alvherre 10727 CBC 479 : for (int i = 0; i < numfks; i++)
1208 michael 10728 271 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
1542 alvherre 10729 ECB :
10730 : /*
10731 : * Get the "check" triggers belonging to the constraint to pass as
459 10732 : * parent OIDs for similar triggers that will be created on the
10733 : * partition in addFkRecurseReferencing(). They are also passed to
10734 : * tryAttachPartitionForeignKey() below to simply assign as parents to
10735 : * the partition's existing "check" triggers, that is, if the
10736 : * corresponding constraints is deemed attachable to the parent
10737 : * constraint.
10738 : */
459 alvherre 10739 GIC 208 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
10740 : constrForm->confrelid, constrForm->conrelid,
10741 : &insertTriggerOid, &updateTriggerOid);
10742 :
10743 : /*
10744 : * Before creating a new constraint, see whether any existing FKs are
10745 : * fit for the purpose. If one is, attach the parent constraint to
10746 : * it, and don't clone anything. This way we avoid the expensive
10747 : * verification step and don't end up with a duplicate FK, and we
10748 : * don't need to recurse to partitions for this constraint.
10749 : */
1467 10750 208 : attached = false;
186 drowley 10751 GNC 250 : foreach(lc, partFKs)
10752 : {
10753 75 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
1542 alvherre 10754 ECB :
1467 alvherre 10755 GIC 75 : if (tryAttachPartitionForeignKey(fk,
1467 alvherre 10756 ECB : RelationGetRelid(partRel),
10757 : parentConstrOid,
10758 : numfks,
10759 : mapped_conkey,
10760 : confkey,
10761 : conpfeqop,
10762 : insertTriggerOid,
10763 : updateTriggerOid,
10764 : trigrel))
10765 : {
1467 alvherre 10766 GIC 33 : attached = true;
10767 33 : table_close(pkrel, NoLock);
10768 33 : break;
10769 : }
10770 : }
10771 208 : if (attached)
1542 alvherre 10772 ECB : {
1542 alvherre 10773 GIC 33 : ReleaseSysCache(tuple);
10774 33 : continue;
10775 : }
1542 alvherre 10776 ECB :
10777 : /* No dice. Set up to create our own constraint */
1467 alvherre 10778 GIC 175 : fkconstraint = makeNode(Constraint);
157 10779 175 : fkconstraint->contype = CONSTRAINT_FOREIGN;
10780 : /* ->conname determined below */
1467 alvherre 10781 CBC 175 : fkconstraint->deferrable = constrForm->condeferrable;
1467 alvherre 10782 GIC 175 : fkconstraint->initdeferred = constrForm->condeferred;
157 10783 175 : fkconstraint->location = -1;
10784 175 : fkconstraint->pktable = NULL;
10785 : /* ->fk_attrs determined below */
157 alvherre 10786 CBC 175 : fkconstraint->pk_attrs = NIL;
1467 10787 175 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
157 alvherre 10788 GIC 175 : fkconstraint->fk_upd_action = constrForm->confupdtype;
10789 175 : fkconstraint->fk_del_action = constrForm->confdeltype;
10790 175 : fkconstraint->fk_del_set_cols = NIL;
10791 175 : fkconstraint->old_conpfeqop = NIL;
10792 175 : fkconstraint->old_pktable_oid = InvalidOid;
10793 175 : fkconstraint->skip_validation = false;
10794 175 : fkconstraint->initially_valid = true;
1467 10795 389 : for (int i = 0; i < numfks; i++)
10796 : {
10797 : Form_pg_attribute att;
10798 :
10799 214 : att = TupleDescAttr(RelationGetDescr(partRel),
10800 : mapped_conkey[i] - 1);
10801 214 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10802 214 : makeString(NameStr(att->attname)));
1467 alvherre 10803 ECB : }
213 alvherre 10804 GIC 175 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10805 : RelationGetRelid(partRel),
10806 175 : NameStr(constrForm->conname)))
10807 3 : fkconstraint->conname =
10808 3 : ChooseConstraintName(RelationGetRelationName(partRel),
10809 3 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10810 : "fkey",
213 alvherre 10811 CBC 3 : RelationGetNamespace(partRel), NIL);
10812 : else
213 alvherre 10813 GIC 172 : fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
10814 :
1467 10815 175 : indexOid = constrForm->conindid;
10816 : constrOid =
10817 175 : CreateConstraintEntry(fkconstraint->conname,
10818 : constrForm->connamespace,
10819 : CONSTRAINT_FOREIGN,
10820 175 : fkconstraint->deferrable,
10821 175 : fkconstraint->initdeferred,
1542 10822 175 : constrForm->convalidated,
1542 alvherre 10823 ECB : parentConstrOid,
10824 : RelationGetRelid(partRel),
10825 : mapped_conkey,
10826 : numfks,
10827 : numfks,
10828 : InvalidOid, /* not a domain constraint */
10829 : indexOid,
10830 : constrForm->confrelid, /* same foreign rel */
10831 : confkey,
10832 : conpfeqop,
10833 : conppeqop,
10834 : conffeqop,
10835 : numfks,
1467 alvherre 10836 GIC 175 : fkconstraint->fk_upd_action,
1467 alvherre 10837 CBC 175 : fkconstraint->fk_del_action,
10838 : confdelsetcols,
487 peter 10839 ECB : numfkdelsetcols,
1467 alvherre 10840 CBC 175 : fkconstraint->fk_matchtype,
10841 : NULL,
10842 : NULL,
10843 : NULL,
10844 : false, /* islocal */
10845 : 1, /* inhcount */
10846 : false, /* conNoInherit */
10847 : true);
1542 alvherre 10848 ECB :
10849 : /* Set up partition dependencies for the new constraint */
1467 alvherre 10850 CBC 175 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
1467 alvherre 10851 GIC 175 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
10852 175 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
1467 alvherre 10853 CBC 175 : ObjectAddressSet(referenced, RelationRelationId,
10854 : RelationGetRelid(partRel));
10855 175 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10856 :
10857 : /* Done with the cloned constraint's tuple */
1467 alvherre 10858 GIC 175 : ReleaseSysCache(tuple);
10859 :
10860 : /* Make all this visible before recursing */
10861 175 : CommandCounterIncrement();
10862 :
10863 175 : addFkRecurseReferencing(wqueue,
10864 : fkconstraint,
10865 : partRel,
10866 : pkrel,
10867 : indexOid,
10868 : constrOid,
10869 : numfks,
10870 : confkey,
10871 : mapped_conkey,
10872 : conpfeqop,
1467 alvherre 10873 ECB : conppeqop,
10874 : conffeqop,
487 peter 10875 EUB : numfkdelsetcols,
487 peter 10876 ECB : confdelsetcols,
10877 : false, /* no old check exists */
10878 : AccessExclusiveLock,
10879 : insertTriggerOid,
10880 : updateTriggerOid);
1467 alvherre 10881 GIC 175 : table_close(pkrel, NoLock);
1467 alvherre 10882 ECB : }
10883 :
459 alvherre 10884 CBC 196 : table_close(trigrel, RowExclusiveLock);
1467 alvherre 10885 ECB : }
10886 :
10887 : /*
10888 : * When the parent of a partition receives [the referencing side of] a foreign
10889 : * key, we must propagate that foreign key to the partition. However, the
10890 : * partition might already have an equivalent foreign key; this routine
10891 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
10892 : * by the other parameters. If they are equivalent, create the link between
10893 : * the two constraints and return true.
10894 : *
10895 : * If the given FK does not match the one defined by rest of the params,
10896 : * return false.
10897 : */
10898 : static bool
1467 alvherre 10899 GIC 90 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
10900 : Oid partRelid,
10901 : Oid parentConstrOid,
10902 : int numfks,
10903 : AttrNumber *mapped_conkey,
10904 : AttrNumber *confkey,
459 alvherre 10905 ECB : Oid *conpfeqop,
10906 : Oid parentInsTrigger,
10907 : Oid parentUpdTrigger,
10908 : Relation trigrel)
10909 : {
10910 : HeapTuple parentConstrTup;
10911 : Form_pg_constraint parentConstr;
10912 : HeapTuple partcontup;
10913 : Form_pg_constraint partConstr;
10914 : ScanKeyData key;
10915 : SysScanDesc scan;
10916 : HeapTuple trigtup;
10917 : Oid insertTriggerOid,
10918 : updateTriggerOid;
1467 10919 :
1467 alvherre 10920 GIC 90 : parentConstrTup = SearchSysCache1(CONSTROID,
1467 alvherre 10921 ECB : ObjectIdGetDatum(parentConstrOid));
1435 tgl 10922 CBC 90 : if (!HeapTupleIsValid(parentConstrTup))
1467 alvherre 10923 LBC 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
1467 alvherre 10924 CBC 90 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
1542 alvherre 10925 ECB :
1467 10926 : /*
10927 : * Do some quick & easy initial checks. If any of these fail, we cannot
10928 : * use this constraint.
10929 : */
1467 alvherre 10930 CBC 90 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
1467 alvherre 10931 ECB : {
1467 alvherre 10932 LBC 0 : ReleaseSysCache(parentConstrTup);
10933 0 : return false;
1467 alvherre 10934 ECB : }
1467 alvherre 10935 CBC 255 : for (int i = 0; i < numfks; i++)
1467 alvherre 10936 ECB : {
1467 alvherre 10937 CBC 165 : if (fk->conkey[i] != mapped_conkey[i] ||
1467 alvherre 10938 GIC 165 : fk->confkey[i] != confkey[i] ||
10939 165 : fk->conpfeqop[i] != conpfeqop[i])
1467 alvherre 10940 ECB : {
1467 alvherre 10941 UIC 0 : ReleaseSysCache(parentConstrTup);
10942 0 : return false;
10943 : }
1467 alvherre 10944 ECB : }
10945 :
10946 : /*
10947 : * Looks good so far; do some more extensive checks. Presumably the check
10948 : * for 'convalidated' could be dropped, since we don't really care about
10949 : * that, but let's be careful for now.
10950 : */
1467 alvherre 10951 GIC 90 : partcontup = SearchSysCache1(CONSTROID,
10952 : ObjectIdGetDatum(fk->conoid));
1435 tgl 10953 90 : if (!HeapTupleIsValid(partcontup))
1435 tgl 10954 UIC 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
1467 alvherre 10955 CBC 90 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
10956 90 : if (OidIsValid(partConstr->conparentid) ||
1467 alvherre 10957 GBC 78 : !partConstr->convalidated ||
1467 alvherre 10958 GIC 78 : partConstr->condeferrable != parentConstr->condeferrable ||
10959 64 : partConstr->condeferred != parentConstr->condeferred ||
10960 64 : partConstr->confupdtype != parentConstr->confupdtype ||
10961 46 : partConstr->confdeltype != parentConstr->confdeltype ||
10962 46 : partConstr->confmatchtype != parentConstr->confmatchtype)
10963 : {
10964 51 : ReleaseSysCache(parentConstrTup);
1467 alvherre 10965 CBC 51 : ReleaseSysCache(partcontup);
1467 alvherre 10966 GIC 51 : return false;
10967 : }
10968 :
1467 alvherre 10969 CBC 39 : ReleaseSysCache(partcontup);
1467 alvherre 10970 GIC 39 : ReleaseSysCache(parentConstrTup);
10971 :
10972 : /*
10973 : * Looks good! Attach this constraint. The action triggers in the new
10974 : * partition become redundant -- the parent table already has equivalent
10975 : * ones, and those will be able to reach the partition. Remove the ones
10976 : * in the partition. We identify them because they have our constraint
10977 : * OID, as well as being on the referenced rel.
10978 : */
10979 39 : ScanKeyInit(&key,
10980 : Anum_pg_trigger_tgconstraint,
10981 : BTEqualStrategyNumber, F_OIDEQ,
10982 : ObjectIdGetDatum(fk->conoid));
10983 39 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
10984 : NULL, 1, &key);
10985 195 : while ((trigtup = systable_getnext(scan)) != NULL)
10986 : {
1467 alvherre 10987 CBC 156 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
1467 alvherre 10988 ECB : ObjectAddress trigger;
10989 :
1467 alvherre 10990 GIC 156 : if (trgform->tgconstrrelid != fk->conrelid)
1467 alvherre 10991 CBC 78 : continue;
10992 78 : if (trgform->tgrelid != fk->confrelid)
1467 alvherre 10993 UIC 0 : continue;
10994 :
10995 : /*
10996 : * The constraint is originally set up to contain this trigger as an
10997 : * implementation object, so there's a dependency record that links
10998 : * the two; however, since the trigger is no longer needed, we remove
10999 : * the dependency link in order to be able to drop the trigger while
11000 : * keeping the constraint intact.
11001 : */
1467 alvherre 11002 GIC 78 : deleteDependencyRecordsFor(TriggerRelationId,
11003 : trgform->oid,
11004 : false);
11005 : /* make dependency deletion visible to performDeletion */
11006 78 : CommandCounterIncrement();
11007 78 : ObjectAddressSet(trigger, TriggerRelationId,
1467 alvherre 11008 ECB : trgform->oid);
1467 alvherre 11009 GIC 78 : performDeletion(&trigger, DROP_RESTRICT, 0);
11010 : /* make trigger drop visible, in case the loop iterates */
11011 78 : CommandCounterIncrement();
1542 alvherre 11012 ECB : }
11013 :
1467 alvherre 11014 GIC 39 : systable_endscan(scan);
11015 :
11016 39 : ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
459 alvherre 11017 ECB :
11018 : /*
11019 : * Like the constraint, attach partition's "check" triggers to the
11020 : * corresponding parent triggers.
11021 : */
459 alvherre 11022 GIC 39 : GetForeignKeyCheckTriggers(trigrel,
11023 : fk->conoid, fk->confrelid, fk->conrelid,
11024 : &insertTriggerOid, &updateTriggerOid);
11025 39 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11026 39 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11027 : partRelid);
459 alvherre 11028 CBC 39 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11029 39 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11030 : partRelid);
459 alvherre 11031 ECB :
1467 alvherre 11032 GBC 39 : CommandCounterIncrement();
1467 alvherre 11033 GIC 39 : return true;
11034 : }
11035 :
11036 : /*
11037 : * GetForeignKeyActionTriggers
11038 : * Returns delete and update "action" triggers of the given relation
11039 : * belonging to the given constraint
11040 : */
11041 : static void
459 alvherre 11042 CBC 60 : GetForeignKeyActionTriggers(Relation trigrel,
11043 : Oid conoid, Oid confrelid, Oid conrelid,
11044 : Oid *deleteTriggerOid,
11045 : Oid *updateTriggerOid)
11046 : {
11047 : ScanKeyData key;
459 alvherre 11048 ECB : SysScanDesc scan;
11049 : HeapTuple trigtup;
11050 :
459 alvherre 11051 GIC 60 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
459 alvherre 11052 CBC 60 : ScanKeyInit(&key,
11053 : Anum_pg_trigger_tgconstraint,
459 alvherre 11054 ECB : BTEqualStrategyNumber, F_OIDEQ,
11055 : ObjectIdGetDatum(conoid));
11056 :
459 alvherre 11057 GIC 60 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11058 : NULL, 1, &key);
11059 270 : while ((trigtup = systable_getnext(scan)) != NULL)
11060 : {
11061 210 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11062 :
11063 210 : if (trgform->tgconstrrelid != conrelid)
11064 90 : continue;
11065 120 : if (trgform->tgrelid != confrelid)
459 alvherre 11066 UIC 0 : continue;
11067 : /* Only ever look at "action" triggers on the PK side. */
212 alvherre 11068 GIC 120 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
212 alvherre 11069 UIC 0 : continue;
459 alvherre 11070 GIC 120 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11071 : {
11072 60 : Assert(*deleteTriggerOid == InvalidOid);
11073 60 : *deleteTriggerOid = trgform->oid;
11074 : }
11075 60 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11076 : {
11077 60 : Assert(*updateTriggerOid == InvalidOid);
11078 60 : *updateTriggerOid = trgform->oid;
459 alvherre 11079 ECB : }
212 11080 : #ifndef USE_ASSERT_CHECKING
212 alvherre 11081 EUB : /* In an assert-enabled build, continue looking to find duplicates */
11082 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
459 alvherre 11083 ECB : break;
11084 : #endif
11085 : }
11086 :
459 alvherre 11087 GIC 60 : if (!OidIsValid(*deleteTriggerOid))
459 alvherre 11088 LBC 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
459 alvherre 11089 ECB : conoid);
459 alvherre 11090 GIC 60 : if (!OidIsValid(*updateTriggerOid))
459 alvherre 11091 UIC 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11092 : conoid);
11093 :
459 alvherre 11094 GIC 60 : systable_endscan(scan);
11095 60 : }
459 alvherre 11096 ECB :
11097 : /*
11098 : * GetForeignKeyCheckTriggers
11099 : * Returns insert and update "check" triggers of the given relation
11100 : * belonging to the given constraint
11101 : */
11102 : static void
459 alvherre 11103 GIC 277 : GetForeignKeyCheckTriggers(Relation trigrel,
459 alvherre 11104 ECB : Oid conoid, Oid confrelid, Oid conrelid,
11105 : Oid *insertTriggerOid,
11106 : Oid *updateTriggerOid)
11107 : {
11108 : ScanKeyData key;
11109 : SysScanDesc scan;
11110 : HeapTuple trigtup;
11111 :
459 alvherre 11112 GIC 277 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11113 277 : ScanKeyInit(&key,
11114 : Anum_pg_trigger_tgconstraint,
11115 : BTEqualStrategyNumber, F_OIDEQ,
459 alvherre 11116 ECB : ObjectIdGetDatum(conoid));
11117 :
459 alvherre 11118 GIC 277 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11119 : NULL, 1, &key);
11120 1199 : while ((trigtup = systable_getnext(scan)) != NULL)
11121 : {
11122 922 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11123 :
11124 922 : if (trgform->tgconstrrelid != confrelid)
11125 326 : continue;
11126 596 : if (trgform->tgrelid != conrelid)
459 alvherre 11127 LBC 0 : continue;
212 alvherre 11128 ECB : /* Only ever look at "check" triggers on the FK side. */
212 alvherre 11129 GIC 596 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
212 alvherre 11130 CBC 42 : continue;
459 alvherre 11131 GIC 554 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
459 alvherre 11132 ECB : {
459 alvherre 11133 GIC 277 : Assert(*insertTriggerOid == InvalidOid);
11134 277 : *insertTriggerOid = trgform->oid;
11135 : }
11136 277 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11137 : {
11138 277 : Assert(*updateTriggerOid == InvalidOid);
11139 277 : *updateTriggerOid = trgform->oid;
11140 : }
11141 : #ifndef USE_ASSERT_CHECKING
11142 : /* In an assert-enabled build, continue looking to find duplicates. */
459 alvherre 11143 ECB : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11144 : break;
212 11145 : #endif
11146 : }
11147 :
459 alvherre 11148 CBC 277 : if (!OidIsValid(*insertTriggerOid))
459 alvherre 11149 UIC 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
459 alvherre 11150 ECB : conoid);
459 alvherre 11151 CBC 277 : if (!OidIsValid(*updateTriggerOid))
459 alvherre 11152 UIC 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11153 : conoid);
11154 :
459 alvherre 11155 CBC 277 : systable_endscan(scan);
11156 277 : }
11157 :
3571 simon 11158 ECB : /*
11159 : * ALTER TABLE ALTER CONSTRAINT
11160 : *
11161 : * Update the attributes of a constraint.
11162 : *
11163 : * Currently only works for Foreign Key constraints.
2937 alvherre 11164 : *
11165 : * If the constraint is modified, returns its address; otherwise, return
11166 : * InvalidObjectAddress.
3571 simon 11167 : */
2937 alvherre 11168 : static ObjectAddress
704 alvherre 11169 CBC 33 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
704 alvherre 11170 ECB : bool recursing, LOCKMODE lockmode)
3571 simon 11171 : {
2898 tgl 11172 : Constraint *cmdcon;
11173 : Relation conrel;
11174 : Relation tgrel;
11175 : SysScanDesc scan;
1678 11176 : ScanKeyData skey[3];
11177 : HeapTuple contuple;
11178 : Form_pg_constraint currcon;
2937 alvherre 11179 : ObjectAddress address;
704 alvherre 11180 GIC 33 : List *otherrelids = NIL;
704 alvherre 11181 ECB : ListCell *lc;
11182 :
2264 andres 11183 CBC 33 : cmdcon = castNode(Constraint, cmd->def);
3571 simon 11184 ECB :
1539 andres 11185 CBC 33 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
704 alvherre 11186 33 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11187 :
3571 simon 11188 ECB : /*
11189 : * Find and check the target constraint
11190 : */
1678 tgl 11191 GIC 33 : ScanKeyInit(&skey[0],
3571 simon 11192 ECB : Anum_pg_constraint_conrelid,
11193 : BTEqualStrategyNumber, F_OIDEQ,
11194 : ObjectIdGetDatum(RelationGetRelid(rel)));
1678 tgl 11195 GIC 33 : ScanKeyInit(&skey[1],
11196 : Anum_pg_constraint_contypid,
1678 tgl 11197 ECB : BTEqualStrategyNumber, F_OIDEQ,
11198 : ObjectIdGetDatum(InvalidOid));
1678 tgl 11199 CBC 33 : ScanKeyInit(&skey[2],
11200 : Anum_pg_constraint_conname,
11201 : BTEqualStrategyNumber, F_NAMEEQ,
1678 tgl 11202 GIC 33 : CStringGetDatum(cmdcon->conname));
11203 33 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11204 : true, NULL, 3, skey);
11205 :
11206 : /* There can be at most one matching row */
11207 33 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
3571 simon 11208 UIC 0 : ereport(ERROR,
11209 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11210 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11211 : cmdcon->conname, RelationGetRelationName(rel))));
11212 :
1678 tgl 11213 CBC 33 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
3571 simon 11214 33 : if (currcon->contype != CONSTRAINT_FOREIGN)
3571 simon 11215 UIC 0 : ereport(ERROR,
11216 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3571 simon 11217 ECB : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11218 : cmdcon->conname, RelationGetRelationName(rel))));
11219 :
11220 : /*
11221 : * If it's not the topmost constraint, raise an error.
11222 : *
11223 : * Altering a non-topmost constraint leaves some triggers untouched, since
11224 : * they are not directly connected to this constraint; also, pg_dump would
11225 : * ignore the deferrability status of the individual constraint, since it
11226 : * only dumps topmost constraints. Avoid these problems by refusing this
704 alvherre 11227 : * operation and telling the user to alter the parent constraint instead.
11228 : */
704 alvherre 11229 CBC 33 : if (OidIsValid(currcon->conparentid))
704 alvherre 11230 ECB : {
11231 : HeapTuple tp;
704 alvherre 11232 CBC 6 : Oid parent = currcon->conparentid;
704 alvherre 11233 GIC 6 : char *ancestorname = NULL;
11234 6 : char *ancestortable = NULL;
704 alvherre 11235 ECB :
11236 : /* Loop to find the topmost constraint */
704 alvherre 11237 GIC 12 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
704 alvherre 11238 ECB : {
704 alvherre 11239 GIC 12 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
704 alvherre 11240 ECB :
11241 : /* If no parent, this is the constraint we want */
704 alvherre 11242 GIC 12 : if (!OidIsValid(contup->conparentid))
11243 : {
11244 6 : ancestorname = pstrdup(NameStr(contup->conname));
11245 6 : ancestortable = get_rel_name(contup->conrelid);
11246 6 : ReleaseSysCache(tp);
11247 6 : break;
11248 : }
11249 :
11250 6 : parent = contup->conparentid;
11251 6 : ReleaseSysCache(tp);
11252 : }
11253 :
11254 6 : ereport(ERROR,
11255 : (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11256 : cmdcon->conname, RelationGetRelationName(rel)),
11257 : ancestorname && ancestortable ?
704 alvherre 11258 ECB : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11259 : cmdcon->conname, ancestorname, ancestortable) : 0,
11260 : errhint("You may alter the constraint it derives from, instead.")));
11261 : }
11262 :
11263 : /*
11264 : * Do the actual catalog work. We can skip changing if already in the
11265 : * desired state, but not if a partitioned table: partitions need to be
11266 : * processed regardless, in case they had the constraint locally changed.
11267 : */
704 alvherre 11268 GIC 27 : address = InvalidObjectAddress;
11269 27 : if (currcon->condeferrable != cmdcon->deferrable ||
11270 3 : currcon->condeferred != cmdcon->initdeferred ||
704 alvherre 11271 UIC 0 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11272 : {
704 alvherre 11273 GIC 27 : if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11274 : &otherrelids, lockmode))
11275 27 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
704 alvherre 11276 ECB : }
11277 :
11278 : /*
11279 : * ATExecConstrRecurse already invalidated relcache for the relations
11280 : * having the constraint itself; here we also invalidate for relations
11281 : * that have any triggers that are part of the constraint.
11282 : */
704 alvherre 11283 GIC 69 : foreach(lc, otherrelids)
11284 42 : CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11285 :
11286 27 : systable_endscan(scan);
11287 :
11288 27 : table_close(tgrel, RowExclusiveLock);
11289 27 : table_close(conrel, RowExclusiveLock);
11290 :
11291 27 : return address;
11292 : }
11293 :
11294 : /*
11295 : * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11296 : * constraint is altered.
704 alvherre 11297 ECB : *
11298 : * *otherrelids is appended OIDs of relations containing affected triggers.
11299 : *
704 alvherre 11300 EUB : * Note that we must recurse even when the values are correct, in case
704 alvherre 11301 ECB : * indirect descendants have had their constraints altered locally.
11302 : * (This could be avoided if we forbade altering constraints in partitions
11303 : * but existing releases don't do that.)
11304 : */
11305 : static bool
704 alvherre 11306 GIC 60 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
704 alvherre 11307 ECB : Relation rel, HeapTuple contuple, List **otherrelids,
11308 : LOCKMODE lockmode)
704 alvherre 11309 EUB : {
11310 : Form_pg_constraint currcon;
11311 : Oid conoid;
704 alvherre 11312 ECB : Oid refrelid;
704 alvherre 11313 GIC 60 : bool changed = false;
704 alvherre 11314 ECB :
704 alvherre 11315 CBC 60 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11316 60 : conoid = currcon->oid;
704 alvherre 11317 GIC 60 : refrelid = currcon->confrelid;
704 alvherre 11318 EUB :
11319 : /*
11320 : * Update pg_constraint with the flags from cmdcon.
11321 : *
11322 : * If called to modify a constraint that's already in the desired state,
11323 : * silently do nothing.
11324 : */
3571 simon 11325 GIC 60 : if (currcon->condeferrable != cmdcon->deferrable ||
11326 3 : currcon->condeferred != cmdcon->initdeferred)
11327 : {
3571 simon 11328 ECB : HeapTuple copyTuple;
11329 : Form_pg_constraint copy_con;
704 alvherre 11330 : HeapTuple tgtuple;
3571 simon 11331 EUB : ScanKeyData tgkey;
3571 simon 11332 ECB : SysScanDesc tgscan;
11333 :
3571 simon 11334 CBC 60 : copyTuple = heap_copytuple(contuple);
11335 60 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11336 60 : copy_con->condeferrable = cmdcon->deferrable;
11337 60 : copy_con->condeferred = cmdcon->initdeferred;
2259 alvherre 11338 60 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
3571 simon 11339 ECB :
3571 simon 11340 GIC 60 : InvokeObjectPostAlterHook(ConstraintRelationId,
704 alvherre 11341 ECB : conoid, 0);
3571 simon 11342 :
3571 simon 11343 CBC 60 : heap_freetuple(copyTuple);
704 alvherre 11344 GIC 60 : changed = true;
11345 :
704 alvherre 11346 ECB : /* Make new constraint flags visible to others */
704 alvherre 11347 CBC 60 : CacheInvalidateRelcache(rel);
11348 :
11349 : /*
11350 : * Now we need to update the multiple entries in pg_trigger that
11351 : * implement the constraint.
11352 : */
3571 simon 11353 GIC 60 : ScanKeyInit(&tgkey,
11354 : Anum_pg_trigger_tgconstraint,
11355 : BTEqualStrategyNumber, F_OIDEQ,
704 alvherre 11356 ECB : ObjectIdGetDatum(conoid));
3571 simon 11357 GIC 60 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11358 : NULL, 1, &tgkey);
11359 234 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
3571 simon 11360 ECB : {
2356 tgl 11361 GIC 174 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
2898 tgl 11362 ECB : Form_pg_trigger copy_tg;
11363 : HeapTuple tgCopyTuple;
11364 :
11365 : /*
11366 : * Remember OIDs of other relation(s) involved in FK constraint.
2356 11367 : * (Note: it's likely that we could skip forcing a relcache inval
11368 : * for other rels that don't have a trigger whose properties
11369 : * change, but let's be conservative.)
2356 tgl 11370 EUB : */
2356 tgl 11371 GIC 174 : if (tgform->tgrelid != RelationGetRelid(rel))
704 alvherre 11372 84 : *otherrelids = list_append_unique_oid(*otherrelids,
11373 : tgform->tgrelid);
11374 :
11375 : /*
11376 : * Update deferrability of RI_FKey_noaction_del,
11377 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11378 : * triggers, but not others; see createForeignKeyActionTriggers
1467 alvherre 11379 ECB : * and CreateFKCheckTrigger.
11380 : */
2356 tgl 11381 GIC 174 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11382 141 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
2356 tgl 11383 CBC 99 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11384 54 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
2356 tgl 11385 GIC 9 : continue;
2356 tgl 11386 ECB :
186 drowley 11387 GNC 165 : tgCopyTuple = heap_copytuple(tgtuple);
11388 165 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11389 :
3571 simon 11390 GIC 165 : copy_tg->tgdeferrable = cmdcon->deferrable;
3571 simon 11391 CBC 165 : copy_tg->tginitdeferred = cmdcon->initdeferred;
186 drowley 11392 GNC 165 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
3571 simon 11393 ECB :
705 alvherre 11394 GIC 165 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11395 :
186 drowley 11396 GNC 165 : heap_freetuple(tgCopyTuple);
11397 : }
11398 :
3571 simon 11399 CBC 60 : systable_endscan(tgscan);
11400 : }
11401 :
704 alvherre 11402 ECB : /*
11403 : * If the table at either end of the constraint is partitioned, we need to
11404 : * recurse and handle every constraint that is a child of this one.
11405 : *
11406 : * (This assumes that the recurse flag is forcibly set for partitioned
11407 : * tables, and not set for legacy inheritance, though we don't check for
11408 : * that here.)
11409 : */
704 alvherre 11410 CBC 108 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
704 alvherre 11411 GIC 48 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11412 : {
11413 : ScanKeyData pkey;
11414 : SysScanDesc pscan;
11415 : HeapTuple childtup;
11416 :
11417 21 : ScanKeyInit(&pkey,
11418 : Anum_pg_constraint_conparentid,
704 alvherre 11419 ECB : BTEqualStrategyNumber, F_OIDEQ,
11420 : ObjectIdGetDatum(conoid));
11421 :
704 alvherre 11422 GIC 21 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11423 : true, NULL, 1, &pkey);
11424 :
11425 54 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11426 : {
11427 33 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
704 alvherre 11428 ECB : Relation childrel;
11429 :
704 alvherre 11430 GIC 33 : childrel = table_open(childcon->conrelid, lockmode);
11431 33 : ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11432 : otherrelids, lockmode);
11433 33 : table_close(childrel, NoLock);
2898 tgl 11434 ECB : }
11435 :
704 alvherre 11436 CBC 21 : systable_endscan(pscan);
11437 : }
3571 simon 11438 ECB :
704 alvherre 11439 GIC 60 : return changed;
3571 simon 11440 ECB : }
11441 :
4443 11442 : /*
4443 simon 11443 EUB : * ALTER TABLE VALIDATE CONSTRAINT
11444 : *
4330 alvherre 11445 ECB : * XXX The reason we handle recursion here rather than at Phase 1 is because
4330 alvherre 11446 EUB : * there's no good way to skip recursing when handling foreign keys: there is
4330 alvherre 11447 ECB : * no need to lock children in that case, yet we wouldn't be able to avoid
11448 : * doing so at that level.
2937 11449 : *
11450 : * Return value is the address of the validated constraint. If the constraint
11451 : * was already validated, InvalidObjectAddress is returned.
4443 simon 11452 : */
11453 : static ObjectAddress
999 drowley 11454 CBC 218 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
999 drowley 11455 ECB : bool recurse, bool recursing, LOCKMODE lockmode)
11456 : {
11457 : Relation conrel;
11458 : SysScanDesc scan;
11459 : ScanKeyData skey[3];
11460 : HeapTuple tuple;
11461 : Form_pg_constraint con;
11462 : ObjectAddress address;
11463 :
1539 andres 11464 CBC 218 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
4443 simon 11465 EUB :
11466 : /*
4443 tgl 11467 ECB : * Find and check the target constraint
4443 simon 11468 EUB : */
1678 tgl 11469 GIC 218 : ScanKeyInit(&skey[0],
11470 : Anum_pg_constraint_conrelid,
4443 simon 11471 ECB : BTEqualStrategyNumber, F_OIDEQ,
11472 : ObjectIdGetDatum(RelationGetRelid(rel)));
1678 tgl 11473 GIC 218 : ScanKeyInit(&skey[1],
11474 : Anum_pg_constraint_contypid,
11475 : BTEqualStrategyNumber, F_OIDEQ,
11476 : ObjectIdGetDatum(InvalidOid));
11477 218 : ScanKeyInit(&skey[2],
11478 : Anum_pg_constraint_conname,
11479 : BTEqualStrategyNumber, F_NAMEEQ,
1678 tgl 11480 ECB : CStringGetDatum(constrName));
1678 tgl 11481 GIC 218 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11482 : true, NULL, 3, skey);
11483 :
11484 : /* There can be at most one matching row */
11485 218 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
4443 tgl 11486 UIC 0 : ereport(ERROR,
11487 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11488 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
4330 alvherre 11489 ECB : constrName, RelationGetRelationName(rel))));
11490 :
1678 tgl 11491 GIC 218 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4330 alvherre 11492 218 : if (con->contype != CONSTRAINT_FOREIGN &&
11493 66 : con->contype != CONSTRAINT_CHECK)
4330 alvherre 11494 UIC 0 : ereport(ERROR,
4330 alvherre 11495 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11496 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
4382 bruce 11497 : constrName, RelationGetRelationName(rel))));
11498 :
4443 tgl 11499 CBC 218 : if (!con->convalidated)
11500 : {
999 drowley 11501 ECB : AlteredTableInfo *tab;
4330 alvherre 11502 : HeapTuple copyTuple;
11503 : Form_pg_constraint copy_con;
4443 simon 11504 EUB :
4330 alvherre 11505 GIC 209 : if (con->contype == CONSTRAINT_FOREIGN)
4330 alvherre 11506 ECB : {
999 drowley 11507 : NewConstraint *newcon;
11508 : Constraint *fkconstraint;
11509 :
11510 : /* Queue validation for phase 3 */
999 drowley 11511 CBC 149 : fkconstraint = makeNode(Constraint);
11512 : /* for now this is all we need */
11513 149 : fkconstraint->conname = constrName;
11514 :
11515 149 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11516 149 : newcon->name = constrName;
999 drowley 11517 GIC 149 : newcon->contype = CONSTR_FOREIGN;
11518 149 : newcon->refrelid = con->confrelid;
11519 149 : newcon->refindid = con->conindid;
11520 149 : newcon->conid = con->oid;
11521 149 : newcon->qual = (Node *) fkconstraint;
11522 :
11523 : /* Find or create work queue entry for this table */
11524 149 : tab = ATGetQueueEntry(wqueue, rel);
999 drowley 11525 CBC 149 : tab->constraints = lappend(tab->constraints, newcon);
4330 alvherre 11526 EUB :
11527 : /*
1831 alvherre 11528 ECB : * We disallow creating invalid foreign keys to or from
1831 alvherre 11529 EUB : * partitioned tables, so ignoring the recursion bit is okay.
11530 : */
11531 : }
4330 alvherre 11532 CBC 60 : else if (con->contype == CONSTRAINT_CHECK)
4330 alvherre 11533 ECB : {
4330 alvherre 11534 GIC 60 : List *children = NIL;
11535 : ListCell *child;
11536 : NewConstraint *newcon;
11537 : Datum val;
11538 : char *conbin;
11539 :
11540 : /*
11541 : * If we're recursing, the parent has already done this, so skip
11542 : * it. Also, if the constraint is a NO INHERIT constraint, we
11543 : * shouldn't try to look for it in the children.
11544 : */
2172 rhaas 11545 CBC 60 : if (!recursing && !con->connoinherit)
4330 alvherre 11546 GIC 33 : children = find_all_inheritors(RelationGetRelid(rel),
11547 : lockmode, NULL);
11548 :
11549 : /*
11550 : * For CHECK constraints, we must ensure that we only mark the
11551 : * constraint as validated on the parent if it's already validated
11552 : * on the children.
11553 : *
11554 : * We recurse before validating on the parent, to reduce risk of
11555 : * deadlocks.
4330 alvherre 11556 ECB : */
4330 alvherre 11557 GIC 117 : foreach(child, children)
11558 : {
3955 bruce 11559 CBC 57 : Oid childoid = lfirst_oid(child);
11560 : Relation childrel;
4443 simon 11561 ECB :
4330 alvherre 11562 CBC 57 : if (childoid == RelationGetRelid(rel))
4330 alvherre 11563 GIC 33 : continue;
11564 :
11565 : /*
11566 : * If we are told not to recurse, there had better not be any
2397 rhaas 11567 ECB : * child tables, because we can't mark the constraint on the
11568 : * parent valid unless it is valid for all child tables.
11569 : */
4330 alvherre 11570 GIC 24 : if (!recurse)
4330 alvherre 11571 LBC 0 : ereport(ERROR,
11572 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11573 : errmsg("constraint must be validated on child tables too")));
11574 :
4330 alvherre 11575 ECB : /* find_all_inheritors already got lock */
1539 andres 11576 GIC 24 : childrel = table_open(childoid, NoLock);
11577 :
999 drowley 11578 CBC 24 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
4330 alvherre 11579 ECB : true, lockmode);
1539 andres 11580 GIC 24 : table_close(childrel, NoLock);
11581 : }
11582 :
999 drowley 11583 ECB : /* Queue validation for phase 3 */
999 drowley 11584 GBC 60 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
999 drowley 11585 GIC 60 : newcon->name = constrName;
11586 60 : newcon->contype = CONSTR_CHECK;
11587 60 : newcon->refrelid = InvalidOid;
11588 60 : newcon->refindid = InvalidOid;
999 drowley 11589 CBC 60 : newcon->conid = con->oid;
999 drowley 11590 ECB :
15 dgustafsson 11591 GNC 60 : val = SysCacheGetAttrNotNull(CONSTROID, tuple,
11592 : Anum_pg_constraint_conbin);
999 drowley 11593 GIC 60 : conbin = TextDatumGetCString(val);
11594 60 : newcon->qual = (Node *) stringToNode(conbin);
11595 :
11596 : /* Find or create work queue entry for this table */
11597 60 : tab = ATGetQueueEntry(wqueue, rel);
11598 60 : tab->constraints = lappend(tab->constraints, newcon);
11599 :
11600 : /*
11601 : * Invalidate relcache so that others see the new validated
4330 alvherre 11602 ECB : * constraint.
11603 : */
4330 alvherre 11604 GIC 60 : CacheInvalidateRelcache(rel);
4330 alvherre 11605 ECB : }
4443 simon 11606 :
11607 : /*
11608 : * Now update the catalog, while we have the door open.
11609 : */
4330 alvherre 11610 CBC 209 : copyTuple = heap_copytuple(tuple);
4330 alvherre 11611 GIC 209 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
4443 simon 11612 CBC 209 : copy_con->convalidated = true;
2259 alvherre 11613 GIC 209 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11614 :
1601 andres 11615 CBC 209 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
11616 :
4443 simon 11617 209 : heap_freetuple(copyTuple);
2937 alvherre 11618 ECB :
1601 andres 11619 CBC 209 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
4443 simon 11620 ECB : }
11621 : else
2878 bruce 11622 GIC 9 : address = InvalidObjectAddress; /* already validated */
4443 simon 11623 ECB :
4443 simon 11624 CBC 218 : systable_endscan(scan);
11625 :
1539 andres 11626 GIC 218 : table_close(conrel, RowExclusiveLock);
2937 alvherre 11627 ECB :
2937 alvherre 11628 GIC 218 : return address;
11629 : }
11630 :
11631 :
11632 : /*
11633 : * transformColumnNameList - transform list of column names
11634 : *
11635 : * Lookup each name and return its attnum and, optionally, type OID
11636 : *
11637 : * Note: the name of this function suggests that it's general-purpose,
11638 : * but actually it's only used to look up names appearing in foreign-key
11639 : * clauses. The error messages would need work to use it in other cases,
11640 : * and perhaps the validity checks as well.
7504 tgl 11641 ECB : */
11642 : static int
7504 tgl 11643 CBC 2584 : transformColumnNameList(Oid relId, List *colList,
7504 tgl 11644 EUB : int16 *attnums, Oid *atttypids)
11645 : {
6892 neilc 11646 ECB : ListCell *l;
11647 : int attnum;
7504 tgl 11648 :
7504 tgl 11649 GIC 2584 : attnum = 0;
11650 4548 : foreach(l, colList)
11651 : {
11652 1997 : char *attname = strVal(lfirst(l));
11653 : HeapTuple atttuple;
11654 : Form_pg_attribute attform;
11655 :
7504 tgl 11656 CBC 1997 : atttuple = SearchSysCacheAttName(relId, attname);
11657 1997 : if (!HeapTupleIsValid(atttuple))
7203 tgl 11658 GIC 27 : ereport(ERROR,
7203 tgl 11659 ECB : (errcode(ERRCODE_UNDEFINED_COLUMN),
11660 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11661 : attname)));
9 tgl 11662 CBC 1970 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
9 tgl 11663 GIC 1970 : if (attform->attnum < 0)
9 tgl 11664 CBC 6 : ereport(ERROR,
11665 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11666 : errmsg("system columns cannot be used in foreign keys")));
7504 tgl 11667 GIC 1964 : if (attnum >= INDEX_MAX_KEYS)
7203 tgl 11668 UIC 0 : ereport(ERROR,
11669 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
11670 : errmsg("cannot have more than %d keys in a foreign key",
11671 : INDEX_MAX_KEYS)));
9 tgl 11672 GIC 1964 : attnums[attnum] = attform->attnum;
487 peter 11673 1964 : if (atttypids != NULL)
9 tgl 11674 1949 : atttypids[attnum] = attform->atttypid;
7504 11675 1964 : ReleaseSysCache(atttuple);
11676 1964 : attnum++;
11677 : }
11678 :
7504 tgl 11679 CBC 2551 : return attnum;
11680 : }
11681 :
11682 : /*
11683 : * transformFkeyGetPrimaryKey -
11684 : *
11685 : * Look up the names, attnums, and types of the primary key attributes
3260 bruce 11686 ECB : * for the pkrel. Also return the index OID and index opclasses of the
11687 : * index supporting the primary key.
6966 tgl 11688 : *
3260 bruce 11689 : * All parameters except pkrel are output parameters. Also, the function
6966 tgl 11690 : * return value is the number of attributes in the primary key.
11691 : *
11692 : * Used when the column list in the REFERENCES specification is omitted.
11693 : */
11694 : static int
7504 tgl 11695 GIC 458 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
11696 : List **attnamelist,
11697 : int16 *attnums, Oid *atttypids,
6966 tgl 11698 ECB : Oid *opclasses)
7504 11699 : {
11700 : List *indexoidlist;
11701 : ListCell *indexoidscan;
7504 tgl 11702 GIC 458 : HeapTuple indexTuple = NULL;
11703 458 : Form_pg_index indexStruct = NULL;
11704 : Datum indclassDatum;
11705 : oidvector *indclass;
7504 tgl 11706 ECB : int i;
11707 :
11708 : /*
6385 bruce 11709 : * Get the list of index OIDs for the table from the relcache, and look up
11710 : * each one in the pg_index syscache until we find one marked primary key
11711 : * (hopefully there isn't more than one such). Insist it's valid, too.
7504 tgl 11712 : */
6406 tgl 11713 GIC 458 : *indexOid = InvalidOid;
11714 :
7504 tgl 11715 CBC 458 : indexoidlist = RelationGetIndexList(pkrel);
7504 tgl 11716 ECB :
7504 tgl 11717 GIC 461 : foreach(indexoidscan, indexoidlist)
11718 : {
6892 neilc 11719 CBC 461 : Oid indexoid = lfirst_oid(indexoidscan);
11720 :
4802 rhaas 11721 GIC 461 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7504 tgl 11722 461 : if (!HeapTupleIsValid(indexTuple))
7203 tgl 11723 UIC 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
7504 tgl 11724 GIC 461 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
1564 peter_e 11725 CBC 461 : if (indexStruct->indisprimary && indexStruct->indisvalid)
11726 : {
11727 : /*
11728 : * Refuse to use a deferrable primary key. This is per SQL spec,
4790 bruce 11729 ECB : * and there would be a lot of interesting semantic problems if we
11730 : * tried to allow it.
5002 tgl 11731 : */
5002 tgl 11732 GIC 458 : if (!indexStruct->indimmediate)
5002 tgl 11733 LBC 0 : ereport(ERROR,
11734 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11735 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11736 : RelationGetRelationName(pkrel))));
11737 :
7504 tgl 11738 GIC 458 : *indexOid = indexoid;
11739 458 : break;
11740 : }
11741 3 : ReleaseSysCache(indexTuple);
11742 : }
7504 tgl 11743 ECB :
6892 neilc 11744 CBC 458 : list_free(indexoidlist);
11745 :
11746 : /*
11747 : * Check that we found it
11748 : */
6406 tgl 11749 GIC 458 : if (!OidIsValid(*indexOid))
7203 tgl 11750 UIC 0 : ereport(ERROR,
11751 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11752 : errmsg("there is no primary key for referenced table \"%s\"",
6385 bruce 11753 ECB : RelationGetRelationName(pkrel))));
7504 tgl 11754 :
6585 11755 : /* Must get indclass the hard way */
15 dgustafsson 11756 GNC 458 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11757 : Anum_pg_index_indclass);
6585 tgl 11758 CBC 458 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
6585 tgl 11759 ECB :
11760 : /*
7188 bruce 11761 : * Now build the list of PK attributes from the indkey definition (we
11762 : * assume a primary key cannot have expressional elements)
7504 tgl 11763 : */
7504 tgl 11764 GIC 458 : *attnamelist = NIL;
1828 teodor 11765 CBC 1032 : for (i = 0; i < indexStruct->indnkeyatts; i++)
11766 : {
6585 tgl 11767 574 : int pkattno = indexStruct->indkey.values[i];
11768 :
7504 tgl 11769 GIC 574 : attnums[i] = pkattno;
7504 tgl 11770 CBC 574 : atttypids[i] = attnumTypeId(pkrel, pkattno);
6585 tgl 11771 GIC 574 : opclasses[i] = indclass->values[i];
7504 11772 574 : *attnamelist = lappend(*attnamelist,
2118 11773 574 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
11774 : }
11775 :
7504 11776 458 : ReleaseSysCache(indexTuple);
11777 :
11778 458 : return i;
11779 : }
11780 :
7504 tgl 11781 ECB : /*
11782 : * transformFkeyCheckAttrs -
11783 : *
11784 : * Make sure that the attributes of a referenced table belong to a unique
11785 : * (or primary key) constraint. Return the OID of the index supporting
11786 : * the constraint, as well as the opclasses associated with the index
11787 : * columns.
11788 : */
11789 : static Oid
7504 tgl 11790 GIC 532 : transformFkeyCheckAttrs(Relation pkrel,
11791 : int numattrs, int16 *attnums,
11792 : Oid *opclasses) /* output parameter */
7504 tgl 11793 ECB : {
7504 tgl 11794 GIC 532 : Oid indexoid = InvalidOid;
11795 532 : bool found = false;
4988 tgl 11796 CBC 532 : bool found_deferrable = false;
11797 : List *indexoidlist;
6892 neilc 11798 ECB : ListCell *indexoidscan;
11799 : int i,
11800 : j;
3165 tgl 11801 :
11802 : /*
11803 : * Reject duplicate appearances of columns in the referenced-columns list.
11804 : * Such a case is forbidden by the SQL standard, and even if we thought it
11805 : * useful to allow it, there would be ambiguity about how to match the
11806 : * list to unique indexes (in particular, it'd be unclear which index
11807 : * opclass goes with which FK column).
11808 : */
3165 tgl 11809 GIC 1206 : for (i = 0; i < numattrs; i++)
3165 tgl 11810 ECB : {
3165 tgl 11811 GIC 837 : for (j = i + 1; j < numattrs; j++)
11812 : {
11813 163 : if (attnums[i] == attnums[j])
3165 tgl 11814 UIC 0 : ereport(ERROR,
11815 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11816 : errmsg("foreign key referenced-columns list must not contain duplicates")));
11817 : }
11818 : }
11819 :
11820 : /*
11821 : * Get the list of index OIDs for the table from the relcache, and look up
11822 : * each one in the pg_index syscache, and match unique indexes to the list
11823 : * of attnums we are given.
11824 : */
7504 tgl 11825 CBC 532 : indexoidlist = RelationGetIndexList(pkrel);
11826 :
7504 tgl 11827 GIC 622 : foreach(indexoidscan, indexoidlist)
11828 : {
11829 : HeapTuple indexTuple;
11830 : Form_pg_index indexStruct;
11831 :
6892 neilc 11832 616 : indexoid = lfirst_oid(indexoidscan);
4802 rhaas 11833 616 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7504 tgl 11834 616 : if (!HeapTupleIsValid(indexTuple))
7203 tgl 11835 LBC 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
7504 tgl 11836 GIC 616 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11837 :
11838 : /*
11839 : * Must have the right number of columns; must be unique and not a
3784 tgl 11840 ECB : * partial index; forget it if there are any expressions, too. Invalid
11841 : * indexes are out as well.
11842 : */
1828 teodor 11843 GIC 616 : if (indexStruct->indnkeyatts == numattrs &&
4988 tgl 11844 CBC 562 : indexStruct->indisunique &&
1564 peter_e 11845 GIC 1110 : indexStruct->indisvalid &&
1838 andrew 11846 1110 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
11847 555 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
7504 tgl 11848 ECB : {
11849 : Datum indclassDatum;
11850 : oidvector *indclass;
6585 11851 :
11852 : /* Must get indclass the hard way */
15 dgustafsson 11853 GNC 555 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11854 : Anum_pg_index_indclass);
6585 tgl 11855 GBC 555 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
11856 :
11857 : /*
11858 : * The given attnum list may match the index columns in any order.
11859 : * Check for a match, and extract the appropriate opclasses while
3165 tgl 11860 ECB : * we're at it.
11861 : *
11862 : * We know that attnums[] is duplicate-free per the test at the
3165 tgl 11863 EUB : * start of this function, and we checked above that the number of
11864 : * index columns agrees, so if we find a match for each attnums[]
11865 : * entry then we must have a one-to-one match in some order.
11866 : */
7256 tgl 11867 GIC 1223 : for (i = 0; i < numattrs; i++)
7256 tgl 11868 ECB : {
7256 tgl 11869 GIC 697 : found = false;
11870 889 : for (j = 0; j < numattrs; j++)
11871 : {
6585 11872 860 : if (attnums[i] == indexStruct->indkey.values[j])
11873 : {
3165 tgl 11874 CBC 668 : opclasses[i] = indclass->values[j];
7256 tgl 11875 GIC 668 : found = true;
11876 668 : break;
11877 : }
11878 : }
11879 697 : if (!found)
7256 tgl 11880 CBC 29 : break;
11881 : }
4988 tgl 11882 ECB :
11883 : /*
4790 bruce 11884 : * Refuse to use a deferrable unique/primary key. This is per SQL
11885 : * spec, and there would be a lot of interesting semantic problems
11886 : * if we tried to allow it.
4988 tgl 11887 : */
4988 tgl 11888 CBC 555 : if (found && !indexStruct->indimmediate)
4988 tgl 11889 ECB : {
11890 : /*
11891 : * Remember that we found an otherwise matching index, so that
11892 : * we can generate a more appropriate error message.
11893 : */
4988 tgl 11894 LBC 0 : found_deferrable = true;
4988 tgl 11895 UIC 0 : found = false;
11896 : }
11897 : }
7504 tgl 11898 GIC 616 : ReleaseSysCache(indexTuple);
11899 616 : if (found)
11900 526 : break;
7576 tgl 11901 ECB : }
11902 :
7504 tgl 11903 CBC 532 : if (!found)
11904 : {
4988 tgl 11905 GIC 6 : if (found_deferrable)
4988 tgl 11906 UIC 0 : ereport(ERROR,
11907 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11908 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
11909 : RelationGetRelationName(pkrel))));
11910 : else
4988 tgl 11911 GIC 6 : ereport(ERROR,
11912 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11913 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
4988 tgl 11914 ECB : RelationGetRelationName(pkrel))));
11915 : }
11916 :
6892 neilc 11917 GIC 526 : list_free(indexoidlist);
11918 :
7504 tgl 11919 526 : return indexoid;
11920 : }
11921 :
11922 : /*
11923 : * findFkeyCast -
11924 : *
11925 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
4059 alvherre 11926 ECB : * Caller has equal regard for binary coercibility and for an exact match.
11927 : */
11928 : static CoercionPathType
4059 alvherre 11929 GIC 6 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
11930 : {
4059 alvherre 11931 ECB : CoercionPathType ret;
11932 :
4059 alvherre 11933 GIC 6 : if (targetTypeId == sourceTypeId)
11934 : {
11935 6 : ret = COERCION_PATH_RELABELTYPE;
11936 6 : *funcid = InvalidOid;
11937 : }
11938 : else
4059 alvherre 11939 ECB : {
4059 alvherre 11940 UBC 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
11941 : COERCION_IMPLICIT, funcid);
4059 alvherre 11942 UIC 0 : if (ret == COERCION_PATH_NONE)
11943 : /* A previously-relied-upon cast is now gone. */
11944 0 : elog(ERROR, "could not find cast from %u to %u",
4059 alvherre 11945 ECB : sourceTypeId, targetTypeId);
11946 : }
11947 :
4059 alvherre 11948 GIC 6 : return ret;
4059 alvherre 11949 ECB : }
11950 :
11951 : /*
11952 : * Permissions checks on the referenced table for ADD FOREIGN KEY
2200 tgl 11953 : *
11954 : * Note: we have already checked that the user owns the referencing table,
11955 : * else we'd have failed much earlier; no additional checks are needed for it.
11956 : */
5190 11957 : static void
5190 tgl 11958 CBC 984 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
11959 : {
11960 984 : Oid roleid = GetUserId();
11961 : AclResult aclresult;
5190 tgl 11962 ECB : int i;
11963 :
11964 : /* Okay if we have relation-level REFERENCES permission */
5190 tgl 11965 GIC 984 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
5190 tgl 11966 ECB : ACL_REFERENCES);
5190 tgl 11967 CBC 984 : if (aclresult == ACLCHECK_OK)
5190 tgl 11968 GIC 984 : return;
11969 : /* Else we must have REFERENCES on each column */
5190 tgl 11970 UIC 0 : for (i = 0; i < natts; i++)
11971 : {
11972 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
5190 tgl 11973 ECB : roleid, ACL_REFERENCES);
5190 tgl 11974 UIC 0 : if (aclresult != ACLCHECK_OK)
1954 peter_e 11975 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
5190 tgl 11976 0 : RelationGetRelationName(rel));
11977 : }
11978 : }
5190 tgl 11979 ECB :
7576 11980 : /*
11981 : * Scan the existing rows in a table to verify they meet a proposed FK
11982 : * constraint.
11983 : *
4638 simon 11984 : * Caller must have opened and locked both relations appropriately.
11985 : */
7576 tgl 11986 : static void
4443 simon 11987 GIC 466 : validateForeignKeyConstraint(char *conname,
7576 tgl 11988 ECB : Relation rel,
11989 : Relation pkrel,
11990 : Oid pkindOid,
5898 11991 : Oid constraintOid)
11992 : {
1490 andres 11993 : TupleTableSlot *slot;
11994 : TableScanDesc scan;
267 peter 11995 GNC 466 : Trigger trig = {0};
11996 : Snapshot snapshot;
1463 andres 11997 ECB : MemoryContext oldcxt;
11998 : MemoryContext perTupCxt;
11999 :
4439 rhaas 12000 GIC 466 : ereport(DEBUG1,
12001 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12002 :
12003 : /*
12004 : * Build a trigger call structure; we'll need it either way.
12005 : */
7576 tgl 12006 466 : trig.tgoid = InvalidOid;
4443 simon 12007 466 : trig.tgname = conname;
5865 JanWieck 12008 466 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
2062 peter_e 12009 466 : trig.tgisinternal = true;
7576 tgl 12010 466 : trig.tgconstrrelid = RelationGetRelid(pkrel);
5003 tgl 12011 CBC 466 : trig.tgconstrindid = pkindOid;
5898 tgl 12012 GIC 466 : trig.tgconstraint = constraintOid;
2062 peter_e 12013 466 : trig.tgdeferrable = false;
12014 466 : trig.tginitdeferred = false;
12015 : /* we needn't fill in remaining fields */
12016 :
5898 tgl 12017 ECB : /*
2062 peter_e 12018 : * See if we can do it with a single LEFT JOIN query. A false result
12019 : * indicates we must proceed with the fire-the-trigger method.
5898 tgl 12020 : */
5898 tgl 12021 GIC 466 : if (RI_Initial_Check(&trig, rel, pkrel))
12022 432 : return;
12023 :
5898 tgl 12024 ECB : /*
12025 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12026 : * if that tuple had just been inserted. If any of those fail, it should
12027 : * ereport(ERROR) and that's that.
12028 : */
3568 rhaas 12029 GIC 6 : snapshot = RegisterSnapshot(GetLatestSnapshot());
1490 andres 12030 CBC 6 : slot = table_slot_create(rel, NULL);
12031 6 : scan = table_beginscan(rel, snapshot, 0, NULL);
7664 tgl 12032 ECB :
1463 andres 12033 GIC 6 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12034 : "validateForeignKeyConstraint",
1463 andres 12035 ECB : ALLOCSET_SMALL_SIZES);
1463 andres 12036 GBC 6 : oldcxt = MemoryContextSwitchTo(perTupCxt);
12037 :
1490 andres 12038 GIC 36 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12039 : {
1534 andres 12040 CBC 33 : LOCAL_FCINFO(fcinfo, 0);
1140 peter 12041 33 : TriggerData trigdata = {0};
7664 tgl 12042 ECB :
1463 andres 12043 CBC 33 : CHECK_FOR_INTERRUPTS();
1463 andres 12044 ECB :
12045 : /*
12046 : * Make a call to the trigger function
7576 tgl 12047 : *
12048 : * No parameters are passed, but we do set a context
12049 : */
1534 andres 12050 GIC 165 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12051 :
12052 : /*
12053 : * We assume RI_FKey_check_ins won't look at flinfo...
12054 : */
7118 bruce 12055 33 : trigdata.type = T_TriggerData;
12056 33 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12057 33 : trigdata.tg_relation = rel;
1464 tgl 12058 33 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
1490 andres 12059 33 : trigdata.tg_trigslot = slot;
7118 bruce 12060 33 : trigdata.tg_trigger = &trig;
12061 :
1534 andres 12062 33 : fcinfo->context = (Node *) &trigdata;
7576 tgl 12063 ECB :
1534 andres 12064 GIC 33 : RI_FKey_check_ins(fcinfo);
12065 :
1463 12066 30 : MemoryContextReset(perTupCxt);
12067 : }
12068 :
12069 3 : MemoryContextSwitchTo(oldcxt);
1463 andres 12070 CBC 3 : MemoryContextDelete(perTupCxt);
1490 12071 3 : table_endscan(scan);
3568 rhaas 12072 GIC 3 : UnregisterSnapshot(snapshot);
1490 andres 12073 3 : ExecDropSingleTupleTableSlot(slot);
12074 : }
12075 :
12076 : /*
12077 : * CreateFKCheckTrigger
12078 : * Creates the insert (on_insert=true) or update "check" trigger that
12079 : * implements a given foreign key
12080 : *
459 alvherre 12081 ECB : * Returns the OID of the so created trigger.
12082 : */
12083 : static Oid
3338 rhaas 12084 GIC 2448 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
459 alvherre 12085 ECB : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12086 : bool on_insert)
7576 tgl 12087 : {
12088 : ObjectAddress trigAddress;
12089 : CreateTrigStmt *fk_trigger;
12090 :
4183 tgl 12091 EUB : /*
4183 tgl 12092 ECB : * Note: for a self-referential FK (referencing and referenced tables are
12093 : * the same), it is important that the ON UPDATE action fires before the
12094 : * CHECK action, since both triggers will fire on the same row during an
12095 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12096 : * state of the row. Triggers fire in name order, so we ensure this by
12097 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12098 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12099 : */
7576 tgl 12100 CBC 2448 : fk_trigger = makeNode(CreateTrigStmt);
876 tgl 12101 GBC 2448 : fk_trigger->replace = false;
876 tgl 12102 GIC 2448 : fk_trigger->isconstraint = true;
4183 12103 2448 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
3338 rhaas 12104 2448 : fk_trigger->relation = NULL;
12105 :
6523 neilc 12106 ECB : /* Either ON INSERT or ON UPDATE */
6523 neilc 12107 CBC 2448 : if (on_insert)
12108 : {
12109 1224 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
5043 tgl 12110 GIC 1224 : fk_trigger->events = TRIGGER_TYPE_INSERT;
12111 : }
6523 neilc 12112 ECB : else
12113 : {
6523 neilc 12114 GIC 1224 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
5043 tgl 12115 1224 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12116 : }
7576 tgl 12117 ECB :
876 tgl 12118 GBC 2448 : fk_trigger->args = NIL;
876 tgl 12119 GIC 2448 : fk_trigger->row = true;
12120 2448 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
4925 12121 2448 : fk_trigger->columns = NIL;
4888 12122 2448 : fk_trigger->whenClause = NULL;
876 12123 2448 : fk_trigger->transitionRels = NIL;
7576 tgl 12124 CBC 2448 : fk_trigger->deferrable = fkconstraint->deferrable;
7576 tgl 12125 GIC 2448 : fk_trigger->initdeferred = fkconstraint->initdeferred;
3338 rhaas 12126 CBC 2448 : fk_trigger->constrrel = NULL;
12127 :
459 alvherre 12128 GIC 2448 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12129 : constraintOid, indexOid, InvalidOid,
12130 : parentTrigOid, NULL, true, false);
12131 :
6523 neilc 12132 ECB : /* Make changes-so-far visible */
6523 neilc 12133 CBC 2448 : CommandCounterIncrement();
12134 :
459 alvherre 12135 2448 : return trigAddress.objectId;
12136 : }
6523 neilc 12137 ECB :
12138 : /*
1831 alvherre 12139 : * createForeignKeyActionTriggers
12140 : * Create the referenced-side "action" triggers that implement a foreign
12141 : * key.
12142 : *
12143 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
459 12144 : * *updateTrigOid.
12145 : */
6523 neilc 12146 : static void
1831 alvherre 12147 GIC 1232 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12148 : Oid constraintOid, Oid indexOid,
12149 : Oid parentDelTrigger, Oid parentUpdTrigger,
12150 : Oid *deleteTrigOid, Oid *updateTrigOid)
12151 : {
12152 : CreateTrigStmt *fk_trigger;
12153 : ObjectAddress trigAddress;
12154 :
12155 : /*
12156 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12157 : * DELETE action on the referenced table.
6913 tgl 12158 ECB : */
6913 tgl 12159 GIC 1232 : fk_trigger = makeNode(CreateTrigStmt);
876 12160 1232 : fk_trigger->replace = false;
12161 1232 : fk_trigger->isconstraint = true;
4183 tgl 12162 CBC 1232 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
3338 rhaas 12163 1232 : fk_trigger->relation = NULL;
876 tgl 12164 1232 : fk_trigger->args = NIL;
6913 tgl 12165 GIC 1232 : fk_trigger->row = true;
4564 12166 1232 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
5043 12167 1232 : fk_trigger->events = TRIGGER_TYPE_DELETE;
4925 12168 1232 : fk_trigger->columns = NIL;
4888 12169 1232 : fk_trigger->whenClause = NULL;
876 12170 1232 : fk_trigger->transitionRels = NIL;
3338 rhaas 12171 1232 : fk_trigger->constrrel = NULL;
6913 tgl 12172 1232 : switch (fkconstraint->fk_del_action)
12173 : {
12174 927 : case FKCONSTR_ACTION_NOACTION:
6744 12175 927 : fk_trigger->deferrable = fkconstraint->deferrable;
12176 927 : fk_trigger->initdeferred = fkconstraint->initdeferred;
6913 tgl 12177 CBC 927 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
6913 tgl 12178 GIC 927 : break;
6913 tgl 12179 CBC 15 : case FKCONSTR_ACTION_RESTRICT:
6913 tgl 12180 GIC 15 : fk_trigger->deferrable = false;
6913 tgl 12181 CBC 15 : fk_trigger->initdeferred = false;
6913 tgl 12182 GBC 15 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
6913 tgl 12183 GIC 15 : break;
12184 218 : case FKCONSTR_ACTION_CASCADE:
6744 12185 218 : fk_trigger->deferrable = false;
12186 218 : fk_trigger->initdeferred = false;
6913 12187 218 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12188 218 : break;
12189 42 : case FKCONSTR_ACTION_SETNULL:
6744 12190 42 : fk_trigger->deferrable = false;
12191 42 : fk_trigger->initdeferred = false;
6913 12192 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
6913 tgl 12193 CBC 42 : break;
6913 tgl 12194 GIC 30 : case FKCONSTR_ACTION_SETDEFAULT:
6744 tgl 12195 CBC 30 : fk_trigger->deferrable = false;
6744 tgl 12196 GIC 30 : fk_trigger->initdeferred = false;
6913 12197 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12198 30 : break;
6913 tgl 12199 UIC 0 : default:
6913 tgl 12200 LBC 0 : elog(ERROR, "unrecognized FK action type: %d",
6913 tgl 12201 ECB : (int) fkconstraint->fk_del_action);
12202 : break;
6913 tgl 12203 EUB : }
6913 tgl 12204 ECB :
459 alvherre 12205 GIC 1232 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12206 : RelationGetRelid(rel),
12207 : constraintOid, indexOid, InvalidOid,
12208 : parentDelTrigger, NULL, true, false);
12209 1232 : if (deleteTrigOid)
12210 1202 : *deleteTrigOid = trigAddress.objectId;
6913 tgl 12211 ECB :
12212 : /* Make changes-so-far visible */
6913 tgl 12213 CBC 1232 : CommandCounterIncrement();
6913 tgl 12214 ECB :
12215 : /*
12216 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12217 : * UPDATE action on the referenced table.
12218 : */
6913 tgl 12219 GIC 1232 : fk_trigger = makeNode(CreateTrigStmt);
876 12220 1232 : fk_trigger->replace = false;
876 tgl 12221 CBC 1232 : fk_trigger->isconstraint = true;
4183 tgl 12222 GIC 1232 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
3338 rhaas 12223 CBC 1232 : fk_trigger->relation = NULL;
876 tgl 12224 GIC 1232 : fk_trigger->args = NIL;
6913 12225 1232 : fk_trigger->row = true;
4564 12226 1232 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
5043 12227 1232 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
4925 12228 1232 : fk_trigger->columns = NIL;
4888 12229 1232 : fk_trigger->whenClause = NULL;
876 12230 1232 : fk_trigger->transitionRels = NIL;
3338 rhaas 12231 1232 : fk_trigger->constrrel = NULL;
6913 tgl 12232 1232 : switch (fkconstraint->fk_upd_action)
12233 : {
12234 1021 : case FKCONSTR_ACTION_NOACTION:
6744 tgl 12235 CBC 1021 : fk_trigger->deferrable = fkconstraint->deferrable;
6744 tgl 12236 GIC 1021 : fk_trigger->initdeferred = fkconstraint->initdeferred;
6913 tgl 12237 CBC 1021 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12238 1021 : break;
6913 tgl 12239 GIC 15 : case FKCONSTR_ACTION_RESTRICT:
6913 tgl 12240 CBC 15 : fk_trigger->deferrable = false;
6913 tgl 12241 GIC 15 : fk_trigger->initdeferred = false;
6913 tgl 12242 CBC 15 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12243 15 : break;
12244 144 : case FKCONSTR_ACTION_CASCADE:
6744 tgl 12245 GIC 144 : fk_trigger->deferrable = false;
12246 144 : fk_trigger->initdeferred = false;
6913 tgl 12247 CBC 144 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12248 144 : break;
6913 tgl 12249 GIC 31 : case FKCONSTR_ACTION_SETNULL:
6744 12250 31 : fk_trigger->deferrable = false;
12251 31 : fk_trigger->initdeferred = false;
6913 12252 31 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12253 31 : break;
12254 21 : case FKCONSTR_ACTION_SETDEFAULT:
6744 12255 21 : fk_trigger->deferrable = false;
6744 tgl 12256 CBC 21 : fk_trigger->initdeferred = false;
6913 tgl 12257 GIC 21 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12258 21 : break;
6913 tgl 12259 UIC 0 : default:
12260 0 : elog(ERROR, "unrecognized FK action type: %d",
12261 : (int) fkconstraint->fk_upd_action);
6913 tgl 12262 EUB : break;
12263 : }
12264 :
459 alvherre 12265 GIC 1232 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
459 alvherre 12266 ECB : RelationGetRelid(rel),
12267 : constraintOid, indexOid, InvalidOid,
12268 : parentUpdTrigger, NULL, true, false);
459 alvherre 12269 GIC 1232 : if (updateTrigOid)
12270 1202 : *updateTrigOid = trigAddress.objectId;
1831 alvherre 12271 CBC 1232 : }
12272 :
1831 alvherre 12273 ECB : /*
1831 alvherre 12274 EUB : * createForeignKeyCheckTriggers
12275 : * Create the referencing-side "check" triggers that implement a foreign
12276 : * key.
12277 : *
12278 : * Returns the OIDs of the so created triggers in *insertTrigOid and
459 alvherre 12279 ECB : * *updateTrigOid.
12280 : */
12281 : static void
1831 alvherre 12282 GIC 1224 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12283 : Constraint *fkconstraint, Oid constraintOid,
12284 : Oid indexOid,
459 alvherre 12285 ECB : Oid parentInsTrigger, Oid parentUpdTrigger,
12286 : Oid *insertTrigOid, Oid *updateTrigOid)
1831 12287 : {
459 alvherre 12288 GIC 1224 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12289 : constraintOid, indexOid,
12290 : parentInsTrigger, true);
12291 1224 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12292 : constraintOid, indexOid,
12293 : parentUpdTrigger, false);
6913 tgl 12294 1224 : }
12295 :
12296 : /*
6913 tgl 12297 ECB : * ALTER TABLE DROP CONSTRAINT
12298 : *
12299 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12300 : */
12301 : static void
5448 tgl 12302 GIC 261 : ATExecDropConstraint(Relation rel, const char *constrName,
5448 tgl 12303 ECB : DropBehavior behavior,
5011 andrew 12304 : bool recurse, bool recursing,
12305 : bool missing_ok, LOCKMODE lockmode)
12306 : {
12307 : Relation conrel;
12308 : SysScanDesc scan;
1678 tgl 12309 EUB : ScanKeyData skey[3];
12310 : HeapTuple tuple;
5448 tgl 12311 GIC 261 : bool found = false;
12312 :
12313 : /* At top level, permission check was done in ATPrepCmd, else do it */
12314 261 : if (recursing)
640 peter 12315 UIC 0 : ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12316 :
1539 andres 12317 GIC 261 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12318 :
12319 : /*
12320 : * Find and drop the target constraint
6913 tgl 12321 ECB : */
1678 tgl 12322 GIC 261 : ScanKeyInit(&skey[0],
5448 tgl 12323 ECB : Anum_pg_constraint_conrelid,
12324 : BTEqualStrategyNumber, F_OIDEQ,
12325 : ObjectIdGetDatum(RelationGetRelid(rel)));
1678 tgl 12326 GIC 261 : ScanKeyInit(&skey[1],
12327 : Anum_pg_constraint_contypid,
1678 tgl 12328 ECB : BTEqualStrategyNumber, F_OIDEQ,
12329 : ObjectIdGetDatum(InvalidOid));
1678 tgl 12330 CBC 261 : ScanKeyInit(&skey[2],
1678 tgl 12331 ECB : Anum_pg_constraint_conname,
12332 : BTEqualStrategyNumber, F_NAMEEQ,
1678 tgl 12333 EUB : CStringGetDatum(constrName));
1678 tgl 12334 GIC 261 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
1678 tgl 12335 EUB : true, NULL, 3, skey);
12336 :
12337 : /* There can be at most one matching row */
1678 tgl 12338 GBC 261 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
6913 tgl 12339 EUB : {
2 alvherre 12340 GNC 246 : dropconstraint_internal(rel, tuple, behavior, recurse, recursing,
12341 : missing_ok, NULL, lockmode);
2 alvherre 12342 GIC 168 : found = true;
12343 : }
12344 :
2 alvherre 12345 CBC 183 : systable_endscan(scan);
5448 tgl 12346 ECB :
2 alvherre 12347 GIC 183 : if (!found)
12348 : {
12349 15 : if (!missing_ok)
5448 tgl 12350 9 : ereport(ERROR,
12351 : errcode(ERRCODE_UNDEFINED_OBJECT),
12352 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12353 : constrName, RelationGetRelationName(rel)));
12354 : else
2 alvherre 12355 6 : ereport(NOTICE,
12356 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12357 : constrName, RelationGetRelationName(rel)));
12358 : }
12359 :
2 alvherre 12360 GNC 174 : table_close(conrel, RowExclusiveLock);
12361 174 : }
12362 :
12363 : /*
12364 : * Remove a constraint, using its pg_constraint tuple
12365 : *
12366 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12367 : * DROP NOT NULL.
12368 : *
12369 : * Returns the address of the constraint being removed.
12370 : */
12371 : static ObjectAddress
12372 401 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
12373 : bool recurse, bool recursing, bool missing_ok, List **readyRels,
12374 : LOCKMODE lockmode)
12375 : {
12376 : Relation conrel;
12377 : Form_pg_constraint con;
12378 : ObjectAddress conobj;
12379 : List *children;
12380 : ListCell *child;
12381 401 : bool is_no_inherit_constraint = false;
12382 401 : bool dropping_pk = false;
12383 : char *constrName;
12384 401 : List *unconstrained_cols = NIL;
12385 : char *colname; /* to match NOT NULL constraints when recursing */
12386 401 : List *ready = NIL;
12387 :
12388 401 : if (readyRels == NULL)
12389 323 : readyRels = &ready;
12390 401 : if (list_member_oid(*readyRels, RelationGetRelid(rel)))
2 alvherre 12391 UNC 0 : return InvalidObjectAddress;
2 alvherre 12392 GNC 401 : *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
12393 :
12394 401 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12395 :
12396 401 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12397 401 : constrName = NameStr(con->conname);
12398 :
12399 : /*
12400 : * If the constraint is marked conislocal and is also inherited, then we
12401 : * just set conislocal false and we're done. The constraint doesn't go
12402 : * away, and we don't modify any children.
12403 : */
12404 401 : if (con->conislocal && con->coninhcount > 0)
12405 : {
12406 : HeapTuple copytup;
12407 :
12408 : /* make a copy we can scribble on */
12409 6 : copytup = heap_copytuple(constraintTup);
12410 6 : con = (Form_pg_constraint) GETSTRUCT(copytup);
12411 6 : con->conislocal = false;
12412 6 : CatalogTupleUpdate(conrel, ©tup->t_self, copytup);
12413 :
12414 6 : table_close(conrel, RowExclusiveLock);
12415 :
12416 6 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
12417 6 : return conobj;
12418 : }
12419 :
12420 : /* Don't drop inherited constraints */
12421 395 : if (con->coninhcount > 0 && !recursing)
12422 60 : ereport(ERROR,
12423 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12424 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12425 : constrName, RelationGetRelationName(rel))));
12426 :
12427 : /*
12428 : * See if we have a NOT NULL constraint or a PRIMARY KEY. If so, we have
12429 : * more checks and actions below, so obtain the list of columns that are
12430 : * constrained by the constraint being dropped.
12431 : */
12432 335 : if (con->contype == CONSTRAINT_NOTNULL)
12433 : {
12434 88 : AttrNumber colnum = extractNotNullColumn(constraintTup);
12435 :
12436 88 : if (colnum != InvalidAttrNumber)
12437 88 : unconstrained_cols = list_make1_int(colnum);
12438 : }
12439 247 : else if (con->contype == CONSTRAINT_PRIMARY)
12440 : {
12441 : Datum adatum;
12442 : ArrayType *arr;
12443 : int numkeys;
12444 : bool isNull;
12445 : int16 *attnums;
12446 :
12447 61 : dropping_pk = true;
12448 :
12449 61 : adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
12450 : RelationGetDescr(conrel), &isNull);
12451 61 : if (isNull)
2 alvherre 12452 UNC 0 : elog(ERROR, "null conkey for constraint %u", con->oid);
2 alvherre 12453 GNC 61 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
12454 61 : numkeys = ARR_DIMS(arr)[0];
12455 61 : if (ARR_NDIM(arr) != 1 ||
12456 61 : numkeys < 0 ||
12457 61 : ARR_HASNULL(arr) ||
12458 61 : ARR_ELEMTYPE(arr) != INT2OID)
2 alvherre 12459 UNC 0 : elog(ERROR, "conkey is not a 1-D smallint array");
2 alvherre 12460 GNC 61 : attnums = (int16 *) ARR_DATA_PTR(arr);
12461 :
12462 138 : for (int i = 0; i < numkeys; i++)
12463 77 : unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
12464 : }
12465 :
12466 335 : is_no_inherit_constraint = con->connoinherit;
12467 :
12468 : /*
12469 : * If it's a foreign-key constraint, we'd better lock the referenced table
12470 : * and check that that's not in use, just as we've already done for the
12471 : * constrained table (else we might, eg, be dropping a trigger that has
12472 : * unfired events). But we can/must skip that in the self-referential
12473 : * case.
12474 : */
12475 335 : if (con->contype == CONSTRAINT_FOREIGN &&
12476 36 : con->confrelid != RelationGetRelid(rel))
12477 : {
12478 : Relation frel;
12479 :
12480 : /* Must match lock taken by RemoveTriggerById: */
12481 36 : frel = table_open(con->confrelid, AccessExclusiveLock);
12482 36 : CheckTableNotInUse(frel, "ALTER TABLE");
12483 33 : table_close(frel, NoLock);
12484 : }
12485 :
12486 : /*
12487 : * Perform the actual constraint deletion
12488 : */
12489 332 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
12490 332 : performDeletion(&conobj, behavior, 0);
12491 :
12492 : /*
12493 : * If this was a NOT NULL or the primary key, the constrained columns must
12494 : * have had pg_attribute.attnotnull set. See if we need to reset it, and
12495 : * do so.
12496 : */
12497 314 : if (unconstrained_cols)
12498 : {
12499 : Relation attrel;
12500 : Bitmapset *pkcols;
12501 : Bitmapset *ircols;
12502 : ListCell *lc;
12503 :
12504 : /* Make the above deletion visible */
12505 131 : CommandCounterIncrement();
12506 :
12507 131 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
12508 :
12509 : /*
12510 : * We want to test columns for their presence in the primary key, but
12511 : * only if we're not dropping it.
12512 : */
12513 131 : pkcols = dropping_pk ? NULL :
12514 88 : RelationGetIndexAttrBitmap(rel,
12515 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
12516 131 : ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
12517 :
12518 263 : foreach(lc, unconstrained_cols)
12519 : {
12520 141 : AttrNumber attnum = lfirst_int(lc);
12521 : HeapTuple atttup;
12522 : HeapTuple contup;
12523 : Form_pg_attribute attForm;
12524 :
12525 : /*
12526 : * Obtain pg_attribute tuple and verify conditions on it. We use
12527 : * a copy we can scribble on.
12528 : */
12529 141 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
12530 141 : if (!HeapTupleIsValid(atttup))
2 alvherre 12531 UNC 0 : elog(ERROR, "cache lookup failed for column %d", attnum);
2 alvherre 12532 GNC 141 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
12533 :
12534 : /*
12535 : * Since the above deletion has been made visible, we can now
12536 : * search for any remaining constraints on this column (or these
12537 : * columns, in the case we're dropping a multicol primary key.)
12538 : * Then, verify whether any further NOT NULL or primary key
12539 : * exists, and reset attnotnull if none.
12540 : *
12541 : * However, if this is a generated identity column, abort the
12542 : * whole thing with a specific error message, because the
12543 : * constraint is required in that case.
12544 : */
12545 141 : contup = findNotNullConstraintAttnum(rel, attnum);
12546 273 : if (contup ||
12547 132 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
12548 : pkcols))
12549 12 : continue;
12550 :
12551 : /*
12552 : * It's not valid to drop the last NOT NULL constraint for a
12553 : * GENERATED AS IDENTITY column.
12554 : */
12555 129 : if (attForm->attidentity)
2 alvherre 12556 UNC 0 : ereport(ERROR,
12557 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12558 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
12559 : get_attname(RelationGetRelid(rel), attnum,
12560 : false),
12561 : RelationGetRelationName(rel)));
12562 :
12563 : /*
12564 : * It's not valid to drop the last NOT NULL constraint for the
12565 : * replica identity either. XXX make exception for FULL?
12566 : */
2 alvherre 12567 GNC 129 : if (bms_is_member(lfirst_int(lc) - FirstLowInvalidHeapAttributeNumber, ircols))
12568 9 : ereport(ERROR,
12569 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12570 : errmsg("column \"%s\" is in index used as replica identity",
12571 : get_attname(RelationGetRelid(rel), lfirst_int(lc), false)));
12572 :
12573 : /* Reset attnotnull */
12574 120 : if (attForm->attnotnull)
12575 : {
12576 120 : attForm->attnotnull = false;
12577 120 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
12578 : }
12579 : }
12580 122 : table_close(attrel, RowExclusiveLock);
5011 andrew 12581 ECB : }
4790 bruce 12582 :
12583 : /*
1536 alvherre 12584 : * For partitioned tables, non-CHECK inherited constraints are dropped via
12585 : * the dependency mechanism, so we're done here.
12586 : */
2 alvherre 12587 GNC 305 : if (con->contype != CONSTRAINT_CHECK &&
1536 alvherre 12588 GIC 179 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12589 : {
12590 24 : table_close(conrel, RowExclusiveLock);
2 alvherre 12591 GNC 24 : return conobj;
12592 : }
12593 :
12594 : /*
12595 : * Propagate to children as appropriate. Unlike most other ALTER
5448 tgl 12596 ECB : * routines, we have to do this one level of recursion at a time; we can't
12597 : * use find_all_inheritors to do it in one pass.
12598 : */
4006 alvherre 12599 CBC 281 : if (!is_no_inherit_constraint)
711 12600 208 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
5448 tgl 12601 ECB : else
5448 tgl 12602 GIC 73 : children = NIL;
5448 tgl 12603 ECB :
12604 : /*
2175 sfrost 12605 : * For a partitioned table, if partitions exist and we are told not to
12606 : * recurse, it's a user error. It doesn't make sense to have a constraint
12607 : * be defined only on the parent, especially if it's a partitioned table.
12608 : */
2175 sfrost 12609 GIC 281 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2175 sfrost 12610 CBC 31 : children != NIL && !recurse)
12611 3 : ereport(ERROR,
2175 sfrost 12612 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12613 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12614 : errhint("Do not specify the ONLY keyword.")));
12615 :
12616 : /* For NOT NULL constraints we recurse by column name */
2 alvherre 12617 GNC 278 : if (con->contype == CONSTRAINT_NOTNULL)
12618 79 : colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
12619 : linitial_int(unconstrained_cols) - 1)->attname);
12620 : else
12621 199 : colname = NULL; /* keep compiler quiet */
12622 :
5448 tgl 12623 GIC 365 : foreach(child, children)
12624 : {
12625 87 : Oid childrelid = lfirst_oid(child);
12626 : Relation childrel;
12627 : HeapTuple tuple;
12628 : Form_pg_constraint childcon;
12629 : HeapTuple copy_tuple;
12630 : SysScanDesc scan;
12631 : ScanKeyData skey[3];
12632 :
2 alvherre 12633 GNC 87 : if (list_member_oid(*readyRels, childrelid))
12634 3 : continue; /* child already processed */
12635 :
12636 : /* find_inheritance_children already got lock */
1539 andres 12637 GIC 84 : childrel = table_open(childrelid, NoLock);
5448 tgl 12638 84 : CheckTableNotInUse(childrel, "ALTER TABLE");
5448 tgl 12639 ECB :
12640 : /*
12641 : * We search for NOT NULL constraint by column number, and other
12642 : * constraints by name.
12643 : */
2 alvherre 12644 GNC 84 : if (con->contype == CONSTRAINT_NOTNULL)
12645 : {
12646 26 : bool found = false;
12647 : AttrNumber child_colnum;
12648 : HeapTuple child_tup;
12649 :
12650 26 : child_colnum = get_attnum(RelationGetRelid(childrel), colname);
12651 26 : ScanKeyInit(&skey[0],
12652 : Anum_pg_constraint_conrelid,
12653 : BTEqualStrategyNumber, F_OIDEQ,
12654 : ObjectIdGetDatum(childrelid));
12655 26 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12656 : true, NULL, 1, skey);
12657 37 : while (HeapTupleIsValid(child_tup = systable_getnext(scan)))
12658 : {
12659 37 : Form_pg_constraint constr = (Form_pg_constraint) GETSTRUCT(child_tup);
12660 : AttrNumber constr_colnum;
5448 tgl 12661 ECB :
2 alvherre 12662 GNC 37 : if (constr->contype != CONSTRAINT_NOTNULL)
12663 2 : continue;
12664 35 : constr_colnum = extractNotNullColumn(child_tup);
12665 35 : if (constr_colnum != child_colnum)
12666 9 : continue;
5448 tgl 12667 ECB :
2 alvherre 12668 GNC 26 : found = true;
12669 26 : break; /* found it */
12670 : }
12671 26 : if (!found) /* shouldn't happen? */
2 alvherre 12672 UNC 0 : elog(ERROR, "failed to find NOT NULL constraint for column \"%s\" in table \"%s\"",
12673 : colname, RelationGetRelationName(childrel));
12674 :
2 alvherre 12675 GNC 26 : copy_tuple = heap_copytuple(child_tup);
12676 26 : systable_endscan(scan);
12677 : }
12678 : else
12679 : {
12680 58 : ScanKeyInit(&skey[0],
12681 : Anum_pg_constraint_conrelid,
12682 : BTEqualStrategyNumber, F_OIDEQ,
12683 : ObjectIdGetDatum(childrelid));
12684 58 : ScanKeyInit(&skey[1],
12685 : Anum_pg_constraint_contypid,
12686 : BTEqualStrategyNumber, F_OIDEQ,
12687 : ObjectIdGetDatum(InvalidOid));
12688 58 : ScanKeyInit(&skey[2],
12689 : Anum_pg_constraint_conname,
12690 : BTEqualStrategyNumber, F_NAMEEQ,
12691 : CStringGetDatum(constrName));
12692 58 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12693 : true, NULL, 3, skey);
12694 : /* There can only be one, so no need to loop */
12695 58 : tuple = systable_getnext(scan);
12696 58 : if (!HeapTupleIsValid(tuple))
2 alvherre 12697 UNC 0 : ereport(ERROR,
12698 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12699 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12700 : constrName,
12701 : RelationGetRelationName(childrel))));
2 alvherre 12702 GNC 58 : copy_tuple = heap_copytuple(tuple);
12703 58 : systable_endscan(scan);
12704 : }
12705 :
12706 84 : childcon = (Form_pg_constraint) GETSTRUCT(copy_tuple);
12707 :
12708 : /* Right now only CHECK and NOT NULL constraints can be inherited */
12709 84 : if (childcon->contype != CONSTRAINT_CHECK &&
12710 26 : childcon->contype != CONSTRAINT_NOTNULL)
2 alvherre 12711 UNC 0 : elog(ERROR, "inherited constraint is not a CHECK or NOT NULL constraint");
12712 :
2 alvherre 12713 GNC 84 : if (childcon->coninhcount <= 0) /* shouldn't happen */
4200 rhaas 12714 LBC 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
4200 rhaas 12715 ECB : childrelid, constrName);
12716 :
4200 rhaas 12717 CBC 84 : if (recurse)
4200 rhaas 12718 ECB : {
12719 : /*
12720 : * If the child constraint has other definition sources, just
3955 bruce 12721 : * decrement its inheritance count; if not, recurse to delete it.
12722 : */
2 alvherre 12723 GNC 81 : if (childcon->coninhcount == 1 && !childcon->conislocal)
12724 : {
12725 : /* Time to delete this child constraint, too */
12726 78 : dropconstraint_internal(childrel, copy_tuple, behavior,
12727 : recurse, true, missing_ok, readyRels,
12728 : lockmode);
12729 : }
5448 tgl 12730 ECB : else
12731 : {
12732 : /* Child constraint must survive my deletion */
2 alvherre 12733 GNC 3 : childcon->coninhcount--;
2259 alvherre 12734 GIC 3 : CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
12735 :
12736 : /* Make update visible */
5448 tgl 12737 3 : CommandCounterIncrement();
12738 : }
12739 : }
12740 : else
12741 : {
4200 rhaas 12742 ECB : /*
12743 : * If we were told to drop ONLY in this table (no recursion), we
12744 : * need to mark the inheritors' constraints as locally defined
12745 : * rather than inherited.
12746 : */
2 alvherre 12747 GNC 3 : childcon->coninhcount--;
12748 3 : childcon->conislocal = true;
12749 :
2259 alvherre 12750 GIC 3 : CatalogTupleUpdate(conrel, ©_tuple->t_self, copy_tuple);
12751 :
12752 : /* Make update visible */
4200 rhaas 12753 3 : CommandCounterIncrement();
4200 rhaas 12754 ECB : }
12755 :
4200 rhaas 12756 CBC 84 : heap_freetuple(copy_tuple);
5448 tgl 12757 ECB :
1539 andres 12758 CBC 84 : table_close(childrel, NoLock);
6913 tgl 12759 ECB : }
5448 12760 :
1539 andres 12761 CBC 278 : table_close(conrel, RowExclusiveLock);
12762 :
2 alvherre 12763 GNC 278 : return conobj;
6913 tgl 12764 ECB : }
12765 :
12766 : /*
12767 : * ALTER COLUMN TYPE
1180 12768 : *
12769 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
12770 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
12771 : * transformed (and must be, because we rely on some transformed fields).
12772 : *
12773 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
12774 : * table will be done "in parallel" during phase 3, so all the USING
12775 : * expressions should be parsed assuming the original column types. Also,
12776 : * this allows a USING expression to refer to a field that will be dropped.
12777 : *
12778 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
12779 : * the first two execution steps in phase 2; they must not see the effects
12780 : * of any other subcommand types, since the USING expressions are parsed
12781 : * against the unmodified table's state.
6913 12782 : */
12783 : static void
6913 tgl 12784 CBC 550 : ATPrepAlterColumnType(List **wqueue,
6913 tgl 12785 ECB : AlteredTableInfo *tab, Relation rel,
12786 : bool recurse, bool recursing,
1180 12787 : AlterTableCmd *cmd, LOCKMODE lockmode,
12788 : AlterTableUtilityContext *context)
6913 12789 : {
6913 tgl 12790 CBC 550 : char *colName = cmd->name;
4414 12791 550 : ColumnDef *def = (ColumnDef *) cmd->def;
12792 550 : TypeName *typeName = def->typeName;
2928 alvherre 12793 550 : Node *transform = def->cooked_default;
6913 tgl 12794 ECB : HeapTuple tuple;
12795 : Form_pg_attribute attTup;
6913 tgl 12796 EUB : AttrNumber attnum;
12797 : Oid targettype;
12798 : int32 targettypmod;
12799 : Oid targetcollid;
12800 : NewColumnValue *newval;
6913 tgl 12801 GIC 550 : ParseState *pstate = make_parsestate(NULL);
4128 peter_e 12802 ECB : AclResult aclresult;
12803 : bool is_expr;
12804 :
4520 peter_e 12805 GIC 550 : if (rel->rd_rel->reloftype && !recursing)
4643 peter_e 12806 CBC 3 : ereport(ERROR,
4643 peter_e 12807 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12808 : errmsg("cannot alter column type of typed table")));
12809 :
6913 tgl 12810 : /* lookup the attribute so we can check inheritance status */
6913 tgl 12811 GIC 547 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
12812 547 : if (!HeapTupleIsValid(tuple))
6913 tgl 12813 UIC 0 : ereport(ERROR,
12814 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12815 : errmsg("column \"%s\" of relation \"%s\" does not exist",
6913 tgl 12816 ECB : colName, RelationGetRelationName(rel))));
6913 tgl 12817 CBC 547 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
12818 547 : attnum = attTup->attnum;
6913 tgl 12819 ECB :
12820 : /* Can't alter a system attribute */
6913 tgl 12821 CBC 547 : if (attnum <= 0)
6913 tgl 12822 LBC 0 : ereport(ERROR,
6913 tgl 12823 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12824 : errmsg("cannot alter system column \"%s\"",
12825 : colName)));
12826 :
1330 12827 : /*
12828 : * Don't alter inherited columns. At outer level, there had better not be
12829 : * any inherited definition; when recursing, we assume this was checked at
12830 : * the parent level (see below).
12831 : */
6913 tgl 12832 CBC 547 : if (attTup->attinhcount > 0 && !recursing)
12833 3 : ereport(ERROR,
6913 tgl 12834 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12835 : errmsg("cannot alter inherited column \"%s\"",
12836 : colName)));
12837 :
2314 rhaas 12838 : /* Don't alter columns used in the partition key */
1921 rhaas 12839 CBC 544 : if (has_partition_attrs(rel,
1921 rhaas 12840 ECB : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
12841 : &is_expr))
1357 tgl 12842 CBC 9 : ereport(ERROR,
1357 tgl 12843 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12844 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12845 : colName, RelationGetRelationName(rel))));
2314 rhaas 12846 :
6913 tgl 12847 : /* Look up the target type */
4414 tgl 12848 CBC 535 : typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
4414 tgl 12849 ECB :
147 peter 12850 GNC 535 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
4128 peter_e 12851 CBC 535 : if (aclresult != ACLCHECK_OK)
3950 12852 6 : aclcheck_error_type(aclresult, targettype);
4128 peter_e 12853 ECB :
4414 tgl 12854 : /* And the collation */
4414 tgl 12855 CBC 529 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
6913 tgl 12856 EUB :
12857 : /* make sure datatype is legal for a column */
4395 tgl 12858 GIC 529 : CheckAttributeType(colName, targettype, targetcollid,
12859 529 : list_make1_oid(rel->rd_rel->reltype),
12860 : 0);
12861 :
2314 rhaas 12862 CBC 526 : if (tab->relkind == RELKIND_RELATION ||
2314 rhaas 12863 GIC 95 : tab->relkind == RELKIND_PARTITIONED_TABLE)
12864 : {
12865 : /*
4382 bruce 12866 ECB : * Set up an expression to transform the old data value to the new
2878 12867 : * type. If a USING option was given, use the expression as
12868 : * transformed by transformAlterTableStmt, else just take the old
12869 : * value and try to coerce it. We do this first so that type
12870 : * incompatibility can be detected before we waste effort, and because
12871 : * we need the expression to be parsed against the original table row
12872 : * type.
12873 : */
2928 alvherre 12874 GIC 458 : if (!transform)
12875 : {
4578 peter_e 12876 356 : transform = (Node *) makeVar(1, attnum,
12877 : attTup->atttypid, attTup->atttypmod,
12878 : attTup->attcollation,
4578 peter_e 12879 ECB : 0);
12880 : }
12881 :
4578 peter_e 12882 GIC 458 : transform = coerce_to_target_type(pstate,
12883 : transform, exprType(transform),
12884 : targettype, targettypmod,
4578 peter_e 12885 ECB : COERCION_ASSIGNMENT,
12886 : COERCE_IMPLICIT_CAST,
12887 : -1);
4578 peter_e 12888 CBC 458 : if (transform == NULL)
12889 : {
12890 : /* error text depends on whether USING was specified or not */
2858 tgl 12891 12 : if (def->cooked_default != NULL)
2858 tgl 12892 GIC 3 : ereport(ERROR,
12893 : (errcode(ERRCODE_DATATYPE_MISMATCH),
12894 : errmsg("result of USING clause for column \"%s\""
12895 : " cannot be cast automatically to type %s",
12896 : colName, format_type_be(targettype)),
12897 : errhint("You might need to add an explicit cast.")));
12898 : else
2858 tgl 12899 CBC 9 : ereport(ERROR,
12900 : (errcode(ERRCODE_DATATYPE_MISMATCH),
12901 : errmsg("column \"%s\" cannot be cast automatically to type %s",
12902 : colName, format_type_be(targettype)),
12903 : /* translator: USING is SQL, don't translate it */
12904 : errhint("You might need to specify \"USING %s::%s\".",
12905 : quote_identifier(colName),
12906 : format_type_with_typemod(targettype,
12907 : targettypmod))));
2858 tgl 12908 ECB : }
12909 :
12910 : /* Fix collations after all else */
4404 tgl 12911 CBC 446 : assign_expr_collations(pstate, transform);
4310 rhaas 12912 EUB :
12913 : /* Plan the expr now so we can accurately assess the need to rewrite. */
4310 rhaas 12914 CBC 446 : transform = (Node *) expression_planner((Expr *) transform);
12915 :
12916 : /*
12917 : * Add a work queue item to make ATRewriteTable update the column
12918 : * contents.
4578 peter_e 12919 ECB : */
4578 peter_e 12920 GIC 446 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
12921 446 : newval->attnum = attnum;
12922 446 : newval->expr = (Expr *) transform;
1187 tgl 12923 CBC 446 : newval->is_generated = false;
12924 :
4578 peter_e 12925 GIC 446 : tab->newvals = lappend(tab->newvals, newval);
4439 rhaas 12926 446 : if (ATColumnChangeRequiresRewrite(transform, attnum))
3044 simon 12927 CBC 364 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
12928 : }
4414 tgl 12929 GIC 68 : else if (transform)
12930 6 : ereport(ERROR,
4414 tgl 12931 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12932 : errmsg("\"%s\" is not a table",
12933 : RelationGetRelationName(rel))));
12934 :
458 tgl 12935 CBC 508 : if (!RELKIND_HAS_STORAGE(tab->relkind))
12936 : {
4578 peter_e 12937 ECB : /*
12938 : * For relations without storage, do this check now. Regular tables
458 tgl 12939 : * will check it later when the table is being rewritten.
12940 : */
4440 rhaas 12941 GIC 89 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
4578 peter_e 12942 ECB : }
12943 :
6913 tgl 12944 CBC 493 : ReleaseSysCache(tuple);
12945 :
6913 tgl 12946 ECB : /*
2281 alvherre 12947 : * Recurse manually by queueing a new command for each child, if
12948 : * necessary. We cannot apply ATSimpleRecursion here because we need to
12949 : * remap attribute numbers in the USING expression, if any.
12950 : *
12951 : * If we are told not to recurse, there had better not be any child
12952 : * tables; else the alter would put them out of step.
12953 : */
6913 tgl 12954 GIC 493 : if (recurse)
12955 : {
2281 alvherre 12956 373 : Oid relid = RelationGetRelid(rel);
1330 tgl 12957 ECB : List *child_oids,
12958 : *child_numparents;
12959 : ListCell *lo,
12960 : *li;
12961 :
1330 tgl 12962 GIC 373 : child_oids = find_all_inheritors(relid, lockmode,
12963 : &child_numparents);
12964 :
12965 : /*
12966 : * find_all_inheritors does the recursive search of the inheritance
12967 : * hierarchy, so all we have to do is process all of the relids in the
12968 : * list that it returns.
2281 alvherre 12969 ECB : */
1330 tgl 12970 GIC 841 : forboth(lo, child_oids, li, child_numparents)
12971 : {
12972 480 : Oid childrelid = lfirst_oid(lo);
12973 480 : int numparents = lfirst_int(li);
12974 : Relation childrel;
12975 : HeapTuple childtuple;
12976 : Form_pg_attribute childattTup;
12977 :
2281 alvherre 12978 CBC 480 : if (childrelid == relid)
12979 373 : continue;
12980 :
2281 alvherre 12981 ECB : /* find_all_inheritors already got lock */
2281 alvherre 12982 GIC 107 : childrel = relation_open(childrelid, NoLock);
2281 alvherre 12983 CBC 107 : CheckTableNotInUse(childrel, "ALTER TABLE");
12984 :
1330 tgl 12985 ECB : /*
12986 : * Verify that the child doesn't have any inherited definitions of
12987 : * this column that came from outside this inheritance hierarchy.
1330 tgl 12988 EUB : * (renameatt makes a similar test, though in a different way
1330 tgl 12989 ECB : * because of its different recursion mechanism.)
12990 : */
1330 tgl 12991 CBC 107 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
12992 : colName);
12993 107 : if (!HeapTupleIsValid(childtuple))
1330 tgl 12994 LBC 0 : ereport(ERROR,
12995 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12996 : errmsg("column \"%s\" of relation \"%s\" does not exist",
12997 : colName, RelationGetRelationName(childrel))));
1330 tgl 12998 GIC 107 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
12999 :
13000 107 : if (childattTup->attinhcount > numparents)
1330 tgl 13001 CBC 3 : ereport(ERROR,
13002 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13003 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13004 : colName, RelationGetRelationName(childrel))));
13005 :
13006 104 : ReleaseSysCache(childtuple);
1330 tgl 13007 ECB :
2281 alvherre 13008 : /*
13009 : * Remap the attribute numbers. If no USING expression was
13010 : * specified, there is no need for this step.
13011 : */
2281 alvherre 13012 GIC 104 : if (def->cooked_default)
2281 alvherre 13013 ECB : {
1208 michael 13014 : AttrMap *attmap;
13015 : bool found_whole_row;
13016 :
13017 : /* create a copy to scribble on */
2281 alvherre 13018 CBC 36 : cmd = copyObject(cmd);
2281 alvherre 13019 ECB :
1208 michael 13020 GIC 36 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13021 : RelationGetDescr(rel),
13022 : false);
2281 alvherre 13023 72 : ((ColumnDef *) cmd->def)->cooked_default =
13024 36 : map_variable_attnos(def->cooked_default,
13025 : 1, 0,
13026 : attmap,
13027 : InvalidOid, &found_whole_row);
13028 36 : if (found_whole_row)
13029 3 : ereport(ERROR,
2281 alvherre 13030 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13031 : errmsg("cannot convert whole-row table reference"),
13032 : errdetail("USING expression contains a whole-row table reference.")));
2281 alvherre 13033 GIC 33 : pfree(attmap);
2281 alvherre 13034 ECB : }
1180 tgl 13035 CBC 101 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
2281 alvherre 13036 GIC 95 : relation_close(childrel, NoLock);
2281 alvherre 13037 ECB : }
13038 : }
6913 tgl 13039 GIC 145 : else if (!recursing &&
711 alvherre 13040 25 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
6913 tgl 13041 UIC 0 : ereport(ERROR,
13042 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13043 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
13044 : colName)));
4520 peter_e 13045 ECB :
4520 peter_e 13046 GIC 481 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
1180 tgl 13047 CBC 25 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
6913 tgl 13048 GIC 478 : }
6913 tgl 13049 ECB :
4439 rhaas 13050 EUB : /*
4437 rhaas 13051 ECB : * When the data type of a column is changed, a rewrite might not be required
13052 : * if the new type is sufficiently identical to the old one, and the USING
13053 : * clause isn't trying to insert some other value. It's safe to skip the
1493 noah 13054 : * rewrite in these cases:
13055 : *
13056 : * - the old type is binary coercible to the new type
1493 noah 13057 EUB : * - the new type is an unconstrained domain over the old type
1493 noah 13058 ECB : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13059 : *
13060 : * In the case of a constrained domain, we could get by with scanning the
13061 : * table and checking the constraint rather than actually rewriting it, but we
13062 : * don't currently try to do that.
13063 : */
4439 rhaas 13064 : static bool
4439 rhaas 13065 GIC 446 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13066 : {
13067 446 : Assert(expr != NULL);
13068 :
13069 : for (;;)
13070 : {
13071 : /* only one varno, so no need to check that */
1058 tgl 13072 498 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
4439 rhaas 13073 CBC 82 : return false;
13074 416 : else if (IsA(expr, RelabelType))
4439 rhaas 13075 GIC 46 : expr = (Node *) ((RelabelType *) expr)->arg;
4437 13076 370 : else if (IsA(expr, CoerceToDomain))
13077 : {
4437 rhaas 13078 UIC 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
4437 rhaas 13079 ECB :
2961 tgl 13080 LBC 0 : if (DomainHasConstraints(d->resulttype))
4437 rhaas 13081 0 : return true;
4437 rhaas 13082 UIC 0 : expr = (Node *) d->arg;
13083 : }
1493 noah 13084 GIC 370 : else if (IsA(expr, FuncExpr))
13085 : {
13086 270 : FuncExpr *f = (FuncExpr *) expr;
1493 noah 13087 ECB :
1493 noah 13088 CBC 270 : switch (f->funcid)
13089 : {
1493 noah 13090 GIC 9 : case F_TIMESTAMPTZ_TIMESTAMP:
13091 : case F_TIMESTAMP_TIMESTAMPTZ:
13092 9 : if (TimestampTimestampTzRequiresRewrite())
13093 3 : return true;
13094 : else
1493 noah 13095 CBC 6 : expr = linitial(f->args);
1493 noah 13096 GIC 6 : break;
13097 261 : default:
13098 261 : return true;
13099 : }
13100 : }
13101 : else
4439 rhaas 13102 100 : return true;
4439 rhaas 13103 ECB : }
13104 : }
13105 :
13106 : /*
13107 : * ALTER COLUMN .. SET DATA TYPE
13108 : *
13109 : * Return the address of the modified column.
13110 : */
2937 alvherre 13111 : static ObjectAddress
6913 tgl 13112 CBC 463 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13113 : AlterTableCmd *cmd, LOCKMODE lockmode)
6913 tgl 13114 ECB : {
4414 tgl 13115 GIC 463 : char *colName = cmd->name;
4414 tgl 13116 CBC 463 : ColumnDef *def = (ColumnDef *) cmd->def;
4414 tgl 13117 GIC 463 : TypeName *typeName = def->typeName;
6913 tgl 13118 ECB : HeapTuple heapTup;
13119 : Form_pg_attribute attTup,
13120 : attOldTup;
13121 : AttrNumber attnum;
13122 : HeapTuple typeTuple;
13123 : Form_pg_type tform;
13124 : Oid targettype;
13125 : int32 targettypmod;
13126 : Oid targetcollid;
13127 : Node *defaultexpr;
13128 : Relation attrelation;
6913 tgl 13129 EUB : Relation depRel;
6913 tgl 13130 ECB : ScanKeyData key[3];
13131 : SysScanDesc scan;
13132 : HeapTuple depTup;
13133 : ObjectAddress address;
13134 :
13135 : /*
13136 : * Clear all the missing values if we're rewriting the table, since this
13137 : * renders them pointless.
13138 : */
1550 andrew 13139 GIC 463 : if (tab->rewrite)
13140 : {
13141 : Relation newrel;
13142 :
1539 andres 13143 CBC 337 : newrel = table_open(RelationGetRelid(rel), NoLock);
1550 andrew 13144 337 : RelationClearMissing(newrel);
13145 337 : relation_close(newrel, NoLock);
13146 : /* make sure we don't conflict with later attribute modifications */
13147 337 : CommandCounterIncrement();
13148 : }
13149 :
1539 andres 13150 GIC 463 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13151 :
13152 : /* Look up the target column */
6913 tgl 13153 CBC 463 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
2118 tgl 13154 GBC 463 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
6913 tgl 13155 UIC 0 : ereport(ERROR,
13156 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13157 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13158 : colName, RelationGetRelationName(rel))));
6913 tgl 13159 GIC 463 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13160 463 : attnum = attTup->attnum;
2058 andres 13161 463 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13162 :
13163 : /* Check for multiple ALTER TYPE on same column --- can't cope */
13164 463 : if (attTup->atttypid != attOldTup->atttypid ||
2058 andres 13165 CBC 463 : attTup->atttypmod != attOldTup->atttypmod)
6913 tgl 13166 LBC 0 : ereport(ERROR,
13167 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13168 : errmsg("cannot alter type of column \"%s\" twice",
13169 : colName)));
13170 :
13171 : /* Look up the target type (should not fail, since prep found it) */
4414 tgl 13172 CBC 463 : typeTuple = typenameType(NULL, typeName, &targettypmod);
6913 tgl 13173 GIC 463 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
1601 andres 13174 CBC 463 : targettype = tform->oid;
4414 tgl 13175 ECB : /* And the collation */
4414 tgl 13176 GIC 463 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13177 :
6913 tgl 13178 ECB : /*
13179 : * If there is a default expression for the column, get it and ensure we
13180 : * can coerce it to the new datatype. (We must do this before changing
13181 : * the column type, because build_column_default itself will try to
13182 : * coerce, and will not issue the error message we want if it fails.)
13183 : *
13184 : * We remove any implicit coercion steps at the top level of the old
6347 bruce 13185 : * default expression; this has been agreed to satisfy the principle of
3260 13186 : * least surprise. (The conversion to the new column type should act like
13187 : * it started from what the user sees as the stored expression, and the
6385 13188 : * implicit coercions aren't going to be shown.)
6913 tgl 13189 : */
6913 tgl 13190 GIC 463 : if (attTup->atthasdef)
13191 : {
13192 31 : defaultexpr = build_column_default(rel, attnum);
13193 31 : Assert(defaultexpr);
6743 13194 31 : defaultexpr = strip_implicit_coercions(defaultexpr);
2118 13195 31 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13196 : defaultexpr, exprType(defaultexpr),
5944 tgl 13197 ECB : targettype, targettypmod,
6913 13198 : COERCION_ASSIGNMENT,
13199 : COERCE_IMPLICIT_CAST,
5337 13200 : -1);
6913 tgl 13201 GIC 31 : if (defaultexpr == NULL)
13202 : {
1471 peter 13203 6 : if (attTup->attgenerated)
13204 3 : ereport(ERROR,
13205 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13206 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
1471 peter 13207 ECB : colName, format_type_be(targettype))));
13208 : else
1471 peter 13209 CBC 3 : ereport(ERROR,
13210 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13211 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13212 : colName, format_type_be(targettype))));
13213 : }
13214 : }
6913 tgl 13215 ECB : else
6913 tgl 13216 CBC 432 : defaultexpr = NULL;
13217 :
13218 : /*
6385 bruce 13219 ECB : * Find everything that depends on the column (constraints, indexes, etc),
13220 : * and record enough information to let us recreate the objects.
6913 tgl 13221 : *
13222 : * The actual recreation does not happen here, but only after we have
3260 bruce 13223 : * performed all the individual ALTER TYPE operations. We have to save
13224 : * the info before executing ALTER TYPE, though, else the deparser will
13225 : * get confused.
13226 : */
1539 andres 13227 GIC 457 : depRel = table_open(DependRelationId, RowExclusiveLock);
13228 :
6913 tgl 13229 457 : ScanKeyInit(&key[0],
13230 : Anum_pg_depend_refclassid,
6913 tgl 13231 ECB : BTEqualStrategyNumber, F_OIDEQ,
6569 13232 : ObjectIdGetDatum(RelationRelationId));
6913 tgl 13233 GIC 457 : ScanKeyInit(&key[1],
13234 : Anum_pg_depend_refobjid,
6913 tgl 13235 ECB : BTEqualStrategyNumber, F_OIDEQ,
13236 : ObjectIdGetDatum(RelationGetRelid(rel)));
6913 tgl 13237 GIC 457 : ScanKeyInit(&key[2],
13238 : Anum_pg_depend_refobjsubid,
13239 : BTEqualStrategyNumber, F_INT4EQ,
13240 : Int32GetDatum((int32) attnum));
13241 :
6569 tgl 13242 CBC 457 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13243 : NULL, 3, key);
6913 tgl 13244 ECB :
6913 tgl 13245 GIC 847 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13246 : {
6797 bruce 13247 402 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
6797 bruce 13248 ECB : ObjectAddress foundObject;
6913 tgl 13249 :
6913 tgl 13250 GIC 402 : foundObject.classId = foundDep->classid;
13251 402 : foundObject.objectId = foundDep->objid;
13252 402 : foundObject.objectSubId = foundDep->objsubid;
6913 tgl 13253 ECB :
6913 tgl 13254 GIC 402 : switch (getObjectClass(&foundObject))
6913 tgl 13255 ECB : {
6913 tgl 13256 GIC 123 : case OCLASS_CLASS:
6913 tgl 13257 ECB : {
6797 bruce 13258 GIC 123 : char relKind = get_rel_relkind(foundObject.objectId);
13259 :
1906 alvherre 13260 CBC 123 : if (relKind == RELKIND_INDEX ||
1906 alvherre 13261 ECB : relKind == RELKIND_PARTITIONED_INDEX)
6913 tgl 13262 : {
6797 bruce 13263 CBC 107 : Assert(foundObject.objectSubId == 0);
1385 tgl 13264 107 : RememberIndexForRebuilding(foundObject.objectId, tab);
13265 : }
6797 bruce 13266 16 : else if (relKind == RELKIND_SEQUENCE)
6797 bruce 13267 ECB : {
13268 : /*
6385 13269 : * This must be a SERIAL column's sequence. We need
6385 bruce 13270 EUB : * not do anything to it.
13271 : */
6797 bruce 13272 GIC 16 : Assert(foundObject.objectSubId == 0);
6797 bruce 13273 ECB : }
13274 : else
13275 : {
13276 : /* Not expecting any other direct dependencies... */
6797 bruce 13277 UIC 0 : elog(ERROR, "unexpected object depending on column: %s",
998 michael 13278 ECB : getObjectDescription(&foundObject, false));
13279 : }
6797 bruce 13280 GIC 123 : break;
13281 : }
6913 tgl 13282 ECB :
6913 tgl 13283 GIC 235 : case OCLASS_CONSTRAINT:
13284 235 : Assert(foundObject.objectSubId == 0);
1385 13285 235 : RememberConstraintForRebuilding(foundObject.objectId, tab);
6913 tgl 13286 CBC 235 : break;
13287 :
6913 tgl 13288 GIC 6 : case OCLASS_REWRITE:
13289 : /* XXX someday see if we can cope with revising views */
6913 tgl 13290 CBC 6 : ereport(ERROR,
13291 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13292 : errmsg("cannot alter type of a column used by a view or rule"),
6913 tgl 13293 ECB : errdetail("%s depends on column \"%s\"",
998 michael 13294 : getObjectDescription(&foundObject, false),
6913 tgl 13295 EUB : colName)));
13296 : break;
13297 :
4572 tgl 13298 UIC 0 : case OCLASS_TRIGGER:
13299 :
4572 tgl 13300 ECB : /*
13301 : * A trigger can depend on a column because the column is
13302 : * specified as an update target, or because the column is
13303 : * used in the trigger's WHEN condition. The first case would
13304 : * not require any extra work, but the second case would
13305 : * require updating the WHEN expression, which will take a
13306 : * significant amount of new code. Since we can't easily tell
13307 : * which case applies, we punt for both. FIXME someday.
13308 : */
4572 tgl 13309 UBC 0 : ereport(ERROR,
13310 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4572 tgl 13311 ECB : errmsg("cannot alter type of a column used in a trigger definition"),
4572 tgl 13312 EUB : errdetail("%s depends on column \"%s\"",
13313 : getObjectDescription(&foundObject, false),
13314 : colName)));
4572 tgl 13315 ECB : break;
13316 :
3055 sfrost 13317 UIC 0 : case OCLASS_POLICY:
13318 :
13319 : /*
13320 : * A policy can depend on a column because the column is
3055 sfrost 13321 ECB : * specified in the policy's USING or WITH CHECK qual
13322 : * expressions. It might be possible to rewrite and recheck
13323 : * the policy expression, but punt for now. It's certainly
2878 bruce 13324 : * easy enough to remove and recreate the policy; still, FIXME
13325 : * someday.
13326 : */
3055 sfrost 13327 UIC 0 : ereport(ERROR,
13328 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13329 : errmsg("cannot alter type of a column used in a policy definition"),
13330 : errdetail("%s depends on column \"%s\"",
998 michael 13331 ECB : getObjectDescription(&foundObject, false),
3055 sfrost 13332 : colName)));
13333 : break;
13334 :
6913 tgl 13335 CBC 31 : case OCLASS_DEFAULT:
13336 : {
384 tgl 13337 GIC 31 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
13338 :
13339 31 : if (col.objectId == RelationGetRelid(rel) &&
13340 31 : col.objectSubId == attnum)
13341 : {
13342 : /*
13343 : * Ignore the column's own default expression, which
13344 : * we will deal with below.
384 tgl 13345 ECB : */
384 tgl 13346 CBC 25 : Assert(defaultexpr);
13347 : }
384 tgl 13348 ECB : else
13349 : {
13350 : /*
13351 : * This must be a reference from the expression of a
13352 : * generated column elsewhere in the same table.
13353 : * Changing the type of a column that is used by a
13354 : * generated column is not allowed by SQL standard, so
13355 : * just punt for now. It might be doable with some
13356 : * thinking and effort.
13357 : */
384 tgl 13358 GIC 6 : ereport(ERROR,
384 tgl 13359 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13360 : errmsg("cannot alter type of a column used by a generated column"),
13361 : errdetail("Column \"%s\" is used by generated column \"%s\".",
13362 : colName,
13363 : get_attname(col.objectId,
13364 : col.objectSubId,
13365 : false))));
13366 : }
384 tgl 13367 GIC 25 : break;
13368 : }
13369 :
2156 13370 7 : case OCLASS_STATISTIC_EXT:
13371 :
13372 : /*
13373 : * Give the extended-stats machinery a chance to fix anything
13374 : * that this column type change would break.
13375 : */
744 tomas.vondra 13376 7 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
2156 tgl 13377 7 : break;
13378 :
6913 tgl 13379 UIC 0 : case OCLASS_PROC:
13380 : case OCLASS_TYPE:
13381 : case OCLASS_CAST:
4439 peter_e 13382 ECB : case OCLASS_COLLATION:
13383 : case OCLASS_CONVERSION:
13384 : case OCLASS_LANGUAGE:
13385 : case OCLASS_LARGEOBJECT:
13386 : case OCLASS_OPERATOR:
13387 : case OCLASS_OPCLASS:
5710 tgl 13388 : case OCLASS_OPFAMILY:
2156 13389 : case OCLASS_AM:
4993 13390 : case OCLASS_AMOP:
13391 : case OCLASS_AMPROC:
13392 : case OCLASS_SCHEMA:
13393 : case OCLASS_TSPARSER:
13394 : case OCLASS_TSDICT:
13395 : case OCLASS_TSTEMPLATE:
13396 : case OCLASS_TSCONFIG:
13397 : case OCLASS_ROLE:
13398 : case OCLASS_ROLE_MEMBERSHIP:
13399 : case OCLASS_DATABASE:
13400 : case OCLASS_TBLSPACE:
13401 : case OCLASS_FDW:
13402 : case OCLASS_FOREIGN_SERVER:
13403 : case OCLASS_USER_MAPPING:
4934 13404 : case OCLASS_DEFACL:
4443 13405 : case OCLASS_EXTENSION:
13406 : case OCLASS_EVENT_TRIGGER:
13407 : case OCLASS_PARAMETER_ACL:
13408 : case OCLASS_PUBLICATION:
13409 : case OCLASS_PUBLICATION_NAMESPACE:
2156 13410 : case OCLASS_PUBLICATION_REL:
13411 : case OCLASS_SUBSCRIPTION:
2156 tgl 13412 EUB : case OCLASS_TRANSFORM:
13413 :
13414 : /*
13415 : * We don't expect any of these sorts of objects to depend on
6385 bruce 13416 ECB : * a column.
6913 tgl 13417 : */
6913 tgl 13418 UIC 0 : elog(ERROR, "unexpected object depending on column: %s",
13419 : getObjectDescription(&foundObject, false));
6913 tgl 13420 ECB : break;
6913 tgl 13421 EUB :
13422 : /*
13423 : * There's intentionally no default: case here; we want the
13424 : * compiler to warn if a new OCLASS hasn't been handled above.
13425 : */
13426 : }
13427 : }
13428 :
6913 tgl 13429 GIC 445 : systable_endscan(scan);
13430 :
6913 tgl 13431 ECB : /*
13432 : * Now scan for dependencies of this column on other things. The only
13433 : * things we should find are the dependency on the column datatype and
13434 : * possibly a collation dependency. Those can be removed.
13435 : */
6913 tgl 13436 GIC 445 : ScanKeyInit(&key[0],
13437 : Anum_pg_depend_classid,
6913 tgl 13438 ECB : BTEqualStrategyNumber, F_OIDEQ,
13439 : ObjectIdGetDatum(RelationRelationId));
6913 tgl 13440 GIC 445 : ScanKeyInit(&key[1],
6913 tgl 13441 ECB : Anum_pg_depend_objid,
13442 : BTEqualStrategyNumber, F_OIDEQ,
13443 : ObjectIdGetDatum(RelationGetRelid(rel)));
6913 tgl 13444 GIC 445 : ScanKeyInit(&key[2],
13445 : Anum_pg_depend_objsubid,
13446 : BTEqualStrategyNumber, F_INT4EQ,
6913 tgl 13447 ECB : Int32GetDatum((int32) attnum));
13448 :
6569 tgl 13449 CBC 445 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
3568 rhaas 13450 ECB : NULL, 3, key);
6913 tgl 13451 :
6913 tgl 13452 GIC 447 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13453 : {
6797 bruce 13454 CBC 2 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13455 : ObjectAddress foundObject;
13456 :
1471 peter 13457 2 : foundObject.classId = foundDep->refclassid;
13458 2 : foundObject.objectId = foundDep->refobjid;
1471 peter 13459 GIC 2 : foundObject.objectSubId = foundDep->refobjsubid;
13460 :
384 tgl 13461 CBC 2 : if (foundDep->deptype != DEPENDENCY_NORMAL)
6913 tgl 13462 LBC 0 : elog(ERROR, "found unexpected dependency type '%c'",
13463 : foundDep->deptype);
4439 peter_e 13464 GIC 2 : if (!(foundDep->refclassid == TypeRelationId &&
13465 2 : foundDep->refobjid == attTup->atttypid) &&
4439 peter_e 13466 UIC 0 : !(foundDep->refclassid == CollationRelationId &&
384 tgl 13467 0 : foundDep->refobjid == attTup->attcollation))
1471 peter 13468 0 : elog(ERROR, "found unexpected dependency for column: %s",
13469 : getObjectDescription(&foundObject, false));
13470 :
2258 tgl 13471 GIC 2 : CatalogTupleDelete(depRel, &depTup->t_self);
13472 : }
6913 tgl 13473 ECB :
6913 tgl 13474 GIC 445 : systable_endscan(scan);
6913 tgl 13475 ECB :
1539 andres 13476 GIC 445 : table_close(depRel, RowExclusiveLock);
13477 :
13478 : /*
13479 : * Here we go --- change the recorded column type and collation. (Note
13480 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
1418 tgl 13481 ECB : * fix up the missing value if any.
13482 : */
1550 andrew 13483 GIC 445 : if (attTup->atthasmissing)
13484 : {
13485 : Datum missingval;
13486 : bool missingNull;
1550 andrew 13487 ECB :
13488 : /* if rewrite is true the missing value should already be cleared */
1550 andrew 13489 GIC 3 : Assert(tab->rewrite == 0);
1550 andrew 13490 ECB :
13491 : /* Get the missing value datum */
1550 andrew 13492 GIC 3 : missingval = heap_getattr(heapTup,
13493 : Anum_pg_attribute_attmissingval,
13494 : attrelation->rd_att,
13495 : &missingNull);
13496 :
13497 : /* if it's a null array there is nothing to do */
1550 andrew 13498 ECB :
1418 tgl 13499 GIC 3 : if (!missingNull)
13500 : {
13501 : /*
13502 : * Get the datum out of the array and repack it in a new array
13503 : * built with the new type data. We assume that since the table
13504 : * doesn't need rewriting, the actual Datum doesn't need to be
13505 : * changed, only the array metadata.
13506 : */
13507 :
13508 3 : int one = 1;
13509 : bool isNull;
267 peter 13510 GNC 3 : Datum valuesAtt[Natts_pg_attribute] = {0};
13511 3 : bool nullsAtt[Natts_pg_attribute] = {0};
13512 3 : bool replacesAtt[Natts_pg_attribute] = {0};
1418 tgl 13513 ECB : HeapTuple newTup;
13514 :
1550 andrew 13515 CBC 6 : missingval = array_get_element(missingval,
1550 andrew 13516 ECB : 1,
13517 : &one,
13518 : 0,
1550 andrew 13519 GIC 3 : attTup->attlen,
1550 andrew 13520 CBC 3 : attTup->attbyval,
13521 3 : attTup->attalign,
1550 andrew 13522 ECB : &isNull);
1165 alvherre 13523 GIC 3 : missingval = PointerGetDatum(construct_array(&missingval,
1418 tgl 13524 ECB : 1,
13525 : targettype,
1418 tgl 13526 GIC 3 : tform->typlen,
13527 3 : tform->typbyval,
13528 3 : tform->typalign));
13529 :
1550 andrew 13530 CBC 3 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
1550 andrew 13531 GIC 3 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13532 3 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13533 :
1549 13534 3 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13535 : valuesAtt, nullsAtt, replacesAtt);
1549 andrew 13536 CBC 3 : heap_freetuple(heapTup);
1549 andrew 13537 GIC 3 : heapTup = newTup;
1550 13538 3 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
1550 andrew 13539 ECB : }
13540 : }
13541 :
6913 tgl 13542 GIC 445 : attTup->atttypid = targettype;
5944 13543 445 : attTup->atttypmod = targettypmod;
4443 peter_e 13544 445 : attTup->attcollation = targetcollid;
12 peter 13545 GNC 445 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
12 peter 13546 UNC 0 : ereport(ERROR,
13547 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13548 : errmsg("too many array dimensions"));
5015 peter_e 13549 GIC 445 : attTup->attndims = list_length(typeName->arrayBounds);
6913 tgl 13550 445 : attTup->attlen = tform->typlen;
13551 445 : attTup->attbyval = tform->typbyval;
13552 445 : attTup->attalign = tform->typalign;
6913 tgl 13553 CBC 445 : attTup->attstorage = tform->typstorage;
682 tgl 13554 GIC 445 : attTup->attcompression = InvalidCompressionMethod;
751 rhaas 13555 ECB :
751 rhaas 13556 GIC 445 : ReleaseSysCache(typeTuple);
13557 :
2259 alvherre 13558 445 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13559 :
1539 andres 13560 445 : table_close(attrelation, RowExclusiveLock);
7576 tgl 13561 ECB :
13562 : /* Install dependencies on new datatype and collation */
4370 tgl 13563 GIC 445 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13564 445 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13565 :
13566 : /*
13567 : * Drop any pg_statistic entry for the column, since it's now wrong type
13568 : */
6798 tgl 13569 CBC 445 : RemoveStatistics(RelationGetRelid(rel), attnum);
13570 :
3675 rhaas 13571 445 : InvokeObjectPostAlterHook(RelationRelationId,
3675 rhaas 13572 ECB : RelationGetRelid(rel), attnum);
13573 :
13574 : /*
13575 : * Update the default, if present, by brute force --- remove and re-add
13576 : * the default. Probably unsafe to take shortcuts, since the new version
6385 bruce 13577 : * may well have additional dependencies. (It's okay to do this now,
13578 : * rather than after other ALTER TYPE commands, since the default won't
13579 : * depend on other column types.)
13580 : */
6913 tgl 13581 CBC 445 : if (defaultexpr)
7576 tgl 13582 ECB : {
13583 : /*
13584 : * If it's a GENERATED default, drop its dependency records, in
13585 : * particular its INTERNAL dependency on the column, which would
13586 : * otherwise cause dependency.c to refuse to perform the deletion.
13587 : */
384 tgl 13588 GIC 25 : if (attTup->attgenerated)
13589 : {
384 tgl 13590 CBC 3 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13591 :
13592 3 : if (!OidIsValid(attrdefoid))
384 tgl 13593 UBC 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13594 : RelationGetRelid(rel), attnum);
384 tgl 13595 GIC 3 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13596 : }
384 tgl 13597 ECB :
13598 : /*
13599 : * Make updates-so-far visible, particularly the new pg_attribute row
13600 : * which will be updated again.
13601 : */
6913 tgl 13602 GIC 25 : CommandCounterIncrement();
13603 :
13604 : /*
6385 bruce 13605 ECB : * We use RESTRICT here for safety, but at present we do not expect
13606 : * anything to depend on the default.
13607 : */
4091 rhaas 13608 GIC 25 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13609 : true);
13610 :
1838 andrew 13611 CBC 25 : StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13612 : }
13613 :
2937 alvherre 13614 GIC 445 : ObjectAddressSubSet(address, RelationRelationId,
13615 : RelationGetRelid(rel), attnum);
13616 :
6913 tgl 13617 ECB : /* Cleanup */
6913 tgl 13618 GIC 445 : heap_freetuple(heapTup);
2937 alvherre 13619 ECB :
2937 alvherre 13620 GIC 445 : return address;
13621 : }
7576 tgl 13622 ECB :
1122 peter 13623 : /*
13624 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
13625 : * needs to be reset.
13626 : */
13627 : static void
1122 peter 13628 CBC 199 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
13629 : {
1122 peter 13630 GIC 199 : if (!get_index_isreplident(indoid))
13631 190 : return;
1122 peter 13632 ECB :
1122 peter 13633 GIC 9 : if (tab->replicaIdentityIndex)
1122 peter 13634 LBC 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
1122 peter 13635 ECB :
1122 peter 13636 GIC 9 : tab->replicaIdentityIndex = get_rel_name(indoid);
13637 : }
1122 peter 13638 ECB :
1098 michael 13639 : /*
1098 michael 13640 EUB : * Subroutine for ATExecAlterColumnType: remember any clustered index.
13641 : */
13642 : static void
1098 michael 13643 GIC 199 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
13644 : {
1098 michael 13645 CBC 199 : if (!get_index_isclustered(indoid))
13646 190 : return;
1098 michael 13647 ECB :
1098 michael 13648 GIC 9 : if (tab->clusterOnIndex)
1098 michael 13649 UIC 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
13650 :
1098 michael 13651 GIC 9 : tab->clusterOnIndex = get_rel_name(indoid);
13652 : }
13653 :
13654 : /*
13655 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
13656 : * to be rebuilt (which we might already know).
13657 : */
13658 : static void
1385 tgl 13659 241 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
13660 : {
13661 : /*
13662 : * This de-duplication check is critical for two independent reasons: we
13663 : * mustn't try to recreate the same constraint twice, and if a constraint
1385 tgl 13664 ECB : * depends on more than one column whose type is to be altered, we must
13665 : * capture its definition string before applying any of the column type
13666 : * changes. ruleutils.c will get confused if we ask again later.
13667 : */
1385 tgl 13668 GIC 241 : if (!list_member_oid(tab->changedConstraintOids, conoid))
13669 : {
13670 : /* OK, capture the constraint's existing definition string */
1385 tgl 13671 CBC 202 : char *defstring = pg_get_constraintdef_command(conoid);
1122 peter 13672 ECB : Oid indoid;
1385 tgl 13673 :
1385 tgl 13674 CBC 202 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
1385 tgl 13675 ECB : conoid);
1385 tgl 13676 GIC 202 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
1385 tgl 13677 EUB : defstring);
13678 :
1098 michael 13679 : /*
13680 : * For the index of a constraint, if any, remember if it is used for
13681 : * the table's replica identity or if it is a clustered index, so that
13682 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
1098 michael 13683 ECB : * those properties.
13684 : */
1122 peter 13685 CBC 202 : indoid = get_constraint_index(conoid);
1122 peter 13686 GIC 202 : if (OidIsValid(indoid))
1098 michael 13687 ECB : {
1122 peter 13688 GIC 102 : RememberReplicaIdentityForRebuilding(indoid, tab);
1098 michael 13689 CBC 102 : RememberClusterOnForRebuilding(indoid, tab);
13690 : }
1385 tgl 13691 ECB : }
1385 tgl 13692 CBC 241 : }
13693 :
1385 tgl 13694 ECB : /*
13695 : * Subroutine for ATExecAlterColumnType: remember that an index needs
13696 : * to be rebuilt (which we might already know).
13697 : */
13698 : static void
1385 tgl 13699 GIC 107 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
13700 : {
1385 tgl 13701 ECB : /*
13702 : * This de-duplication check is critical for two independent reasons: we
13703 : * mustn't try to recreate the same index twice, and if an index depends
13704 : * on more than one column whose type is to be altered, we must capture
13705 : * its definition string before applying any of the column type changes.
13706 : * ruleutils.c will get confused if we ask again later.
13707 : */
1385 tgl 13708 GIC 107 : if (!list_member_oid(tab->changedIndexOids, indoid))
13709 : {
13710 : /*
1385 tgl 13711 ECB : * Before adding it as an index-to-rebuild, we'd better see if it
13712 : * belongs to a constraint, and if so rebuild the constraint instead.
13713 : * Typically this check fails, because constraint indexes normally
13714 : * have only dependencies on their constraint. But it's possible for
13715 : * such an index to also have direct dependencies on table columns,
13716 : * for example with a partial exclusion constraint.
13717 : */
1385 tgl 13718 GIC 103 : Oid conoid = get_index_constraint(indoid);
13719 :
13720 103 : if (OidIsValid(conoid))
13721 : {
13722 6 : RememberConstraintForRebuilding(conoid, tab);
13723 : }
13724 : else
13725 : {
13726 : /* OK, capture the index's existing definition string */
13727 97 : char *defstring = pg_get_indexdef_string(indoid);
13728 :
13729 97 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
13730 : indoid);
13731 97 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
13732 : defstring);
13733 :
13734 : /*
13735 : * Remember if this index is used for the table's replica identity
13736 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13737 : * can queue up commands necessary to restore those properties.
1098 michael 13738 ECB : */
1122 peter 13739 GIC 97 : RememberReplicaIdentityForRebuilding(indoid, tab);
1098 michael 13740 97 : RememberClusterOnForRebuilding(indoid, tab);
13741 : }
1385 tgl 13742 ECB : }
1385 tgl 13743 CBC 107 : }
1385 tgl 13744 ECB :
13745 : /*
744 tomas.vondra 13746 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
13747 : * needs to be rebuilt (which we might already know).
13748 : */
13749 : static void
744 tomas.vondra 13750 GIC 7 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
13751 : {
744 tomas.vondra 13752 ECB : /*
13753 : * This de-duplication check is critical for two independent reasons: we
744 tomas.vondra 13754 EUB : * mustn't try to recreate the same statistics object twice, and if the
13755 : * statistics object depends on more than one column whose type is to be
13756 : * altered, we must capture its definition string before applying any of
13757 : * the type changes. ruleutils.c will get confused if we ask again later.
744 tomas.vondra 13758 ECB : */
744 tomas.vondra 13759 CBC 7 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
744 tomas.vondra 13760 ECB : {
13761 : /* OK, capture the statistics object's existing definition string */
744 tomas.vondra 13762 GIC 7 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
744 tomas.vondra 13763 ECB :
744 tomas.vondra 13764 CBC 7 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
744 tomas.vondra 13765 EUB : stxoid);
744 tomas.vondra 13766 GIC 7 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
13767 : defstring);
13768 : }
13769 7 : }
13770 :
7576 tgl 13771 ECB : /*
6913 13772 : * Cleanup after we've finished all the ALTER TYPE operations for a
13773 : * particular relation. We have to drop and recreate all the indexes
13774 : * and constraints that depend on the altered columns. We do the
1385 13775 : * actual dropping here, but re-creation is managed by adding work
13776 : * queue entries to do those steps later.
13777 : */
13778 : static void
4638 simon 13779 GIC 430 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
13780 : {
13781 : ObjectAddress obj;
13782 : ObjectAddresses *objects;
13783 : ListCell *def_item;
13784 : ListCell *oid_item;
13785 :
13786 : /*
13787 : * Collect all the constraints and indexes to drop so we can process them
13788 : * in a single call. That way we don't have to worry about dependencies
1668 alvherre 13789 ECB : * among them.
13790 : */
1668 alvherre 13791 CBC 430 : objects = new_object_addresses();
1668 alvherre 13792 ECB :
6913 tgl 13793 : /*
6385 bruce 13794 : * Re-parse the index and constraint definitions, and attach them to the
13795 : * appropriate work queue entries. We do this before dropping because in
13796 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13797 : * lock on the table the constraint is attached to, and we need to get
13798 : * that before reparsing/dropping.
13799 : *
3260 13800 : * We can't rely on the output of deparsing to tell us which relation to
13801 : * operate on, because concurrent activity might have made the name
3338 rhaas 13802 : * resolve differently. Instead, we've got to use the OID of the
3260 bruce 13803 : * constraint or index we're processing to figure out which relation to
13804 : * operate on.
13805 : */
4283 rhaas 13806 GIC 632 : forboth(oid_item, tab->changedConstraintOids,
13807 : def_item, tab->changedConstraintDefs)
3338 rhaas 13808 ECB : {
3260 bruce 13809 GIC 202 : Oid oldId = lfirst_oid(oid_item);
13810 : HeapTuple tup;
13811 : Form_pg_constraint con;
13812 : Oid relid;
13813 : Oid confrelid;
13814 : char contype;
2697 tgl 13815 ECB : bool conislocal;
13816 :
2697 tgl 13817 GIC 202 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
2118 13818 202 : if (!HeapTupleIsValid(tup)) /* should not happen */
2697 tgl 13819 UIC 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
2697 tgl 13820 GIC 202 : con = (Form_pg_constraint) GETSTRUCT(tup);
1985 13821 202 : if (OidIsValid(con->conrelid))
13822 195 : relid = con->conrelid;
13823 : else
13824 : {
13825 : /* must be a domain constraint */
1985 tgl 13826 CBC 7 : relid = get_typ_typrelid(getBaseType(con->contypid));
1985 tgl 13827 GIC 7 : if (!OidIsValid(relid))
1985 tgl 13828 LBC 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
13829 : }
2697 tgl 13830 GIC 202 : confrelid = con->confrelid;
1651 13831 202 : contype = con->contype;
2697 tgl 13832 CBC 202 : conislocal = con->conislocal;
2697 tgl 13833 GIC 202 : ReleaseSysCache(tup);
13834 :
1651 13835 202 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
1668 alvherre 13836 CBC 202 : add_exact_object_address(&obj, objects);
13837 :
13838 : /*
13839 : * If the constraint is inherited (only), we don't want to inject a
13840 : * new definition here; it'll get recreated when ATAddCheckConstraint
2697 tgl 13841 ECB : * recurses from adding the parent table's constraint. But we had to
13842 : * carry the info this far so that we can drop the constraint below.
13843 : */
2697 tgl 13844 CBC 202 : if (!conislocal)
2697 tgl 13845 GIC 8 : continue;
3338 rhaas 13846 ECB :
13847 : /*
13848 : * When rebuilding an FK constraint that references the table we're
1651 tgl 13849 : * modifying, we might not yet have any lock on the FK's table, so get
13850 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13851 : * step, so there's no value in asking for anything weaker.
13852 : */
1651 tgl 13853 CBC 194 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
1651 tgl 13854 GIC 18 : LockRelationOid(relid, AccessExclusiveLock);
1651 tgl 13855 ECB :
3338 rhaas 13856 GIC 194 : ATPostAlterTypeParse(oldId, relid, confrelid,
3338 rhaas 13857 CBC 194 : (char *) lfirst(def_item),
4283 rhaas 13858 GIC 194 : wqueue, lockmode, tab->rewrite);
3338 rhaas 13859 ECB : }
4283 rhaas 13860 GIC 527 : forboth(oid_item, tab->changedIndexOids,
13861 : def_item, tab->changedIndexDefs)
3338 rhaas 13862 ECB : {
3260 bruce 13863 CBC 97 : Oid oldId = lfirst_oid(oid_item);
13864 : Oid relid;
3338 rhaas 13865 ECB :
3338 rhaas 13866 GIC 97 : relid = IndexGetRelation(oldId, false);
13867 97 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
13868 97 : (char *) lfirst(def_item),
4283 13869 97 : wqueue, lockmode, tab->rewrite);
13870 :
1651 tgl 13871 CBC 97 : ObjectAddressSet(obj, RelationRelationId, oldId);
1668 alvherre 13872 GIC 97 : add_exact_object_address(&obj, objects);
13873 : }
13874 :
13875 : /* add dependencies for new statistics */
744 tomas.vondra 13876 GBC 437 : forboth(oid_item, tab->changedStatisticsOids,
13877 : def_item, tab->changedStatisticsDefs)
13878 : {
744 tomas.vondra 13879 CBC 7 : Oid oldId = lfirst_oid(oid_item);
13880 : Oid relid;
13881 :
13882 7 : relid = StatisticsGetRelation(oldId, false);
13883 7 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
13884 7 : (char *) lfirst(def_item),
13885 7 : wqueue, lockmode, tab->rewrite);
13886 :
13887 7 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
744 tomas.vondra 13888 GIC 7 : add_exact_object_address(&obj, objects);
744 tomas.vondra 13889 ECB : }
13890 :
13891 : /*
13892 : * Queue up command to restore replica identity index marking
13893 : */
1122 peter 13894 GIC 430 : if (tab->replicaIdentityIndex)
13895 : {
13896 9 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
1122 peter 13897 GBC 9 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
13898 :
1122 peter 13899 GIC 9 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
13900 9 : subcmd->name = tab->replicaIdentityIndex;
13901 9 : cmd->subtype = AT_ReplicaIdentity;
13902 9 : cmd->def = (Node *) subcmd;
13903 :
13904 : /* do it after indexes and constraints */
13905 9 : tab->subcmds[AT_PASS_OLD_CONSTR] =
13906 9 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13907 : }
1122 peter 13908 EUB :
13909 : /*
13910 : * Queue up command to restore marking of index used for cluster.
13911 : */
1098 michael 13912 GIC 430 : if (tab->clusterOnIndex)
13913 : {
13914 9 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
13915 :
1098 michael 13916 GBC 9 : cmd->subtype = AT_ClusterOn;
1098 michael 13917 GIC 9 : cmd->name = tab->clusterOnIndex;
13918 :
13919 : /* do it after indexes and constraints */
13920 9 : tab->subcmds[AT_PASS_OLD_CONSTR] =
13921 9 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13922 : }
13923 :
13924 : /*
13925 : * It should be okay to use DROP_RESTRICT here, since nothing else should
1668 alvherre 13926 EUB : * be depending on these objects.
13927 : */
1668 alvherre 13928 GIC 430 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
13929 :
13930 430 : free_object_addresses(objects);
13931 :
13932 : /*
13933 : * The objects will get recreated during subsequent passes over the work
6385 bruce 13934 ECB : * queue.
13935 : */
6913 tgl 13936 CBC 430 : }
13937 :
1385 tgl 13938 ECB : /*
744 tomas.vondra 13939 : * Parse the previously-saved definition string for a constraint, index or
13940 : * statistics object against the newly-established column data type(s), and
13941 : * queue up the resulting command parsetrees for execution.
13942 : *
13943 : * This might fail if, for example, you have a WHERE clause that uses an
13944 : * operator that's not available for the new column type.
1385 tgl 13945 : */
13946 : static void
3338 rhaas 13947 GIC 298 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
13948 : List **wqueue, LOCKMODE lockmode, bool rewrite)
13949 : {
13950 : List *raw_parsetree_list;
13951 : List *querytree_list;
13952 : ListCell *list_item;
13953 : Relation rel;
13954 :
13955 : /*
13956 : * We expect that we will get only ALTER TABLE and CREATE INDEX
5624 bruce 13957 ECB : * statements. Hence, there is no need to pass them through
13958 : * parse_analyze_*() or the rewriter, but instead we need to pass them
13959 : * through parse_utilcmd.c to make them ready for execution.
13960 : */
825 tgl 13961 GIC 298 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
6913 13962 298 : querytree_list = NIL;
13963 596 : foreach(list_item, raw_parsetree_list)
13964 : {
2190 13965 298 : RawStmt *rs = lfirst_node(RawStmt, list_item);
2276 tgl 13966 CBC 298 : Node *stmt = rs->stmt;
13967 :
5769 tgl 13968 GIC 298 : if (IsA(stmt, IndexStmt))
5769 tgl 13969 CBC 97 : querytree_list = lappend(querytree_list,
3338 rhaas 13970 GIC 97 : transformIndexStmt(oldRelId,
13971 : (IndexStmt *) stmt,
13972 : cmd));
5769 tgl 13973 201 : else if (IsA(stmt, AlterTableStmt))
13974 : {
1180 tgl 13975 ECB : List *beforeStmts;
13976 : List *afterStmts;
13977 :
1180 tgl 13978 GBC 187 : stmt = (Node *) transformAlterTableStmt(oldRelId,
13979 : (AlterTableStmt *) stmt,
13980 : cmd,
13981 : &beforeStmts,
13982 : &afterStmts);
1180 tgl 13983 GIC 187 : querytree_list = list_concat(querytree_list, beforeStmts);
13984 187 : querytree_list = lappend(querytree_list, stmt);
13985 187 : querytree_list = list_concat(querytree_list, afterStmts);
13986 : }
744 tomas.vondra 13987 14 : else if (IsA(stmt, CreateStatsStmt))
13988 7 : querytree_list = lappend(querytree_list,
13989 7 : transformStatsStmt(oldRelId,
13990 : (CreateStatsStmt *) stmt,
13991 : cmd));
13992 : else
5769 tgl 13993 7 : querytree_list = lappend(querytree_list, stmt);
13994 : }
13995 :
13996 : /* Caller should already have acquired whatever lock we need. */
3338 rhaas 13997 298 : rel = relation_open(oldRelId, NoLock);
13998 :
13999 : /*
14000 : * Attach each generated command to the proper place in the work queue.
14001 : * Note this could result in creation of entirely new work-queue entries.
14002 : *
14003 : * Also note that we have to tweak the command subtypes, because it turns
14004 : * out that re-creation of indexes and constraints has to act a bit
14005 : * differently from initial creation.
14006 : */
6913 tgl 14007 596 : foreach(list_item, querytree_list)
14008 : {
5769 14009 298 : Node *stm = (Node *) lfirst(list_item);
14010 : AlteredTableInfo *tab;
14011 :
2826 heikki.linnakangas 14012 298 : tab = ATGetQueueEntry(wqueue, rel);
14013 :
14014 298 : if (IsA(stm, IndexStmt))
14015 : {
14016 97 : IndexStmt *stmt = (IndexStmt *) stm;
2826 heikki.linnakangas 14017 EUB : AlterTableCmd *newcmd;
14018 :
2826 heikki.linnakangas 14019 GIC 97 : if (!rewrite)
14020 27 : TryReuseIndex(oldId, stmt);
1445 alvherre 14021 97 : stmt->reset_default_tblspc = true;
14022 : /* keep the index's comment */
2826 heikki.linnakangas 14023 97 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14024 :
14025 97 : newcmd = makeNode(AlterTableCmd);
14026 97 : newcmd->subtype = AT_ReAddIndex;
14027 97 : newcmd->def = (Node *) stmt;
2826 heikki.linnakangas 14028 CBC 97 : tab->subcmds[AT_PASS_OLD_INDEX] =
2826 heikki.linnakangas 14029 GIC 97 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14030 : }
14031 201 : else if (IsA(stm, AlterTableStmt))
14032 : {
14033 187 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
14034 : ListCell *lcmd;
2826 heikki.linnakangas 14035 ECB :
2826 heikki.linnakangas 14036 GIC 434 : foreach(lcmd, stmt->cmds)
14037 : {
629 peter 14038 247 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
2826 heikki.linnakangas 14039 ECB :
2826 heikki.linnakangas 14040 GIC 247 : if (cmd->subtype == AT_AddIndex)
14041 : {
14042 : IndexStmt *indstmt;
2826 heikki.linnakangas 14043 ECB : Oid indoid;
14044 :
2264 andres 14045 GIC 102 : indstmt = castNode(IndexStmt, cmd->def);
2826 heikki.linnakangas 14046 102 : indoid = get_constraint_index(oldId);
14047 :
4283 rhaas 14048 CBC 102 : if (!rewrite)
2826 heikki.linnakangas 14049 GIC 24 : TryReuseIndex(indoid, indstmt);
14050 : /* keep any comment on the index */
2826 heikki.linnakangas 14051 CBC 102 : indstmt->idxcomment = GetComment(indoid,
14052 : RelationRelationId, 0);
1445 alvherre 14053 102 : indstmt->reset_default_tblspc = true;
14054 :
2826 heikki.linnakangas 14055 GIC 102 : cmd->subtype = AT_ReAddIndex;
6797 bruce 14056 CBC 102 : tab->subcmds[AT_PASS_OLD_INDEX] =
2826 heikki.linnakangas 14057 102 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
2826 heikki.linnakangas 14058 ECB :
14059 : /* recreate any comment on the constraint */
2826 heikki.linnakangas 14060 CBC 102 : RebuildConstraintComment(tab,
2826 heikki.linnakangas 14061 EUB : AT_PASS_OLD_INDEX,
14062 : oldId,
1985 tgl 14063 ECB : rel,
14064 : NIL,
1985 tgl 14065 GBC 102 : indstmt->idxname);
6797 bruce 14066 EUB : }
2826 heikki.linnakangas 14067 GBC 145 : else if (cmd->subtype == AT_AddConstraint)
14068 : {
1985 tgl 14069 GIC 85 : Constraint *con = castNode(Constraint, cmd->def);
2826 heikki.linnakangas 14070 ECB :
2826 heikki.linnakangas 14071 GIC 85 : con->old_pktable_oid = refRelId;
14072 : /* rewriting neither side of a FK */
2826 heikki.linnakangas 14073 CBC 85 : if (con->contype == CONSTR_FOREIGN &&
2826 heikki.linnakangas 14074 GIC 30 : !rewrite && tab->rewrite == 0)
2826 heikki.linnakangas 14075 CBC 3 : TryReuseForeignKey(oldId, con);
1445 alvherre 14076 GIC 85 : con->reset_default_tblspc = true;
2826 heikki.linnakangas 14077 85 : cmd->subtype = AT_ReAddConstraint;
14078 85 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14079 85 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14080 :
14081 : /* recreate any comment on the constraint */
2826 heikki.linnakangas 14082 CBC 85 : RebuildConstraintComment(tab,
14083 : AT_PASS_OLD_CONSTR,
14084 : oldId,
14085 : rel,
14086 : NIL,
1985 tgl 14087 GIC 85 : con->conname);
6913 tgl 14088 ECB : }
2 alvherre 14089 GNC 60 : else if (cmd->subtype == AT_SetAttNotNull)
14090 : {
1447 tgl 14091 ECB : /*
14092 : * The parser will create AT_AttSetNotNull subcommands for
14093 : * columns of PRIMARY KEY indexes/constraints, but we need
14094 : * not do anything with them here, because the columns'
14095 : * NOT NULL marks will already have been propagated into
14096 : * the new table definition.
14097 : */
14098 : }
14099 : else
2697 tgl 14100 UIC 0 : elog(ERROR, "unexpected statement subtype: %d",
14101 : (int) cmd->subtype);
14102 : }
14103 : }
1985 tgl 14104 GIC 14 : else if (IsA(stm, AlterDomainStmt))
14105 : {
14106 7 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
1985 tgl 14107 ECB :
1985 tgl 14108 GIC 7 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
1985 tgl 14109 ECB : {
1985 tgl 14110 CBC 7 : Constraint *con = castNode(Constraint, stmt->def);
14111 7 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14112 :
1985 tgl 14113 GIC 7 : cmd->subtype = AT_ReAddDomainConstraint;
1985 tgl 14114 CBC 7 : cmd->def = (Node *) stmt;
1985 tgl 14115 GIC 7 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14116 7 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14117 :
1985 tgl 14118 ECB : /* recreate any comment on the constraint */
1985 tgl 14119 CBC 7 : RebuildConstraintComment(tab,
1985 tgl 14120 ECB : AT_PASS_OLD_CONSTR,
14121 : oldId,
14122 : NULL,
14123 : stmt->typeName,
1985 tgl 14124 GIC 7 : con->conname);
1985 tgl 14125 ECB : }
14126 : else
1985 tgl 14127 LBC 0 : elog(ERROR, "unexpected statement subtype: %d",
14128 : (int) stmt->subtype);
1985 tgl 14129 ECB : }
744 tomas.vondra 14130 CBC 7 : else if (IsA(stm, CreateStatsStmt))
744 tomas.vondra 14131 ECB : {
697 tgl 14132 GIC 7 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
744 tomas.vondra 14133 ECB : AlterTableCmd *newcmd;
14134 :
14135 : /* keep the statistics object's comment */
744 tomas.vondra 14136 CBC 7 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
744 tomas.vondra 14137 ECB :
744 tomas.vondra 14138 GIC 7 : newcmd = makeNode(AlterTableCmd);
14139 7 : newcmd->subtype = AT_ReAddStatistics;
14140 7 : newcmd->def = (Node *) stmt;
744 tomas.vondra 14141 CBC 7 : tab->subcmds[AT_PASS_MISC] =
14142 7 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
744 tomas.vondra 14143 ECB : }
2826 heikki.linnakangas 14144 : else
2826 heikki.linnakangas 14145 UBC 0 : elog(ERROR, "unexpected statement type: %d",
14146 : (int) nodeTag(stm));
14147 : }
3338 rhaas 14148 ECB :
3338 rhaas 14149 CBC 298 : relation_close(rel, NoLock);
7653 tgl 14150 298 : }
7653 tgl 14151 ECB :
2826 heikki.linnakangas 14152 : /*
1985 tgl 14153 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14154 : * for a table or domain constraint that is being rebuilt.
14155 : *
14156 : * objid is the OID of the constraint.
14157 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14158 : * as a string list) for a domain constraint.
14159 : * (We could dig that info, as well as the conname, out of the pg_constraint
14160 : * entry; but callers already have them so might as well pass them.)
14161 : */
2826 heikki.linnakangas 14162 : static void
2826 heikki.linnakangas 14163 CBC 194 : RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
14164 : Relation rel, List *domname,
14165 : const char *conname)
14166 : {
14167 : CommentStmt *cmd;
2826 heikki.linnakangas 14168 ECB : char *comment_str;
14169 : AlterTableCmd *newcmd;
14170 :
14171 : /* Look for comment for object wanted, and leave if none */
2826 heikki.linnakangas 14172 GIC 194 : comment_str = GetComment(objid, ConstraintRelationId, 0);
14173 194 : if (comment_str == NULL)
14174 161 : return;
14175 :
14176 : /* Build CommentStmt node, copying all input data for safety */
14177 33 : cmd = makeNode(CommentStmt);
1985 tgl 14178 33 : if (rel)
14179 : {
1985 tgl 14180 CBC 27 : cmd->objtype = OBJECT_TABCONSTRAINT;
1985 tgl 14181 GIC 27 : cmd->object = (Node *)
14182 27 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14183 : makeString(pstrdup(RelationGetRelationName(rel))),
14184 : makeString(pstrdup(conname)));
14185 : }
14186 : else
1985 tgl 14187 ECB : {
1985 tgl 14188 GIC 6 : cmd->objtype = OBJECT_DOMCONSTRAINT;
1985 tgl 14189 CBC 6 : cmd->object = (Node *)
1985 tgl 14190 GIC 6 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
1985 tgl 14191 ECB : makeString(pstrdup(conname)));
1985 tgl 14192 EUB : }
2826 heikki.linnakangas 14193 GIC 33 : cmd->comment = comment_str;
2826 heikki.linnakangas 14194 ECB :
14195 : /* Append it to list of commands */
2826 heikki.linnakangas 14196 GIC 33 : newcmd = makeNode(AlterTableCmd);
14197 33 : newcmd->subtype = AT_ReAddComment;
14198 33 : newcmd->def = (Node *) cmd;
14199 33 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14200 : }
2826 heikki.linnakangas 14201 ECB :
14202 : /*
14203 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14204 : * for the real analysis, then mutates the IndexStmt based on that verdict.
14205 : */
14206 : static void
4283 rhaas 14207 CBC 51 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
14208 : {
4283 rhaas 14209 GIC 51 : if (CheckIndexCompatible(oldId,
4283 rhaas 14210 CBC 51 : stmt->accessMethod,
14211 : stmt->indexParams,
14212 : stmt->excludeOpNames))
4283 rhaas 14213 ECB : {
3955 bruce 14214 GIC 51 : Relation irel = index_open(oldId, NoLock);
14215 :
14216 : /* If it's a partitioned index, there is no storage to share. */
1444 tgl 14217 CBC 51 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14218 : {
277 rhaas 14219 GNC 36 : stmt->oldNumber = irel->rd_locator.relNumber;
1100 noah 14220 GIC 36 : stmt->oldCreateSubid = irel->rd_createSubid;
277 rhaas 14221 GNC 36 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14222 : }
4283 rhaas 14223 GIC 51 : index_close(irel, NoLock);
14224 : }
14225 51 : }
14226 :
4059 alvherre 14227 ECB : /*
14228 : * Subroutine for ATPostAlterTypeParse().
14229 : *
14230 : * Stash the old P-F equality operator into the Constraint node, for possible
14231 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14232 : * this constraint can be skipped.
4059 alvherre 14233 EUB : */
14234 : static void
4059 alvherre 14235 CBC 3 : TryReuseForeignKey(Oid oldId, Constraint *con)
14236 : {
14237 : HeapTuple tup;
14238 : Datum adatum;
14239 : ArrayType *arr;
14240 : Oid *rawarr;
4059 alvherre 14241 ECB : int numkeys;
14242 : int i;
14243 :
4059 alvherre 14244 CBC 3 : Assert(con->contype == CONSTR_FOREIGN);
3955 bruce 14245 GIC 3 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
4059 alvherre 14246 ECB :
4059 alvherre 14247 GBC 3 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
4059 alvherre 14248 GIC 3 : if (!HeapTupleIsValid(tup)) /* should not happen */
4059 alvherre 14249 LBC 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14250 :
15 dgustafsson 14251 GNC 3 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14252 : Anum_pg_constraint_conpfeqop);
4059 alvherre 14253 GIC 3 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14254 3 : numkeys = ARR_DIMS(arr)[0];
4059 alvherre 14255 ECB : /* test follows the one in ri_FetchConstraintInfo() */
4059 alvherre 14256 GIC 3 : if (ARR_NDIM(arr) != 1 ||
14257 3 : ARR_HASNULL(arr) ||
14258 3 : ARR_ELEMTYPE(arr) != OIDOID)
4059 alvherre 14259 UIC 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
4059 alvherre 14260 GIC 3 : rawarr = (Oid *) ARR_DATA_PTR(arr);
14261 :
14262 : /* stash a List of the operator Oids in our Constraint node */
14263 6 : for (i = 0; i < numkeys; i++)
1363 tgl 14264 CBC 3 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14265 :
4059 alvherre 14266 GIC 3 : ReleaseSysCache(tup);
4059 alvherre 14267 CBC 3 : }
14268 :
14269 : /*
1385 tgl 14270 ECB : * ALTER COLUMN .. OPTIONS ( ... )
14271 : *
14272 : * Returns the address of the modified column
14273 : */
14274 : static ObjectAddress
1385 tgl 14275 GIC 82 : ATExecAlterColumnGenericOptions(Relation rel,
14276 : const char *colName,
14277 : List *options,
14278 : LOCKMODE lockmode)
14279 : {
14280 : Relation ftrel;
1385 tgl 14281 ECB : Relation attrel;
14282 : ForeignServer *server;
14283 : ForeignDataWrapper *fdw;
14284 : HeapTuple tuple;
14285 : HeapTuple newtuple;
14286 : bool isnull;
14287 : Datum repl_val[Natts_pg_attribute];
14288 : bool repl_null[Natts_pg_attribute];
14289 : bool repl_repl[Natts_pg_attribute];
14290 : Datum datum;
14291 : Form_pg_foreign_table fttableform;
14292 : Form_pg_attribute atttableform;
14293 : AttrNumber attnum;
14294 : ObjectAddress address;
14295 :
1385 tgl 14296 GIC 82 : if (options == NIL)
1385 tgl 14297 UIC 0 : return InvalidObjectAddress;
14298 :
14299 : /* First, determine FDW validator associated to the foreign table. */
1385 tgl 14300 GIC 82 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14301 82 : tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
14302 82 : if (!HeapTupleIsValid(tuple))
1385 tgl 14303 UIC 0 : ereport(ERROR,
1385 tgl 14304 ECB : (errcode(ERRCODE_UNDEFINED_OBJECT),
14305 : errmsg("foreign table \"%s\" does not exist",
14306 : RelationGetRelationName(rel))));
1385 tgl 14307 GIC 82 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14308 82 : server = GetForeignServer(fttableform->ftserver);
14309 82 : fdw = GetForeignDataWrapper(server->fdwid);
14310 :
14311 82 : table_close(ftrel, AccessShareLock);
14312 82 : ReleaseSysCache(tuple);
14313 :
1385 tgl 14314 CBC 82 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
1385 tgl 14315 GIC 82 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
1385 tgl 14316 CBC 82 : if (!HeapTupleIsValid(tuple))
1385 tgl 14317 UIC 0 : ereport(ERROR,
1385 tgl 14318 ECB : (errcode(ERRCODE_UNDEFINED_COLUMN),
14319 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14320 : colName, RelationGetRelationName(rel))));
14321 :
14322 : /* Prevent them from altering a system attribute */
1385 tgl 14323 CBC 82 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
1385 tgl 14324 GIC 82 : attnum = atttableform->attnum;
1385 tgl 14325 CBC 82 : if (attnum <= 0)
1385 tgl 14326 GIC 3 : ereport(ERROR,
1385 tgl 14327 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14328 : errmsg("cannot alter system column \"%s\"", colName)));
14329 :
14330 :
14331 : /* Initialize buffers for new tuple values */
1385 tgl 14332 GIC 79 : memset(repl_val, 0, sizeof(repl_val));
14333 79 : memset(repl_null, false, sizeof(repl_null));
14334 79 : memset(repl_repl, false, sizeof(repl_repl));
1385 tgl 14335 ECB :
14336 : /* Extract the current options */
1385 tgl 14337 GIC 79 : datum = SysCacheGetAttr(ATTNAME,
14338 : tuple,
1385 tgl 14339 ECB : Anum_pg_attribute_attfdwoptions,
14340 : &isnull);
1385 tgl 14341 GIC 79 : if (isnull)
14342 74 : datum = PointerGetDatum(NULL);
14343 :
14344 : /* Transform the options */
14345 79 : datum = transformGenericOptions(AttributeRelationId,
1385 tgl 14346 ECB : datum,
14347 : options,
14348 : fdw->fdwvalidator);
14349 :
1385 tgl 14350 GIC 79 : if (PointerIsValid(DatumGetPointer(datum)))
14351 79 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14352 : else
1385 tgl 14353 UIC 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14354 :
1385 tgl 14355 CBC 79 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14356 :
14357 : /* Everything looks good - update the tuple */
1385 tgl 14358 ECB :
1385 tgl 14359 GIC 79 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
1385 tgl 14360 ECB : repl_val, repl_null, repl_repl);
14361 :
1385 tgl 14362 CBC 79 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14363 :
1385 tgl 14364 GIC 79 : InvokeObjectPostAlterHook(RelationRelationId,
1385 tgl 14365 ECB : RelationGetRelid(rel),
14366 : atttableform->attnum);
1385 tgl 14367 GIC 79 : ObjectAddressSubSet(address, RelationRelationId,
14368 : RelationGetRelid(rel), attnum);
14369 :
14370 79 : ReleaseSysCache(tuple);
14371 :
14372 79 : table_close(attrel, RowExclusiveLock);
14373 :
14374 79 : heap_freetuple(newtuple);
1385 tgl 14375 ECB :
1385 tgl 14376 GIC 79 : return address;
14377 : }
14378 :
14379 : /*
14380 : * ALTER TABLE OWNER
14381 : *
14382 : * recursing is true if we are recursing from a table to its indexes,
14383 : * sequences, or toast table. We don't allow the ownership of those things to
14384 : * be changed separately from the parent table. Also, we can skip permission
14385 : * checks (this is necessary not just an optimization, else we'd fail to
14386 : * handle toast tables properly).
5812 tgl 14387 ECB : *
14388 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14389 : * free-standing composite type.
14390 : */
14391 : void
4638 simon 14392 GIC 940 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14393 : {
14394 : Relation target_rel;
14395 : Relation class_rel;
14396 : HeapTuple tuple;
14397 : Form_pg_class tuple_class;
14398 :
14399 : /*
14400 : * Get exclusive lock till end of transaction on the target table. Use
14401 : * relation_open so that we can work on indexes and sequences.
6772 tgl 14402 ECB : */
4638 simon 14403 GIC 940 : target_rel = relation_open(relationOid, lockmode);
14404 :
7653 tgl 14405 ECB : /* Get its pg_class tuple, too */
1539 andres 14406 GIC 940 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
14407 :
4802 rhaas 14408 940 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
7653 tgl 14409 940 : if (!HeapTupleIsValid(tuple))
7203 tgl 14410 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
7653 tgl 14411 GIC 940 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
14412 :
7653 tgl 14413 ECB : /* Can we change the ownership of this tuple? */
7203 tgl 14414 CBC 940 : switch (tuple_class->relkind)
7203 tgl 14415 EUB : {
7203 tgl 14416 CBC 826 : case RELKIND_RELATION:
7203 tgl 14417 ECB : case RELKIND_VIEW:
3689 kgrittn 14418 : case RELKIND_MATVIEW:
14419 : case RELKIND_FOREIGN_TABLE:
14420 : case RELKIND_PARTITIONED_TABLE:
14421 : /* ok to change owner */
7203 tgl 14422 CBC 826 : break;
6457 14423 42 : case RELKIND_INDEX:
6439 tgl 14424 GBC 42 : if (!recursing)
14425 : {
6439 tgl 14426 ECB : /*
14427 : * Because ALTER INDEX OWNER used to be allowed, and in fact
14428 : * is generated by old versions of pg_dump, we give a warning
14429 : * and do nothing rather than erroring out. Also, to avoid
14430 : * unnecessary chatter while restoring those old dumps, say
14431 : * nothing at all if the command would be a no-op anyway.
14432 : */
6439 tgl 14433 UIC 0 : if (tuple_class->relowner != newOwnerId)
14434 0 : ereport(WARNING,
14435 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14436 : errmsg("cannot change owner of index \"%s\"",
14437 : NameStr(tuple_class->relname)),
14438 : errhint("Change the ownership of the index's table, instead.")));
14439 : /* quick hack to exit via the no-op path */
6439 tgl 14440 LBC 0 : newOwnerId = tuple_class->relowner;
6439 tgl 14441 ECB : }
6439 tgl 14442 GIC 42 : break;
1906 alvherre 14443 10 : case RELKIND_PARTITIONED_INDEX:
14444 10 : if (recursing)
14445 10 : break;
1906 alvherre 14446 UIC 0 : ereport(ERROR,
14447 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14448 : errmsg("cannot change owner of index \"%s\"",
1906 alvherre 14449 ECB : NameStr(tuple_class->relname)),
14450 : errhint("Change the ownership of the index's table, instead.")));
14451 : break;
6075 tgl 14452 CBC 41 : case RELKIND_SEQUENCE:
14453 41 : if (!recursing &&
14454 26 : tuple_class->relowner != newOwnerId)
14455 : {
6075 tgl 14456 ECB : /* if it's an owned sequence, disallow changing it by itself */
14457 : Oid tableId;
14458 : int32 colId;
14459 :
2194 peter_e 14460 UIC 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
14461 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
6075 tgl 14462 LBC 0 : ereport(ERROR,
6075 tgl 14463 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14464 : errmsg("cannot change owner of sequence \"%s\"",
14465 : NameStr(tuple_class->relname)),
14466 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
2118 14467 : NameStr(tuple_class->relname),
14468 : get_rel_name(tableId))));
14469 : }
6075 tgl 14470 GIC 41 : break;
5812 14471 3 : case RELKIND_COMPOSITE_TYPE:
5671 tgl 14472 CBC 3 : if (recursing)
5671 tgl 14473 GIC 3 : break;
5671 tgl 14474 UIC 0 : ereport(ERROR,
5671 tgl 14475 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14476 : errmsg("\"%s\" is a composite type",
14477 : NameStr(tuple_class->relname)),
14478 : errhint("Use ALTER TYPE instead.")));
14479 : break;
5671 tgl 14480 CBC 18 : case RELKIND_TOASTVALUE:
6457 14481 18 : if (recursing)
6457 tgl 14482 GIC 18 : break;
1061 alvherre 14483 ECB : /* FALL THRU */
7203 tgl 14484 : default:
7203 tgl 14485 UIC 0 : ereport(ERROR,
14486 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14487 : errmsg("cannot change owner of relation \"%s\"",
14488 : NameStr(tuple_class->relname)),
14489 : errdetail_relkind_not_supported(tuple_class->relkind)));
7203 tgl 14490 ECB : }
14491 :
6797 bruce 14492 : /*
6862 tgl 14493 : * If the new owner is the same as the existing owner, consider the
14494 : * command to have succeeded. This is for dump restoration purposes.
7655 bruce 14495 : */
6494 tgl 14496 CBC 940 : if (tuple_class->relowner != newOwnerId)
6862 tgl 14497 ECB : {
6825 14498 : Datum repl_val[Natts_pg_class];
14499 : bool repl_null[Natts_pg_class];
14500 : bool repl_repl[Natts_pg_class];
6797 bruce 14501 : Acl *newAcl;
6825 tgl 14502 : Datum aclDatum;
14503 : bool isNull;
14504 : HeapTuple newtuple;
14505 :
14506 : /* skip permission checks when recursing to index or toast table */
6457 tgl 14507 GIC 224 : if (!recursing)
6457 tgl 14508 ECB : {
14509 : /* Superusers can always do it */
6439 tgl 14510 CBC 136 : if (!superuser())
14511 : {
6385 bruce 14512 21 : Oid namespaceOid = tuple_class->relnamespace;
6439 tgl 14513 ECB : AclResult aclresult;
14514 :
14515 : /* Otherwise, must be owner of the existing object */
147 peter 14516 GNC 21 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
1954 peter_e 14517 LBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
6439 tgl 14518 UIC 0 : RelationGetRelationName(target_rel));
14519 :
14520 : /* Must be able to become new owner */
142 rhaas 14521 GNC 21 : check_can_set_role(GetUserId(), newOwnerId);
14522 :
14523 : /* New owner must have CREATE privilege on namespace */
147 peter 14524 15 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
14525 : ACL_CREATE);
6439 tgl 14526 CBC 15 : if (aclresult != ACLCHECK_OK)
1954 peter_e 14527 UIC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
6439 tgl 14528 0 : get_namespace_name(namespaceOid));
14529 : }
14530 : }
14531 :
5271 tgl 14532 CBC 218 : memset(repl_null, false, sizeof(repl_null));
5271 tgl 14533 GIC 218 : memset(repl_repl, false, sizeof(repl_repl));
14534 :
14535 218 : repl_repl[Anum_pg_class_relowner - 1] = true;
6494 14536 218 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
14537 :
14538 : /*
14539 : * Determine the modified ACL for the new owner. This is only
14540 : * necessary when the ACL is non-null.
14541 : */
6825 14542 218 : aclDatum = SysCacheGetAttr(RELOID, tuple,
6825 tgl 14543 ECB : Anum_pg_class_relacl,
14544 : &isNull);
6825 tgl 14545 GIC 218 : if (!isNull)
14546 : {
14547 16 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
14548 : tuple_class->relowner, newOwnerId);
5271 14549 16 : repl_repl[Anum_pg_class_relacl - 1] = true;
6825 14550 16 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
14551 : }
14552 :
5271 14553 218 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
14554 :
2259 alvherre 14555 218 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
14556 :
6825 tgl 14557 CBC 218 : heap_freetuple(newtuple);
7655 bruce 14558 ECB :
4127 tgl 14559 : /*
14560 : * We must similarly update any per-column ACLs to reflect the new
14561 : * owner; for neatness reasons that's split out as a subroutine.
14562 : */
4127 tgl 14563 GIC 218 : change_owner_fix_column_acls(relationOid,
4127 tgl 14564 ECB : tuple_class->relowner,
14565 : newOwnerId);
14566 :
14567 : /*
14568 : * Update owner dependency reference, if any. A composite type has
5812 14569 : * none, because it's tracked for the pg_type entry instead of here;
14570 : * indexes and TOAST tables don't have their own entries either.
14571 : */
5812 tgl 14572 GIC 218 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
5809 14573 215 : tuple_class->relkind != RELKIND_INDEX &&
1906 alvherre 14574 CBC 173 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
5809 tgl 14575 GIC 163 : tuple_class->relkind != RELKIND_TOASTVALUE)
5812 14576 145 : changeDependencyOnOwner(RelationRelationId, relationOid,
14577 : newOwnerId);
14578 :
6457 tgl 14579 ECB : /*
4309 peter_e 14580 : * Also change the ownership of the table's row type, if it has one
6457 tgl 14581 : */
1006 tgl 14582 GIC 218 : if (OidIsValid(tuple_class->reltype))
2670 alvherre 14583 CBC 139 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
6457 tgl 14584 ECB :
6862 14585 : /*
14586 : * If we are operating on a table or materialized view, also change
14587 : * the ownership of any indexes and sequences that belong to the
14588 : * relation, as well as its toast table (if it has one).
14589 : */
6862 tgl 14590 GIC 218 : if (tuple_class->relkind == RELKIND_RELATION ||
1906 alvherre 14591 112 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
3689 kgrittn 14592 93 : tuple_class->relkind == RELKIND_MATVIEW ||
6862 tgl 14593 CBC 93 : tuple_class->relkind == RELKIND_TOASTVALUE)
14594 : {
14595 : List *index_oid_list;
14596 : ListCell *i;
14597 :
14598 : /* Find all the indexes belonging to this relation */
6862 tgl 14599 GIC 143 : index_oid_list = RelationGetIndexList(target_rel);
14600 :
14601 : /* For each index, recursively change its ownership */
14602 195 : foreach(i, index_oid_list)
4638 simon 14603 CBC 52 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
14604 :
6862 tgl 14605 143 : list_free(index_oid_list);
14606 : }
14607 :
1760 peter_e 14608 ECB : /* If it has a toast table, recurse to change its ownership */
1760 peter_e 14609 GIC 218 : if (tuple_class->reltoastrelid != InvalidOid)
1760 peter_e 14610 CBC 18 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
14611 : true, lockmode);
6772 tgl 14612 ECB :
14613 : /* If it has dependent sequences, recurse to change them too */
1760 peter_e 14614 GIC 218 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
7655 bruce 14615 ECB : }
14616 :
3675 rhaas 14617 CBC 934 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
14618 :
6825 tgl 14619 934 : ReleaseSysCache(tuple);
1539 andres 14620 GIC 934 : table_close(class_rel, RowExclusiveLock);
7601 tgl 14621 CBC 934 : relation_close(target_rel, NoLock);
7655 bruce 14622 934 : }
7655 bruce 14623 ECB :
4127 tgl 14624 : /*
14625 : * change_owner_fix_column_acls
14626 : *
14627 : * Helper function for ATExecChangeOwner. Scan the columns of the table
14628 : * and fix any non-null column ACLs to reflect the new owner.
14629 : */
14630 : static void
4127 tgl 14631 GIC 218 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
4127 tgl 14632 ECB : {
14633 : Relation attRelation;
14634 : SysScanDesc scan;
14635 : ScanKeyData key[1];
14636 : HeapTuple attributeTuple;
14637 :
1539 andres 14638 GIC 218 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
4127 tgl 14639 218 : ScanKeyInit(&key[0],
14640 : Anum_pg_attribute_attrelid,
4127 tgl 14641 ECB : BTEqualStrategyNumber, F_OIDEQ,
14642 : ObjectIdGetDatum(relationOid));
4127 tgl 14643 GIC 218 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
3568 rhaas 14644 ECB : true, NULL, 1, key);
4127 tgl 14645 CBC 1502 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
14646 : {
14647 1284 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
14648 : Datum repl_val[Natts_pg_attribute];
4127 tgl 14649 ECB : bool repl_null[Natts_pg_attribute];
14650 : bool repl_repl[Natts_pg_attribute];
14651 : Acl *newAcl;
14652 : Datum aclDatum;
14653 : bool isNull;
14654 : HeapTuple newtuple;
14655 :
14656 : /* Ignore dropped columns */
4127 tgl 14657 GIC 1284 : if (att->attisdropped)
14658 1284 : continue;
14659 :
14660 1284 : aclDatum = heap_getattr(attributeTuple,
4127 tgl 14661 ECB : Anum_pg_attribute_attacl,
14662 : RelationGetDescr(attRelation),
14663 : &isNull);
14664 : /* Null ACLs do not require changes */
4127 tgl 14665 CBC 1284 : if (isNull)
4127 tgl 14666 GIC 1284 : continue;
4127 tgl 14667 ECB :
4127 tgl 14668 UIC 0 : memset(repl_null, false, sizeof(repl_null));
4127 tgl 14669 LBC 0 : memset(repl_repl, false, sizeof(repl_repl));
4127 tgl 14670 ECB :
4127 tgl 14671 LBC 0 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
4127 tgl 14672 ECB : oldOwnerId, newOwnerId);
4127 tgl 14673 LBC 0 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
14674 0 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
4127 tgl 14675 ECB :
4127 tgl 14676 UIC 0 : newtuple = heap_modify_tuple(attributeTuple,
14677 : RelationGetDescr(attRelation),
4127 tgl 14678 ECB : repl_val, repl_null, repl_repl);
14679 :
2259 alvherre 14680 UIC 0 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
14681 :
4127 tgl 14682 0 : heap_freetuple(newtuple);
4127 tgl 14683 ECB : }
4127 tgl 14684 GIC 218 : systable_endscan(scan);
1539 andres 14685 CBC 218 : table_close(attRelation, RowExclusiveLock);
4127 tgl 14686 GIC 218 : }
14687 :
14688 : /*
14689 : * change_owner_recurse_to_sequences
14690 : *
14691 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
14692 : * for sequences that are dependent on serial columns, and changes their
14693 : * ownership.
14694 : */
14695 : static void
4638 simon 14696 GBC 218 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
14697 : {
14698 : Relation depRel;
14699 : SysScanDesc scan;
6385 bruce 14700 ECB : ScanKeyData key[2];
14701 : HeapTuple tup;
6772 tgl 14702 :
14703 : /*
6075 14704 : * SERIAL sequences are those having an auto dependency on one of the
14705 : * table's columns (we don't care *which* column, exactly).
6772 14706 : */
1539 andres 14707 CBC 218 : depRel = table_open(DependRelationId, AccessShareLock);
14708 :
6772 tgl 14709 218 : ScanKeyInit(&key[0],
6385 bruce 14710 ECB : Anum_pg_depend_refclassid,
14711 : BTEqualStrategyNumber, F_OIDEQ,
14712 : ObjectIdGetDatum(RelationRelationId));
6772 tgl 14713 GIC 218 : ScanKeyInit(&key[1],
14714 : Anum_pg_depend_refobjid,
6385 bruce 14715 ECB : BTEqualStrategyNumber, F_OIDEQ,
14716 : ObjectIdGetDatum(relationOid));
14717 : /* we leave refobjsubid unspecified */
14718 :
6569 tgl 14719 GIC 218 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
3568 rhaas 14720 ECB : NULL, 2, key);
14721 :
6772 tgl 14722 GIC 605 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
6772 tgl 14723 EUB : {
6772 tgl 14724 GIC 387 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
14725 : Relation seqRel;
6772 tgl 14726 ECB :
14727 : /* skip dependencies other than auto dependencies on columns */
6772 tgl 14728 CBC 387 : if (depForm->refobjsubid == 0 ||
6569 tgl 14729 GIC 134 : depForm->classid != RelationRelationId ||
6772 14730 61 : depForm->objsubid != 0 ||
2194 peter_e 14731 61 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
6772 tgl 14732 CBC 326 : continue;
14733 :
6772 tgl 14734 ECB : /* Use relation_open just in case it's an index */
4638 simon 14735 CBC 61 : seqRel = relation_open(depForm->objid, lockmode);
6772 tgl 14736 ECB :
14737 : /* skip non-sequence relations */
6772 tgl 14738 CBC 61 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
14739 : {
14740 : /* No need to keep the lock */
4638 simon 14741 GBC 52 : relation_close(seqRel, lockmode);
6772 tgl 14742 GIC 52 : continue;
14743 : }
14744 :
6772 tgl 14745 ECB : /* We don't need to close the sequence while we alter it. */
4638 simon 14746 CBC 9 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
14747 :
14748 : /* Now we can close it. Keep the lock till end of transaction. */
6772 tgl 14749 GIC 9 : relation_close(seqRel, NoLock);
14750 : }
14751 :
14752 218 : systable_endscan(scan);
14753 :
6589 14754 218 : relation_close(depRel, AccessShareLock);
6772 14755 218 : }
14756 :
14757 : /*
14758 : * ALTER TABLE CLUSTER ON
7325 bruce 14759 ECB : *
14760 : * The only thing we have to do is to change the indisclustered bits.
14761 : *
14762 : * Return the address of the new clustering index.
14763 : */
14764 : static ObjectAddress
4638 simon 14765 GIC 32 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
14766 : {
14767 : Oid indexOid;
2937 alvherre 14768 ECB : ObjectAddress address;
7188 bruce 14769 :
7325 bruce 14770 CBC 32 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
14771 :
7325 bruce 14772 GIC 32 : if (!OidIsValid(indexOid))
7203 tgl 14773 LBC 0 : ereport(ERROR,
7203 tgl 14774 ECB : (errcode(ERRCODE_UNDEFINED_OBJECT),
14775 : errmsg("index \"%s\" for table \"%s\" does not exist",
6913 14776 : indexName, RelationGetRelationName(rel))));
7325 bruce 14777 :
6912 tgl 14778 : /* Check index is valid to cluster on */
361 michael 14779 GIC 32 : check_index_is_clusterable(rel, indexOid, lockmode);
14780 :
14781 : /* And do the work */
3675 rhaas 14782 32 : mark_index_clustered(rel, indexOid, false);
14783 :
2937 alvherre 14784 CBC 29 : ObjectAddressSet(address,
2937 alvherre 14785 ECB : RelationRelationId, indexOid);
14786 :
2937 alvherre 14787 GIC 29 : return address;
14788 : }
7325 bruce 14789 ECB :
14790 : /*
14791 : * ALTER TABLE SET WITHOUT CLUSTER
6885 14792 : *
14793 : * We have to find any indexes on the table that have indisclustered bit
14794 : * set and turn it off.
14795 : */
14796 : static void
4638 simon 14797 GIC 9 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
14798 : {
3675 rhaas 14799 9 : mark_index_clustered(rel, InvalidOid, false);
6885 bruce 14800 6 : }
14801 :
14802 : /*
620 michael 14803 ECB : * Preparation phase for SET ACCESS METHOD
14804 : *
14805 : * Check that access method exists. If it is the same as the table's current
14806 : * access method, it is a no-op. Otherwise, a table rewrite is necessary.
14807 : */
14808 : static void
620 michael 14809 GIC 15 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
620 michael 14810 ECB : {
14811 : Oid amoid;
14812 :
14813 : /* Check that the table access method exists */
620 michael 14814 GIC 15 : amoid = get_table_am_oid(amname, false);
620 michael 14815 ECB :
620 michael 14816 CBC 15 : if (rel->rd_rel->relam == amoid)
620 michael 14817 LBC 0 : return;
14818 :
620 michael 14819 ECB : /* Save info for Phase 3 to do the real work */
620 michael 14820 GIC 15 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
620 michael 14821 CBC 15 : tab->newAccessMethod = amoid;
14822 : }
14823 :
14824 : /*
14825 : * ALTER TABLE SET TABLESPACE
14826 : */
14827 : static void
1986 peter_e 14828 GIC 79 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
14829 : {
14830 : Oid tablespaceId;
6846 tgl 14831 ECB :
14832 : /* Check that the tablespace exists */
4630 rhaas 14833 GIC 79 : tablespaceId = get_tablespace_oid(tablespacename, false);
14834 :
14835 : /* Check permissions except when moving to database's default */
3368 sfrost 14836 79 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
14837 : {
14838 : AclResult aclresult;
14839 :
147 peter 14840 GNC 33 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
3368 sfrost 14841 CBC 33 : if (aclresult != ACLCHECK_OK)
1954 peter_e 14842 UIC 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
3368 sfrost 14843 ECB : }
6846 tgl 14844 :
6846 tgl 14845 EUB : /* Save info for Phase 3 to do the real work */
6846 tgl 14846 GIC 79 : if (OidIsValid(tab->newTableSpace))
6846 tgl 14847 LBC 0 : ereport(ERROR,
14848 : (errcode(ERRCODE_SYNTAX_ERROR),
6385 bruce 14849 ECB : errmsg("cannot have multiple SET TABLESPACE subcommands")));
3368 sfrost 14850 :
6846 tgl 14851 GIC 79 : tab->newTableSpace = tablespaceId;
6846 tgl 14852 CBC 79 : }
6846 tgl 14853 ECB :
6125 bruce 14854 : /*
4126 rhaas 14855 EUB : * Set, reset, or replace reloptions.
6125 bruce 14856 ECB : */
14857 : static void
4126 rhaas 14858 GIC 464 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
4126 rhaas 14859 ECB : LOCKMODE lockmode)
6125 bruce 14860 : {
14861 : Oid relid;
14862 : Relation pgclass;
14863 : HeapTuple tuple;
14864 : HeapTuple newtuple;
14865 : Datum datum;
14866 : bool isnull;
14867 : Datum newOptions;
14868 : Datum repl_val[Natts_pg_class];
14869 : bool repl_null[Natts_pg_class];
14870 : bool repl_repl[Natts_pg_class];
5050 14871 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
14872 :
4126 rhaas 14873 GIC 464 : if (defList == NIL && operation != AT_ReplaceRelOptions)
6124 tgl 14874 UIC 0 : return; /* nothing to do */
14875 :
1539 andres 14876 GIC 464 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
14877 :
14878 : /* Fetch heap tuple */
6124 tgl 14879 464 : relid = RelationGetRelid(rel);
4802 rhaas 14880 464 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
6125 bruce 14881 464 : if (!HeapTupleIsValid(tuple))
6125 bruce 14882 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
14883 :
4126 rhaas 14884 GIC 464 : if (operation == AT_ReplaceRelOptions)
14885 : {
14886 : /*
14887 : * If we're supposed to replace the reloptions list, we just pretend
14888 : * there were none before.
14889 : */
14890 97 : datum = (Datum) 0;
14891 97 : isnull = true;
4126 rhaas 14892 ECB : }
4126 rhaas 14893 EUB : else
14894 : {
14895 : /* Get the old reloptions */
4126 rhaas 14896 CBC 367 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
4126 rhaas 14897 ECB : &isnull);
14898 : }
6125 bruce 14899 EUB :
14900 : /* Generate new proposed reloptions (text array) */
6124 tgl 14901 GIC 464 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
14902 : defList, NULL, validnsps, false,
3955 bruce 14903 ECB : operation == AT_ResetRelOptions);
6125 14904 :
6124 tgl 14905 : /* Validate */
6125 bruce 14906 GIC 461 : switch (rel->rd_rel->relkind)
6125 bruce 14907 ECB : {
6125 bruce 14908 CBC 253 : case RELKIND_RELATION:
14909 : case RELKIND_TOASTVALUE:
3689 kgrittn 14910 ECB : case RELKIND_MATVIEW:
6124 tgl 14911 CBC 253 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
6125 bruce 14912 253 : break;
1242 michael 14913 GBC 3 : case RELKIND_PARTITIONED_TABLE:
1242 michael 14914 GIC 3 : (void) partitioned_table_reloptions(newOptions, true);
1242 michael 14915 UIC 0 : break;
3191 alvherre 14916 GIC 148 : case RELKIND_VIEW:
14917 148 : (void) view_reloptions(newOptions, true);
14918 139 : break;
6125 bruce 14919 CBC 57 : case RELKIND_INDEX:
1906 alvherre 14920 ECB : case RELKIND_PARTITIONED_INDEX:
1539 andres 14921 CBC 57 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
6125 bruce 14922 46 : break;
6125 bruce 14923 UIC 0 : default:
6124 tgl 14924 0 : ereport(ERROR,
14925 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14926 : errmsg("cannot set options for relation \"%s\"",
14927 : RelationGetRelationName(rel)),
640 peter 14928 ECB : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6125 bruce 14929 : break;
14930 : }
14931 :
14932 : /* Special-case validation of view options */
3552 sfrost 14933 CBC 438 : if (rel->rd_rel->relkind == RELKIND_VIEW)
14934 : {
3552 sfrost 14935 GIC 139 : Query *view_query = get_view_query(rel);
14936 139 : List *view_options = untransformRelOptions(newOptions);
3552 sfrost 14937 ECB : ListCell *cell;
3552 sfrost 14938 CBC 139 : bool check_option = false;
14939 :
3552 sfrost 14940 GIC 190 : foreach(cell, view_options)
3552 sfrost 14941 ECB : {
3552 sfrost 14942 GIC 51 : DefElem *defel = (DefElem *) lfirst(cell);
14943 :
1899 tgl 14944 51 : if (strcmp(defel->defname, "check_option") == 0)
3552 sfrost 14945 12 : check_option = true;
3552 sfrost 14946 ECB : }
14947 :
14948 : /*
3552 sfrost 14949 EUB : * If the check option is specified, look to see if the view is
14950 : * actually auto-updatable or not.
3552 sfrost 14951 ECB : */
3552 sfrost 14952 GIC 139 : if (check_option)
14953 : {
14954 : const char *view_updatable_error =
3260 bruce 14955 CBC 12 : view_query_is_auto_updatable(view_query, true);
14956 :
3552 sfrost 14957 GIC 12 : if (view_updatable_error)
3552 sfrost 14958 LBC 0 : ereport(ERROR,
14959 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3101 peter_e 14960 ECB : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
14961 : errhint("%s", _(view_updatable_error))));
14962 : }
3552 sfrost 14963 : }
14964 :
14965 : /*
6124 tgl 14966 : * All we need do here is update the pg_class row; the new options will be
14967 : * propagated into relcaches during post-commit cache inval.
14968 : */
6124 tgl 14969 GIC 438 : memset(repl_val, 0, sizeof(repl_val));
5271 tgl 14970 CBC 438 : memset(repl_null, false, sizeof(repl_null));
5271 tgl 14971 GIC 438 : memset(repl_repl, false, sizeof(repl_repl));
6125 bruce 14972 ECB :
6124 tgl 14973 GIC 438 : if (newOptions != (Datum) 0)
14974 294 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
14975 : else
5271 14976 144 : repl_null[Anum_pg_class_reloptions - 1] = true;
14977 :
14978 438 : repl_repl[Anum_pg_class_reloptions - 1] = true;
14979 :
14980 438 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
14981 : repl_val, repl_null, repl_repl);
14982 :
2259 alvherre 14983 438 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
14984 :
3675 rhaas 14985 438 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
14986 :
6124 tgl 14987 438 : heap_freetuple(newtuple);
6125 bruce 14988 ECB :
6125 bruce 14989 GIC 438 : ReleaseSysCache(tuple);
14990 :
14991 : /* repeat the whole exercise for the toast table, if there's one */
5179 alvherre 14992 438 : if (OidIsValid(rel->rd_rel->reltoastrelid))
14993 : {
14994 : Relation toastrel;
14995 128 : Oid toastid = rel->rd_rel->reltoastrelid;
14996 :
1539 andres 14997 128 : toastrel = table_open(toastid, lockmode);
14998 :
4126 rhaas 14999 ECB : /* Fetch heap tuple */
4802 rhaas 15000 GIC 128 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
5179 alvherre 15001 128 : if (!HeapTupleIsValid(tuple))
5179 alvherre 15002 LBC 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
15003 :
4126 rhaas 15004 CBC 128 : if (operation == AT_ReplaceRelOptions)
4126 rhaas 15005 ECB : {
4126 rhaas 15006 EUB : /*
4126 rhaas 15007 ECB : * If we're supposed to replace the reloptions list, we just
15008 : * pretend there were none before.
15009 : */
4126 rhaas 15010 LBC 0 : datum = (Datum) 0;
4126 rhaas 15011 UIC 0 : isnull = true;
4126 rhaas 15012 ECB : }
15013 : else
15014 : {
15015 : /* Get the old reloptions */
4126 rhaas 15016 GIC 128 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15017 : &isnull);
4126 rhaas 15018 ECB : }
5179 alvherre 15019 :
5179 alvherre 15020 CBC 128 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15021 : defList, "toast", validnsps, false,
15022 : operation == AT_ResetRelOptions);
15023 :
5179 alvherre 15024 GIC 128 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15025 :
15026 128 : memset(repl_val, 0, sizeof(repl_val));
15027 128 : memset(repl_null, false, sizeof(repl_null));
15028 128 : memset(repl_repl, false, sizeof(repl_repl));
5179 alvherre 15029 EUB :
5179 alvherre 15030 GBC 128 : if (newOptions != (Datum) 0)
5179 alvherre 15031 GIC 21 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15032 : else
15033 107 : repl_null[Anum_pg_class_reloptions - 1] = true;
15034 :
15035 128 : repl_repl[Anum_pg_class_reloptions - 1] = true;
5179 alvherre 15036 EUB :
5179 alvherre 15037 GIC 128 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
5179 alvherre 15038 ECB : repl_val, repl_null, repl_repl);
15039 :
2259 alvherre 15040 CBC 128 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
5179 alvherre 15041 ECB :
3675 rhaas 15042 GBC 128 : InvokeObjectPostAlterHookArg(RelationRelationId,
15043 : RelationGetRelid(toastrel), 0,
15044 : InvalidOid, true);
15045 :
5179 alvherre 15046 GIC 128 : heap_freetuple(newtuple);
15047 :
5179 alvherre 15048 CBC 128 : ReleaseSysCache(tuple);
5179 alvherre 15049 ECB :
1539 andres 15050 CBC 128 : table_close(toastrel, NoLock);
15051 : }
15052 :
1539 andres 15053 GIC 438 : table_close(pgclass, RowExclusiveLock);
15054 : }
15055 :
6846 tgl 15056 EUB : /*
15057 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15058 : * rewriting to be done, so we just want to copy the data as fast as possible.
15059 : */
15060 : static void
4638 simon 15061 GIC 81 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15062 : {
15063 : Relation rel;
15064 : Oid reltoastrelid;
15065 : RelFileNumber newrelfilenumber;
15066 : RelFileLocator newrlocator;
3566 fujii 15067 CBC 81 : List *reltoastidxids = NIL;
3566 fujii 15068 ECB : ListCell *lc;
6846 tgl 15069 :
6096 tgl 15070 EUB : /*
15071 : * Need lock here in case we are recursing to toast table or index
15072 : */
4638 simon 15073 GIC 81 : rel = relation_open(tableOid, lockmode);
15074 :
15075 : /* Check first if relation can be moved to new tablespace */
802 michael 15076 CBC 81 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
4809 tgl 15077 ECB : {
3675 rhaas 15078 CBC 1 : InvokeObjectPostAlterHook(RelationRelationId,
15079 : RelationGetRelid(rel), 0);
4809 tgl 15080 GIC 1 : relation_close(rel, NoLock);
4809 tgl 15081 GBC 1 : return;
15082 : }
15083 :
6846 tgl 15084 GIC 80 : reltoastrelid = rel->rd_rel->reltoastrelid;
15085 : /* Fetch the list of indexes on toast relation if necessary */
3566 fujii 15086 80 : if (OidIsValid(reltoastrelid))
15087 : {
3260 bruce 15088 10 : Relation toastRel = relation_open(reltoastrelid, lockmode);
15089 :
3566 fujii 15090 10 : reltoastidxids = RelationGetIndexList(toastRel);
15091 10 : relation_close(toastRel, lockmode);
3566 fujii 15092 ECB : }
15093 :
15094 : /*
15095 : * Relfilenumbers are not unique in databases across tablespaces, so we
15096 : * need to allocate a new one in the new tablespace.
15097 : */
193 rhaas 15098 GNC 80 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
277 15099 80 : rel->rd_rel->relpersistence);
15100 :
15101 : /* Open old and new relation */
15102 80 : newrlocator = rel->rd_locator;
15103 80 : newrlocator.relNumber = newrelfilenumber;
15104 80 : newrlocator.spcOid = newTableSpace;
15105 :
15106 : /* hand off to AM to actually create new rel storage and copy the data */
1473 andres 15107 GIC 80 : if (rel->rd_rel->relkind == RELKIND_INDEX)
5354 heikki.linnakangas 15108 ECB : {
277 rhaas 15109 GNC 31 : index_copy_data(rel, newrlocator);
15110 : }
15111 : else
1473 andres 15112 ECB : {
492 peter 15113 GBC 49 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
277 rhaas 15114 GNC 49 : table_relation_copy_data(rel, &newrlocator);
15115 : }
15116 :
1441 andres 15117 ECB : /*
15118 : * Update the pg_class row.
15119 : *
15120 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15121 : * executed on pg_class or its indexes (the above copy wouldn't contain
802 michael 15122 : * the updated pg_class entry), but that's forbidden with
802 michael 15123 EUB : * CheckRelationTableSpaceMove().
1441 andres 15124 : */
277 rhaas 15125 GNC 80 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15126 :
3675 rhaas 15127 GIC 80 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
3675 rhaas 15128 ECB :
277 rhaas 15129 GNC 80 : RelationAssumeNewRelfilelocator(rel);
15130 :
6846 tgl 15131 CBC 80 : relation_close(rel, NoLock);
6846 tgl 15132 ECB :
15133 : /* Make sure the reltablespace change is visible */
6846 tgl 15134 GIC 80 : CommandCounterIncrement();
15135 :
15136 : /* Move associated toast relation and/or indexes, too */
15137 80 : if (OidIsValid(reltoastrelid))
4638 simon 15138 CBC 10 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
3566 fujii 15139 GIC 90 : foreach(lc, reltoastidxids)
15140 10 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
3566 fujii 15141 ECB :
15142 : /* Clean up */
3566 fujii 15143 CBC 80 : list_free(reltoastidxids);
15144 : }
6846 tgl 15145 ECB :
1618 alvherre 15146 : /*
15147 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15148 : * storage that have an interest in preserving tablespace.
1574 15149 : *
15150 : * Since these have no storage the tablespace can be updated with a simple
15151 : * metadata only operation to update the tablespace.
15152 : */
1618 15153 : static void
1574 alvherre 15154 GIC 18 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15155 : {
15156 : /*
15157 : * Shouldn't be called on relations having storage; these are processed in
15158 : * phase 3.
1574 alvherre 15159 ECB : */
1556 alvherre 15160 GIC 18 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15161 :
15162 : /* check if relation can be moved to its new tablespace */
802 michael 15163 18 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15164 : {
802 michael 15165 UIC 0 : InvokeObjectPostAlterHook(RelationRelationId,
15166 : RelationGetRelid(rel),
15167 : 0);
1618 alvherre 15168 LBC 0 : return;
1618 alvherre 15169 ECB : }
15170 :
802 michael 15171 : /* Update can be done, so change reltablespace */
802 michael 15172 CBC 15 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15173 :
802 michael 15174 GIC 15 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15175 :
15176 : /* Make sure the reltablespace change is visible */
1618 alvherre 15177 15 : CommandCounterIncrement();
1618 alvherre 15178 ECB : }
15179 :
15180 : /*
15181 : * Alter Table ALL ... SET TABLESPACE
15182 : *
15183 : * Allows a user to move all objects of some type in a given tablespace in the
15184 : * current database to another tablespace. Objects can be chosen based on the
15185 : * owner of the object also, to allow users to move only their objects.
3153 sfrost 15186 : * The user must have CREATE rights on the new tablespace, as usual. The main
15187 : * permissions handling is done by the lower-level table move function.
15188 : *
15189 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
15190 : * lock can't be acquired then we ereport(ERROR).
15191 : */
15192 : Oid
3153 sfrost 15193 GIC 15 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15194 : {
3153 sfrost 15195 CBC 15 : List *relations = NIL;
15196 : ListCell *l;
15197 : ScanKeyData key[1];
3153 sfrost 15198 ECB : Relation rel;
1490 andres 15199 : TableScanDesc scan;
15200 : HeapTuple tuple;
3153 sfrost 15201 : Oid orig_tablespaceoid;
15202 : Oid new_tablespaceoid;
2953 alvherre 15203 GIC 15 : List *role_oids = roleSpecsToIds(stmt->roles);
15204 :
3153 sfrost 15205 ECB : /* Ensure we were not asked to move something we can't */
3153 sfrost 15206 CBC 15 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
3153 sfrost 15207 GIC 6 : stmt->objtype != OBJECT_MATVIEW)
3153 sfrost 15208 UIC 0 : ereport(ERROR,
15209 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3153 sfrost 15210 ECB : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15211 :
15212 : /* Get the orig and new tablespace OIDs */
3153 sfrost 15213 CBC 15 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
3153 sfrost 15214 GIC 15 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
3153 sfrost 15215 ECB :
15216 : /* Can't move shared relations in to or out of pg_global */
15217 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
3153 sfrost 15218 CBC 15 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15219 : new_tablespaceoid == GLOBALTABLESPACE_OID)
3153 sfrost 15220 UIC 0 : ereport(ERROR,
15221 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15222 : errmsg("cannot move relations in to or out of pg_global tablespace")));
15223 :
15224 : /*
15225 : * Must have CREATE rights on the new tablespace, unless it is the
15226 : * database default tablespace (which all users implicitly have CREATE
3153 sfrost 15227 ECB : * rights on).
15228 : */
3153 sfrost 15229 GIC 15 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15230 : {
15231 : AclResult aclresult;
15232 :
147 peter 15233 UNC 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
3153 sfrost 15234 ECB : ACL_CREATE);
3153 sfrost 15235 LBC 0 : if (aclresult != ACLCHECK_OK)
1954 peter_e 15236 UIC 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
3153 sfrost 15237 0 : get_tablespace_name(new_tablespaceoid));
15238 : }
3153 sfrost 15239 ECB :
15240 : /*
15241 : * Now that the checks are done, check if we should set either to
15242 : * InvalidOid because it is our database's default tablespace.
15243 : */
3153 sfrost 15244 GIC 15 : if (orig_tablespaceoid == MyDatabaseTableSpace)
3153 sfrost 15245 UIC 0 : orig_tablespaceoid = InvalidOid;
15246 :
3153 sfrost 15247 GIC 15 : if (new_tablespaceoid == MyDatabaseTableSpace)
15248 15 : new_tablespaceoid = InvalidOid;
15249 :
15250 : /* no-op */
15251 15 : if (orig_tablespaceoid == new_tablespaceoid)
3153 sfrost 15252 UIC 0 : return new_tablespaceoid;
3153 sfrost 15253 ECB :
15254 : /*
15255 : * Walk the list of objects in the tablespace and move them. This will
15256 : * only find objects in our database, of course.
15257 : */
3153 sfrost 15258 GIC 15 : ScanKeyInit(&key[0],
15259 : Anum_pg_class_reltablespace,
15260 : BTEqualStrategyNumber, F_OIDEQ,
3153 sfrost 15261 ECB : ObjectIdGetDatum(orig_tablespaceoid));
15262 :
1539 andres 15263 GIC 15 : rel = table_open(RelationRelationId, AccessShareLock);
1490 andres 15264 GBC 15 : scan = table_beginscan_catalog(rel, 1, key);
3153 sfrost 15265 66 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15266 : {
1601 andres 15267 51 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
1601 andres 15268 GIC 51 : Oid relOid = relForm->oid;
3153 sfrost 15269 EUB :
15270 : /*
15271 : * Do not move objects in pg_catalog as part of this, if an admin
15272 : * really wishes to do so, they can issue the individual ALTER
15273 : * commands directly.
15274 : *
15275 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
15276 : * (TOAST will be moved with the main table).
15277 : */
1432 tgl 15278 GBC 51 : if (IsCatalogNamespace(relForm->relnamespace) ||
1432 tgl 15279 GIC 102 : relForm->relisshared ||
3153 sfrost 15280 CBC 102 : isAnyTempNamespace(relForm->relnamespace) ||
1432 tgl 15281 51 : IsToastNamespace(relForm->relnamespace))
3153 sfrost 15282 LBC 0 : continue;
15283 :
15284 : /* Only move the object type requested */
3153 sfrost 15285 GIC 51 : if ((stmt->objtype == OBJECT_TABLE &&
2314 rhaas 15286 30 : relForm->relkind != RELKIND_RELATION &&
15287 18 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
3153 sfrost 15288 33 : (stmt->objtype == OBJECT_INDEX &&
1906 alvherre 15289 18 : relForm->relkind != RELKIND_INDEX &&
15290 3 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
3153 sfrost 15291 30 : (stmt->objtype == OBJECT_MATVIEW &&
3153 sfrost 15292 CBC 3 : relForm->relkind != RELKIND_MATVIEW))
3153 sfrost 15293 GIC 21 : continue;
15294 :
15295 : /* Check if we are only moving objects owned by certain roles */
15296 30 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
3153 sfrost 15297 UIC 0 : continue;
15298 :
15299 : /*
15300 : * Handle permissions-checking here since we are locking the tables
15301 : * and also to avoid doing a bunch of work only to fail part-way. Note
15302 : * that permissions will also be checked by AlterTableInternal().
3153 sfrost 15303 ECB : *
15304 : * Caller must be considered an owner on the table to move it.
15305 : */
147 peter 15306 GNC 30 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
1954 peter_e 15307 UIC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
3153 sfrost 15308 0 : NameStr(relForm->relname));
3153 sfrost 15309 ECB :
3153 sfrost 15310 GIC 30 : if (stmt->nowait &&
3153 sfrost 15311 UIC 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
15312 0 : ereport(ERROR,
15313 : (errcode(ERRCODE_OBJECT_IN_USE),
15314 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
2878 bruce 15315 ECB : get_namespace_name(relForm->relnamespace),
15316 : NameStr(relForm->relname))));
15317 : else
3153 sfrost 15318 CBC 30 : LockRelationOid(relOid, AccessExclusiveLock);
15319 :
3153 sfrost 15320 ECB : /* Add to our list of objects to move */
3153 sfrost 15321 GIC 30 : relations = lappend_oid(relations, relOid);
15322 : }
15323 :
1490 andres 15324 CBC 15 : table_endscan(scan);
1539 15325 15 : table_close(rel, AccessShareLock);
3153 sfrost 15326 ECB :
3153 sfrost 15327 CBC 15 : if (relations == NIL)
15328 6 : ereport(NOTICE,
15329 : (errcode(ERRCODE_NO_DATA_FOUND),
15330 : errmsg("no matching relations in tablespace \"%s\" found",
2118 tgl 15331 ECB : orig_tablespaceoid == InvalidOid ? "(database default)" :
15332 : get_tablespace_name(orig_tablespaceoid))));
15333 :
3153 sfrost 15334 : /* Everything is locked, loop through and move all of the relations. */
3153 sfrost 15335 GIC 45 : foreach(l, relations)
15336 : {
3153 sfrost 15337 CBC 30 : List *cmds = NIL;
15338 30 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15339 :
3153 sfrost 15340 GIC 30 : cmd->subtype = AT_SetTableSpace;
15341 30 : cmd->name = stmt->new_tablespacename;
3153 sfrost 15342 ECB :
3153 sfrost 15343 GIC 30 : cmds = lappend(cmds, cmd);
15344 :
2890 alvherre 15345 CBC 30 : EventTriggerAlterTableStart((Node *) stmt);
15346 : /* OID is set by AlterTableInternal */
3153 sfrost 15347 GIC 30 : AlterTableInternal(lfirst_oid(l), cmds, false);
2890 alvherre 15348 CBC 30 : EventTriggerAlterTableEnd();
15349 : }
3153 sfrost 15350 ECB :
3153 sfrost 15351 CBC 15 : return new_tablespaceoid;
15352 : }
15353 :
15354 : static void
277 rhaas 15355 GNC 31 : index_copy_data(Relation rel, RelFileLocator newrlocator)
15356 : {
15357 : SMgrRelation dstrel;
15358 :
15359 31 : dstrel = smgropen(newrlocator, rel->rd_backend);
15360 :
1441 andres 15361 ECB : /*
15362 : * Since we copy the file directly without looking at the shared buffers,
15363 : * we'd better first flush out any pages of the source relation that are
15364 : * in shared buffers. We assume no new changes will be made while we are
15365 : * holding exclusive lock on the rel.
15366 : */
1441 andres 15367 GIC 31 : FlushRelationBuffers(rel);
1441 andres 15368 ECB :
6846 tgl 15369 EUB : /*
15370 : * Create and copy all forks of the relation, and schedule unlinking of
15371 : * old physical files.
15372 : *
15373 : * NOTE: any conflict in relfilenumber value will be caught in
15374 : * RelationCreateStorage().
6846 tgl 15375 ECB : */
277 rhaas 15376 GNC 31 : RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
15377 :
1473 andres 15378 ECB : /* copy main fork */
636 tgl 15379 GIC 31 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
1473 andres 15380 CBC 31 : rel->rd_rel->relpersistence);
15381 :
15382 : /* copy those extra forks that exist */
15383 31 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
1473 andres 15384 GIC 124 : forkNum <= MAX_FORKNUM; forkNum++)
15385 : {
636 tgl 15386 93 : if (smgrexists(RelationGetSmgr(rel), forkNum))
15387 : {
1473 andres 15388 UIC 0 : smgrcreate(dstrel, forkNum, false);
15389 :
15390 : /*
15391 : * WAL log creation if the relation is persistent, or this is the
15392 : * init fork of an unlogged relation.
1473 andres 15393 ECB : */
748 bruce 15394 UIC 0 : if (RelationIsPermanent(rel) ||
1473 andres 15395 LBC 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
1473 andres 15396 ECB : forkNum == INIT_FORKNUM))
277 rhaas 15397 UNC 0 : log_smgrcreate(&newrlocator, forkNum);
636 tgl 15398 UIC 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
1473 andres 15399 0 : rel->rd_rel->relpersistence);
15400 : }
15401 : }
15402 :
15403 : /* drop old relation, and close new one */
1473 andres 15404 GIC 31 : RelationDropStorage(rel);
1473 andres 15405 CBC 31 : smgrclose(dstrel);
6846 tgl 15406 GIC 31 : }
15407 :
15408 : /*
15409 : * ALTER TABLE ENABLE/DISABLE TRIGGER
6438 tgl 15410 ECB : *
15411 : * We just pass this off to trigger.c.
15412 : */
6438 tgl 15413 EUB : static void
1986 peter_e 15414 GIC 168 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
15415 : char fires_when, bool skip_system, bool recurse,
248 alvherre 15416 ECB : LOCKMODE lockmode)
5865 JanWieck 15417 : {
36 tgl 15418 GNC 168 : EnableDisableTrigger(rel, trigname, InvalidOid,
15419 : fires_when, skip_system, recurse,
15420 : lockmode);
5865 JanWieck 15421 GIC 168 : }
15422 :
15423 : /*
5865 JanWieck 15424 ECB : * ALTER TABLE ENABLE/DISABLE RULE
15425 : *
15426 : * We just pass this off to rewriteDefine.c.
15427 : */
15428 : static void
1986 peter_e 15429 CBC 9 : ATExecEnableDisableRule(Relation rel, const char *rulename,
15430 : char fires_when, LOCKMODE lockmode)
15431 : {
2228 15432 9 : EnableDisableRule(rel, rulename, fires_when);
6438 tgl 15433 GIC 9 : }
15434 :
15435 : /*
6125 neilc 15436 ECB : * ALTER TABLE INHERIT
15437 : *
6125 neilc 15438 EUB : * Add a parent to the child's parents. This verifies that all the columns and
15439 : * check constraints of the parent appear in the child and that they have the
15440 : * same data types and expressions.
15441 : */
4643 peter_e 15442 ECB : static void
4643 peter_e 15443 GBC 143 : ATPrepAddInherit(Relation child_rel)
15444 : {
4643 peter_e 15445 GIC 143 : if (child_rel->rd_rel->reloftype)
15446 3 : ereport(ERROR,
4643 peter_e 15447 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15448 : errmsg("cannot change inheritance of typed table")));
15449 :
2314 rhaas 15450 GIC 140 : if (child_rel->rd_rel->relispartition)
15451 3 : ereport(ERROR,
15452 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15453 : errmsg("cannot change inheritance of a partition")));
2314 rhaas 15454 ECB :
2314 rhaas 15455 GIC 137 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15456 3 : ereport(ERROR,
15457 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15458 : errmsg("cannot change inheritance of partitioned table")));
4643 peter_e 15459 134 : }
15460 :
15461 : /*
15462 : * Return the address of the new parent relation.
15463 : */
15464 : static ObjectAddress
4638 simon 15465 134 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
15466 : {
15467 : Relation parent_rel;
15468 : List *children;
2937 alvherre 15469 ECB : ObjectAddress address;
2111 rhodiumtoad 15470 EUB : const char *trigger_name;
15471 :
6022 tgl 15472 ECB : /*
15473 : * A self-exclusive lock is needed here. See the similar case in
15474 : * MergeAttributes() for a full explanation.
15475 : */
1539 andres 15476 CBC 134 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
6125 bruce 15477 ECB :
6125 bruce 15478 EUB : /*
15479 : * Must be owner of both parent and child -- child was checked by
6125 bruce 15480 ECB : * ATSimplePermissions call in ATPrepCmd
15481 : */
640 peter 15482 GIC 134 : ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
15483 :
15484 : /* Permanent rels cannot inherit from temporary ones */
3765 tgl 15485 134 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
3765 tgl 15486 CBC 3 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
6125 bruce 15487 LBC 0 : ereport(ERROR,
15488 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15489 : errmsg("cannot inherit from temporary relation \"%s\"",
15490 : RelationGetRelationName(parent_rel))));
15491 :
3765 tgl 15492 ECB : /* If parent rel is temp, it must belong to this session */
3765 tgl 15493 GIC 134 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15494 3 : !parent_rel->rd_islocaltemp)
3765 tgl 15495 UIC 0 : ereport(ERROR,
15496 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2118 tgl 15497 ECB : errmsg("cannot inherit from temporary relation of another session")));
15498 :
15499 : /* Ditto for the child */
3765 tgl 15500 GIC 134 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15501 3 : !child_rel->rd_islocaltemp)
3765 tgl 15502 LBC 0 : ereport(ERROR,
15503 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2118 tgl 15504 ECB : errmsg("cannot inherit to temporary relation of another session")));
15505 :
15506 : /* Prevent partitioned tables from becoming inheritance parents */
2314 rhaas 15507 CBC 134 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15508 3 : ereport(ERROR,
2314 rhaas 15509 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15510 : errmsg("cannot inherit from partitioned table \"%s\"",
2314 rhaas 15511 EUB : parent->relname)));
2314 rhaas 15512 ECB :
15513 : /* Likewise for partitions */
2314 rhaas 15514 CBC 131 : if (parent_rel->rd_rel->relispartition)
15515 3 : ereport(ERROR,
15516 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2314 rhaas 15517 ECB : errmsg("cannot inherit from a partition")));
15518 :
6125 bruce 15519 EUB : /*
2314 rhaas 15520 : * Prevent circularity by seeing if proposed parent inherits from child.
15521 : * (In particular, this disallows making a rel inherit from itself.)
15522 : *
15523 : * This is not completely bulletproof because of race conditions: in
15524 : * multi-level inheritance trees, someone else could concurrently be
15525 : * making another inheritance link that closes the loop but does not join
15526 : * either of the rels we have locked. Preventing that seems to require
15527 : * exclusive locks on the entire inheritance tree, which is a cure worse
15528 : * than the disease. find_all_inheritors() will cope with circularity
2314 rhaas 15529 ECB : * anyway, so don't sweat it too much.
15530 : *
15531 : * We use weakest lock we can on child's children, namely AccessShareLock.
6125 bruce 15532 : */
2314 rhaas 15533 GIC 128 : children = find_all_inheritors(RelationGetRelid(child_rel),
2314 rhaas 15534 ECB : AccessShareLock, NULL);
15535 :
2314 rhaas 15536 CBC 128 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
2314 rhaas 15537 GIC 6 : ereport(ERROR,
2314 rhaas 15538 ECB : (errcode(ERRCODE_DUPLICATE_TABLE),
15539 : errmsg("circular inheritance not allowed"),
15540 : errdetail("\"%s\" is already a child of \"%s\".",
15541 : parent->relname,
15542 : RelationGetRelationName(child_rel))));
15543 :
15544 : /*
15545 : * If child_rel has row-level triggers with transition tables, we
15546 : * currently don't allow it to become an inheritance child. See also
15547 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
2111 rhodiumtoad 15548 : */
2111 rhodiumtoad 15549 GIC 122 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
15550 122 : if (trigger_name != NULL)
2111 rhodiumtoad 15551 CBC 3 : ereport(ERROR,
15552 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2111 rhodiumtoad 15553 ECB : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
2111 rhodiumtoad 15554 EUB : trigger_name, RelationGetRelationName(child_rel)),
15555 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
15556 :
15557 : /* OK to create inheritance */
2314 rhaas 15558 GIC 119 : CreateInheritance(child_rel, parent_rel);
15559 :
15560 95 : ObjectAddressSet(address, RelationRelationId,
15561 : RelationGetRelid(parent_rel));
15562 :
15563 : /* keep our lock on the parent relation until commit */
1539 andres 15564 95 : table_close(parent_rel, NoLock);
2314 rhaas 15565 ECB :
2314 rhaas 15566 CBC 95 : return address;
2314 rhaas 15567 ECB : }
15568 :
15569 : /*
15570 : * CreateInheritance
15571 : * Catalog manipulation portion of creating inheritance between a child
15572 : * table and a parent table.
15573 : *
15574 : * Common to ATExecAddInherit() and ATExecAttachPartition().
15575 : */
15576 : static void
2314 rhaas 15577 GIC 1087 : CreateInheritance(Relation child_rel, Relation parent_rel)
15578 : {
2314 rhaas 15579 ECB : Relation catalogRelation;
15580 : SysScanDesc scan;
15581 : ScanKeyData key;
15582 : HeapTuple inheritsTuple;
15583 : int32 inhseqno;
15584 :
15585 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
1539 andres 15586 GIC 1087 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
15587 :
2314 rhaas 15588 ECB : /*
15589 : * Check for duplicates in the list of parents, and determine the highest
15590 : * inhseqno already present; we'll use the next one for the new parent.
2308 15591 : * Also, if proposed child is a partition, it cannot already be
15592 : * inheriting.
2314 15593 : *
15594 : * Note: we do not reject the case where the child already inherits from
15595 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15596 : */
2314 rhaas 15597 CBC 1087 : ScanKeyInit(&key,
2314 rhaas 15598 EUB : Anum_pg_inherits_inhrelid,
15599 : BTEqualStrategyNumber, F_OIDEQ,
6125 neilc 15600 ECB : ObjectIdGetDatum(RelationGetRelid(child_rel)));
6125 bruce 15601 GIC 1087 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
15602 : true, NULL, 1, &key);
15603 :
15604 : /* inhseqno sequences start at 1 */
neilc 15605 1087 : inhseqno = 0;
6125 bruce 15606 GBC 1108 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
6125 bruce 15607 EUB : {
6125 bruce 15608 GIC 24 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
15609 :
neilc 15610 24 : if (inh->inhparent == RelationGetRelid(parent_rel))
bruce 15611 3 : ereport(ERROR,
6125 bruce 15612 ECB : (errcode(ERRCODE_DUPLICATE_TABLE),
15613 : errmsg("relation \"%s\" would be inherited from more than once",
15614 : RelationGetRelationName(parent_rel))));
15615 :
6022 tgl 15616 CBC 21 : if (inh->inhseqno > inhseqno)
6125 bruce 15617 GIC 21 : inhseqno = inh->inhseqno;
15618 : }
15619 1084 : systable_endscan(scan);
6125 bruce 15620 ECB :
15621 : /* Match up the columns and bump attinhcount as needed */
6125 neilc 15622 CBC 1084 : MergeAttributesIntoExisting(child_rel, parent_rel);
6125 bruce 15623 ECB :
5448 tgl 15624 : /* Match up the constraints and bump coninhcount as needed */
6125 neilc 15625 GIC 1045 : MergeConstraintsIntoExisting(child_rel, parent_rel);
6125 bruce 15626 ECB :
6022 tgl 15627 : /*
15628 : * OK, it looks valid. Make the catalog entries that show inheritance.
15629 : */
6125 neilc 15630 GIC 1030 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
6125 neilc 15631 ECB : RelationGetRelid(parent_rel),
15632 : inhseqno + 1,
2225 simon 15633 : catalogRelation,
2225 simon 15634 GIC 1030 : parent_rel->rd_rel->relkind ==
15635 : RELKIND_PARTITIONED_TABLE);
6022 tgl 15636 ECB :
15637 : /* Now we're done with pg_inherits */
1539 andres 15638 CBC 1030 : table_close(catalogRelation, RowExclusiveLock);
6125 bruce 15639 GIC 1030 : }
15640 :
15641 : /*
6022 tgl 15642 ECB : * Obtain the source-text form of the constraint expression for a check
15643 : * constraint, given its pg_constraint tuple
15644 : */
15645 : static char *
6022 tgl 15646 CBC 66 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
15647 : {
15648 : Form_pg_constraint con;
6022 tgl 15649 ECB : bool isnull;
15650 : Datum attr;
15651 : Datum expr;
15652 :
6022 tgl 15653 GIC 66 : con = (Form_pg_constraint) GETSTRUCT(contup);
15654 66 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
15655 66 : if (isnull)
1601 andres 15656 UIC 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
6022 tgl 15657 ECB :
6022 tgl 15658 GIC 66 : expr = DirectFunctionCall2(pg_get_expr, attr,
15659 : ObjectIdGetDatum(con->conrelid));
5493 15660 66 : return TextDatumGetCString(expr);
15661 : }
15662 :
5448 tgl 15663 ECB : /*
15664 : * Determine whether two check constraints are functionally equivalent
15665 : *
15666 : * The test we apply is to see whether they reverse-compile to the same
15667 : * source string. This insulates us from issues like whether attributes
15668 : * have the same physical column numbers in parent and child relations.
15669 : */
15670 : static bool
5448 tgl 15671 GIC 33 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
5448 tgl 15672 ECB : {
5448 tgl 15673 GIC 33 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
5448 tgl 15674 CBC 33 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
15675 :
15676 33 : if (acon->condeferrable != bcon->condeferrable ||
15677 33 : acon->condeferred != bcon->condeferred ||
5448 tgl 15678 GIC 33 : strcmp(decompile_conbin(a, tupleDesc),
15679 33 : decompile_conbin(b, tupleDesc)) != 0)
5448 tgl 15680 CBC 3 : return false;
15681 : else
15682 30 : return true;
15683 : }
5448 tgl 15684 ECB :
15685 : /*
6022 15686 : * Check columns in child table match up with columns in parent, and increment
15687 : * their attinhcount.
15688 : *
15689 : * Called by CreateInheritance
15690 : *
15691 : * Currently all parent columns must be found in child. Missing columns are an
15692 : * error. One day we might consider creating new columns like CREATE TABLE
15693 : * does. However, that is widely unpopular --- in the common use case of
15694 : * partitioned tables it's a foot-gun.
6125 bruce 15695 : *
15696 : * The data type must match exactly. If the parent column is NOT NULL then
15697 : * the child must be as well. Defaults are not compared, however.
15698 : */
15699 : static void
6125 neilc 15700 CBC 1084 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
15701 : {
15702 : Relation attrrel;
6031 bruce 15703 ECB : AttrNumber parent_attno;
15704 : int parent_natts;
15705 : TupleDesc tupleDesc;
15706 : HeapTuple tuple;
2314 rhaas 15707 GIC 1084 : bool child_is_partition = false;
15708 :
1539 andres 15709 CBC 1084 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
6022 tgl 15710 ECB :
6125 neilc 15711 GIC 1084 : tupleDesc = RelationGetDescr(parent_rel);
15712 1084 : parent_natts = tupleDesc->natts;
15713 :
15714 : /* If parent_rel is a partitioned table, child_rel must be a partition */
2314 rhaas 15715 1084 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15716 968 : child_is_partition = true;
15717 :
6125 neilc 15718 3533 : for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
15719 : {
2058 andres 15720 2488 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2058 andres 15721 ECB : parent_attno - 1);
6125 bruce 15722 GIC 2488 : char *attributeName = NameStr(attribute->attname);
6125 bruce 15723 ECB :
15724 : /* Ignore dropped columns in the parent. */
6125 bruce 15725 CBC 2488 : if (attribute->attisdropped)
6125 bruce 15726 GIC 148 : continue;
6125 bruce 15727 ECB :
15728 : /* Find same column in child (matching on column name). */
6125 neilc 15729 GIC 2340 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
6125 neilc 15730 ECB : attributeName);
6125 bruce 15731 GIC 2340 : if (HeapTupleIsValid(tuple))
15732 : {
4375 tgl 15733 ECB : /* Check they are same type, typmod, and collation */
6125 bruce 15734 CBC 2334 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
6125 bruce 15735 ECB :
6125 bruce 15736 CBC 2334 : if (attribute->atttypid != childatt->atttypid ||
6125 neilc 15737 GIC 2331 : attribute->atttypmod != childatt->atttypmod)
bruce 15738 6 : ereport(ERROR,
6125 bruce 15739 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
15740 : errmsg("child table \"%s\" has different type for column \"%s\"",
15741 : RelationGetRelationName(child_rel),
15742 : attributeName)));
15743 :
4375 tgl 15744 GIC 2328 : if (attribute->attcollation != childatt->attcollation)
15745 3 : ereport(ERROR,
15746 : (errcode(ERRCODE_COLLATION_MISMATCH),
15747 : errmsg("child table \"%s\" has different collation for column \"%s\"",
15748 : RelationGetRelationName(child_rel),
15749 : attributeName)));
4375 tgl 15750 ECB :
15751 : /*
15752 : * Check child doesn't discard NOT NULL property. (Other
15753 : * constraints are checked elsewhere.)
15754 : */
6125 neilc 15755 GIC 2325 : if (attribute->attnotnull && !childatt->attnotnull)
6125 neilc 15756 CBC 6 : ereport(ERROR,
15757 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15758 : errmsg("column \"%s\" in child table must be marked NOT NULL",
2118 tgl 15759 ECB : attributeName)));
15760 :
705 peter 15761 EUB : /*
15762 : * Child column must be generated if and only if parent column is.
15763 : */
705 peter 15764 GBC 2319 : if (attribute->attgenerated && !childatt->attgenerated)
705 peter 15765 GIC 12 : ereport(ERROR,
15766 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15767 : errmsg("column \"%s\" in child table must be a generated column",
705 peter 15768 ECB : attributeName)));
88 tgl 15769 GNC 2307 : if (childatt->attgenerated && !attribute->attgenerated)
15770 6 : ereport(ERROR,
15771 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15772 : errmsg("column \"%s\" in child table must not be a generated column",
15773 : attributeName)));
15774 :
15775 : /*
15776 : * OK, bump the child column's inheritance count. (If we fail
15777 : * later on, this change will just roll back.)
15778 : */
6022 tgl 15779 CBC 2301 : childatt->attinhcount++;
12 peter 15780 GNC 2301 : if (childatt->attinhcount < 0)
12 peter 15781 UNC 0 : ereport(ERROR,
15782 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15783 : errmsg("too many inheritance parents"));
15784 :
15785 : /*
15786 : * In case of partitions, we must enforce that value of attislocal
2314 rhaas 15787 EUB : * is same in all partitions. (Note: there are only inherited
15788 : * attributes in partitions)
15789 : */
2314 rhaas 15790 GBC 2301 : if (child_is_partition)
2314 rhaas 15791 EUB : {
2314 rhaas 15792 GIC 2067 : Assert(childatt->attinhcount == 1);
15793 2067 : childatt->attislocal = false;
15794 : }
15795 :
2259 alvherre 15796 2301 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
6022 tgl 15797 2301 : heap_freetuple(tuple);
6125 bruce 15798 ECB : }
6125 bruce 15799 EUB : else
15800 : {
6125 bruce 15801 CBC 6 : ereport(ERROR,
6125 bruce 15802 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
15803 : errmsg("child table is missing column \"%s\"",
15804 : attributeName)));
15805 : }
6125 bruce 15806 EUB : }
15807 :
1539 andres 15808 GIC 1045 : table_close(attrrel, RowExclusiveLock);
6125 bruce 15809 1045 : }
15810 :
15811 : /*
5448 tgl 15812 ECB : * Check constraints in child table match up with constraints in parent,
15813 : * and increment their coninhcount.
15814 : *
15815 : * Constraints that are marked ONLY in the parent are ignored.
15816 : *
2314 rhaas 15817 : * Called by CreateInheritance
6125 bruce 15818 : *
15819 : * Currently all constraints in parent must be present in the child. One day we
15820 : * may consider adding new constraints like CREATE TABLE does.
15821 : *
6022 tgl 15822 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
15823 : * constraints. As long as tables have more like 10 constraints it shouldn't be
15824 : * a problem though. Even 100 constraints ought not be the end of the world.
15825 : *
15826 : * XXX See MergeWithExistingConstraint too if you change this code.
15827 : */
15828 : static void
6125 neilc 15829 GIC 1045 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
15830 : {
15831 : Relation catalog_relation;
5448 tgl 15832 ECB : TupleDesc tuple_desc;
15833 : SysScanDesc parent_scan;
15834 : ScanKeyData parent_key;
15835 : HeapTuple parent_tuple;
2 alvherre 15836 GNC 1045 : Oid parent_relid = RelationGetRelid(parent_rel);
2314 rhaas 15837 GBC 1045 : bool child_is_partition = false;
15838 :
1539 andres 15839 GIC 1045 : catalog_relation = table_open(ConstraintRelationId, RowExclusiveLock);
5448 tgl 15840 CBC 1045 : tuple_desc = RelationGetDescr(catalog_relation);
6125 bruce 15841 ECB :
2314 rhaas 15842 : /* If parent_rel is a partitioned table, child_rel must be a partition */
2314 rhaas 15843 CBC 1045 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15844 944 : child_is_partition = true;
2314 rhaas 15845 ECB :
5448 tgl 15846 : /* Outer loop scans through the parent's constraint definitions */
5448 tgl 15847 CBC 1045 : ScanKeyInit(&parent_key,
6125 bruce 15848 ECB : Anum_pg_constraint_conrelid,
15849 : BTEqualStrategyNumber, F_OIDEQ,
15850 : ObjectIdGetDatum(parent_relid));
1678 tgl 15851 CBC 1045 : parent_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
3568 rhaas 15852 EUB : true, NULL, 1, &parent_key);
15853 :
5448 tgl 15854 GIC 1549 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
15855 : {
5050 bruce 15856 519 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
15857 : SysScanDesc child_scan;
15858 : ScanKeyData child_key;
15859 : HeapTuple child_tuple;
15860 519 : bool found = false;
6125 bruce 15861 ECB :
2 alvherre 15862 GNC 519 : if (parent_con->contype != CONSTRAINT_CHECK &&
15863 464 : parent_con->contype != CONSTRAINT_NOTNULL)
6125 bruce 15864 GBC 284 : continue;
15865 :
4006 alvherre 15866 ECB : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
4006 alvherre 15867 GBC 245 : if (parent_con->connoinherit)
4101 15868 10 : continue;
15869 :
15870 : /* Search for a child constraint matching this one */
5448 tgl 15871 GIC 235 : ScanKeyInit(&child_key,
15872 : Anum_pg_constraint_conrelid,
15873 : BTEqualStrategyNumber, F_OIDEQ,
5448 tgl 15874 ECB : ObjectIdGetDatum(RelationGetRelid(child_rel)));
1678 tgl 15875 GIC 235 : child_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
15876 : true, NULL, 1, &child_key);
6022 tgl 15877 ECB :
5448 tgl 15878 GIC 432 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
15879 : {
5050 bruce 15880 CBC 420 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
5050 bruce 15881 ECB : HeapTuple child_copy;
15882 :
2 alvherre 15883 GNC 420 : if (child_con->contype != parent_con->contype)
5448 tgl 15884 CBC 105 : continue;
15885 :
15886 : /*
15887 : * CHECK constraint are matched by name, NOT NULL ones by
15888 : * attribute number
15889 : */
2 alvherre 15890 GNC 315 : if (child_con->contype == CONSTRAINT_CHECK &&
15891 48 : strcmp(NameStr(parent_con->conname),
5448 tgl 15892 GIC 48 : NameStr(child_con->conname)) != 0)
15893 15 : continue;
2 alvherre 15894 GNC 300 : else if (child_con->contype == CONSTRAINT_NOTNULL)
15895 : {
15896 267 : AttrNumber parent_attno = extractNotNullColumn(parent_tuple);
15897 267 : AttrNumber child_attno = extractNotNullColumn(child_tuple);
15898 :
15899 267 : if (strcmp(get_attname(parent_relid, parent_attno, false),
15900 267 : get_attname(RelationGetRelid(child_rel), child_attno,
15901 : false)) != 0)
15902 77 : continue;
15903 : }
15904 :
15905 223 : if (child_con->contype == CONSTRAINT_CHECK &&
15906 33 : !constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
5448 tgl 15907 CBC 3 : ereport(ERROR,
15908 : (errcode(ERRCODE_DATATYPE_MISMATCH),
5448 tgl 15909 ECB : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15910 : RelationGetRelationName(child_rel),
15911 : NameStr(parent_con->conname))));
6125 bruce 15912 :
2374 tgl 15913 : /* If the child constraint is "no inherit" then cannot merge */
4006 alvherre 15914 GIC 220 : if (child_con->connoinherit)
4101 alvherre 15915 LBC 0 : ereport(ERROR,
15916 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
4101 alvherre 15917 ECB : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15918 : NameStr(child_con->conname),
15919 : RelationGetRelationName(child_rel))));
15920 :
15921 : /*
15922 : * If the child constraint is "not valid" then cannot merge with a
2374 tgl 15923 : * valid parent constraint
15924 : */
2374 tgl 15925 GIC 220 : if (parent_con->convalidated && !child_con->convalidated)
2374 tgl 15926 UIC 0 : ereport(ERROR,
2374 tgl 15927 ECB : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15928 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
15929 : NameStr(child_con->conname),
15930 : RelationGetRelationName(child_rel))));
15931 :
15932 : /*
15933 : * OK, bump the child constraint's inheritance count. (If we fail
15934 : * later on, this change will just roll back.)
15935 : */
5448 tgl 15936 GIC 220 : child_copy = heap_copytuple(child_tuple);
15937 220 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
15938 220 : child_con->coninhcount++;
12 peter 15939 GNC 220 : if (child_con->coninhcount < 0)
12 peter 15940 UNC 0 : ereport(ERROR,
15941 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15942 : errmsg("too many inheritance parents"));
2314 rhaas 15943 ECB :
15944 : /*
15945 : * In case of partitions, an inherited constraint must be
15946 : * inherited only once since it cannot have multiple parents and
15947 : * it is never considered local.
15948 : */
2314 rhaas 15949 GIC 220 : if (child_is_partition)
15950 : {
15951 187 : Assert(child_con->coninhcount == 1);
2314 rhaas 15952 CBC 187 : child_con->conislocal = false;
15953 : }
15954 :
2259 alvherre 15955 220 : CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
5448 tgl 15956 220 : heap_freetuple(child_copy);
15957 :
5448 tgl 15958 GIC 220 : found = true;
5448 tgl 15959 CBC 220 : break;
6125 bruce 15960 ECB : }
15961 :
5448 tgl 15962 CBC 232 : systable_endscan(child_scan);
15963 :
6125 bruce 15964 GBC 232 : if (!found)
6125 bruce 15965 GIC 12 : ereport(ERROR,
15966 : (errcode(ERRCODE_DATATYPE_MISMATCH),
15967 : errmsg("child table is missing constraint \"%s\"",
15968 : NameStr(parent_con->conname))));
15969 : }
6125 neilc 15970 EUB :
5448 tgl 15971 GBC 1030 : systable_endscan(parent_scan);
1539 andres 15972 GIC 1030 : table_close(catalog_relation, RowExclusiveLock);
6125 bruce 15973 GBC 1030 : }
6125 bruce 15974 EUB :
neilc 15975 : /*
15976 : * ALTER TABLE NO INHERIT
15977 : *
15978 : * Return value is the address of the relation that is no longer parent.
15979 : */
2314 rhaas 15980 ECB : static ObjectAddress
2314 rhaas 15981 CBC 22 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
2314 rhaas 15982 ECB : {
15983 : ObjectAddress address;
15984 : Relation parent_rel;
15985 :
2314 rhaas 15986 GIC 22 : if (rel->rd_rel->relispartition)
2314 rhaas 15987 UIC 0 : ereport(ERROR,
15988 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15989 : errmsg("cannot change inheritance of a partition")));
2314 rhaas 15990 ECB :
15991 : /*
15992 : * AccessShareLock on the parent is probably enough, seeing that DROP
15993 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
15994 : * be inspecting the parent's schema.
15995 : */
1539 andres 15996 GIC 22 : parent_rel = table_openrv(parent, AccessShareLock);
2314 rhaas 15997 ECB :
15998 : /*
15999 : * We don't bother to check ownership of the parent table --- ownership of
16000 : * the child is presumed enough rights.
16001 : */
16002 :
16003 : /* Off to RemoveInheritance() where most of the work happens */
745 alvherre 16004 GIC 22 : RemoveInheritance(rel, parent_rel, false);
2314 rhaas 16005 ECB :
2314 rhaas 16006 GIC 19 : ObjectAddressSet(address, RelationRelationId,
16007 : RelationGetRelid(parent_rel));
2314 rhaas 16008 ECB :
1763 tgl 16009 : /* keep our lock on the parent relation until commit */
1539 andres 16010 GIC 19 : table_close(parent_rel, NoLock);
16011 :
2314 rhaas 16012 19 : return address;
16013 : }
16014 :
16015 : /*
16016 : * MarkInheritDetached
16017 : *
16018 : * Set inhdetachpending for a partition, for ATExecDetachPartition
711 alvherre 16019 ECB : * in concurrent mode. While at it, verify that no other partition is
16020 : * already pending detach.
745 16021 : */
16022 : static void
745 alvherre 16023 GIC 73 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
16024 : {
16025 : Relation catalogRelation;
697 tgl 16026 ECB : SysScanDesc scan;
745 alvherre 16027 : ScanKeyData key;
16028 : HeapTuple inheritsTuple;
745 alvherre 16029 GIC 73 : bool found = false;
16030 :
745 alvherre 16031 CBC 73 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
745 alvherre 16032 ECB :
16033 : /*
16034 : * Find pg_inherits entries by inhparent. (We need to scan them all in
711 16035 : * order to verify that no other partition is pending detach.)
16036 : */
745 alvherre 16037 GIC 73 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16038 73 : ScanKeyInit(&key,
16039 : Anum_pg_inherits_inhparent,
16040 : BTEqualStrategyNumber, F_OIDEQ,
711 alvherre 16041 ECB : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
711 alvherre 16042 GIC 73 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16043 : true, NULL, 1, &key);
16044 :
745 16045 288 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16046 : {
16047 : Form_pg_inherits inhForm;
16048 :
711 16049 143 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16050 143 : if (inhForm->inhdetachpending)
16051 1 : ereport(ERROR,
711 alvherre 16052 ECB : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16053 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16054 : get_rel_name(inhForm->inhrelid),
16055 : get_namespace_name(parent_rel->rd_rel->relnamespace),
16056 : RelationGetRelationName(parent_rel)),
16057 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
745 16058 :
711 alvherre 16059 GIC 142 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
16060 : {
711 alvherre 16061 ECB : HeapTuple newtup;
745 16062 :
711 alvherre 16063 GBC 72 : newtup = heap_copytuple(inheritsTuple);
711 alvherre 16064 GIC 72 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16065 :
16066 72 : CatalogTupleUpdate(catalogRelation,
16067 : &inheritsTuple->t_self,
16068 : newtup);
711 alvherre 16069 CBC 72 : found = true;
16070 72 : heap_freetuple(newtup);
711 alvherre 16071 EUB : /* keep looking, to ensure we catch others pending detach */
16072 : }
16073 : }
16074 :
16075 : /* Done */
745 alvherre 16076 CBC 72 : systable_endscan(scan);
16077 72 : table_close(catalogRelation, RowExclusiveLock);
745 alvherre 16078 EUB :
745 alvherre 16079 GIC 72 : if (!found)
745 alvherre 16080 UIC 0 : ereport(ERROR,
16081 : (errcode(ERRCODE_UNDEFINED_TABLE),
16082 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
745 alvherre 16083 ECB : RelationGetRelationName(child_rel),
16084 : RelationGetRelationName(parent_rel))));
745 alvherre 16085 GIC 72 : }
16086 :
16087 : /*
16088 : * RemoveInheritance
16089 : *
6125 neilc 16090 ECB : * Drop a parent from the child's parents. This just adjusts the attinhcount
bruce 16091 : * and attislocal of the columns and removes the pg_inherit and pg_depend
16092 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16093 : *
16094 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16095 : * up attislocal stays true, which means if a child is ever removed from a
16096 : * parent then its columns will never be automatically dropped which may
16097 : * surprise. But at least we'll never surprise by dropping columns someone
16098 : * isn't expecting to be dropped which would actually mean data loss.
16099 : *
16100 : * coninhcount and conislocal for inherited constraints are adjusted in
16101 : * exactly the same way.
16102 : *
16103 : * Common to ATExecDropInherit() and ATExecDetachPartition().
16104 : */
16105 : static void
745 alvherre 16106 GIC 241 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16107 : {
16108 : Relation catalogRelation;
6125 bruce 16109 ECB : SysScanDesc scan;
16110 : ScanKeyData key[3];
16111 : HeapTuple attributeTuple,
4372 rhaas 16112 : constraintTuple;
5448 tgl 16113 : List *connames;
16114 : List *nncolumns;
16115 : bool found;
2314 rhaas 16116 GIC 241 : bool child_is_partition = false;
16117 :
16118 : /* If parent_rel is a partitioned table, child_rel must be a partition */
16119 241 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16120 219 : child_is_partition = true;
16121 :
1906 alvherre 16122 241 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16123 : RelationGetRelid(parent_rel),
16124 : expect_detached,
745 16125 241 : RelationGetRelationName(child_rel));
6125 bruce 16126 CBC 241 : if (!found)
2314 rhaas 16127 ECB : {
2314 rhaas 16128 CBC 12 : if (child_is_partition)
2314 rhaas 16129 GIC 9 : ereport(ERROR,
16130 : (errcode(ERRCODE_UNDEFINED_TABLE),
16131 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16132 : RelationGetRelationName(child_rel),
16133 : RelationGetRelationName(parent_rel))));
16134 : else
2314 rhaas 16135 CBC 3 : ereport(ERROR,
16136 : (errcode(ERRCODE_UNDEFINED_TABLE),
2118 tgl 16137 ECB : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16138 : RelationGetRelationName(parent_rel),
16139 : RelationGetRelationName(child_rel))));
16140 : }
6125 bruce 16141 :
16142 : /*
6022 tgl 16143 : * Search through child columns looking for ones matching parent rel
16144 : */
1539 andres 16145 GIC 229 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
6022 tgl 16146 229 : ScanKeyInit(&key[0],
16147 : Anum_pg_attribute_attrelid,
16148 : BTEqualStrategyNumber, F_OIDEQ,
16149 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
6125 bruce 16150 229 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16151 : true, NULL, 1, key);
16152 2011 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16153 : {
6125 neilc 16154 CBC 1782 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16155 :
16156 : /* Ignore if dropped or not inherited */
6125 bruce 16157 GIC 1782 : if (att->attisdropped)
6125 bruce 16158 UIC 0 : continue;
6022 tgl 16159 GIC 1782 : if (att->attinhcount <= 0)
16160 1383 : continue;
16161 :
16162 399 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
6022 tgl 16163 CBC 399 : NameStr(att->attname)))
16164 : {
16165 : /* Decrement inhcount and possibly set islocal to true */
6125 bruce 16166 GIC 393 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
neilc 16167 393 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16168 :
bruce 16169 393 : copy_att->attinhcount--;
16170 393 : if (copy_att->attinhcount == 0)
16171 393 : copy_att->attislocal = true;
16172 :
2259 alvherre 16173 393 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
6125 bruce 16174 CBC 393 : heap_freetuple(copyTuple);
16175 : }
16176 : }
6125 bruce 16177 GIC 229 : systable_endscan(scan);
1539 andres 16178 CBC 229 : table_close(catalogRelation, RowExclusiveLock);
16179 :
16180 : /*
16181 : * Likewise, find inherited check constraints and disinherit them. To do
5050 bruce 16182 ECB : * this, we first need a list of the names of the parent's check
5448 tgl 16183 : * constraints. (We cheat a bit by only checking for name matches,
16184 : * assuming that the expressions will match.)
16185 : *
16186 : * For NOT NULL columns, we store column numbers to match.
16187 : */
1539 andres 16188 GIC 229 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
5448 tgl 16189 CBC 229 : ScanKeyInit(&key[0],
5448 tgl 16190 ECB : Anum_pg_constraint_conrelid,
16191 : BTEqualStrategyNumber, F_OIDEQ,
16192 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
1678 tgl 16193 GIC 229 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16194 : true, NULL, 1, key);
5448 tgl 16195 ECB :
5448 tgl 16196 CBC 229 : connames = NIL;
2 alvherre 16197 GNC 229 : nncolumns = NIL;
16198 :
5448 tgl 16199 CBC 330 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16200 : {
5448 tgl 16201 GIC 101 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
5448 tgl 16202 ECB :
5448 tgl 16203 GIC 101 : if (con->contype == CONSTRAINT_CHECK)
16204 6 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
2 alvherre 16205 GNC 101 : if (con->contype == CONSTRAINT_NOTNULL)
16206 19 : nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
5448 tgl 16207 ECB : }
16208 :
5448 tgl 16209 GIC 229 : systable_endscan(scan);
16210 :
16211 : /* Now scan the child's constraints */
5448 tgl 16212 CBC 229 : ScanKeyInit(&key[0],
16213 : Anum_pg_constraint_conrelid,
16214 : BTEqualStrategyNumber, F_OIDEQ,
16215 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
1678 16216 229 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16217 : true, NULL, 1, key);
16218 :
5448 tgl 16219 GIC 428 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
5448 tgl 16220 ECB : {
5448 tgl 16221 CBC 199 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
2 alvherre 16222 GNC 199 : bool match = false;
16223 : ListCell *lc;
16224 :
16225 : /*
16226 : * Match CHECK constraints by name, NOT NULL constraints by column
16227 : * number, and ignore all others.
16228 : */
16229 199 : if (con->contype == CONSTRAINT_CHECK)
16230 : {
16231 92 : foreach(lc, connames)
16232 : {
16233 12 : if (con->contype == CONSTRAINT_CHECK &&
16234 12 : strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
16235 : {
16236 6 : match = true;
16237 6 : break;
16238 : }
5448 tgl 16239 ECB : }
16240 : }
2 alvherre 16241 GNC 113 : else if (con->contype == CONSTRAINT_NOTNULL)
16242 : {
16243 28 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
16244 :
16245 40 : foreach(lc, nncolumns)
16246 : {
16247 31 : if (lfirst_int(lc) == child_attno)
16248 : {
16249 19 : match = true;
16250 19 : break;
16251 : }
16252 : }
16253 : }
16254 : else
16255 85 : continue;
5448 tgl 16256 ECB :
5448 tgl 16257 GBC 114 : if (match)
16258 : {
5448 tgl 16259 ECB : /* Decrement inhcount and possibly set islocal to true */
5448 tgl 16260 GIC 25 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
5448 tgl 16261 CBC 25 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
16262 :
2118 tgl 16263 GIC 25 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
5448 tgl 16264 UIC 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
16265 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
16266 :
5448 tgl 16267 GIC 25 : copy_con->coninhcount--;
16268 25 : if (copy_con->coninhcount == 0)
16269 25 : copy_con->conislocal = true;
16270 :
2259 alvherre 16271 25 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
5448 tgl 16272 CBC 25 : heap_freetuple(copyTuple);
16273 : }
5448 tgl 16274 ECB : }
16275 :
5448 tgl 16276 GIC 229 : systable_endscan(scan);
1539 andres 16277 CBC 229 : table_close(catalogRelation, RowExclusiveLock);
5448 tgl 16278 ECB :
2314 rhaas 16279 CBC 229 : drop_parent_dependency(RelationGetRelid(child_rel),
4372 rhaas 16280 ECB : RelationRelationId,
2126 16281 : RelationGetRelid(parent_rel),
16282 : child_dependency_type(child_is_partition));
2308 16283 :
16284 : /*
16285 : * Post alter hook of this inherits. Since object_access_hook doesn't take
16286 : * multiple object identifiers, we relay oid of parent relation using
16287 : * auxiliary_id argument.
16288 : */
3675 rhaas 16289 GIC 229 : InvokeObjectPostAlterHookArg(InheritsRelationId,
16290 : RelationGetRelid(child_rel), 0,
16291 : RelationGetRelid(parent_rel), false);
4372 16292 229 : }
16293 :
16294 : /*
16295 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16296 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16297 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16298 : * be TypeRelationId). There's no convenient way to do this, so go trawling
16299 : * through pg_depend.
16300 : */
4372 rhaas 16301 ECB : static void
2126 rhaas 16302 GIC 235 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
16303 : DependencyType deptype)
16304 : {
16305 : Relation catalogRelation;
16306 : SysScanDesc scan;
16307 : ScanKeyData key[3];
4372 rhaas 16308 ECB : HeapTuple depTuple;
16309 :
1539 andres 16310 CBC 235 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
16311 :
6125 bruce 16312 235 : ScanKeyInit(&key[0],
6125 bruce 16313 ECB : Anum_pg_depend_classid,
16314 : BTEqualStrategyNumber, F_OIDEQ,
16315 : ObjectIdGetDatum(RelationRelationId));
6125 bruce 16316 CBC 235 : ScanKeyInit(&key[1],
6125 bruce 16317 ECB : Anum_pg_depend_objid,
16318 : BTEqualStrategyNumber, F_OIDEQ,
4372 rhaas 16319 : ObjectIdGetDatum(relid));
6022 tgl 16320 GIC 235 : ScanKeyInit(&key[2],
6022 tgl 16321 ECB : Anum_pg_depend_objsubid,
16322 : BTEqualStrategyNumber, F_INT4EQ,
16323 : Int32GetDatum(0));
16324 :
6125 bruce 16325 GIC 235 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
3568 rhaas 16326 ECB : NULL, 3, key);
6125 bruce 16327 :
6125 bruce 16328 GIC 720 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
16329 : {
6125 bruce 16330 CBC 485 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
16331 :
4372 rhaas 16332 485 : if (dep->refclassid == refclassid &&
4372 rhaas 16333 GIC 241 : dep->refobjid == refobjid &&
6022 tgl 16334 235 : dep->refobjsubid == 0 &&
2126 rhaas 16335 CBC 235 : dep->deptype == deptype)
2258 tgl 16336 GIC 235 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
6125 bruce 16337 ECB : }
16338 :
6125 neilc 16339 CBC 235 : systable_endscan(scan);
1539 andres 16340 GIC 235 : table_close(catalogRelation, RowExclusiveLock);
4372 rhaas 16341 235 : }
16342 :
16343 : /*
16344 : * ALTER TABLE OF
4372 rhaas 16345 ECB : *
16346 : * Attach a table to a composite type, as though it had been created with CREATE
16347 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
16348 : * subject table must not have inheritance parents. These restrictions ensure
16349 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
16350 : *
16351 : * The address of the type is returned.
16352 : */
16353 : static ObjectAddress
4372 rhaas 16354 GIC 33 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
16355 : {
4372 rhaas 16356 CBC 33 : Oid relid = RelationGetRelid(rel);
4372 rhaas 16357 ECB : Type typetuple;
16358 : Form_pg_type typeform;
16359 : Oid typeid;
16360 : Relation inheritsRelation,
16361 : relationRelation;
16362 : SysScanDesc scan;
16363 : ScanKeyData key;
16364 : AttrNumber table_attno,
16365 : type_attno;
16366 : TupleDesc typeTupleDesc,
16367 : tableTupleDesc;
16368 : ObjectAddress tableobj,
16369 : typeobj;
16370 : HeapTuple classtuple;
16371 :
16372 : /* Validate the type. */
4372 rhaas 16373 GIC 33 : typetuple = typenameType(NULL, ofTypename, NULL);
16374 33 : check_of_type(typetuple);
1601 andres 16375 33 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
16376 33 : typeid = typeform->oid;
16377 :
16378 : /* Fail if the table has any inheritance parents. */
1539 16379 33 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
4372 rhaas 16380 CBC 33 : ScanKeyInit(&key,
4372 rhaas 16381 ECB : Anum_pg_inherits_inhrelid,
4372 rhaas 16382 EUB : BTEqualStrategyNumber, F_OIDEQ,
16383 : ObjectIdGetDatum(relid));
4372 rhaas 16384 GIC 33 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
16385 : true, NULL, 1, &key);
16386 33 : if (HeapTupleIsValid(systable_getnext(scan)))
16387 3 : ereport(ERROR,
16388 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16389 : errmsg("typed tables cannot inherit")));
16390 30 : systable_endscan(scan);
1539 andres 16391 CBC 30 : table_close(inheritsRelation, AccessShareLock);
16392 :
4372 rhaas 16393 ECB : /*
16394 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
16395 : * require that the order also match. However, attnotnull need not match.
16396 : */
4372 rhaas 16397 CBC 30 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
16398 30 : tableTupleDesc = RelationGetDescr(rel);
4372 rhaas 16399 GIC 30 : table_attno = 1;
16400 95 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
16401 : {
4372 rhaas 16402 ECB : Form_pg_attribute type_attr,
16403 : table_attr;
16404 : const char *type_attname,
16405 : *table_attname;
16406 :
16407 : /* Get the next non-dropped type attribute. */
2058 andres 16408 GIC 77 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
4372 rhaas 16409 CBC 77 : if (type_attr->attisdropped)
16410 22 : continue;
4372 rhaas 16411 GIC 55 : type_attname = NameStr(type_attr->attname);
16412 :
16413 : /* Get the next non-dropped table attribute. */
16414 : do
16415 : {
16416 61 : if (table_attno > tableTupleDesc->natts)
16417 3 : ereport(ERROR,
16418 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16419 : errmsg("table is missing column \"%s\"",
16420 : type_attname)));
2058 andres 16421 58 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
16422 58 : table_attno++;
4372 rhaas 16423 58 : } while (table_attr->attisdropped);
16424 52 : table_attname = NameStr(table_attr->attname);
16425 :
16426 : /* Compare name. */
16427 52 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
16428 3 : ereport(ERROR,
16429 : (errcode(ERRCODE_DATATYPE_MISMATCH),
2118 tgl 16430 ECB : errmsg("table has column \"%s\" where type requires \"%s\"",
16431 : table_attname, type_attname)));
16432 :
16433 : /* Compare type. */
4372 rhaas 16434 GIC 49 : if (table_attr->atttypid != type_attr->atttypid ||
16435 46 : table_attr->atttypmod != type_attr->atttypmod ||
16436 43 : table_attr->attcollation != type_attr->attcollation)
4372 rhaas 16437 CBC 6 : ereport(ERROR,
4372 rhaas 16438 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
16439 : errmsg("table \"%s\" has different type for column \"%s\"",
2118 tgl 16440 : RelationGetRelationName(rel), type_attname)));
4372 rhaas 16441 : }
480 tgl 16442 GIC 18 : ReleaseTupleDesc(typeTupleDesc);
16443 :
4372 rhaas 16444 ECB : /* Any remaining columns at the end of the table had better be dropped. */
4372 rhaas 16445 CBC 18 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
16446 : {
2058 andres 16447 GIC 3 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
2058 andres 16448 ECB : table_attno - 1);
16449 :
4372 rhaas 16450 GIC 3 : if (!table_attr->attisdropped)
16451 3 : ereport(ERROR,
4372 rhaas 16452 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
16453 : errmsg("table has extra column \"%s\"",
16454 : NameStr(table_attr->attname))));
16455 : }
16456 :
16457 : /* If the table was already typed, drop the existing dependency. */
4372 rhaas 16458 GIC 15 : if (rel->rd_rel->reloftype)
2126 16459 3 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16460 : DEPENDENCY_NORMAL);
4372 rhaas 16461 ECB :
16462 : /* Record a dependency on the new type. */
4372 rhaas 16463 CBC 15 : tableobj.classId = RelationRelationId;
16464 15 : tableobj.objectId = relid;
16465 15 : tableobj.objectSubId = 0;
4372 rhaas 16466 GIC 15 : typeobj.classId = TypeRelationId;
16467 15 : typeobj.objectId = typeid;
4372 rhaas 16468 CBC 15 : typeobj.objectSubId = 0;
16469 15 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
16470 :
16471 : /* Update pg_class.reloftype */
1539 andres 16472 15 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
4372 rhaas 16473 GIC 15 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16474 15 : if (!HeapTupleIsValid(classtuple))
4372 rhaas 16475 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
4372 rhaas 16476 CBC 15 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
2259 alvherre 16477 GIC 15 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
16478 :
3675 rhaas 16479 CBC 15 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16480 :
4372 16481 15 : heap_freetuple(classtuple);
1539 andres 16482 GIC 15 : table_close(relationRelation, RowExclusiveLock);
16483 :
4372 rhaas 16484 CBC 15 : ReleaseSysCache(typetuple);
2937 alvherre 16485 ECB :
2937 alvherre 16486 GIC 15 : return typeobj;
16487 : }
16488 :
16489 : /*
16490 : * ALTER TABLE NOT OF
4372 rhaas 16491 ECB : *
3260 bruce 16492 : * Detach a typed table from its originating type. Just clear reloftype and
4372 rhaas 16493 : * remove the dependency.
16494 : */
16495 : static void
4372 rhaas 16496 GIC 3 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
4372 rhaas 16497 ECB : {
4372 rhaas 16498 CBC 3 : Oid relid = RelationGetRelid(rel);
16499 : Relation relationRelation;
4372 rhaas 16500 ECB : HeapTuple tuple;
16501 :
4372 rhaas 16502 GIC 3 : if (!OidIsValid(rel->rd_rel->reloftype))
4372 rhaas 16503 LBC 0 : ereport(ERROR,
16504 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16505 : errmsg("\"%s\" is not a typed table",
4372 rhaas 16506 ECB : RelationGetRelationName(rel))));
16507 :
16508 : /*
16509 : * We don't bother to check ownership of the type --- ownership of the
16510 : * table is presumed enough rights. No lock required on the type, either.
16511 : */
16512 :
2126 rhaas 16513 GIC 3 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16514 : DEPENDENCY_NORMAL);
4372 rhaas 16515 ECB :
4372 rhaas 16516 EUB : /* Clear pg_class.reloftype */
1539 andres 16517 GIC 3 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
4372 rhaas 16518 3 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16519 3 : if (!HeapTupleIsValid(tuple))
4372 rhaas 16520 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
4372 rhaas 16521 GIC 3 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
2259 alvherre 16522 3 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
16523 :
3675 rhaas 16524 3 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16525 :
4372 rhaas 16526 CBC 3 : heap_freetuple(tuple);
1539 andres 16527 GBC 3 : table_close(relationRelation, RowExclusiveLock);
6125 bruce 16528 GIC 3 : }
16529 :
16530 : /*
16531 : * relation_mark_replica_identity: Update a table's replica identity
16532 : *
16533 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
16534 : * index. Otherwise, it must be InvalidOid.
16535 : *
16536 : * Caller had better hold an exclusive lock on the relation, as the results
78 tgl 16537 ECB : * of running two of these concurrently wouldn't be pretty.
3439 rhaas 16538 : */
16539 : static void
3439 rhaas 16540 CBC 186 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
3439 rhaas 16541 EUB : bool is_internal)
16542 : {
16543 : Relation pg_index;
16544 : Relation pg_class;
16545 : HeapTuple pg_class_tuple;
16546 : HeapTuple pg_index_tuple;
16547 : Form_pg_class pg_class_form;
16548 : Form_pg_index pg_index_form;
16549 : ListCell *index;
3439 rhaas 16550 ECB :
16551 : /*
16552 : * Check whether relreplident has changed, and update it if so.
16553 : */
1539 andres 16554 GIC 186 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3439 rhaas 16555 186 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
2118 tgl 16556 ECB : ObjectIdGetDatum(RelationGetRelid(rel)));
3439 rhaas 16557 CBC 186 : if (!HeapTupleIsValid(pg_class_tuple))
3439 rhaas 16558 UIC 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
3437 peter_e 16559 ECB : RelationGetRelationName(rel));
3439 rhaas 16560 CBC 186 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
3439 rhaas 16561 GIC 186 : if (pg_class_form->relreplident != ri_type)
16562 : {
3439 rhaas 16563 CBC 164 : pg_class_form->relreplident = ri_type;
2259 alvherre 16564 GIC 164 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
3439 rhaas 16565 ECB : }
1539 andres 16566 CBC 186 : table_close(pg_class, RowExclusiveLock);
3439 rhaas 16567 GIC 186 : heap_freetuple(pg_class_tuple);
16568 :
16569 : /*
16570 : * Update the per-index indisreplident flags correctly.
16571 : */
1539 andres 16572 CBC 186 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
3439 rhaas 16573 513 : foreach(index, RelationGetIndexList(rel))
3439 rhaas 16574 ECB : {
3439 rhaas 16575 GIC 327 : Oid thisIndexOid = lfirst_oid(index);
16576 327 : bool dirty = false;
16577 :
16578 327 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
16579 : ObjectIdGetDatum(thisIndexOid));
16580 327 : if (!HeapTupleIsValid(pg_index_tuple))
3439 rhaas 16581 UIC 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
3439 rhaas 16582 CBC 327 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
16583 :
78 tgl 16584 GIC 327 : if (thisIndexOid == indexOid)
16585 : {
16586 : /* Set the bit if not already set. */
78 tgl 16587 CBC 104 : if (!pg_index_form->indisreplident)
78 tgl 16588 EUB : {
78 tgl 16589 GIC 98 : dirty = true;
16590 98 : pg_index_form->indisreplident = true;
16591 : }
16592 : }
16593 : else
16594 : {
16595 : /* Unset the bit if set. */
16596 223 : if (pg_index_form->indisreplident)
78 tgl 16597 ECB : {
78 tgl 16598 GIC 20 : dirty = true;
16599 20 : pg_index_form->indisreplident = false;
16600 : }
16601 : }
16602 :
3439 rhaas 16603 327 : if (dirty)
16604 : {
2259 alvherre 16605 CBC 118 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
3439 rhaas 16606 GIC 118 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
3437 peter_e 16607 ECB : InvalidOid, is_internal);
16608 :
16609 : /*
16610 : * Invalidate the relcache for the table, so that after we commit
509 akapila 16611 : * all sessions will refresh the table's replica identity index
16612 : * before attempting any UPDATE or DELETE on the table. (If we
78 tgl 16613 : * changed the table's pg_class row above, then a relcache inval
16614 : * is already queued due to that; but we might not have.)
16615 : */
509 akapila 16616 GIC 118 : CacheInvalidateRelcache(rel);
16617 : }
3439 rhaas 16618 327 : heap_freetuple(pg_index_tuple);
16619 : }
16620 :
1539 andres 16621 186 : table_close(pg_index, RowExclusiveLock);
3439 rhaas 16622 186 : }
16623 :
3439 rhaas 16624 ECB : /*
16625 : * ALTER TABLE <name> REPLICA IDENTITY ...
16626 : */
16627 : static void
3439 rhaas 16628 GIC 207 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
16629 : {
3439 rhaas 16630 ECB : Oid indexOid;
16631 : Relation indexRel;
16632 : int key;
16633 :
3439 rhaas 16634 GIC 207 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
16635 : {
16636 3 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16637 3 : return;
3439 rhaas 16638 ECB : }
3439 rhaas 16639 CBC 204 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
16640 : {
3439 rhaas 16641 GIC 61 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16642 61 : return;
3439 rhaas 16643 ECB : }
3439 rhaas 16644 GIC 143 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
16645 : {
3439 rhaas 16646 CBC 18 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
3439 rhaas 16647 GIC 18 : return;
16648 : }
16649 125 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
3439 rhaas 16650 ECB : {
3260 bruce 16651 : /* fallthrough */ ;
3439 rhaas 16652 : }
16653 : else
3439 rhaas 16654 UIC 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
16655 :
16656 : /* Check that the index exists */
3439 rhaas 16657 GIC 125 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
16658 125 : if (!OidIsValid(indexOid))
3439 rhaas 16659 UIC 0 : ereport(ERROR,
3439 rhaas 16660 ECB : (errcode(ERRCODE_UNDEFINED_OBJECT),
16661 : errmsg("index \"%s\" for table \"%s\" does not exist",
16662 : stmt->name, RelationGetRelationName(rel))));
16663 :
3439 rhaas 16664 CBC 125 : indexRel = index_open(indexOid, ShareLock);
3439 rhaas 16665 ECB :
16666 : /* Check that the index is on the relation we're altering. */
3439 rhaas 16667 CBC 125 : if (indexRel->rd_index == NULL ||
3439 rhaas 16668 GIC 125 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
16669 3 : ereport(ERROR,
3439 rhaas 16670 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16671 : errmsg("\"%s\" is not an index for table \"%s\"",
16672 : RelationGetRelationName(indexRel),
16673 : RelationGetRelationName(rel))));
16674 : /* The AM must support uniqueness, and the index must in fact be unique. */
1539 andres 16675 GIC 122 : if (!indexRel->rd_indam->amcanunique ||
2639 tgl 16676 119 : !indexRel->rd_index->indisunique)
3439 rhaas 16677 CBC 6 : ereport(ERROR,
3439 rhaas 16678 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16679 : errmsg("cannot use non-unique index \"%s\" as replica identity",
2118 tgl 16680 : RelationGetRelationName(indexRel))));
3439 rhaas 16681 EUB : /* Deferred indexes are not guaranteed to be always unique. */
3439 rhaas 16682 GIC 116 : if (!indexRel->rd_index->indimmediate)
16683 3 : ereport(ERROR,
16684 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16685 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
2118 tgl 16686 ECB : RelationGetRelationName(indexRel))));
16687 : /* Expression indexes aren't supported. */
3439 rhaas 16688 GIC 113 : if (RelationGetIndexExpressions(indexRel) != NIL)
16689 3 : ereport(ERROR,
16690 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16691 : errmsg("cannot use expression index \"%s\" as replica identity",
16692 : RelationGetRelationName(indexRel))));
16693 : /* Predicate indexes aren't supported. */
16694 110 : if (RelationGetIndexPredicate(indexRel) != NIL)
16695 3 : ereport(ERROR,
16696 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16697 : errmsg("cannot use partial index \"%s\" as replica identity",
16698 : RelationGetRelationName(indexRel))));
16699 :
16700 : /* Check index for nullable columns. */
1828 teodor 16701 237 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
16702 : {
3260 bruce 16703 133 : int16 attno = indexRel->rd_index->indkey.values[key];
16704 : Form_pg_attribute attr;
16705 :
16706 : /*
2550 tgl 16707 ECB : * Reject any other system columns. (Going forward, we'll disallow
16708 : * indexes containing such columns in the first place, but they might
16709 : * exist in older branches.)
16710 : */
2550 tgl 16711 GIC 133 : if (attno <= 0)
2550 tgl 16712 UIC 0 : ereport(ERROR,
16713 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
16714 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16715 : RelationGetRelationName(indexRel), attno)));
16716 :
2058 andres 16717 CBC 133 : attr = TupleDescAttr(rel->rd_att, attno - 1);
3439 rhaas 16718 GIC 133 : if (!attr->attnotnull)
16719 3 : ereport(ERROR,
3437 peter_e 16720 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16721 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16722 : RelationGetRelationName(indexRel),
16723 : NameStr(attr->attname))));
16724 : }
16725 :
3439 rhaas 16726 : /* This index is suitable for use as a replica identity. Mark it. */
3439 rhaas 16727 CBC 104 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
16728 :
16729 104 : index_close(indexRel, NoLock);
3439 rhaas 16730 ECB : }
16731 :
16732 : /*
16733 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
16734 : */
16735 : static void
768 michael 16736 CBC 139 : ATExecSetRowSecurity(Relation rel, bool rls)
16737 : {
16738 : Relation pg_class;
16739 : Oid relid;
16740 : HeapTuple tuple;
16741 :
3124 sfrost 16742 GIC 139 : relid = RelationGetRelid(rel);
16743 :
16744 : /* Pull the record for this relation and update it */
1539 andres 16745 139 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3124 sfrost 16746 ECB :
3124 sfrost 16747 CBC 139 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16748 :
3124 sfrost 16749 GIC 139 : if (!HeapTupleIsValid(tuple))
3124 sfrost 16750 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
3124 sfrost 16751 ECB :
768 michael 16752 GIC 139 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
2259 alvherre 16753 CBC 139 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16754 :
1539 andres 16755 139 : table_close(pg_class, RowExclusiveLock);
2744 sfrost 16756 GIC 139 : heap_freetuple(tuple);
16757 139 : }
2744 sfrost 16758 ECB :
2744 sfrost 16759 EUB : /*
2744 sfrost 16760 ECB : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
16761 : */
16762 : static void
2744 sfrost 16763 CBC 55 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
2744 sfrost 16764 ECB : {
16765 : Relation pg_class;
16766 : Oid relid;
16767 : HeapTuple tuple;
16768 :
2744 sfrost 16769 GIC 55 : relid = RelationGetRelid(rel);
2744 sfrost 16770 ECB :
1539 andres 16771 CBC 55 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
2744 sfrost 16772 ECB :
2744 sfrost 16773 GIC 55 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
2744 sfrost 16774 ECB :
2744 sfrost 16775 CBC 55 : if (!HeapTupleIsValid(tuple))
2744 sfrost 16776 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16777 :
2744 sfrost 16778 CBC 55 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
2259 alvherre 16779 55 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16780 :
1539 andres 16781 GIC 55 : table_close(pg_class, RowExclusiveLock);
3124 sfrost 16782 55 : heap_freetuple(tuple);
16783 55 : }
16784 :
16785 : /*
16786 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
16787 : */
16788 : static void
4481 rhaas 16789 CBC 23 : ATExecGenericOptions(Relation rel, List *options)
4481 rhaas 16790 ECB : {
16791 : Relation ftrel;
16792 : ForeignServer *server;
16793 : ForeignDataWrapper *fdw;
4382 bruce 16794 : HeapTuple tuple;
16795 : bool isnull;
16796 : Datum repl_val[Natts_pg_foreign_table];
16797 : bool repl_null[Natts_pg_foreign_table];
16798 : bool repl_repl[Natts_pg_foreign_table];
16799 : Datum datum;
16800 : Form_pg_foreign_table tableform;
16801 :
4382 bruce 16802 CBC 23 : if (options == NIL)
4481 rhaas 16803 UIC 0 : return;
4481 rhaas 16804 ECB :
1539 andres 16805 CBC 23 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
4481 rhaas 16806 ECB :
4481 rhaas 16807 CBC 23 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
4481 rhaas 16808 GIC 23 : if (!HeapTupleIsValid(tuple))
4481 rhaas 16809 UIC 0 : ereport(ERROR,
4481 rhaas 16810 ECB : (errcode(ERRCODE_UNDEFINED_OBJECT),
16811 : errmsg("foreign table \"%s\" does not exist",
16812 : RelationGetRelationName(rel))));
4481 rhaas 16813 CBC 23 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
4481 rhaas 16814 GIC 23 : server = GetForeignServer(tableform->ftserver);
16815 23 : fdw = GetForeignDataWrapper(server->fdwid);
16816 :
4481 rhaas 16817 CBC 23 : memset(repl_val, 0, sizeof(repl_val));
4481 rhaas 16818 GIC 23 : memset(repl_null, false, sizeof(repl_null));
16819 23 : memset(repl_repl, false, sizeof(repl_repl));
4481 rhaas 16820 ECB :
16821 : /* Extract the current options */
4481 rhaas 16822 CBC 23 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
4481 rhaas 16823 ECB : tuple,
16824 : Anum_pg_foreign_table_ftoptions,
16825 : &isnull);
4481 rhaas 16826 GIC 23 : if (isnull)
16827 2 : datum = PointerGetDatum(NULL);
16828 :
16829 : /* Transform the options */
4481 rhaas 16830 CBC 23 : datum = transformGenericOptions(ForeignTableRelationId,
16831 : datum,
4481 rhaas 16832 ECB : options,
16833 : fdw->fdwvalidator);
16834 :
4481 rhaas 16835 CBC 22 : if (PointerIsValid(DatumGetPointer(datum)))
4481 rhaas 16836 GIC 22 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
4481 rhaas 16837 ECB : else
4481 rhaas 16838 LBC 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
16839 :
4481 rhaas 16840 GIC 22 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
16841 :
4481 rhaas 16842 ECB : /* Everything looks good - update the tuple */
16843 :
4481 rhaas 16844 CBC 22 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
16845 : repl_val, repl_null, repl_repl);
4481 rhaas 16846 ECB :
2259 alvherre 16847 GIC 22 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
4481 rhaas 16848 ECB :
16849 : /*
2284 tgl 16850 : * Invalidate relcache so that all sessions will refresh any cached plans
16851 : * that might depend on the old options.
16852 : */
2284 tgl 16853 GIC 22 : CacheInvalidateRelcache(rel);
16854 :
3675 rhaas 16855 22 : InvokeObjectPostAlterHook(ForeignTableRelationId,
3675 rhaas 16856 ECB : RelationGetRelid(rel), 0);
16857 :
1539 andres 16858 CBC 22 : table_close(ftrel, RowExclusiveLock);
16859 :
4481 rhaas 16860 GIC 22 : heap_freetuple(tuple);
4481 rhaas 16861 ECB : }
16862 :
16863 : /*
751 16864 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
751 rhaas 16865 EUB : *
16866 : * Return value is the address of the modified column
16867 : */
751 rhaas 16868 ECB : static ObjectAddress
270 peter 16869 GNC 33 : ATExecSetCompression(Relation rel,
16870 : const char *column,
751 rhaas 16871 ECB : Node *newValue,
16872 : LOCKMODE lockmode)
16873 : {
16874 : Relation attrel;
16875 : HeapTuple tuple;
16876 : Form_pg_attribute atttableform;
16877 : AttrNumber attnum;
16878 : char *compression;
748 16879 : char cmethod;
16880 : ObjectAddress address;
16881 :
751 rhaas 16882 GIC 33 : compression = strVal(newValue);
16883 :
16884 33 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
16885 :
16886 : /* copy the cache entry so we can scribble on it below */
749 tgl 16887 33 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
751 rhaas 16888 CBC 33 : if (!HeapTupleIsValid(tuple))
751 rhaas 16889 UIC 0 : ereport(ERROR,
16890 : (errcode(ERRCODE_UNDEFINED_COLUMN),
751 rhaas 16891 ECB : errmsg("column \"%s\" of relation \"%s\" does not exist",
16892 : column, RelationGetRelationName(rel))));
16893 :
16894 : /* prevent them from altering a system attribute */
751 rhaas 16895 GIC 33 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16896 33 : attnum = atttableform->attnum;
16897 33 : if (attnum <= 0)
751 rhaas 16898 UIC 0 : ereport(ERROR,
16899 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16900 : errmsg("cannot alter system column \"%s\"", column)));
751 rhaas 16901 ECB :
16902 : /*
16903 : * Check that column type is compressible, then get the attribute
16904 : * compression method code
16905 : */
682 tgl 16906 GIC 33 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
16907 :
16908 : /* update pg_attribute entry */
748 rhaas 16909 CBC 30 : atttableform->attcompression = cmethod;
751 rhaas 16910 GIC 30 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
751 rhaas 16911 ECB :
751 rhaas 16912 GIC 30 : InvokeObjectPostAlterHook(RelationRelationId,
16913 : RelationGetRelid(rel),
16914 : attnum);
751 rhaas 16915 ECB :
16916 : /*
16917 : * Apply the change to indexes as well (only for simple index columns,
16918 : * matching behavior of index.c ConstructTupleDescriptor()).
749 tgl 16919 : */
682 tgl 16920 GIC 30 : SetIndexStorageProperties(rel, attrel, attnum,
16921 : false, 0,
16922 : true, cmethod,
16923 : lockmode);
749 tgl 16924 ECB :
749 tgl 16925 GIC 30 : heap_freetuple(tuple);
16926 :
751 rhaas 16927 CBC 30 : table_close(attrel, RowExclusiveLock);
16928 :
751 rhaas 16929 ECB : /* make changes visible */
751 rhaas 16930 GIC 30 : CommandCounterIncrement();
751 rhaas 16931 ECB :
751 rhaas 16932 CBC 30 : ObjectAddressSubSet(address, RelationRelationId,
749 tgl 16933 ECB : RelationGetRelid(rel), attnum);
751 rhaas 16934 CBC 30 : return address;
751 rhaas 16935 ECB : }
16936 :
16937 :
3152 alvherre 16938 : /*
16939 : * Preparation phase for SET LOGGED/UNLOGGED
16940 : *
16941 : * This verifies that we're not trying to change a temp table. Also,
16942 : * existing foreign key constraints are checked to avoid ending up with
16943 : * permanent tables referencing unlogged tables.
16944 : *
16945 : * Return value is false if the operation is a no-op (in which case the
16946 : * checks are skipped), otherwise true.
16947 : */
16948 : static bool
3149 alvherre 16949 GIC 35 : ATPrepChangePersistence(Relation rel, bool toLogged)
16950 : {
16951 : Relation pg_constraint;
16952 : HeapTuple tuple;
3152 alvherre 16953 ECB : SysScanDesc scan;
16954 : ScanKeyData skey[1];
16955 :
16956 : /*
16957 : * Disallow changing status for a temp table. Also verify whether we can
16958 : * get away with doing nothing; in such cases we don't need to run the
16959 : * checks below, either.
16960 : */
3152 alvherre 16961 GIC 35 : switch (rel->rd_rel->relpersistence)
16962 : {
3152 alvherre 16963 UIC 0 : case RELPERSISTENCE_TEMP:
16964 0 : ereport(ERROR,
16965 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
16966 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
16967 : RelationGetRelationName(rel)),
16968 : errtable(rel)));
16969 : break;
3152 alvherre 16970 GIC 19 : case RELPERSISTENCE_PERMANENT:
16971 19 : if (toLogged)
3152 alvherre 16972 ECB : /* nothing to do */
3152 alvherre 16973 CBC 3 : return false;
16974 16 : break;
16975 16 : case RELPERSISTENCE_UNLOGGED:
3152 alvherre 16976 GIC 16 : if (!toLogged)
16977 : /* nothing to do */
3152 alvherre 16978 CBC 3 : return false;
16979 13 : break;
16980 : }
16981 :
16982 : /*
16983 : * Check that the table is not part of any publication when changing to
16984 : * UNLOGGED, as UNLOGGED tables can't be published.
2271 peter_e 16985 ECB : */
2271 peter_e 16986 CBC 45 : if (!toLogged &&
235 tgl 16987 GNC 16 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
2271 peter_e 16988 UIC 0 : ereport(ERROR,
2271 peter_e 16989 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16990 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
16991 : RelationGetRelationName(rel)),
16992 : errdetail("Unlogged relations cannot be replicated.")));
16993 :
16994 : /*
16995 : * Check existing foreign key constraints to preserve the invariant that
2811 heikki.linnakangas 16996 : * permanent tables cannot reference unlogged ones. Self-referencing
3152 alvherre 16997 : * foreign keys can safely be ignored.
16998 : */
1539 andres 16999 CBC 29 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17000 :
17001 : /*
17002 : * Scan conrelid if changing to permanent, else confrelid. This also
17003 : * determines whether a useful index exists.
17004 : */
3152 alvherre 17005 GIC 29 : ScanKeyInit(&skey[0],
17006 : toLogged ? Anum_pg_constraint_conrelid :
3152 alvherre 17007 ECB : Anum_pg_constraint_confrelid,
17008 : BTEqualStrategyNumber, F_OIDEQ,
17009 : ObjectIdGetDatum(RelationGetRelid(rel)));
3152 alvherre 17010 CBC 29 : scan = systable_beginscan(pg_constraint,
17011 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17012 : true, NULL, 1, skey);
17013 :
3152 alvherre 17014 GIC 53 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
3152 alvherre 17015 ECB : {
3152 alvherre 17016 CBC 30 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17017 :
3152 alvherre 17018 GIC 30 : if (con->contype == CONSTRAINT_FOREIGN)
17019 : {
3152 alvherre 17020 ECB : Oid foreignrelid;
17021 : Relation foreignrel;
17022 :
17023 : /* the opposite end of what we used as scankey */
3152 alvherre 17024 GIC 15 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
17025 :
3152 alvherre 17026 ECB : /* ignore if self-referencing */
3152 alvherre 17027 CBC 15 : if (RelationGetRelid(rel) == foreignrelid)
3152 alvherre 17028 GIC 6 : continue;
17029 :
17030 9 : foreignrel = relation_open(foreignrelid, AccessShareLock);
17031 :
17032 9 : if (toLogged)
3152 alvherre 17033 ECB : {
748 bruce 17034 CBC 3 : if (!RelationIsPermanent(foreignrel))
3152 alvherre 17035 3 : ereport(ERROR,
3152 alvherre 17036 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17037 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17038 : RelationGetRelationName(rel),
17039 : RelationGetRelationName(foreignrel)),
17040 : errtableconstraint(rel, NameStr(con->conname))));
17041 : }
17042 : else
17043 : {
748 bruce 17044 CBC 6 : if (RelationIsPermanent(foreignrel))
3152 alvherre 17045 GIC 3 : ereport(ERROR,
3152 alvherre 17046 ECB : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17047 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17048 : RelationGetRelationName(rel),
2697 tgl 17049 : RelationGetRelationName(foreignrel)),
3152 alvherre 17050 : errtableconstraint(rel, NameStr(con->conname))));
17051 : }
17052 :
3152 alvherre 17053 GIC 3 : relation_close(foreignrel, AccessShareLock);
17054 : }
17055 : }
17056 :
3152 alvherre 17057 CBC 23 : systable_endscan(scan);
3152 alvherre 17058 ECB :
1539 andres 17059 GIC 23 : table_close(pg_constraint, AccessShareLock);
17060 :
3152 alvherre 17061 23 : return true;
3152 alvherre 17062 ECB : }
17063 :
4133 rhaas 17064 : /*
17065 : * Execute ALTER TABLE SET SCHEMA
17066 : */
2959 alvherre 17067 : ObjectAddress
2959 alvherre 17068 CBC 49 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17069 : {
17070 : Relation rel;
4133 rhaas 17071 ECB : Oid relid;
17072 : Oid oldNspOid;
17073 : Oid nspOid;
4101 rhaas 17074 EUB : RangeVar *newrv;
3812 alvherre 17075 ECB : ObjectAddresses *objsMoved;
2959 17076 : ObjectAddress myself;
17077 :
4111 rhaas 17078 CBC 49 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
1836 andres 17079 GIC 49 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4111 rhaas 17080 ECB : RangeVarCallbackForAlterRelation,
17081 : (void *) stmt);
17082 :
4094 simon 17083 CBC 48 : if (!OidIsValid(relid))
17084 : {
17085 6 : ereport(NOTICE,
17086 : (errmsg("relation \"%s\" does not exist, skipping",
17087 : stmt->relation->relname)));
2959 alvherre 17088 GIC 6 : return InvalidObjectAddress;
17089 : }
17090 :
4133 rhaas 17091 42 : rel = relation_open(relid, NoLock);
17092 :
17093 42 : oldNspOid = RelationGetNamespace(rel);
17094 :
4133 rhaas 17095 ECB : /* If it's an owned sequence, disallow moving it by itself. */
4133 rhaas 17096 GIC 42 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
4133 rhaas 17097 ECB : {
17098 : Oid tableId;
17099 : int32 colId;
17100 :
2194 peter_e 17101 CBC 2 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
2194 peter_e 17102 GBC 1 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
4133 rhaas 17103 UIC 0 : ereport(ERROR,
17104 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17105 : errmsg("cannot move an owned sequence into another schema"),
17106 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
17107 : RelationGetRelationName(rel),
17108 : get_rel_name(tableId))));
17109 : }
17110 :
17111 : /* Get and lock schema OID and check its permissions. */
4101 rhaas 17112 CBC 42 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
4101 rhaas 17113 GIC 42 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17114 :
17115 : /* common checks on switching namespaces */
2698 rhaas 17116 CBC 42 : CheckSetNamespace(oldNspOid, nspOid);
6460 tgl 17117 ECB :
3812 alvherre 17118 CBC 42 : objsMoved = new_object_addresses();
3812 alvherre 17119 GBC 42 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
3812 alvherre 17120 CBC 42 : free_object_addresses(objsMoved);
3812 alvherre 17121 ECB :
2959 alvherre 17122 GIC 42 : ObjectAddressSet(myself, RelationRelationId, relid);
2959 alvherre 17123 ECB :
2959 alvherre 17124 GIC 42 : if (oldschema)
2959 alvherre 17125 CBC 42 : *oldschema = oldNspOid;
2959 alvherre 17126 ECB :
3812 17127 : /* close rel, but keep lock until commit */
3812 alvherre 17128 GIC 42 : relation_close(rel, NoLock);
17129 :
2959 17130 42 : return myself;
17131 : }
17132 :
17133 : /*
17134 : * The guts of relocating a table or materialized view to another namespace:
17135 : * besides moving the relation itself, its dependent objects are relocated to
17136 : * the new schema.
17137 : */
17138 : void
3812 alvherre 17139 CBC 42 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17140 : ObjectAddresses *objsMoved)
17141 : {
17142 : Relation classRel;
17143 :
3812 alvherre 17144 GIC 42 : Assert(objsMoved != NULL);
17145 :
17146 : /* OK, modify the pg_class row and pg_depend entry */
1539 andres 17147 42 : classRel = table_open(RelationRelationId, RowExclusiveLock);
17148 :
3812 alvherre 17149 42 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17150 : nspOid, true, objsMoved);
17151 :
17152 : /* Fix the table's row type too, if it has one */
1006 tgl 17153 CBC 42 : if (OidIsValid(rel->rd_rel->reltype))
17154 41 : AlterTypeNamespaceInternal(rel->rd_rel->reltype,
17155 : nspOid, false, false, objsMoved);
6460 tgl 17156 ECB :
6460 tgl 17157 EUB : /* Fix other dependent stuff */
3689 kgrittn 17158 GIC 42 : if (rel->rd_rel->relkind == RELKIND_RELATION ||
2314 rhaas 17159 CBC 10 : rel->rd_rel->relkind == RELKIND_MATVIEW ||
17160 7 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17161 : {
3812 alvherre 17162 35 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17163 35 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17164 : objsMoved, AccessExclusiveLock);
17165 35 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
3812 alvherre 17166 ECB : false, objsMoved);
17167 : }
17168 :
1539 andres 17169 GIC 42 : table_close(classRel, RowExclusiveLock);
6460 tgl 17170 42 : }
6460 tgl 17171 ECB :
17172 : /*
17173 : * The guts of relocating a relation to another namespace: fix the pg_class
17174 : * entry, and the pg_depend entry if any. Caller must already have
17175 : * opened and write-locked pg_class.
17176 : */
17177 : void
6460 tgl 17178 GIC 88 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
6460 tgl 17179 ECB : Oid oldNspOid, Oid newNspOid,
3599 sfrost 17180 EUB : bool hasDependEntry,
3599 sfrost 17181 ECB : ObjectAddresses *objsMoved)
17182 : {
6385 bruce 17183 : HeapTuple classTup;
17184 : Form_pg_class classForm;
17185 : ObjectAddress thisobj;
2698 rhaas 17186 CBC 88 : bool already_done = false;
17187 :
4802 17188 88 : classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
6460 tgl 17189 88 : if (!HeapTupleIsValid(classTup))
6460 tgl 17190 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
6460 tgl 17191 GIC 88 : classForm = (Form_pg_class) GETSTRUCT(classTup);
17192 :
17193 88 : Assert(classForm->relnamespace == oldNspOid);
17194 :
3812 alvherre 17195 CBC 88 : thisobj.classId = RelationRelationId;
3812 alvherre 17196 GIC 88 : thisobj.objectId = relOid;
3812 alvherre 17197 CBC 88 : thisobj.objectSubId = 0;
3812 alvherre 17198 ECB :
17199 : /*
17200 : * If the object has already been moved, don't move it again. If it's
17201 : * already in the right place, don't move it, but still fire the object
2698 rhaas 17202 : * access hook.
17203 : */
2698 rhaas 17204 CBC 88 : already_done = object_address_present(&thisobj, objsMoved);
17205 88 : if (!already_done && oldNspOid != newNspOid)
17206 : {
17207 : /* check for duplicate name (more friendly than unique-index failure) */
3812 alvherre 17208 GIC 67 : if (get_relname_relid(NameStr(classForm->relname),
17209 : newNspOid) != InvalidOid)
3812 alvherre 17210 UIC 0 : ereport(ERROR,
17211 : (errcode(ERRCODE_DUPLICATE_TABLE),
17212 : errmsg("relation \"%s\" already exists in schema \"%s\"",
17213 : NameStr(classForm->relname),
17214 : get_namespace_name(newNspOid))));
6460 tgl 17215 ECB :
17216 : /* classTup is a copy, so OK to scribble on */
3812 alvherre 17217 CBC 67 : classForm->relnamespace = newNspOid;
17218 :
2259 alvherre 17219 GIC 67 : CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
6460 tgl 17220 ECB :
3812 alvherre 17221 : /* Update dependency on schema if caller said so */
3812 alvherre 17222 GIC 115 : if (hasDependEntry &&
3599 sfrost 17223 48 : changeDependencyFor(RelationRelationId,
17224 : relOid,
17225 : NamespaceRelationId,
17226 : oldNspOid,
3599 sfrost 17227 ECB : newNspOid) != 1)
3812 alvherre 17228 UIC 0 : elog(ERROR, "failed to change schema dependency for relation \"%s\"",
17229 : NameStr(classForm->relname));
17230 : }
2698 rhaas 17231 GIC 88 : if (!already_done)
17232 : {
3812 alvherre 17233 CBC 88 : add_exact_object_address(&thisobj, objsMoved);
17234 :
3675 rhaas 17235 88 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
3812 alvherre 17236 ECB : }
17237 :
6460 tgl 17238 CBC 88 : heap_freetuple(classTup);
6460 tgl 17239 GIC 88 : }
6460 tgl 17240 ECB :
17241 : /*
17242 : * Move all indexes for the specified relation to another namespace.
17243 : *
17244 : * Note: we assume adequate permission checking was done by the caller,
17245 : * and that the caller has a suitable lock on the owning relation.
17246 : */
17247 : static void
6460 tgl 17248 CBC 35 : AlterIndexNamespaces(Relation classRel, Relation rel,
17249 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
17250 : {
17251 : List *indexList;
17252 : ListCell *l;
6460 tgl 17253 EUB :
6460 tgl 17254 GIC 35 : indexList = RelationGetIndexList(rel);
17255 :
6460 tgl 17256 CBC 57 : foreach(l, indexList)
6460 tgl 17257 ECB : {
6385 bruce 17258 GBC 22 : Oid indexOid = lfirst_oid(l);
17259 : ObjectAddress thisobj;
17260 :
3812 alvherre 17261 GIC 22 : thisobj.classId = RelationRelationId;
17262 22 : thisobj.objectId = indexOid;
3812 alvherre 17263 CBC 22 : thisobj.objectSubId = 0;
17264 :
17265 : /*
6385 bruce 17266 ECB : * Note: currently, the index will not have its own dependency on the
17267 : * namespace, so we don't need to do changeDependencyFor(). There's no
4309 peter_e 17268 : * row type in pg_type, either.
17269 : *
17270 : * XXX this objsMoved test may be pointless -- surely we have a single
17271 : * dependency link from a relation to each index?
17272 : */
3812 alvherre 17273 GIC 22 : if (!object_address_present(&thisobj, objsMoved))
3812 alvherre 17274 ECB : {
3812 alvherre 17275 CBC 22 : AlterRelationNamespaceInternal(classRel, indexOid,
3812 alvherre 17276 ECB : oldNspOid, newNspOid,
17277 : false, objsMoved);
3812 alvherre 17278 GIC 22 : add_exact_object_address(&thisobj, objsMoved);
17279 : }
17280 : }
6460 tgl 17281 ECB :
6460 tgl 17282 CBC 35 : list_free(indexList);
6460 tgl 17283 GIC 35 : }
17284 :
17285 : /*
17286 : * Move all identity and SERIAL-column sequences of the specified relation to another
6460 tgl 17287 ECB : * namespace.
17288 : *
17289 : * Note: we assume adequate permission checking was done by the caller,
17290 : * and that the caller has a suitable lock on the owning relation.
17291 : */
17292 : static void
6460 tgl 17293 CBC 35 : AlterSeqNamespaces(Relation classRel, Relation rel,
3812 alvherre 17294 ECB : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
17295 : LOCKMODE lockmode)
17296 : {
17297 : Relation depRel;
17298 : SysScanDesc scan;
17299 : ScanKeyData key[2];
6460 tgl 17300 : HeapTuple tup;
17301 :
17302 : /*
17303 : * SERIAL sequences are those having an auto dependency on one of the
17304 : * table's columns (we don't care *which* column, exactly).
17305 : */
1539 andres 17306 GIC 35 : depRel = table_open(DependRelationId, AccessShareLock);
17307 :
6460 tgl 17308 35 : ScanKeyInit(&key[0],
17309 : Anum_pg_depend_refclassid,
6460 tgl 17310 ECB : BTEqualStrategyNumber, F_OIDEQ,
6460 tgl 17311 EUB : ObjectIdGetDatum(RelationRelationId));
6460 tgl 17312 GIC 35 : ScanKeyInit(&key[1],
17313 : Anum_pg_depend_refobjid,
17314 : BTEqualStrategyNumber, F_OIDEQ,
17315 : ObjectIdGetDatum(RelationGetRelid(rel)));
6460 tgl 17316 ECB : /* we leave refobjsubid unspecified */
17317 :
6460 tgl 17318 CBC 35 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
17319 : NULL, 2, key);
17320 :
6460 tgl 17321 GIC 246 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
17322 : {
17323 211 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
17324 : Relation seqRel;
17325 :
6075 tgl 17326 ECB : /* skip dependencies other than auto dependencies on columns */
6460 tgl 17327 GIC 211 : if (depForm->refobjsubid == 0 ||
6460 tgl 17328 CBC 150 : depForm->classid != RelationRelationId ||
6460 tgl 17329 GIC 18 : depForm->objsubid != 0 ||
2194 peter_e 17330 18 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
6460 tgl 17331 193 : continue;
17332 :
17333 : /* Use relation_open just in case it's an index */
4638 simon 17334 18 : seqRel = relation_open(depForm->objid, lockmode);
6460 tgl 17335 ECB :
17336 : /* skip non-sequence relations */
6460 tgl 17337 GIC 18 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
17338 : {
17339 : /* No need to keep the lock */
4638 simon 17340 UIC 0 : relation_close(seqRel, lockmode);
6460 tgl 17341 LBC 0 : continue;
17342 : }
17343 :
6460 tgl 17344 ECB : /* Fix the pg_class and pg_depend entries */
6460 tgl 17345 GIC 18 : AlterRelationNamespaceInternal(classRel, depForm->objid,
6460 tgl 17346 ECB : oldNspOid, newNspOid,
17347 : true, objsMoved);
6385 bruce 17348 :
6460 tgl 17349 EUB : /*
17350 : * Sequences used to have entries in pg_type, but no longer do. If we
1006 tgl 17351 ECB : * ever re-instate that, we'll need to move the pg_type entry to the
17352 : * new namespace, too (using AlterTypeNamespaceInternal).
17353 : */
1006 tgl 17354 CBC 18 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
6460 tgl 17355 ECB :
17356 : /* Now we can close it. Keep the lock till end of transaction. */
6460 tgl 17357 GIC 18 : relation_close(seqRel, NoLock);
17358 : }
17359 :
17360 35 : systable_endscan(scan);
17361 :
6460 tgl 17362 CBC 35 : relation_close(depRel, AccessShareLock);
6460 tgl 17363 GIC 35 : }
17364 :
17365 :
17366 : /*
17367 : * This code supports
7454 tgl 17368 ECB : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
17369 : *
17370 : * Because we only support this for TEMP tables, it's sufficient to remember
17371 : * the state in a backend-local data structure.
17372 : */
17373 :
17374 : /*
7454 tgl 17375 EUB : * Register a newly-created relation's ON COMMIT action.
17376 : */
7456 bruce 17377 ECB : void
7454 tgl 17378 CBC 80 : register_on_commit_action(Oid relid, OnCommitAction action)
17379 : {
7188 bruce 17380 ECB : OnCommitItem *oc;
7454 tgl 17381 : MemoryContext oldcxt;
7456 bruce 17382 :
17383 : /*
17384 : * We needn't bother registering the relation unless there is an ON COMMIT
17385 : * action we need to take.
17386 : */
7454 tgl 17387 GIC 80 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
7456 bruce 17388 CBC 12 : return;
17389 :
7454 tgl 17390 GIC 68 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
17391 :
17392 68 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
17393 68 : oc->relid = relid;
17394 68 : oc->oncommit = action;
6779 17395 68 : oc->creating_subid = GetCurrentSubTransactionId();
17396 68 : oc->deleting_subid = InvalidSubTransactionId;
17397 :
17398 : /*
17399 : * We use lcons() here so that ON COMMIT actions are processed in reverse
17400 : * order of registration. That might not be essential but it seems
1362 tgl 17401 ECB : * reasonable.
1362 tgl 17402 EUB : */
7454 tgl 17403 GIC 68 : on_commits = lcons(oc, on_commits);
7456 bruce 17404 ECB :
7454 tgl 17405 GIC 68 : MemoryContextSwitchTo(oldcxt);
7454 tgl 17406 ECB : }
7456 bruce 17407 :
7454 tgl 17408 EUB : /*
17409 : * Unregister any ON COMMIT action when a relation is deleted.
17410 : *
17411 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
7454 tgl 17412 ECB : */
17413 : void
7454 tgl 17414 CBC 19167 : remove_on_commit_action(Oid relid)
17415 : {
6892 neilc 17416 ECB : ListCell *l;
7456 bruce 17417 :
7454 tgl 17418 CBC 19231 : foreach(l, on_commits)
17419 : {
7188 bruce 17420 GIC 126 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
7456 bruce 17421 ECB :
7454 tgl 17422 GIC 126 : if (oc->relid == relid)
17423 : {
6779 17424 62 : oc->deleting_subid = GetCurrentSubTransactionId();
7454 tgl 17425 CBC 62 : break;
7456 bruce 17426 ECB : }
17427 : }
7456 bruce 17428 GIC 19167 : }
7456 bruce 17429 ECB :
17430 : /*
17431 : * Perform ON COMMIT actions.
17432 : *
17433 : * This is invoked just before actually committing, since it's possible
7454 tgl 17434 : * to encounter errors.
7456 bruce 17435 : */
17436 : void
7454 tgl 17437 GBC 465920 : PreCommit_on_commit_actions(void)
17438 : {
6892 neilc 17439 ECB : ListCell *l;
6646 tgl 17440 GIC 465920 : List *oids_to_truncate = NIL;
1612 michael 17441 465920 : List *oids_to_drop = NIL;
17442 :
7454 tgl 17443 CBC 466275 : foreach(l, on_commits)
17444 : {
7188 bruce 17445 GIC 355 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
7456 bruce 17446 ECB :
17447 : /* Ignore entry if already dropped in this xact */
6779 tgl 17448 GIC 355 : if (oc->deleting_subid != InvalidSubTransactionId)
7454 17449 34 : continue;
17450 :
17451 321 : switch (oc->oncommit)
7454 tgl 17452 ECB : {
7454 tgl 17453 UIC 0 : case ONCOMMIT_NOOP:
7454 tgl 17454 ECB : case ONCOMMIT_PRESERVE_ROWS:
17455 : /* Do nothing (there shouldn't be such entries, actually) */
7454 tgl 17456 UIC 0 : break;
7454 tgl 17457 CBC 299 : case ONCOMMIT_DELETE_ROWS:
17458 :
3722 heikki.linnakangas 17459 ECB : /*
17460 : * If this transaction hasn't accessed any temporary
17461 : * relations, we can skip truncating ON COMMIT DELETE ROWS
17462 : * tables, as they must still be empty.
17463 : */
1534 michael 17464 GIC 299 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
3722 heikki.linnakangas 17465 200 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
7454 tgl 17466 299 : break;
17467 22 : case ONCOMMIT_DROP:
1612 michael 17468 CBC 22 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
1612 michael 17469 GIC 22 : break;
17470 : }
17471 : }
17472 :
17473 : /*
17474 : * Truncate relations before dropping so that all dependencies between
17475 : * relations are removed after they are worked on. Doing it like this
17476 : * might be a waste as it is possible that a relation being truncated will
17477 : * be dropped anyway due to its parent being dropped, but this makes the
17478 : * code more robust because of not having to re-check that the relation
17479 : * exists at truncation time.
17480 : */
6646 tgl 17481 CBC 465920 : if (oids_to_truncate != NIL)
6646 tgl 17482 GIC 167 : heap_truncate(oids_to_truncate);
1608 michael 17483 ECB :
1612 michael 17484 GIC 465917 : if (oids_to_drop != NIL)
17485 : {
1612 michael 17486 CBC 19 : ObjectAddresses *targetObjects = new_object_addresses();
1612 michael 17487 EUB :
1612 michael 17488 GIC 41 : foreach(l, oids_to_drop)
17489 : {
17490 : ObjectAddress object;
17491 :
17492 22 : object.classId = RelationRelationId;
1612 michael 17493 CBC 22 : object.objectId = lfirst_oid(l);
17494 22 : object.objectSubId = 0;
1612 michael 17495 ECB :
1612 michael 17496 GBC 22 : Assert(!object_address_present(&object, targetObjects));
17497 :
1612 michael 17498 GIC 22 : add_exact_object_address(&object, targetObjects);
17499 : }
17500 :
17501 : /*
17502 : * Since this is an automatic drop, rather than one directly initiated
17503 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
1612 michael 17504 ECB : */
1612 michael 17505 GIC 19 : performMultipleDeletions(targetObjects, DROP_CASCADE,
17506 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
1612 michael 17507 ECB :
17508 : #ifdef USE_ASSERT_CHECKING
17509 :
17510 : /*
17511 : * Note that table deletion will call remove_on_commit_action, so the
17512 : * entry should get marked as deleted.
17513 : */
1612 michael 17514 GIC 66 : foreach(l, on_commits)
17515 : {
17516 47 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17517 :
1612 michael 17518 CBC 47 : if (oc->oncommit != ONCOMMIT_DROP)
1612 michael 17519 GIC 25 : continue;
17520 :
17521 22 : Assert(oc->deleting_subid != InvalidSubTransactionId);
17522 : }
1612 michael 17523 ECB : #endif
17524 : }
7456 bruce 17525 CBC 465917 : }
17526 :
17527 : /*
7454 tgl 17528 ECB : * Post-commit or post-abort cleanup for ON COMMIT management.
17529 : *
17530 : * All we do here is remove no-longer-needed OnCommitItem entries.
17531 : *
17532 : * During commit, remove entries that were deleted during this transaction;
17533 : * during abort, remove those created during this transaction.
17534 : */
17535 : void
6779 tgl 17536 GIC 485839 : AtEOXact_on_commit_actions(bool isCommit)
17537 : {
17538 : ListCell *cur_item;
17539 :
1364 17540 486209 : foreach(cur_item, on_commits)
17541 : {
6892 neilc 17542 370 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17543 :
6779 tgl 17544 421 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
17545 51 : oc->creating_subid != InvalidSubTransactionId)
17546 : {
6892 neilc 17547 ECB : /* cur_item must be removed */
1364 tgl 17548 GIC 68 : on_commits = foreach_delete_current(on_commits, cur_item);
7454 17549 68 : pfree(oc);
17550 : }
17551 : else
17552 : {
17553 : /* cur_item must be preserved */
6779 17554 302 : oc->creating_subid = InvalidSubTransactionId;
17555 302 : oc->deleting_subid = InvalidSubTransactionId;
17556 : }
17557 : }
6856 17558 485839 : }
6856 tgl 17559 ECB :
17560 : /*
6856 tgl 17561 EUB : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
17562 : *
17563 : * During subabort, we can immediately remove entries created during this
17564 : * subtransaction. During subcommit, just relabel entries marked during
17565 : * this subtransaction as being the parent's responsibility.
17566 : */
17567 : void
6779 tgl 17568 CBC 8785 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
6779 tgl 17569 ECB : SubTransactionId parentSubid)
17570 : {
6797 bruce 17571 : ListCell *cur_item;
6856 tgl 17572 :
1364 tgl 17573 CBC 8785 : foreach(cur_item, on_commits)
6856 tgl 17574 ECB : {
6856 tgl 17575 UIC 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
6856 tgl 17576 ECB :
6779 tgl 17577 LBC 0 : if (!isCommit && oc->creating_subid == mySubid)
17578 : {
17579 : /* cur_item must be removed */
1364 tgl 17580 UIC 0 : on_commits = foreach_delete_current(on_commits, cur_item);
6856 17581 0 : pfree(oc);
17582 : }
17583 : else
6856 tgl 17584 ECB : {
17585 : /* cur_item must be preserved */
6779 tgl 17586 UBC 0 : if (oc->creating_subid == mySubid)
6779 tgl 17587 UIC 0 : oc->creating_subid = parentSubid;
17588 0 : if (oc->deleting_subid == mySubid)
17589 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
17590 : }
17591 : }
7456 bruce 17592 GIC 8785 : }
17593 :
17594 : /*
17595 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
17596 : * the relation to be locked only if (1) it's a plain or partitioned table,
372 alvherre 17597 ECB : * materialized view, or TOAST table and (2) the current user is the owner (or
17598 : * the superuser) or has been granted MAINTAIN. This meets the
17599 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
17600 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
17601 : */
17602 : void
117 jdavis 17603 GNC 437 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
17604 : Oid relId, Oid oldRelId, void *arg)
17605 : {
17606 : char relkind;
17607 :
4127 rhaas 17608 ECB : /* Nothing to do if the relation was not found. */
4127 rhaas 17609 GIC 437 : if (!OidIsValid(relId))
17610 3 : return;
17611 :
4127 rhaas 17612 ECB : /*
17613 : * If the relation does exist, check whether it's an index. But note that
3955 bruce 17614 : * the relation might have been dropped between the time we did the name
17615 : * lookup and now. In that case, there's nothing to do.
4127 rhaas 17616 : */
4127 rhaas 17617 GIC 434 : relkind = get_rel_relkind(relId);
17618 434 : if (!relkind)
4127 rhaas 17619 UIC 0 : return;
3689 kgrittn 17620 GIC 434 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
2314 rhaas 17621 50 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
4127 rhaas 17622 CBC 14 : ereport(ERROR,
17623 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17624 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
4127 rhaas 17625 ECB :
17626 : /* Check permissions */
86 jdavis 17627 GNC 420 : if (pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN) != ACLCHECK_OK &&
17628 12 : !has_partition_ancestor_privs(relId, GetUserId(), ACL_MAINTAIN))
117 17629 12 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TABLE,
17630 12 : relation->relname);
17631 : }
17632 :
17633 : /*
17634 : * If relid is a partition, returns whether userid has any of the privileges
17635 : * specified in acl on any of its ancestors. Otherwise, returns false.
17636 : */
17637 : bool
86 17638 277 : has_partition_ancestor_privs(Oid relid, Oid userid, AclMode acl)
17639 : {
17640 : List *ancestors;
17641 : ListCell *lc;
17642 :
17643 277 : if (!get_rel_relispartition(relid))
17644 113 : return false;
17645 :
17646 164 : ancestors = get_partition_ancestors(relid);
17647 218 : foreach(lc, ancestors)
17648 : {
17649 164 : Oid ancestor = lfirst_oid(lc);
17650 :
17651 328 : if (OidIsValid(ancestor) &&
17652 164 : pg_class_aclcheck(ancestor, userid, acl) == ACLCHECK_OK)
17653 110 : return true;
17654 : }
17655 :
17656 54 : return false;
17657 : }
86 jdavis 17658 ECB :
17659 : /*
1703 michael 17660 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
17661 : */
17662 : static void
1703 michael 17663 GIC 809 : RangeVarCallbackForTruncate(const RangeVar *relation,
17664 : Oid relId, Oid oldRelId, void *arg)
17665 : {
17666 : HeapTuple tuple;
17667 :
17668 : /* Nothing to do if the relation was not found. */
17669 809 : if (!OidIsValid(relId))
1703 michael 17670 LBC 0 : return;
1703 michael 17671 ECB :
1703 michael 17672 GIC 809 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17673 809 : if (!HeapTupleIsValid(tuple)) /* should not happen */
1703 michael 17674 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
17675 :
1703 michael 17676 GIC 809 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
1164 fujii 17677 807 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
17678 :
1703 michael 17679 CBC 791 : ReleaseSysCache(tuple);
17680 : }
17681 :
17682 : /*
3338 rhaas 17683 ECB : * Callback to RangeVarGetRelidExtended(), similar to
17684 : * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
17685 : */
17686 : void
3338 rhaas 17687 CBC 6659 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
17688 : Oid relId, Oid oldRelId, void *arg)
17689 : {
17690 : HeapTuple tuple;
17691 :
17692 : /* Nothing to do if the relation was not found. */
3338 rhaas 17693 GIC 6659 : if (!OidIsValid(relId))
3338 rhaas 17694 CBC 7 : return;
17695 :
3338 rhaas 17696 GIC 6652 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
2118 tgl 17697 6652 : if (!HeapTupleIsValid(tuple)) /* should not happen */
3338 rhaas 17698 UIC 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
17699 :
147 peter 17700 GNC 6652 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
1954 peter_e 17701 GIC 3 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
3338 rhaas 17702 3 : relation->relname);
17703 :
3338 rhaas 17704 CBC 13238 : if (!allowSystemTableMods &&
17705 6589 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
3338 rhaas 17706 GIC 1 : ereport(ERROR,
17707 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17708 : errmsg("permission denied: \"%s\" is a system catalog",
3338 rhaas 17709 ECB : relation->relname)));
17710 :
3338 rhaas 17711 CBC 6648 : ReleaseSysCache(tuple);
17712 : }
17713 :
4111 rhaas 17714 ECB : /*
17715 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
17716 : * processing.
17717 : */
17718 : static void
4111 rhaas 17719 CBC 45540 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
17720 : void *arg)
17721 : {
3955 bruce 17722 45540 : Node *stmt = (Node *) arg;
17723 : ObjectType reltype;
17724 : HeapTuple tuple;
17725 : Form_pg_class classform;
17726 : AclResult aclresult;
3955 bruce 17727 ECB : char relkind;
4111 rhaas 17728 :
4111 rhaas 17729 GBC 45540 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
4111 rhaas 17730 GIC 45540 : if (!HeapTupleIsValid(tuple))
3955 bruce 17731 110 : return; /* concurrently dropped */
4111 rhaas 17732 45430 : classform = (Form_pg_class) GETSTRUCT(tuple);
17733 45430 : relkind = classform->relkind;
17734 :
17735 : /* Must own relation. */
147 peter 17736 GNC 45430 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
1954 peter_e 17737 GIC 30 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
4111 rhaas 17738 ECB :
17739 : /* No system table modifications unless explicitly allowed. */
3419 rhaas 17740 GIC 45400 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
4111 17741 14 : ereport(ERROR,
4111 rhaas 17742 ECB : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17743 : errmsg("permission denied: \"%s\" is a system catalog",
17744 : rv->relname)));
17745 :
17746 : /*
17747 : * Extract the specified relation type from the statement parse tree.
17748 : *
17749 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
17750 : * have CREATE rights on the containing namespace.
17751 : */
4111 rhaas 17752 GIC 45386 : if (IsA(stmt, RenameStmt))
17753 : {
147 peter 17754 GNC 247 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
17755 : GetUserId(), ACL_CREATE);
4111 rhaas 17756 CBC 247 : if (aclresult != ACLCHECK_OK)
1954 peter_e 17757 UIC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
4111 rhaas 17758 0 : get_namespace_name(classform->relnamespace));
4111 rhaas 17759 GIC 247 : reltype = ((RenameStmt *) stmt)->renameType;
17760 : }
17761 45139 : else if (IsA(stmt, AlterObjectSchemaStmt))
17762 42 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
17763 :
17764 45097 : else if (IsA(stmt, AlterTableStmt))
1002 michael 17765 CBC 45097 : reltype = ((AlterTableStmt *) stmt)->objtype;
17766 : else
17767 : {
4111 rhaas 17768 UIC 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
17769 : reltype = OBJECT_TABLE; /* placate compiler */
4111 rhaas 17770 ECB : }
17771 :
17772 : /*
3955 bruce 17773 : * For compatibility with prior releases, we allow ALTER TABLE to be used
17774 : * with most other types of relations (but not composite types). We allow
17775 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
17776 : * otherwise. Otherwise, the user must select the correct form of the
17777 : * command for the relation at issue.
17778 : */
4111 rhaas 17779 CBC 45386 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
4111 rhaas 17780 LBC 0 : ereport(ERROR,
17781 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17782 : errmsg("\"%s\" is not a sequence", rv->relname)));
17783 :
4111 rhaas 17784 CBC 45386 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
4111 rhaas 17785 LBC 0 : ereport(ERROR,
4111 rhaas 17786 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17787 : errmsg("\"%s\" is not a view", rv->relname)));
17788 :
3689 kgrittn 17789 CBC 45386 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
3689 kgrittn 17790 UIC 0 : ereport(ERROR,
3689 kgrittn 17791 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17792 : errmsg("\"%s\" is not a materialized view", rv->relname)));
17793 :
4111 rhaas 17794 GIC 45386 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
4111 rhaas 17795 LBC 0 : ereport(ERROR,
4111 rhaas 17796 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17797 : errmsg("\"%s\" is not a foreign table", rv->relname)));
17798 :
4111 rhaas 17799 GIC 45386 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
4111 rhaas 17800 UIC 0 : ereport(ERROR,
17801 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17802 : errmsg("\"%s\" is not a composite type", rv->relname)));
17803 :
1906 alvherre 17804 CBC 45386 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
17805 : relkind != RELKIND_PARTITIONED_INDEX
4111 rhaas 17806 GIC 18 : && !IsA(stmt, RenameStmt))
17807 3 : ereport(ERROR,
17808 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17809 : errmsg("\"%s\" is not an index", rv->relname)));
17810 :
17811 : /*
4111 rhaas 17812 ECB : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17813 : * TYPE for that.
17814 : */
4111 rhaas 17815 CBC 45383 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
4111 rhaas 17816 UBC 0 : ereport(ERROR,
4111 rhaas 17817 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17818 : errmsg("\"%s\" is a composite type", rv->relname),
17819 : errhint("Use ALTER TYPE instead.")));
17820 :
17821 : /*
3955 bruce 17822 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17823 : * to a different schema, such as indexes and TOAST tables.
17824 : */
640 peter 17825 GIC 45383 : if (IsA(stmt, AlterObjectSchemaStmt))
17826 : {
17827 42 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
640 peter 17828 UIC 0 : ereport(ERROR,
17829 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
640 peter 17830 ECB : errmsg("cannot change schema of index \"%s\"",
17831 : rv->relname),
17832 : errhint("Change the schema of the table instead.")));
640 peter 17833 GIC 42 : else if (relkind == RELKIND_COMPOSITE_TYPE)
640 peter 17834 LBC 0 : ereport(ERROR,
17835 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
640 peter 17836 EUB : errmsg("cannot change schema of composite type \"%s\"",
17837 : rv->relname),
17838 : errhint("Use ALTER TYPE instead.")));
640 peter 17839 GIC 42 : else if (relkind == RELKIND_TOASTVALUE)
640 peter 17840 UIC 0 : ereport(ERROR,
17841 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17842 : errmsg("cannot change schema of TOAST table \"%s\"",
640 peter 17843 ECB : rv->relname),
17844 : errhint("Change the schema of the table instead.")));
17845 : }
17846 :
4111 rhaas 17847 GIC 45383 : ReleaseSysCache(tuple);
4111 rhaas 17848 ECB : }
2314 17849 :
17850 : /*
17851 : * Transform any expressions present in the partition key
17852 : *
17853 : * Returns a transformed PartitionSpec.
2314 rhaas 17854 EUB : */
17855 : static PartitionSpec *
157 alvherre 17856 GNC 2163 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
2314 rhaas 17857 ECB : {
17858 : PartitionSpec *newspec;
2308 17859 : ParseState *pstate;
17860 : ParseNamespaceItem *nsitem;
17861 : ListCell *l;
17862 :
2298 peter_e 17863 GIC 2163 : newspec = makeNode(PartitionSpec);
2314 rhaas 17864 ECB :
2314 rhaas 17865 CBC 2163 : newspec->strategy = partspec->strategy;
2314 rhaas 17866 GIC 2163 : newspec->partParams = NIL;
2142 tgl 17867 2163 : newspec->location = partspec->location;
17868 :
2142 tgl 17869 ECB : /* Check valid number of columns for strategy */
157 alvherre 17870 GNC 3261 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
2142 tgl 17871 CBC 1098 : list_length(partspec->partParams) != 1)
2142 tgl 17872 GIC 3 : ereport(ERROR,
17873 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2142 tgl 17874 ECB : errmsg("cannot use \"list\" partition strategy with more than one column")));
17875 :
2314 rhaas 17876 : /*
17877 : * Create a dummy ParseState and insert the target relation as its sole
17878 : * rangetable entry. We need a ParseState for transformExpr.
17879 : */
2314 rhaas 17880 GIC 2160 : pstate = make_parsestate(NULL);
1193 tgl 17881 2160 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
17882 : NULL, false, true);
17883 2160 : addNSItemToQuery(pstate, nsitem, true, true, true);
17884 :
17885 : /* take care of any partition expressions */
2314 rhaas 17886 CBC 4500 : foreach(l, partspec->partParams)
17887 : {
629 peter 17888 2352 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
17889 :
2314 rhaas 17890 GIC 2352 : if (pelem->expr)
2314 rhaas 17891 ECB : {
17892 : /* Copy, to avoid scribbling on the input */
2142 tgl 17893 GIC 143 : pelem = copyObject(pelem);
17894 :
2314 rhaas 17895 ECB : /* Now do parse transformation of the expression */
2314 rhaas 17896 CBC 143 : pelem->expr = transformExpr(pstate, pelem->expr,
17897 : EXPR_KIND_PARTITION_EXPRESSION);
17898 :
17899 : /* we have to fix its collations too */
2314 rhaas 17900 GIC 131 : assign_expr_collations(pstate, pelem->expr);
17901 : }
17902 :
17903 2340 : newspec->partParams = lappend(newspec->partParams, pelem);
17904 : }
17905 :
2314 rhaas 17906 CBC 2148 : return newspec;
17907 : }
17908 :
17909 : /*
17910 : * Compute per-partition-column information from a list of PartitionElems.
17911 : * Expressions in the PartitionElems must be parse-analyzed already.
17912 : */
17913 : static void
1691 peter_e 17914 GIC 2148 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
17915 : List **partexprs, Oid *partopclass, Oid *partcollation,
17916 : PartitionStrategy strategy)
17917 : {
17918 : int attn;
2314 rhaas 17919 ECB : ListCell *lc;
17920 : Oid am_oid;
17921 :
2314 rhaas 17922 GIC 2148 : attn = 0;
17923 4446 : foreach(lc, partParams)
17924 : {
629 peter 17925 CBC 2340 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
17926 : Oid atttype;
17927 : Oid attcollation;
17928 :
2314 rhaas 17929 GIC 2340 : if (pelem->name != NULL)
17930 : {
2314 rhaas 17931 ECB : /* Simple attribute reference */
17932 : HeapTuple atttuple;
17933 : Form_pg_attribute attform;
17934 :
2142 tgl 17935 GIC 2209 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
2142 tgl 17936 CBC 2209 : pelem->name);
2314 rhaas 17937 GIC 2209 : if (!HeapTupleIsValid(atttuple))
17938 6 : ereport(ERROR,
17939 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2118 tgl 17940 ECB : errmsg("column \"%s\" named in partition key does not exist",
1691 peter_e 17941 : pelem->name),
17942 : parser_errposition(pstate, pelem->location)));
2314 rhaas 17943 CBC 2203 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
2314 rhaas 17944 ECB :
2314 rhaas 17945 GIC 2203 : if (attform->attnum <= 0)
17946 3 : ereport(ERROR,
2142 tgl 17947 ECB : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17948 : errmsg("cannot use system column \"%s\" in partition key",
17949 : pelem->name),
1691 peter_e 17950 : parser_errposition(pstate, pelem->location)));
17951 :
17952 : /*
1471 peter 17953 EUB : * Generated columns cannot work: They are computed after BEFORE
17954 : * triggers, but partition routing is done before all triggers.
17955 : */
1471 peter 17956 GIC 2200 : if (attform->attgenerated)
17957 3 : ereport(ERROR,
1471 peter 17958 ECB : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17959 : errmsg("cannot use generated column in partition key"),
17960 : errdetail("Column \"%s\" is a generated column.",
17961 : pelem->name),
17962 : parser_errposition(pstate, pelem->location)));
17963 :
2314 rhaas 17964 GIC 2197 : partattrs[attn] = attform->attnum;
17965 2197 : atttype = attform->atttypid;
17966 2197 : attcollation = attform->attcollation;
2314 rhaas 17967 CBC 2197 : ReleaseSysCache(atttuple);
17968 : }
17969 : else
2314 rhaas 17970 ECB : {
17971 : /* Expression */
2314 rhaas 17972 GIC 131 : Node *expr = pelem->expr;
1203 tgl 17973 ECB : char partattname[16];
17974 :
2314 rhaas 17975 CBC 131 : Assert(expr != NULL);
17976 131 : atttype = exprType(expr);
2314 rhaas 17977 GIC 131 : attcollation = exprCollation(expr);
17978 :
17979 : /*
17980 : * The expression must be of a storable type (e.g., not RECORD).
17981 : * The test is the same as for whether a table column is of a safe
17982 : * type (which is why we needn't check for the non-expression
17983 : * case).
17984 : */
1203 tgl 17985 131 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
17986 131 : CheckAttributeType(partattname,
17987 : atttype, attcollation,
17988 : NIL, CHKATYPE_IS_PARTKEY);
17989 :
17990 : /*
2314 rhaas 17991 ECB : * Strip any top-level COLLATE clause. This ensures that we treat
17992 : * "x COLLATE y" and "(x COLLATE y)" alike.
17993 : */
2314 rhaas 17994 GIC 125 : while (IsA(expr, CollateExpr))
2314 rhaas 17995 UIC 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
17996 :
2314 rhaas 17997 GIC 125 : if (IsA(expr, Var) &&
2142 tgl 17998 6 : ((Var *) expr)->varattno > 0)
17999 : {
2314 rhaas 18000 ECB : /*
18001 : * User wrote "(column)" or "(column COLLATE something)".
18002 : * Treat it like simple attribute anyway.
18003 : */
2314 rhaas 18004 GIC 3 : partattrs[attn] = ((Var *) expr)->varattno;
2314 rhaas 18005 ECB : }
18006 : else
18007 : {
2308 rhaas 18008 CBC 122 : Bitmapset *expr_attrs = NULL;
2142 tgl 18009 ECB : int i;
18010 :
2308 rhaas 18011 GIC 122 : partattrs[attn] = 0; /* marks the column as expression */
2314 18012 122 : *partexprs = lappend(*partexprs, expr);
18013 :
18014 : /*
18015 : * Try to simplify the expression before checking for
2142 tgl 18016 ECB : * mutability. The main practical value of doing it in this
18017 : * order is that an inline-able SQL-language function will be
18018 : * accepted if its expansion is immutable, whether or not the
18019 : * function itself is marked immutable.
18020 : *
18021 : * Note that expression_planner does not change the passed in
18022 : * expression destructively and we have already saved the
18023 : * expression to be stored into the catalog above.
18024 : */
2314 rhaas 18025 GIC 122 : expr = (Node *) expression_planner((Expr *) expr);
18026 :
2314 rhaas 18027 ECB : /*
18028 : * Partition expression cannot contain mutable functions,
18029 : * because a given row must always map to the same partition
18030 : * as long as there is no change in the partition boundary
18031 : * structure.
18032 : */
2314 rhaas 18033 CBC 122 : if (contain_mutable_functions(expr))
2314 rhaas 18034 GIC 3 : ereport(ERROR,
2314 rhaas 18035 ECB : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18036 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
18037 :
18038 : /*
18039 : * transformPartitionSpec() should have already rejected
18040 : * subqueries, aggregates, window functions, and SRFs, based
2308 18041 : * on the EXPR_KIND_ for partition expressions.
18042 : */
18043 :
18044 : /*
18045 : * Cannot allow system column references, since that would
18046 : * make partition routing impossible: their values won't be
18047 : * known yet when we need to do that.
18048 : */
2314 rhaas 18049 GIC 119 : pull_varattnos(expr, 1, &expr_attrs);
2142 tgl 18050 CBC 952 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18051 : {
2142 tgl 18052 GIC 833 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
2142 tgl 18053 ECB : expr_attrs))
2142 tgl 18054 LBC 0 : ereport(ERROR,
18055 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2142 tgl 18056 ECB : errmsg("partition key expressions cannot contain system column references")));
18057 : }
18058 :
18059 : /*
18060 : * Generated columns cannot work: They are computed after
1471 peter 18061 : * BEFORE triggers, but partition routing is done before all
18062 : * triggers.
18063 : */
1471 peter 18064 CBC 119 : i = -1;
1471 peter 18065 GIC 264 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
1471 peter 18066 EUB : {
1418 tgl 18067 GIC 148 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18068 :
1201 tgl 18069 GBC 148 : if (attno > 0 &&
1201 tgl 18070 CBC 145 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
1471 peter 18071 GIC 3 : ereport(ERROR,
18072 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18073 : errmsg("cannot use generated column in partition key"),
18074 : errdetail("Column \"%s\" is a generated column.",
18075 : get_attname(RelationGetRelid(rel), attno, false)),
18076 : parser_errposition(pstate, pelem->location)));
1471 peter 18077 ECB : }
18078 :
2142 tgl 18079 : /*
18080 : * While it is not exactly *wrong* for a partition expression
18081 : * to be a constant, it seems better to reject such keys.
18082 : */
2142 tgl 18083 GIC 116 : if (IsA(expr, Const))
18084 6 : ereport(ERROR,
18085 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18086 : errmsg("cannot use constant expression as partition key")));
18087 : }
18088 : }
18089 :
18090 : /*
18091 : * Apply collation override if any
18092 : */
2314 rhaas 18093 2310 : if (pelem->collation)
2314 rhaas 18094 CBC 15 : attcollation = get_collation_oid(pelem->collation, false);
2314 rhaas 18095 ECB :
18096 : /*
18097 : * Check we have a collation iff it's a collatable type. The only
18098 : * expected failures here are (1) COLLATE applied to a noncollatable
2308 18099 : * type, or (2) partition expression had an unresolved collation. But
18100 : * we might as well code this to be a complete consistency check.
2314 18101 : */
2314 rhaas 18102 GIC 2310 : if (type_is_collatable(atttype))
18103 : {
18104 283 : if (!OidIsValid(attcollation))
2314 rhaas 18105 LBC 0 : ereport(ERROR,
2314 rhaas 18106 ECB : (errcode(ERRCODE_INDETERMINATE_COLLATION),
18107 : errmsg("could not determine which collation to use for partition expression"),
18108 : errhint("Use the COLLATE clause to set the collation explicitly.")));
18109 : }
18110 : else
18111 : {
2314 rhaas 18112 GIC 2027 : if (OidIsValid(attcollation))
2314 rhaas 18113 UIC 0 : ereport(ERROR,
18114 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18115 : errmsg("collations are not supported by type %s",
18116 : format_type_be(atttype))));
18117 : }
2314 rhaas 18118 ECB :
2314 rhaas 18119 GIC 2310 : partcollation[attn] = attcollation;
18120 :
18121 : /*
18122 : * Identify the appropriate operator class. For list and range
18123 : * partitioning, we use a btree operator class; hash partitioning uses
18124 : * a hash operator class.
18125 : */
1977 18126 2310 : if (strategy == PARTITION_STRATEGY_HASH)
1977 rhaas 18127 CBC 131 : am_oid = HASH_AM_OID;
18128 : else
18129 2179 : am_oid = BTREE_AM_OID;
18130 :
2314 18131 2310 : if (!pelem->opclass)
2314 rhaas 18132 ECB : {
1977 rhaas 18133 GIC 2244 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
2314 rhaas 18134 ECB :
2314 rhaas 18135 GIC 2244 : if (!OidIsValid(partopclass[attn]))
18136 : {
1977 18137 6 : if (strategy == PARTITION_STRATEGY_HASH)
1977 rhaas 18138 LBC 0 : ereport(ERROR,
18139 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18140 : errmsg("data type %s has no default operator class for access method \"%s\"",
18141 : format_type_be(atttype), "hash"),
18142 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18143 : else
1977 rhaas 18144 GIC 6 : ereport(ERROR,
18145 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18146 : errmsg("data type %s has no default operator class for access method \"%s\"",
18147 : format_type_be(atttype), "btree"),
18148 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
1977 rhaas 18149 ECB : }
18150 : }
18151 : else
2314 rhaas 18152 GIC 66 : partopclass[attn] = ResolveOpClass(pelem->opclass,
2314 rhaas 18153 ECB : atttype,
18154 : am_oid == HASH_AM_OID ? "hash" : "btree",
1977 18155 : am_oid);
18156 :
2314 rhaas 18157 CBC 2298 : attn++;
2314 rhaas 18158 ECB : }
2314 rhaas 18159 GIC 2106 : }
18160 :
2061 rhaas 18161 ECB : /*
18162 : * PartConstraintImpliedByRelConstraint
18163 : * Do scanrel's existing constraints imply the partition constraint?
18164 : *
18165 : * "Existing constraints" include its check constraints and column-level
18166 : * NOT NULL constraints. partConstraint describes the partition constraint,
1857 tgl 18167 : * in implicit-AND form.
2061 rhaas 18168 : */
18169 : bool
2061 rhaas 18170 GIC 1442 : PartConstraintImpliedByRelConstraint(Relation scanrel,
2061 rhaas 18171 ECB : List *partConstraint)
18172 : {
2061 rhaas 18173 GIC 1442 : List *existConstraint = NIL;
18174 1442 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18175 : int i;
18176 :
18177 1442 : if (constr && constr->has_not_null)
18178 : {
18179 312 : int natts = scanrel->rd_att->natts;
18180 :
2061 rhaas 18181 CBC 1019 : for (i = 1; i <= natts; i++)
18182 : {
2058 andres 18183 GIC 707 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18184 :
2061 rhaas 18185 707 : if (att->attnotnull && !att->attisdropped)
2061 rhaas 18186 ECB : {
2061 rhaas 18187 GIC 414 : NullTest *ntest = makeNode(NullTest);
2061 rhaas 18188 EUB :
2061 rhaas 18189 GIC 414 : ntest->arg = (Expr *) makeVar(1,
2061 rhaas 18190 EUB : i,
18191 : att->atttypid,
18192 : att->atttypmod,
18193 : att->attcollation,
18194 : 0);
2061 rhaas 18195 GIC 414 : ntest->nulltesttype = IS_NOT_NULL;
18196 :
18197 : /*
18198 : * argisrow=false is correct even for a composite column,
2061 rhaas 18199 EUB : * because attnotnull does not represent a SQL-spec IS NOT
18200 : * NULL test in such a case, just IS DISTINCT FROM NULL.
18201 : */
2061 rhaas 18202 GBC 414 : ntest->argisrow = false;
2061 rhaas 18203 GIC 414 : ntest->location = -1;
18204 414 : existConstraint = lappend(existConstraint, ntest);
2061 rhaas 18205 ECB : }
18206 : }
18207 : }
18208 :
1488 rhaas 18209 GIC 1442 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18210 : }
18211 :
18212 : /*
18213 : * ConstraintImpliedByRelConstraint
18214 : * Do scanrel's existing constraints imply the given constraint?
18215 : *
1488 rhaas 18216 ECB : * testConstraint is the constraint to validate. provenConstraint is a
18217 : * caller-provided list of conditions which this function may assume
18218 : * to be true. Both provenConstraint and testConstraint must be in
18219 : * implicit-AND form, must only contain immutable clauses, and must
18220 : * contain only Vars with varno = 1.
18221 : */
18222 : bool
1488 rhaas 18223 CBC 1995 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
18224 : {
1418 tgl 18225 GIC 1995 : List *existConstraint = list_copy(provenConstraint);
1488 rhaas 18226 1995 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18227 : int num_check,
18228 : i;
18229 :
2061 rhaas 18230 CBC 1995 : num_check = (constr != NULL) ? constr->num_check : 0;
18231 2253 : for (i = 0; i < num_check; i++)
2061 rhaas 18232 EUB : {
2061 rhaas 18233 ECB : Node *cexpr;
18234 :
18235 : /*
18236 : * If this constraint hasn't been fully validated yet, we must ignore
18237 : * it here.
18238 : */
2061 rhaas 18239 GIC 258 : if (!constr->check[i].ccvalid)
2061 rhaas 18240 CBC 4 : continue;
2061 rhaas 18241 ECB :
2061 rhaas 18242 CBC 254 : cexpr = stringToNode(constr->check[i].ccbin);
2061 rhaas 18243 ECB :
18244 : /*
18245 : * Run each expression through const-simplification and
18246 : * canonicalization. It is necessary, because we will be comparing it
18247 : * to similarly-processed partition constraint expressions, and may
18248 : * fail to detect valid matches without this.
18249 : */
2061 rhaas 18250 GIC 254 : cexpr = eval_const_expressions(NULL, cexpr);
1855 tgl 18251 CBC 254 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
18252 :
2061 rhaas 18253 GIC 254 : existConstraint = list_concat(existConstraint,
18254 254 : make_ands_implicit((Expr *) cexpr));
18255 : }
2061 rhaas 18256 ECB :
1857 tgl 18257 : /*
18258 : * Try to make the proof. Since we are comparing CHECK constraints, we
18259 : * need to use weak implication, i.e., we assume existConstraint is
1488 rhaas 18260 : * not-false and try to prove the same for testConstraint.
18261 : *
1857 tgl 18262 : * Note that predicate_implied_by assumes its first argument is known
18263 : * immutable. That should always be true for both NOT NULL and partition
1418 18264 : * constraints, so we don't test it here.
1857 18265 : */
1488 rhaas 18266 CBC 1995 : return predicate_implied_by(testConstraint, existConstraint, true);
18267 : }
18268 :
2061 rhaas 18269 ECB : /*
18270 : * QueuePartitionConstraintValidation
18271 : *
18272 : * Add an entry to wqueue to have the given partition constraint validated by
18273 : * Phase 3, for the given relation, and all its children.
18274 : *
18275 : * We first verify whether the given constraint is implied by pre-existing
1824 alvherre 18276 : * relation constraints; if it is, there's no need to scan the table to
18277 : * validate, so don't queue in that case.
18278 : */
18279 : static void
1824 alvherre 18280 GIC 1139 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
18281 : List *partConstraint,
1824 alvherre 18282 ECB : bool validate_default)
2061 rhaas 18283 EUB : {
18284 : /*
1824 alvherre 18285 ECB : * Based on the table's existing constraints, determine whether or not we
18286 : * may skip scanning the table.
2061 rhaas 18287 EUB : */
2061 rhaas 18288 GIC 1139 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
2061 rhaas 18289 ECB : {
2012 rhaas 18290 CBC 45 : if (!validate_default)
1310 tgl 18291 GIC 34 : ereport(DEBUG1,
781 peter 18292 ECB : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18293 : RelationGetRelationName(scanrel))));
18294 : else
1310 tgl 18295 GIC 11 : ereport(DEBUG1,
18296 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18297 : RelationGetRelationName(scanrel))));
2061 rhaas 18298 45 : return;
18299 : }
2061 rhaas 18300 ECB :
18301 : /*
18302 : * Constraints proved insufficient. For plain relations, queue a
18303 : * validation item now; for partitioned tables, recurse to process each
18304 : * partition.
18305 : */
1824 alvherre 18306 CBC 1094 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
2061 rhaas 18307 ECB : {
18308 : AlteredTableInfo *tab;
18309 :
1824 alvherre 18310 : /* Grab a work queue entry. */
1824 alvherre 18311 GBC 909 : tab = ATGetQueueEntry(wqueue, scanrel);
1824 alvherre 18312 GIC 909 : Assert(tab->partition_constraint == NULL);
1824 alvherre 18313 CBC 909 : tab->partition_constraint = (Expr *) linitial(partConstraint);
18314 909 : tab->validate_default = validate_default;
1824 alvherre 18315 ECB : }
1824 alvherre 18316 GIC 185 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1824 alvherre 18317 ECB : {
717 alvherre 18318 CBC 161 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
1824 alvherre 18319 ECB : int i;
18320 :
1824 alvherre 18321 GIC 334 : for (i = 0; i < partdesc->nparts; i++)
18322 : {
18323 : Relation part_rel;
1824 alvherre 18324 ECB : List *thisPartConstraint;
18325 :
18326 : /*
18327 : * This is the minimum lock we need to prevent deadlocks.
18328 : */
1539 andres 18329 GIC 173 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
18330 :
18331 : /*
2061 rhaas 18332 ECB : * Adjust the constraint for scanrel so that it matches this
18333 : * partition's attribute numbers.
18334 : */
1824 alvherre 18335 : thisPartConstraint =
1824 alvherre 18336 GIC 173 : map_partition_varattnos(partConstraint, 1,
18337 : part_rel, scanrel);
18338 :
18339 173 : QueuePartitionConstraintValidation(wqueue, part_rel,
18340 : thisPartConstraint,
18341 : validate_default);
1539 andres 18342 CBC 173 : table_close(part_rel, NoLock); /* keep lock till commit */
2061 rhaas 18343 ECB : }
18344 : }
18345 : }
18346 :
18347 : /*
18348 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
2314 18349 : *
18350 : * Return the address of the newly attached partition.
18351 : */
18352 : static ObjectAddress
928 tgl 18353 CBC 1034 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
928 tgl 18354 ECB : AlterTableUtilityContext *context)
18355 : {
18356 : Relation attachrel,
18357 : catalog;
18358 : List *attachrel_children;
18359 : List *partConstraint;
18360 : SysScanDesc scan;
18361 : ScanKeyData skey;
18362 : AttrNumber attno;
18363 : int natts;
18364 : TupleDesc tupleDesc;
2314 rhaas 18365 : ObjectAddress address;
18366 : const char *trigger_name;
2039 18367 : Oid defaultPartOid;
18368 : List *partBoundConstraint;
928 tgl 18369 CBC 1034 : ParseState *pstate = make_parsestate(NULL);
928 tgl 18370 EUB :
928 tgl 18371 GBC 1034 : pstate->p_sourcetext = context->queryString;
2039 rhaas 18372 ECB :
18373 : /*
1824 alvherre 18374 : * We must lock the default partition if one exists, because attaching a
18375 : * new partition will change its partition constraint.
18376 : */
2039 rhaas 18377 : defaultPartOid =
717 alvherre 18378 CBC 1034 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
2039 rhaas 18379 GIC 1034 : if (OidIsValid(defaultPartOid))
18380 91 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
2314 rhaas 18381 EUB :
1539 andres 18382 GIC 1034 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
18383 :
18384 : /*
18385 : * XXX I think it'd be a good idea to grab locks on all tables referenced
18386 : * by FKs at this point also.
18387 : */
18388 :
18389 : /*
18390 : * Must be owner of both parent and source table -- parent was checked by
18391 : * ATSimplePermissions call in ATPrepCmd
2314 rhaas 18392 ECB : */
640 peter 18393 GBC 1031 : ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
18394 :
18395 : /* A partition can only have one parent */
2075 rhaas 18396 GIC 1028 : if (attachrel->rd_rel->relispartition)
2314 rhaas 18397 CBC 3 : ereport(ERROR,
2314 rhaas 18398 EUB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18399 : errmsg("\"%s\" is already a partition",
18400 : RelationGetRelationName(attachrel))));
18401 :
2075 rhaas 18402 CBC 1025 : if (OidIsValid(attachrel->rd_rel->reloftype))
2314 rhaas 18403 GBC 3 : ereport(ERROR,
18404 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18405 : errmsg("cannot attach a typed table as partition")));
18406 :
2314 rhaas 18407 ECB : /*
2314 rhaas 18408 EUB : * Table being attached should not already be part of inheritance; either
18409 : * as a child table...
18410 : */
1539 andres 18411 GIC 1022 : catalog = table_open(InheritsRelationId, AccessShareLock);
2314 rhaas 18412 CBC 1022 : ScanKeyInit(&skey,
2314 rhaas 18413 EUB : Anum_pg_inherits_inhrelid,
18414 : BTEqualStrategyNumber, F_OIDEQ,
18415 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
2314 rhaas 18416 GIC 1022 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
2314 rhaas 18417 ECB : NULL, 1, &skey);
2314 rhaas 18418 GIC 1022 : if (HeapTupleIsValid(systable_getnext(scan)))
2314 rhaas 18419 CBC 3 : ereport(ERROR,
2314 rhaas 18420 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18421 : errmsg("cannot attach inheritance child as partition")));
2314 rhaas 18422 GIC 1019 : systable_endscan(scan);
18423 :
18424 : /* ...or as a parent table (except the case when it is partitioned) */
18425 1019 : ScanKeyInit(&skey,
18426 : Anum_pg_inherits_inhparent,
18427 : BTEqualStrategyNumber, F_OIDEQ,
2075 rhaas 18428 ECB : ObjectIdGetDatum(RelationGetRelid(attachrel)));
2314 rhaas 18429 GBC 1019 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
18430 : 1, &skey);
2314 rhaas 18431 GIC 1019 : if (HeapTupleIsValid(systable_getnext(scan)) &&
2075 18432 112 : attachrel->rd_rel->relkind == RELKIND_RELATION)
2314 18433 3 : ereport(ERROR,
18434 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18435 : errmsg("cannot attach inheritance parent as partition")));
18436 1016 : systable_endscan(scan);
1539 andres 18437 1016 : table_close(catalog, AccessShareLock);
2314 rhaas 18438 ECB :
18439 : /*
2075 18440 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
2308 rhaas 18441 EUB : * particular, this disallows making a rel a partition of itself.)
18442 : *
18443 : * We do that by checking if rel is a member of the list of attachrel's
18444 : * partitions provided the latter is partitioned at all. We want to avoid
18445 : * having to construct this list again, so we request the strongest lock
1823 alvherre 18446 ECB : * on all partitions. We need the strongest lock, because we may decide
1823 alvherre 18447 EUB : * to scan them if we find out that the table being attached (or its leaf
18448 : * partitions) may contain rows that violate the partition constraint. If
18449 : * the table has a constraint that would prevent such rows, which by
18450 : * definition is present in all the partitions, we need not scan the
18451 : * table, nor its partitions. But we cannot risk a deadlock by taking a
1823 alvherre 18452 ECB : * weaker lock now and the stronger one only when needed.
2314 rhaas 18453 EUB : */
2075 rhaas 18454 GIC 1016 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
18455 : AccessExclusiveLock, NULL);
18456 1016 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
2314 18457 6 : ereport(ERROR,
18458 : (errcode(ERRCODE_DUPLICATE_TABLE),
18459 : errmsg("circular inheritance not allowed"),
2314 rhaas 18460 ECB : errdetail("\"%s\" is already a child of \"%s\".",
18461 : RelationGetRelationName(rel),
18462 : RelationGetRelationName(attachrel))));
18463 :
18464 : /* If the parent is permanent, so must be all of its partitions. */
1754 michael 18465 GIC 1010 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
18466 998 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
18467 3 : ereport(ERROR,
18468 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1754 michael 18469 ECB : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
18470 : RelationGetRelationName(rel))));
18471 :
18472 : /* Temp parent cannot have a partition that is itself not a temp */
2314 rhaas 18473 GIC 1007 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2075 18474 12 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
2314 18475 9 : ereport(ERROR,
2314 rhaas 18476 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18477 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
18478 : RelationGetRelationName(rel))));
18479 :
18480 : /* If the parent is temp, it must belong to this session */
2314 rhaas 18481 GIC 998 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18482 3 : !rel->rd_islocaltemp)
2314 rhaas 18483 LBC 0 : ereport(ERROR,
2314 rhaas 18484 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2308 18485 : errmsg("cannot attach as partition of temporary relation of another session")));
18486 :
18487 : /* Ditto for the partition */
2075 rhaas 18488 GIC 998 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18489 3 : !attachrel->rd_islocaltemp)
2314 rhaas 18490 UIC 0 : ereport(ERROR,
18491 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18492 : errmsg("cannot attach temporary relation of another session as partition")));
2314 rhaas 18493 ECB :
2075 18494 : /* Check if there are any columns in attachrel that aren't in the parent */
2075 rhaas 18495 GIC 998 : tupleDesc = RelationGetDescr(attachrel);
2314 rhaas 18496 CBC 998 : natts = tupleDesc->natts;
2314 rhaas 18497 GIC 3427 : for (attno = 1; attno <= natts; attno++)
18498 : {
2058 andres 18499 CBC 2438 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
2314 rhaas 18500 GIC 2438 : char *attributeName = NameStr(attribute->attname);
2314 rhaas 18501 ECB :
18502 : /* Ignore dropped */
2314 rhaas 18503 CBC 2438 : if (attribute->attisdropped)
2314 rhaas 18504 GIC 278 : continue;
18505 :
2299 rhaas 18506 ECB : /* Try to find the column in parent (matching on column name) */
2299 rhaas 18507 GIC 2160 : if (!SearchSysCacheExists2(ATTNAME,
18508 : ObjectIdGetDatum(RelationGetRelid(rel)),
2299 rhaas 18509 ECB : CStringGetDatum(attributeName)))
2314 rhaas 18510 GIC 9 : ereport(ERROR,
18511 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18512 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
2075 rhaas 18513 ECB : RelationGetRelationName(attachrel), attributeName,
18514 : RelationGetRelationName(rel)),
18515 : errdetail("The new partition may contain only the columns present in parent.")));
2314 18516 : }
18517 :
18518 : /*
2111 rhodiumtoad 18519 : * If child_rel has row-level triggers with transition tables, we
18520 : * currently don't allow it to become a partition. See also prohibitions
18521 : * in ATExecAddInherit() and CreateTrigger().
18522 : */
2075 rhaas 18523 GIC 989 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
2111 rhodiumtoad 18524 989 : if (trigger_name != NULL)
18525 3 : ereport(ERROR,
18526 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2111 rhodiumtoad 18527 ECB : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
18528 : trigger_name, RelationGetRelationName(attachrel)),
18529 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
18530 :
18531 : /*
18532 : * Check that the new partition's bound is valid and does not overlap any
18533 : * of existing partitions of the parent - note that it does not return on
18534 : * error.
2314 rhaas 18535 : */
2075 rhaas 18536 CBC 986 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
18537 : cmd->bound, pstate);
2314 rhaas 18538 ECB :
18539 : /* OK to create inheritance. Rest of the checks performed there */
1646 alvherre 18540 GIC 968 : CreateInheritance(attachrel, rel);
18541 :
2314 rhaas 18542 ECB : /* Update the pg_class entry. */
2075 rhaas 18543 GIC 935 : StorePartitionBound(attachrel, rel, cmd->bound);
18544 :
18545 : /* Ensure there exists a correct set of indexes in the partition. */
2 alvherre 18546 GNC 935 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
18547 :
1843 alvherre 18548 ECB : /* and triggers */
1843 alvherre 18549 CBC 920 : CloneRowTriggersToPartition(rel, attachrel);
1843 alvherre 18550 ECB :
1831 18551 : /*
18552 : * Clone foreign key constraints. Callee is responsible for setting up
18553 : * for phase 3 constraint verification.
18554 : */
1467 alvherre 18555 GIC 917 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
1831 alvherre 18556 ECB :
18557 : /*
2314 rhaas 18558 : * Generate partition constraint from the partition bound specification.
18559 : * If the parent itself is a partition, make sure to include its
18560 : * constraint as well.
18561 : */
634 john.naylor 18562 GIC 917 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
2039 rhaas 18563 917 : partConstraint = list_concat(partBoundConstraint,
2286 18564 917 : RelationGetPartitionQual(rel));
18565 :
18566 : /* Skip validation if there are no constraints to validate. */
2039 18567 917 : if (partConstraint)
18568 : {
1855 tgl 18569 ECB : /*
18570 : * Run the partition quals through const-simplification similar to
18571 : * check constraints. We skip canonicalize_qual, though, because
18572 : * partition quals should be in canonical form already.
18573 : */
18574 : partConstraint =
2039 rhaas 18575 GIC 893 : (List *) eval_const_expressions(NULL,
18576 : (Node *) partConstraint);
1855 tgl 18577 ECB :
18578 : /* XXX this sure looks wrong */
2039 rhaas 18579 CBC 893 : partConstraint = list_make1(make_ands_explicit(partConstraint));
2039 rhaas 18580 ECB :
18581 : /*
18582 : * Adjust the generated constraint to match this partition's attribute
18583 : * numbers.
18584 : */
2039 rhaas 18585 CBC 893 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
18586 : rel);
18587 :
2039 rhaas 18588 ECB : /* Validate partition constraints against the table being attached. */
1824 alvherre 18589 CBC 893 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
1824 alvherre 18590 ECB : false);
18591 : }
18592 :
18593 : /*
18594 : * If we're attaching a partition other than the default partition and a
18595 : * default one exists, then that partition's partition constraint changes,
18596 : * so add an entry to the work queue to validate it, too. (We must not do
18597 : * this when the partition being attached is the default one; we already
1809 tgl 18598 : * did it above!)
2074 rhaas 18599 : */
2039 rhaas 18600 GIC 917 : if (OidIsValid(defaultPartOid))
18601 : {
18602 : Relation defaultrel;
18603 : List *defPartConstraint;
18604 :
1824 alvherre 18605 73 : Assert(!cmd->bound->is_default);
18606 :
1824 alvherre 18607 ECB : /* we already hold a lock on the default partition */
1539 andres 18608 GBC 73 : defaultrel = table_open(defaultPartOid, NoLock);
18609 : defPartConstraint =
2039 rhaas 18610 CBC 73 : get_proposed_default_constraint(partBoundConstraint);
1378 tgl 18611 ECB :
18612 : /*
18613 : * Map the Vars in the constraint expression from rel's attnos to
18614 : * defaultrel's.
18615 : */
18616 : defPartConstraint =
1381 alvherre 18617 CBC 73 : map_partition_varattnos(defPartConstraint,
18618 : 1, defaultrel, rel);
1824 alvherre 18619 GIC 73 : QueuePartitionConstraintValidation(wqueue, defaultrel,
18620 : defPartConstraint, true);
2074 rhaas 18621 ECB :
18622 : /* keep our lock until commit. */
1539 andres 18623 GIC 73 : table_close(defaultrel, NoLock);
2039 rhaas 18624 ECB : }
2314 18625 :
2075 rhaas 18626 GIC 917 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
18627 :
18628 : /*
18629 : * If the partition we just attached is partitioned itself, invalidate
18630 : * relcache for all descendent partitions too to ensure that their
18631 : * rd_partcheck expression trees are rebuilt; partitions already locked at
18632 : * the beginning of this function.
18633 : */
538 alvherre 18634 917 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18635 : {
18636 : ListCell *l;
18637 :
538 alvherre 18638 CBC 450 : foreach(l, attachrel_children)
18639 : {
538 alvherre 18640 GIC 301 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
18641 : }
18642 : }
18643 :
18644 : /* keep our lock until commit */
1539 andres 18645 917 : table_close(attachrel, NoLock);
2314 rhaas 18646 ECB :
2314 rhaas 18647 CBC 917 : return address;
18648 : }
18649 :
18650 : /*
18651 : * AttachPartitionEnsureIndexes
18652 : * subroutine for ATExecAttachPartition to create/match indexes
18653 : *
18654 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
18655 : * PARTITION: every partition must have an index attached to each index on the
18656 : * partitioned table.
18657 : */
18658 : static void
2 alvherre 18659 GNC 935 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
18660 : {
18661 : List *idxes;
1906 alvherre 18662 ECB : List *attachRelIdxs;
18663 : Relation *attachrelIdxRels;
18664 : IndexInfo **attachInfos;
18665 : ListCell *cell;
1906 alvherre 18666 EUB : MemoryContext cxt;
18667 : MemoryContext oldcxt;
18668 :
1906 alvherre 18669 GIC 935 : cxt = AllocSetContextCreate(CurrentMemoryContext,
18670 : "AttachPartitionEnsureIndexes",
18671 : ALLOCSET_DEFAULT_SIZES);
18672 935 : oldcxt = MemoryContextSwitchTo(cxt);
18673 :
18674 935 : idxes = RelationGetIndexList(rel);
18675 935 : attachRelIdxs = RelationGetIndexList(attachrel);
1906 alvherre 18676 CBC 935 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
18677 935 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
18678 :
1906 alvherre 18679 ECB : /* Build arrays of all existing indexes and their IndexInfos */
1906 alvherre 18680 CBC 1084 : foreach(cell, attachRelIdxs)
1906 alvherre 18681 ECB : {
1906 alvherre 18682 CBC 149 : Oid cldIdxId = lfirst_oid(cell);
2 alvherre 18683 GNC 149 : int i = foreach_current_index(cell);
18684 :
1906 alvherre 18685 GIC 149 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
18686 149 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
18687 : }
18688 :
18689 : /*
18690 : * If we're attaching a foreign table, we must fail if any of the indexes
18691 : * is a constraint index; otherwise, there's nothing to do here. Do this
18692 : * before starting work, to avoid wasting the effort of building a few
18693 : * non-unique indexes before coming across a unique one.
1383 alvherre 18694 ECB : */
1383 alvherre 18695 CBC 935 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
18696 : {
1383 alvherre 18697 GIC 43 : foreach(cell, idxes)
18698 : {
18699 18 : Oid idx = lfirst_oid(cell);
18700 18 : Relation idxRel = index_open(idx, AccessShareLock);
18701 :
18702 18 : if (idxRel->rd_index->indisunique ||
18703 12 : idxRel->rd_index->indisprimary)
1383 alvherre 18704 CBC 6 : ereport(ERROR,
1383 alvherre 18705 ECB : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18706 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18707 : RelationGetRelationName(attachrel),
18708 : RelationGetRelationName(rel)),
18709 : errdetail("Partitioned table \"%s\" contains unique indexes.",
18710 : RelationGetRelationName(rel))));
1383 alvherre 18711 GIC 12 : index_close(idxRel, AccessShareLock);
18712 : }
1383 alvherre 18713 ECB :
1383 alvherre 18714 GIC 25 : goto out;
1383 alvherre 18715 ECB : }
1383 alvherre 18716 EUB :
18717 : /*
18718 : * For each index on the partitioned table, find a matching one in the
18719 : * partition-to-be; if one is not found, create one.
18720 : */
1906 alvherre 18721 GIC 1088 : foreach(cell, idxes)
18722 : {
1906 alvherre 18723 CBC 193 : Oid idx = lfirst_oid(cell);
1906 alvherre 18724 GBC 193 : Relation idxRel = index_open(idx, AccessShareLock);
18725 : IndexInfo *info;
18726 : AttrMap *attmap;
1906 alvherre 18727 GIC 193 : bool found = false;
18728 : Oid constraintOid;
18729 :
1906 alvherre 18730 ECB : /*
18731 : * Ignore indexes in the partitioned table other than partitioned
18732 : * indexes.
18733 : */
1906 alvherre 18734 GIC 193 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
18735 : {
1906 alvherre 18736 UIC 0 : index_close(idxRel, AccessShareLock);
1906 alvherre 18737 LBC 0 : continue;
1906 alvherre 18738 ECB : }
18739 :
18740 : /* construct an indexinfo to compare existing indexes against */
1906 alvherre 18741 GIC 193 : info = BuildIndexInfo(idxRel);
1208 michael 18742 CBC 193 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
18743 : RelationGetDescr(rel),
18744 : false);
1875 alvherre 18745 193 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
18746 :
1906 alvherre 18747 ECB : /*
18748 : * Scan the list of existing indexes in the partition-to-be, and mark
18749 : * the first matching, unattached one we find, if any, as partition of
1906 alvherre 18750 EUB : * the parent index. If we find one, we're done.
18751 : */
2 alvherre 18752 GNC 220 : for (int i = 0; i < list_length(attachRelIdxs); i++)
18753 : {
1809 tgl 18754 GIC 110 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
18755 110 : Oid cldConstrOid = InvalidOid;
1875 alvherre 18756 ECB :
18757 : /* does this index have a parent? if so, can't use it */
1824 alvherre 18758 GIC 110 : if (attachrelIdxRels[i]->rd_rel->relispartition)
1906 18759 6 : continue;
18760 :
18761 104 : if (CompareIndexInfo(attachInfos[i], info,
18762 104 : attachrelIdxRels[i]->rd_indcollation,
18763 : idxRel->rd_indcollation,
1906 alvherre 18764 CBC 104 : attachrelIdxRels[i]->rd_opfamily,
18765 : idxRel->rd_opfamily,
18766 : attmap))
18767 : {
18768 : /*
1875 alvherre 18769 ECB : * If this index is being created in the parent because of a
18770 : * constraint, then the child needs to have a constraint also,
18771 : * so look for one. If there is no such constraint, this
18772 : * index is no good, so keep looking.
18773 : */
1875 alvherre 18774 GIC 86 : if (OidIsValid(constraintOid))
18775 : {
18776 : cldConstrOid =
18777 43 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
18778 : cldIdxId);
18779 : /* no dice */
18780 43 : if (!OidIsValid(cldConstrOid))
18781 3 : continue;
1875 alvherre 18782 ECB : }
18783 :
18784 : /* bingo. */
1906 alvherre 18785 CBC 83 : IndexSetParentIndex(attachrelIdxRels[i], idx);
1875 18786 83 : if (OidIsValid(constraintOid))
1518 tgl 18787 GIC 40 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
18788 : RelationGetRelid(attachrel));
1906 alvherre 18789 CBC 83 : found = true;
18790 :
1467 18791 83 : CommandCounterIncrement();
1906 alvherre 18792 GIC 83 : break;
1906 alvherre 18793 ECB : }
18794 : }
18795 :
18796 : /*
18797 : * If no suitable index was found in the partition-to-be, create one
18798 : * now.
18799 : */
1906 alvherre 18800 GIC 193 : if (!found)
1906 alvherre 18801 ECB : {
18802 : IndexStmt *stmt;
18803 : Oid conOid;
18804 :
1447 tgl 18805 GIC 110 : stmt = generateClonedIndexStmt(NULL,
18806 : idxRel, attmap,
18807 : &conOid);
18808 :
18809 : /*
18810 : * If the index is a primary key, mark all columns as NOT NULL if
18811 : * they aren't already.
18812 : */
2 alvherre 18813 GNC 110 : if (stmt->primary)
18814 : {
18815 59 : MemoryContextSwitchTo(oldcxt);
18816 124 : for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
18817 : {
18818 : AttrNumber childattno;
18819 :
18820 65 : childattno = get_attnum(RelationGetRelid(attachrel),
18821 65 : get_attname(RelationGetRelid(rel),
18822 65 : info->ii_IndexAttrNumbers[j],
18823 : false));
18824 65 : set_attnotnull(wqueue, attachrel, childattno,
18825 : true, AccessExclusiveLock);
18826 : }
18827 59 : MemoryContextSwitchTo(cxt);
18828 : }
18829 :
1906 alvherre 18830 GIC 110 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
18831 : RelationGetRelid(idxRel),
18832 : conOid,
18833 : -1,
18834 : true, false, false, false, false);
18835 : }
18836 :
1906 alvherre 18837 CBC 184 : index_close(idxRel, AccessShareLock);
1906 alvherre 18838 ECB : }
18839 :
1383 alvherre 18840 GIC 920 : out:
18841 : /* Clean up. */
2 alvherre 18842 GNC 1063 : for (int i = 0; i < list_length(attachRelIdxs); i++)
1906 alvherre 18843 GIC 143 : index_close(attachrelIdxRels[i], AccessShareLock);
1906 alvherre 18844 CBC 920 : MemoryContextSwitchTo(oldcxt);
1906 alvherre 18845 GIC 920 : MemoryContextDelete(cxt);
18846 920 : }
18847 :
18848 : /*
18849 : * CloneRowTriggersToPartition
18850 : * subroutine for ATExecAttachPartition/DefineRelation to create row
18851 : * triggers on partitions
18852 : */
18853 : static void
1843 18854 1108 : CloneRowTriggersToPartition(Relation parent, Relation partition)
18855 : {
18856 : Relation pg_trigger;
18857 : ScanKeyData key;
1843 alvherre 18858 ECB : SysScanDesc scan;
18859 : HeapTuple tuple;
1406 tgl 18860 : MemoryContext perTupCxt;
1843 alvherre 18861 :
1843 alvherre 18862 GIC 1108 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
18863 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
1539 andres 18864 1108 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
1843 alvherre 18865 CBC 1108 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
1843 alvherre 18866 ECB : true, NULL, 1, &key);
18867 :
1843 alvherre 18868 GIC 1108 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
18869 : "clone trig", ALLOCSET_SMALL_SIZES);
18870 :
18871 1788 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18872 : {
1406 tgl 18873 683 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
1843 alvherre 18874 ECB : CreateTrigStmt *trigStmt;
1843 alvherre 18875 CBC 683 : Node *qual = NULL;
18876 : Datum value;
1843 alvherre 18877 ECB : bool isnull;
1843 alvherre 18878 GIC 683 : List *cols = NIL;
1370 18879 683 : List *trigargs = NIL;
18880 : MemoryContext oldcxt;
18881 :
18882 : /*
18883 : * Ignore statement-level triggers; those are not cloned.
18884 : */
1843 alvherre 18885 CBC 683 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
18886 605 : continue;
18887 :
1193 alvherre 18888 ECB : /*
459 18889 : * Don't clone internal triggers, because the constraint cloning code
18890 : * will.
18891 : */
459 alvherre 18892 GIC 683 : if (trigForm->tgisinternal)
1832 18893 605 : continue;
18894 :
18895 : /*
18896 : * Complain if we find an unexpected trigger type.
18897 : */
1117 18898 78 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
18899 69 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
1843 alvherre 18900 UIC 0 : elog(ERROR, "unexpected trigger \"%s\" found",
1843 alvherre 18901 ECB : NameStr(trigForm->tgname));
18902 :
18903 : /* Use short-lived context for CREATE TRIGGER */
1406 tgl 18904 GIC 78 : oldcxt = MemoryContextSwitchTo(perTupCxt);
18905 :
18906 : /*
18907 : * If there is a WHEN clause, generate a 'cooked' version of it that's
18908 : * appropriate for the partition.
18909 : */
1843 alvherre 18910 78 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
18911 : RelationGetDescr(pg_trigger), &isnull);
18912 78 : if (!isnull)
18913 : {
18914 3 : qual = stringToNode(TextDatumGetCString(value));
1843 alvherre 18915 CBC 3 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
18916 : partition, parent);
1843 alvherre 18917 GIC 3 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
18918 : partition, parent);
18919 : }
18920 :
18921 : /*
18922 : * If there is a column list, transform it to a list of column names.
1843 alvherre 18923 ECB : * Note we don't need to map this list in any way ...
18924 : */
1843 alvherre 18925 CBC 78 : if (trigForm->tgattr.dim1 > 0)
1843 alvherre 18926 ECB : {
18927 : int i;
18928 :
1843 alvherre 18929 GIC 6 : for (i = 0; i < trigForm->tgattr.dim1; i++)
1843 alvherre 18930 ECB : {
18931 : Form_pg_attribute col;
18932 :
1843 alvherre 18933 CBC 3 : col = TupleDescAttr(parent->rd_att,
18934 : trigForm->tgattr.values[i] - 1);
1812 alvherre 18935 GIC 3 : cols = lappend(cols,
18936 3 : makeString(pstrdup(NameStr(col->attname))));
18937 : }
18938 : }
18939 :
18940 : /* Reconstruct trigger arguments list. */
1370 alvherre 18941 CBC 78 : if (trigForm->tgnargs > 0)
18942 : {
18943 : char *p;
18944 :
1370 alvherre 18945 GIC 6 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
1370 alvherre 18946 ECB : RelationGetDescr(pg_trigger), &isnull);
1370 alvherre 18947 CBC 6 : if (isnull)
1370 alvherre 18948 LBC 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
1370 alvherre 18949 ECB : NameStr(trigForm->tgname), RelationGetRelationName(partition));
18950 :
1370 alvherre 18951 CBC 6 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
18952 :
18953 18 : for (int i = 0; i < trigForm->tgnargs; i++)
18954 : {
1370 alvherre 18955 GIC 12 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
1370 alvherre 18956 CBC 12 : p += strlen(p) + 1;
18957 : }
18958 : }
18959 :
1843 alvherre 18960 GIC 78 : trigStmt = makeNode(CreateTrigStmt);
876 tgl 18961 78 : trigStmt->replace = false;
18962 78 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
1843 alvherre 18963 78 : trigStmt->trigname = NameStr(trigForm->tgname);
1843 alvherre 18964 CBC 78 : trigStmt->relation = NULL;
1843 alvherre 18965 GIC 78 : trigStmt->funcname = NULL; /* passed separately */
1370 18966 78 : trigStmt->args = trigargs;
1843 18967 78 : trigStmt->row = true;
18968 78 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
18969 78 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
18970 78 : trigStmt->columns = cols;
1843 alvherre 18971 CBC 78 : trigStmt->whenClause = NULL; /* passed separately */
1843 alvherre 18972 GIC 78 : trigStmt->transitionRels = NIL; /* not supported at present */
18973 78 : trigStmt->deferrable = trigForm->tgdeferrable;
1843 alvherre 18974 CBC 78 : trigStmt->initdeferred = trigForm->tginitdeferred;
1843 alvherre 18975 GIC 78 : trigStmt->constrrel = NULL; /* passed separately */
18976 :
632 alvherre 18977 CBC 78 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
18978 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
18979 : trigForm->tgfoid, trigForm->oid, qual,
632 alvherre 18980 GIC 78 : false, true, trigForm->tgenabled);
18981 :
1406 tgl 18982 75 : MemoryContextSwitchTo(oldcxt);
1843 alvherre 18983 75 : MemoryContextReset(perTupCxt);
18984 : }
18985 :
18986 1105 : MemoryContextDelete(perTupCxt);
18987 :
1843 alvherre 18988 CBC 1105 : systable_endscan(scan);
1539 andres 18989 GIC 1105 : table_close(pg_trigger, RowExclusiveLock);
1843 alvherre 18990 1105 : }
18991 :
18992 : /*
18993 : * ALTER TABLE DETACH PARTITION
18994 : *
18995 : * Return the address of the relation that is no longer a partition of rel.
18996 : *
18997 : * If concurrent mode is requested, we run in two transactions. A side-
18998 : * effect is that this command cannot run in a multi-part ALTER TABLE.
18999 : * Currently, that's enforced by the grammar.
19000 : *
19001 : * The strategy for concurrency is to first modify the partition's
19002 : * pg_inherit catalog row to make it visible to everyone that the
19003 : * partition is detached, lock the partition against writes, and commit
745 alvherre 19004 ECB : * the transaction; anyone who requests the partition descriptor from
19005 : * that point onwards has to ignore such a partition. In a second
19006 : * transaction, we wait until all transactions that could have seen the
19007 : * partition as attached are gone, then we remove the rest of partition
19008 : * metadata (pg_inherits and pg_class.relpartbounds).
19009 : */
19010 : static ObjectAddress
745 alvherre 19011 GIC 252 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19012 : RangeVar *name, bool concurrent)
2314 rhaas 19013 ECB : {
745 alvherre 19014 : Relation partRel;
2314 rhaas 19015 : ObjectAddress address;
19016 : Oid defaultPartOid;
2039 19017 :
19018 : /*
19019 : * We must lock the default partition, because detaching this partition
19020 : * will change its partition constraint.
19021 : */
19022 : defaultPartOid =
717 alvherre 19023 GIC 252 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
2039 rhaas 19024 252 : if (OidIsValid(defaultPartOid))
19025 : {
19026 : /*
19027 : * Concurrent detaching when a default partition exists is not
745 alvherre 19028 ECB : * supported. The main problem is that the default partition
19029 : * constraint would change. And there's a definitional problem: what
19030 : * should happen to the tuples that are being inserted that belong to
19031 : * the partition being detached? Putting them on the partition being
503 19032 : * detached would be wrong, since they'd become "lost" after the
19033 : * detaching completes but we cannot put them in the default partition
19034 : * either until we alter its partition constraint.
19035 : *
19036 : * I think we could solve this problem if we effected the constraint
745 19037 : * change before committing the first transaction. But the lock would
19038 : * have to remain AEL and it would cause concurrent query planning to
19039 : * be blocked, so changing it that way would be even worse.
19040 : */
745 alvherre 19041 GIC 53 : if (concurrent)
19042 6 : ereport(ERROR,
19043 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19044 : errmsg("cannot detach partitions concurrently when a default partition exists")));
2039 rhaas 19045 47 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
745 alvherre 19046 ECB : }
2314 rhaas 19047 :
19048 : /*
19049 : * In concurrent mode, the partition is locked with share-update-exclusive
19050 : * in the first transaction. This allows concurrent transactions to be
745 alvherre 19051 : * doing DML to the partition.
19052 : */
745 alvherre 19053 CBC 246 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
745 alvherre 19054 ECB : AccessExclusiveLock);
19055 :
19056 : /*
697 tgl 19057 : * Check inheritance conditions and either delete the pg_inherits row (in
19058 : * non-concurrent mode) or just set the inhdetachpending flag.
19059 : */
745 alvherre 19060 CBC 240 : if (!concurrent)
745 alvherre 19061 GIC 167 : RemoveInheritance(partRel, rel, false);
19062 : else
19063 73 : MarkInheritDetached(partRel, rel);
745 alvherre 19064 ECB :
19065 : /*
19066 : * Ensure that foreign keys still hold after this detach. This keeps
19067 : * locks on the referencing tables, which prevents concurrent transactions
19068 : * from adding rows that we wouldn't see. For this to work in concurrent
19069 : * mode, it is critical that the partition appears as no longer attached
19070 : * for the RI queries as soon as the first transaction commits.
19071 : */
1467 alvherre 19072 CBC 230 : ATDetachCheckNoForeignKeyRefs(partRel);
19073 :
19074 : /*
19075 : * Concurrent mode has to work harder; first we add a new constraint to
19076 : * the partition that matches the partition constraint. Then we close our
19077 : * existing transaction, and in a new one wait for all processes to catch
19078 : * up on the catalog updates we've done so far; at that point we can
19079 : * complete the operation.
19080 : */
745 alvherre 19081 GIC 213 : if (concurrent)
19082 : {
19083 : Oid partrelid,
19084 : parentrelid;
19085 : LOCKTAG tag;
19086 : char *parentrelname;
19087 : char *partrelname;
19088 :
745 alvherre 19089 ECB : /*
19090 : * Add a new constraint to the partition being detached, which
19091 : * supplants the partition constraint (unless there is one already).
19092 : */
745 alvherre 19093 GIC 70 : DetachAddConstraintIfNeeded(wqueue, partRel);
19094 :
19095 : /*
19096 : * We're almost done now; the only traces that remain are the
19097 : * pg_inherits tuple and the partition's relpartbounds. Before we can
19098 : * remove those, we need to wait until all transactions that know that
19099 : * this is a partition are gone.
745 alvherre 19100 ECB : */
2314 rhaas 19101 :
745 alvherre 19102 : /*
19103 : * Remember relation OIDs to re-acquire them later; and relation names
19104 : * too, for error messages if something is dropped in between.
19105 : */
745 alvherre 19106 GIC 70 : partrelid = RelationGetRelid(partRel);
19107 70 : parentrelid = RelationGetRelid(rel);
745 alvherre 19108 CBC 70 : parentrelname = MemoryContextStrdup(PortalContext,
19109 70 : RelationGetRelationName(rel));
19110 70 : partrelname = MemoryContextStrdup(PortalContext,
745 alvherre 19111 GIC 70 : RelationGetRelationName(partRel));
19112 :
19113 : /* Invalidate relcache entries for the parent -- must be before close */
19114 70 : CacheInvalidateRelcache(rel);
19115 :
745 alvherre 19116 CBC 70 : table_close(partRel, NoLock);
19117 70 : table_close(rel, NoLock);
745 alvherre 19118 GBC 70 : tab->rel = NULL;
19119 :
19120 : /* Make updated catalog entry visible */
745 alvherre 19121 GIC 70 : PopActiveSnapshot();
19122 70 : CommitTransactionCommand();
745 alvherre 19123 ECB :
745 alvherre 19124 CBC 70 : StartTransactionCommand();
2314 rhaas 19125 EUB :
19126 : /*
19127 : * Now wait. This ensures that all queries that were planned
19128 : * including the partition are finished before we remove the rest of
19129 : * catalog entries. We don't need or indeed want to acquire this
697 tgl 19130 ECB : * lock, though -- that would block later queries.
1845 alvherre 19131 : *
745 19132 : * We don't need to concern ourselves with waiting for a lock on the
19133 : * partition itself, since we will acquire AccessExclusiveLock below.
2039 rhaas 19134 : */
745 alvherre 19135 CBC 70 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
745 alvherre 19136 GIC 70 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19137 :
745 alvherre 19138 ECB : /*
19139 : * Now acquire locks in both relations again. Note they may have been
19140 : * removed in the meantime, so care is required.
19141 : */
745 alvherre 19142 CBC 45 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
745 alvherre 19143 GIC 45 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
19144 :
745 alvherre 19145 ECB : /* If the relations aren't there, something bad happened; bail out */
745 alvherre 19146 GIC 45 : if (rel == NULL)
19147 : {
745 alvherre 19148 UIC 0 : if (partRel != NULL) /* shouldn't happen */
19149 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19150 : partrelname);
19151 0 : ereport(ERROR,
19152 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19153 : errmsg("partitioned table \"%s\" was removed concurrently",
19154 : parentrelname)));
19155 : }
745 alvherre 19156 GIC 45 : if (partRel == NULL)
745 alvherre 19157 UIC 0 : ereport(ERROR,
745 alvherre 19158 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19159 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
19160 :
745 alvherre 19161 GIC 45 : tab->rel = rel;
19162 : }
19163 :
19164 : /* Do the final part of detaching */
19165 188 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19166 :
19167 187 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19168 :
19169 : /* keep our lock until commit */
19170 187 : table_close(partRel, NoLock);
1906 alvherre 19171 ECB :
745 alvherre 19172 GIC 187 : return address;
19173 : }
19174 :
745 alvherre 19175 ECB : /*
19176 : * Second part of ALTER TABLE .. DETACH.
19177 : *
19178 : * This is separate so that it can be run independently when the second
19179 : * transaction of the concurrent algorithm fails (crash or abort).
19180 : */
19181 : static void
745 alvherre 19182 GIC 195 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
19183 : Oid defaultPartOid)
745 alvherre 19184 ECB : {
19185 : Relation classRel;
19186 : List *fks;
19187 : ListCell *cell;
19188 : List *indexes;
19189 : Datum new_val[Natts_pg_class];
19190 : bool new_null[Natts_pg_class],
19191 : new_repl[Natts_pg_class];
19192 : HeapTuple tuple,
19193 : newtuple;
459 alvherre 19194 GIC 195 : Relation trigrel = NULL;
19195 :
745 19196 195 : if (concurrent)
745 alvherre 19197 ECB : {
19198 : /*
19199 : * We can remove the pg_inherits row now. (In the non-concurrent case,
19200 : * this was already done).
19201 : */
745 alvherre 19202 CBC 52 : RemoveInheritance(partRel, rel, true);
19203 : }
19204 :
19205 : /* Drop any triggers that were cloned on creation/attach. */
1083 alvherre 19206 GIC 195 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
19207 :
19208 : /*
19209 : * Detach any foreign keys that are inherited. This includes creating
1539 alvherre 19210 ECB : * additional action triggers.
19211 : */
1640 alvherre 19212 GIC 195 : fks = copyObject(RelationGetFKeyList(partRel));
459 19213 195 : if (fks != NIL)
459 alvherre 19214 CBC 24 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
1640 alvherre 19215 GIC 234 : foreach(cell, fks)
19216 : {
19217 39 : ForeignKeyCacheInfo *fk = lfirst(cell);
19218 : HeapTuple contup;
19219 : Form_pg_constraint conform;
1539 alvherre 19220 ECB : Constraint *fkconstraint;
19221 : Oid insertTriggerOid,
19222 : updateTriggerOid;
19223 :
1640 alvherre 19224 CBC 39 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
1435 tgl 19225 GIC 39 : if (!HeapTupleIsValid(contup))
1640 alvherre 19226 UIC 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
1539 alvherre 19227 GIC 39 : conform = (Form_pg_constraint) GETSTRUCT(contup);
19228 :
19229 : /* consider only the inherited foreign keys */
19230 39 : if (conform->contype != CONSTRAINT_FOREIGN ||
19231 39 : !OidIsValid(conform->conparentid))
19232 : {
19233 9 : ReleaseSysCache(contup);
19234 9 : continue;
1539 alvherre 19235 ECB : }
19236 :
19237 : /* unset conparentid and adjust conislocal, coninhcount, etc. */
1518 tgl 19238 GIC 30 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
19239 :
459 alvherre 19240 ECB : /*
19241 : * Also, look up the partition's "check" triggers corresponding to the
19242 : * constraint being detached and detach them from the parent triggers.
19243 : */
459 alvherre 19244 GIC 30 : GetForeignKeyCheckTriggers(trigrel,
459 alvherre 19245 ECB : fk->conoid, fk->confrelid, fk->conrelid,
19246 : &insertTriggerOid, &updateTriggerOid);
459 alvherre 19247 GIC 30 : Assert(OidIsValid(insertTriggerOid));
19248 30 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
19249 : RelationGetRelid(partRel));
19250 30 : Assert(OidIsValid(updateTriggerOid));
19251 30 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
459 alvherre 19252 ECB : RelationGetRelid(partRel));
19253 :
1539 19254 : /*
19255 : * Make the action triggers on the referenced relation. When this was
19256 : * a partition the action triggers pointed to the parent rel (they
19257 : * still do), but now we need separate ones of our own.
19258 : */
1539 alvherre 19259 GIC 30 : fkconstraint = makeNode(Constraint);
157 19260 30 : fkconstraint->contype = CONSTRAINT_FOREIGN;
1539 alvherre 19261 CBC 30 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
1539 alvherre 19262 GIC 30 : fkconstraint->deferrable = conform->condeferrable;
19263 30 : fkconstraint->initdeferred = conform->condeferred;
157 19264 30 : fkconstraint->location = -1;
19265 30 : fkconstraint->pktable = NULL;
19266 30 : fkconstraint->fk_attrs = NIL;
19267 30 : fkconstraint->pk_attrs = NIL;
19268 30 : fkconstraint->fk_matchtype = conform->confmatchtype;
157 alvherre 19269 CBC 30 : fkconstraint->fk_upd_action = conform->confupdtype;
157 alvherre 19270 GIC 30 : fkconstraint->fk_del_action = conform->confdeltype;
19271 30 : fkconstraint->fk_del_set_cols = NIL;
19272 30 : fkconstraint->old_conpfeqop = NIL;
157 alvherre 19273 CBC 30 : fkconstraint->old_pktable_oid = InvalidOid;
157 alvherre 19274 GIC 30 : fkconstraint->skip_validation = false;
157 alvherre 19275 CBC 30 : fkconstraint->initially_valid = true;
19276 :
1539 alvherre 19277 GIC 30 : createForeignKeyActionTriggers(partRel, conform->confrelid,
19278 : fkconstraint, fk->conoid,
19279 : conform->conindid,
459 alvherre 19280 ECB : InvalidOid, InvalidOid,
19281 : NULL, NULL);
1539 19282 :
1640 alvherre 19283 GIC 30 : ReleaseSysCache(contup);
19284 : }
19285 195 : list_free_deep(fks);
459 19286 195 : if (trigrel)
19287 24 : table_close(trigrel, RowExclusiveLock);
19288 :
19289 : /*
19290 : * Any sub-constraints that are in the referenced-side of a larger
19291 : * constraint have to be removed. This partition is no longer part of the
19292 : * key space of the constraint.
19293 : */
1467 alvherre 19294 CBC 213 : foreach(cell, GetParentedForeignKeyRefs(partRel))
19295 : {
1467 alvherre 19296 GIC 19 : Oid constrOid = lfirst_oid(cell);
19297 : ObjectAddress constraint;
19298 :
19299 19 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
19300 19 : deleteDependencyRecordsForClass(ConstraintRelationId,
19301 : constrOid,
19302 : ConstraintRelationId,
19303 : DEPENDENCY_INTERNAL);
1467 alvherre 19304 CBC 19 : CommandCounterIncrement();
19305 :
1467 alvherre 19306 GIC 19 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
1467 alvherre 19307 CBC 19 : performDeletion(&constraint, DROP_RESTRICT, 0);
19308 : }
745 alvherre 19309 ECB :
19310 : /* Now we can detach indexes */
745 alvherre 19311 CBC 194 : indexes = RelationGetIndexList(partRel);
19312 264 : foreach(cell, indexes)
19313 : {
745 alvherre 19314 GIC 70 : Oid idxid = lfirst_oid(cell);
745 alvherre 19315 ECB : Relation idx;
19316 : Oid constrOid;
19317 :
745 alvherre 19318 CBC 70 : if (!has_superclass(idxid))
745 alvherre 19319 GIC 6 : continue;
745 alvherre 19320 ECB :
745 alvherre 19321 CBC 64 : Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
19322 : RelationGetRelid(rel)));
19323 :
745 alvherre 19324 GIC 64 : idx = index_open(idxid, AccessExclusiveLock);
19325 64 : IndexSetParentIndex(idx, InvalidOid);
19326 :
19327 : /* If there's a constraint associated with the index, detach it too */
19328 64 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
19329 : idxid);
745 alvherre 19330 CBC 64 : if (OidIsValid(constrOid))
745 alvherre 19331 GIC 30 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
745 alvherre 19332 ECB :
745 alvherre 19333 GIC 64 : index_close(idx, NoLock);
745 alvherre 19334 ECB : }
19335 :
19336 : /* Update pg_class tuple */
745 alvherre 19337 CBC 194 : classRel = table_open(RelationRelationId, RowExclusiveLock);
19338 194 : tuple = SearchSysCacheCopy1(RELOID,
745 alvherre 19339 ECB : ObjectIdGetDatum(RelationGetRelid(partRel)));
745 alvherre 19340 GIC 194 : if (!HeapTupleIsValid(tuple))
745 alvherre 19341 UIC 0 : elog(ERROR, "cache lookup failed for relation %u",
19342 : RelationGetRelid(partRel));
745 alvherre 19343 GIC 194 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
19344 :
19345 : /* Clear relpartbound and reset relispartition */
745 alvherre 19346 CBC 194 : memset(new_val, 0, sizeof(new_val));
745 alvherre 19347 GIC 194 : memset(new_null, false, sizeof(new_null));
19348 194 : memset(new_repl, false, sizeof(new_repl));
745 alvherre 19349 CBC 194 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
745 alvherre 19350 GIC 194 : new_null[Anum_pg_class_relpartbound - 1] = true;
19351 194 : new_repl[Anum_pg_class_relpartbound - 1] = true;
19352 194 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
19353 : new_val, new_null, new_repl);
19354 :
19355 194 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
745 alvherre 19356 CBC 194 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
745 alvherre 19357 GIC 194 : heap_freetuple(newtuple);
745 alvherre 19358 CBC 194 : table_close(classRel, RowExclusiveLock);
745 alvherre 19359 ECB :
745 alvherre 19360 GIC 194 : if (OidIsValid(defaultPartOid))
19361 : {
745 alvherre 19362 ECB : /*
19363 : * If the relation being detached is the default partition itself,
19364 : * remove it from the parent's pg_partitioned_table entry.
19365 : *
19366 : * If not, we must invalidate default partition's relcache entry, as
19367 : * in StorePartitionBound: its partition constraint depends on every
19368 : * other partition's partition constraint.
19369 : */
745 alvherre 19370 GIC 23 : if (RelationGetRelid(partRel) == defaultPartOid)
745 alvherre 19371 GBC 1 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
745 alvherre 19372 EUB : else
745 alvherre 19373 GIC 22 : CacheInvalidateRelcacheByRelid(defaultPartOid);
19374 : }
19375 :
2314 rhaas 19376 ECB : /*
2308 19377 : * Invalidate the parent's relcache so that the partition is no longer
19378 : * included in its partition descriptor.
19379 : */
2314 rhaas 19380 CBC 194 : CacheInvalidateRelcache(rel);
19381 :
19382 : /*
19383 : * If the partition we just detached is partitioned itself, invalidate
19384 : * relcache for all descendent partitions too to ensure that their
19385 : * rd_partcheck expression trees are rebuilt; must lock partitions before
19386 : * doing so, using the same lockmode as what partRel has been locked with
332 tgl 19387 ECB : * by the caller.
19388 : */
538 alvherre 19389 CBC 194 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
538 alvherre 19390 ECB : {
19391 : List *children;
19392 :
538 alvherre 19393 CBC 25 : children = find_all_inheritors(RelationGetRelid(partRel),
538 alvherre 19394 ECB : AccessExclusiveLock, NULL);
538 alvherre 19395 GIC 81 : foreach(cell, children)
538 alvherre 19396 ECB : {
538 alvherre 19397 CBC 56 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
19398 : }
538 alvherre 19399 ECB : }
745 alvherre 19400 GIC 194 : }
19401 :
19402 : /*
19403 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
19404 : *
19405 : * To use when a DETACH PARTITION command previously did not run to
19406 : * completion; this completes the detaching process.
19407 : */
19408 : static ObjectAddress
745 alvherre 19409 CBC 7 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
19410 : {
19411 : Relation partRel;
745 alvherre 19412 ECB : ObjectAddress address;
745 alvherre 19413 GIC 7 : Snapshot snap = GetActiveSnapshot();
19414 :
745 alvherre 19415 CBC 7 : partRel = table_openrv(name, AccessExclusiveLock);
745 alvherre 19416 ECB :
19417 : /*
19418 : * Wait until existing snapshots are gone. This is important if the
19419 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
19420 : * user could immediately run DETACH FINALIZE without actually waiting for
19421 : * existing transactions. We must not complete the detach action until
19422 : * all such queries are complete (otherwise we would present them with an
19423 : * inconsistent view of catalogs).
19424 : */
745 alvherre 19425 GIC 7 : WaitForOlderSnapshots(snap->xmin, false);
745 alvherre 19426 ECB :
745 alvherre 19427 CBC 7 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
19428 :
2314 rhaas 19429 GIC 7 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19430 :
1539 andres 19431 7 : table_close(partRel, NoLock);
19432 :
2314 rhaas 19433 7 : return address;
19434 : }
1906 alvherre 19435 ECB :
19436 : /*
19437 : * DetachAddConstraintIfNeeded
19438 : * Subroutine for ATExecDetachPartition. Create a constraint that
19439 : * takes the place of the partition constraint, but avoid creating
718 19440 : * a dupe if an constraint already exists which implies the needed
19441 : * constraint.
19442 : */
19443 : static void
745 alvherre 19444 GIC 70 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
19445 : {
19446 : List *constraintExpr;
19447 :
718 alvherre 19448 CBC 70 : constraintExpr = RelationGetPartitionQual(partRel);
718 alvherre 19449 GIC 70 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
745 alvherre 19450 ECB :
718 19451 : /*
19452 : * Avoid adding a new constraint if the needed constraint is implied by an
19453 : * existing constraint
19454 : */
718 alvherre 19455 CBC 70 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
745 alvherre 19456 ECB : {
718 19457 : AlteredTableInfo *tab;
19458 : Constraint *n;
19459 :
718 alvherre 19460 GIC 67 : tab = ATGetQueueEntry(wqueue, partRel);
19461 :
718 alvherre 19462 ECB : /* Add constraint on partition, equivalent to the partition constraint */
718 alvherre 19463 GIC 67 : n = makeNode(Constraint);
19464 67 : n->contype = CONSTR_CHECK;
718 alvherre 19465 CBC 67 : n->conname = NULL;
718 alvherre 19466 GIC 67 : n->location = -1;
19467 67 : n->is_no_inherit = false;
19468 67 : n->raw_expr = NULL;
19469 67 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
19470 67 : n->initially_valid = true;
19471 67 : n->skip_validation = true;
718 alvherre 19472 ECB : /* It's a re-add, since it nominally already exists */
718 alvherre 19473 GIC 67 : ATAddCheckConstraint(wqueue, tab, partRel, n,
19474 : true, false, true, ShareUpdateExclusiveLock);
745 alvherre 19475 ECB : }
745 alvherre 19476 GIC 70 : }
745 alvherre 19477 ECB :
1083 19478 : /*
19479 : * DropClonedTriggersFromPartition
19480 : * subroutine for ATExecDetachPartition to remove any triggers that were
19481 : * cloned to the partition when it was created-as-partition or attached.
19482 : * This undoes what CloneRowTriggersToPartition did.
19483 : */
19484 : static void
1083 alvherre 19485 GIC 195 : DropClonedTriggersFromPartition(Oid partitionId)
19486 : {
19487 : ScanKeyData skey;
19488 : SysScanDesc scan;
1083 alvherre 19489 ECB : HeapTuple trigtup;
19490 : Relation tgrel;
19491 : ObjectAddresses *objects;
19492 :
1083 alvherre 19493 GIC 195 : objects = new_object_addresses();
19494 :
19495 : /*
19496 : * Scan pg_trigger to search for all triggers on this rel.
1083 alvherre 19497 ECB : */
1083 alvherre 19498 GIC 195 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
1083 alvherre 19499 ECB : F_OIDEQ, ObjectIdGetDatum(partitionId));
1083 alvherre 19500 CBC 195 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1083 alvherre 19501 GIC 195 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
19502 : true, NULL, 1, &skey);
1083 alvherre 19503 CBC 320 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
19504 : {
1083 alvherre 19505 GIC 125 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
1083 alvherre 19506 ECB : ObjectAddress trig;
19507 :
19508 : /* Ignore triggers that weren't cloned */
1083 alvherre 19509 GIC 125 : if (!OidIsValid(pg_trigger->tgparentid))
1083 alvherre 19510 CBC 116 : continue;
19511 :
19512 : /*
459 alvherre 19513 ECB : * Ignore internal triggers that are implementation objects of foreign
19514 : * keys, because these will be detached when the foreign keys
19515 : * themselves are.
19516 : */
459 alvherre 19517 GIC 107 : if (OidIsValid(pg_trigger->tgconstrrelid))
19518 98 : continue;
19519 :
1083 alvherre 19520 ECB : /*
19521 : * This is ugly, but necessary: remove the dependency markings on the
19522 : * trigger so that it can be removed.
19523 : */
1083 alvherre 19524 GIC 9 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19525 : TriggerRelationId,
19526 : DEPENDENCY_PARTITION_PRI);
1083 alvherre 19527 CBC 9 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
1083 alvherre 19528 ECB : RelationRelationId,
19529 : DEPENDENCY_PARTITION_SEC);
19530 :
19531 : /* remember this trigger to remove it below */
1083 alvherre 19532 GIC 9 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
1083 alvherre 19533 CBC 9 : add_exact_object_address(&trig, objects);
1083 alvherre 19534 ECB : }
1083 alvherre 19535 EUB :
19536 : /* make the dependency removal visible to the deletion below */
1083 alvherre 19537 GIC 195 : CommandCounterIncrement();
19538 195 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
1083 alvherre 19539 ECB :
19540 : /* done */
1083 alvherre 19541 GIC 195 : free_object_addresses(objects);
19542 195 : systable_endscan(scan);
19543 195 : table_close(tgrel, RowExclusiveLock);
19544 195 : }
1083 alvherre 19545 ECB :
19546 : /*
1906 19547 : * Before acquiring lock on an index, acquire the same lock on the owning
19548 : * table.
19549 : */
19550 : struct AttachIndexCallbackState
19551 : {
1809 tgl 19552 : Oid partitionOid;
19553 : Oid parentTblOid;
19554 : bool lockedParentTbl;
19555 : };
19556 :
19557 : static void
1906 alvherre 19558 GIC 193 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
19559 : void *arg)
1906 alvherre 19560 ECB : {
19561 : struct AttachIndexCallbackState *state;
19562 : Form_pg_class classform;
19563 : HeapTuple tuple;
19564 :
1906 alvherre 19565 GIC 193 : state = (struct AttachIndexCallbackState *) arg;
19566 :
19567 193 : if (!state->lockedParentTbl)
1906 alvherre 19568 ECB : {
1906 alvherre 19569 GIC 186 : LockRelationOid(state->parentTblOid, AccessShareLock);
1906 alvherre 19570 CBC 186 : state->lockedParentTbl = true;
1906 alvherre 19571 ECB : }
19572 :
19573 : /*
19574 : * If we previously locked some other heap, and the name we're looking up
19575 : * no longer refers to an index on that relation, release the now-useless
19576 : * lock. XXX maybe we should do *after* we verify whether the index does
19577 : * not actually belong to the same relation ...
19578 : */
1906 alvherre 19579 GIC 193 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
1906 alvherre 19580 ECB : {
1906 alvherre 19581 UIC 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
1906 alvherre 19582 LBC 0 : state->partitionOid = InvalidOid;
1906 alvherre 19583 EUB : }
19584 :
19585 : /* Didn't find a relation, so no need for locking or permission checks. */
1906 alvherre 19586 CBC 193 : if (!OidIsValid(relOid))
1906 alvherre 19587 GIC 3 : return;
1906 alvherre 19588 ECB :
1906 alvherre 19589 GIC 190 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1906 alvherre 19590 CBC 190 : if (!HeapTupleIsValid(tuple))
1906 alvherre 19591 LBC 0 : return; /* concurrently dropped, so nothing to do */
1906 alvherre 19592 GIC 190 : classform = (Form_pg_class) GETSTRUCT(tuple);
19593 190 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
19594 147 : classform->relkind != RELKIND_INDEX)
1906 alvherre 19595 CBC 3 : ereport(ERROR,
1906 alvherre 19596 ECB : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19597 : errmsg("\"%s\" is not an index", rv->relname)));
1906 alvherre 19598 CBC 187 : ReleaseSysCache(tuple);
1906 alvherre 19599 ECB :
19600 : /*
19601 : * Since we need only examine the heap's tupledesc, an access share lock
19602 : * on it (preventing any DDL) is sufficient.
19603 : */
1906 alvherre 19604 CBC 187 : state->partitionOid = IndexGetRelation(relOid, false);
19605 187 : LockRelationOid(state->partitionOid, AccessShareLock);
1906 alvherre 19606 ECB : }
19607 :
19608 : /*
19609 : * ALTER INDEX i1 ATTACH PARTITION i2
19610 : */
19611 : static ObjectAddress
1906 alvherre 19612 CBC 186 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
19613 : {
19614 : Relation partIdx;
1906 alvherre 19615 ECB : Relation partTbl;
19616 : Relation parentTbl;
19617 : ObjectAddress address;
19618 : Oid partIdxId;
19619 : Oid currParent;
19620 : struct AttachIndexCallbackState state;
19621 :
19622 : /*
19623 : * We need to obtain lock on the index 'name' to modify it, but we also
19624 : * need to read its owning table's tuple descriptor -- so we need to lock
19625 : * both. To avoid deadlocks, obtain lock on the table before doing so on
19626 : * the index. Furthermore, we need to examine the parent table of the
19627 : * partition, so lock that one too.
19628 : */
1906 alvherre 19629 GIC 186 : state.partitionOid = InvalidOid;
19630 186 : state.parentTblOid = parentIdx->rd_index->indrelid;
19631 186 : state.lockedParentTbl = false;
19632 : partIdxId =
1836 andres 19633 186 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
19634 : RangeVarCallbackForAttachIndex,
19635 : (void *) &state);
19636 : /* Not there? */
1906 alvherre 19637 180 : if (!OidIsValid(partIdxId))
1906 alvherre 19638 UIC 0 : ereport(ERROR,
19639 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19640 : errmsg("index \"%s\" does not exist", name->relname)));
19641 :
19642 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
1906 alvherre 19643 GIC 180 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
19644 :
19645 : /* we already hold locks on both tables, so this is safe: */
1906 alvherre 19646 CBC 180 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
1906 alvherre 19647 GIC 180 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
19648 :
19649 180 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
19650 :
19651 : /* Silently do nothing if already in the right state */
1824 19652 360 : currParent = partIdx->rd_rel->relispartition ?
745 19653 180 : get_partition_parent(partIdxId, false) : InvalidOid;
1906 19654 180 : if (currParent != RelationGetRelid(parentIdx))
19655 : {
19656 : IndexInfo *childInfo;
19657 : IndexInfo *parentInfo;
1208 michael 19658 ECB : AttrMap *attmap;
1906 alvherre 19659 : bool found;
19660 : int i;
19661 : PartitionDesc partDesc;
19662 : Oid constraintOid,
1875 alvherre 19663 GIC 174 : cldConstrId = InvalidOid;
19664 :
19665 : /*
19666 : * If this partition already has an index attached, refuse the
19667 : * operation.
19668 : */
1906 19669 174 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
19670 :
19671 171 : if (OidIsValid(currParent))
1906 alvherre 19672 UIC 0 : ereport(ERROR,
19673 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19674 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19675 : RelationGetRelationName(partIdx),
1906 alvherre 19676 ECB : RelationGetRelationName(parentIdx)),
19677 : errdetail("Index \"%s\" is already attached to another index.",
19678 : RelationGetRelationName(partIdx))));
19679 :
19680 : /* Make sure it indexes a partition of the other index's table */
717 alvherre 19681 GIC 171 : partDesc = RelationGetPartitionDesc(parentTbl, true);
1906 19682 171 : found = false;
19683 272 : for (i = 0; i < partDesc->nparts; i++)
19684 : {
19685 269 : if (partDesc->oids[i] == state.partitionOid)
19686 : {
19687 168 : found = true;
1906 alvherre 19688 CBC 168 : break;
19689 : }
19690 : }
1906 alvherre 19691 GIC 171 : if (!found)
19692 3 : ereport(ERROR,
19693 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19694 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
1906 alvherre 19695 ECB : RelationGetRelationName(partIdx),
19696 : RelationGetRelationName(parentIdx)),
19697 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19698 : RelationGetRelationName(partIdx),
19699 : RelationGetRelationName(parentTbl))));
19700 :
19701 : /* Ensure the indexes are compatible */
1906 alvherre 19702 GIC 168 : childInfo = BuildIndexInfo(partIdx);
19703 168 : parentInfo = BuildIndexInfo(parentIdx);
1208 michael 19704 168 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
19705 : RelationGetDescr(parentTbl),
19706 : false);
1906 alvherre 19707 168 : if (!CompareIndexInfo(childInfo, parentInfo,
1906 alvherre 19708 ECB : partIdx->rd_indcollation,
19709 : parentIdx->rd_indcollation,
19710 : partIdx->rd_opfamily,
19711 : parentIdx->rd_opfamily,
19712 : attmap))
1906 alvherre 19713 GIC 21 : ereport(ERROR,
19714 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19715 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19716 : RelationGetRelationName(partIdx),
1906 alvherre 19717 ECB : RelationGetRelationName(parentIdx)),
19718 : errdetail("The index definitions do not match.")));
19719 :
19720 : /*
19721 : * If there is a constraint in the parent, make sure there is one in
19722 : * the child too.
19723 : */
1875 alvherre 19724 GIC 147 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
19725 : RelationGetRelid(parentIdx));
19726 :
19727 147 : if (OidIsValid(constraintOid))
19728 : {
1875 alvherre 19729 CBC 64 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
19730 : partIdxId);
1875 alvherre 19731 GIC 64 : if (!OidIsValid(cldConstrId))
19732 3 : ereport(ERROR,
19733 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19734 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19735 : RelationGetRelationName(partIdx),
19736 : RelationGetRelationName(parentIdx)),
19737 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19738 : RelationGetRelationName(parentIdx),
19739 : RelationGetRelationName(parentTbl),
19740 : RelationGetRelationName(partIdx))));
19741 : }
1875 alvherre 19742 ECB :
19743 : /*
19744 : * If it's a primary key, make sure the columns in the partition are
19745 : * NOT NULL.
19746 : */
2 alvherre 19747 GNC 144 : if (parentIdx->rd_index->indisprimary)
19748 49 : verifyPartitionIndexNotNull(childInfo, partTbl);
19749 :
1906 alvherre 19750 ECB : /* All good -- do it */
1906 alvherre 19751 CBC 141 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
1875 19752 141 : if (OidIsValid(constraintOid))
1518 tgl 19753 58 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
1518 tgl 19754 ECB : RelationGetRelid(partTbl));
19755 :
1208 michael 19756 GIC 141 : free_attrmap(attmap);
1906 alvherre 19757 ECB :
1906 alvherre 19758 GIC 141 : validatePartitionedIndex(parentIdx, parentTbl);
1906 alvherre 19759 ECB : }
19760 :
1906 alvherre 19761 CBC 147 : relation_close(parentTbl, AccessShareLock);
19762 : /* keep these locks till commit */
1906 alvherre 19763 GIC 147 : relation_close(partTbl, NoLock);
1906 alvherre 19764 CBC 147 : relation_close(partIdx, NoLock);
1906 alvherre 19765 ECB :
1906 alvherre 19766 GIC 147 : return address;
1906 alvherre 19767 ECB : }
19768 :
19769 : /*
19770 : * Verify whether the given partition already contains an index attached
19771 : * to the given partitioned index. If so, raise an error.
19772 : */
19773 : static void
1906 alvherre 19774 GIC 174 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
19775 : {
19776 : Oid existingIdx;
19777 :
1481 alvherre 19778 CBC 174 : existingIdx = index_get_partition(partitionTbl,
1481 alvherre 19779 ECB : RelationGetRelid(parentIdx));
1481 alvherre 19780 GIC 174 : if (OidIsValid(existingIdx))
19781 3 : ereport(ERROR,
19782 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19783 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19784 : RelationGetRelationName(partIdx),
1481 alvherre 19785 ECB : RelationGetRelationName(parentIdx)),
19786 : errdetail("Another index is already attached for partition \"%s\".",
19787 : RelationGetRelationName(partitionTbl))));
1906 alvherre 19788 GIC 171 : }
1906 alvherre 19789 ECB :
19790 : /*
1906 alvherre 19791 EUB : * Verify whether the set of attached partition indexes to a parent index on
19792 : * a partitioned table is complete. If it is, mark the parent index valid.
19793 : *
19794 : * This should be called each time a partition index is attached.
19795 : */
19796 : static void
1906 alvherre 19797 GIC 159 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
19798 : {
1809 tgl 19799 ECB : Relation inheritsRel;
1809 tgl 19800 EUB : SysScanDesc scan;
19801 : ScanKeyData key;
1809 tgl 19802 GIC 159 : int tuples = 0;
19803 : HeapTuple inhTup;
1809 tgl 19804 CBC 159 : bool updated = false;
19805 :
1906 alvherre 19806 GIC 159 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
19807 :
1906 alvherre 19808 ECB : /*
19809 : * Scan pg_inherits for this parent index. Count each valid index we find
19810 : * (verifying the pg_index entry for each), and if we reach the total
19811 : * amount we expect, we can mark this parent index as valid.
19812 : */
1539 andres 19813 CBC 159 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
1906 alvherre 19814 GIC 159 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
1906 alvherre 19815 ECB : BTEqualStrategyNumber, F_OIDEQ,
19816 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
1906 alvherre 19817 GIC 159 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
19818 : NULL, 1, &key);
19819 420 : while ((inhTup = systable_getnext(scan)) != NULL)
19820 : {
19821 261 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
19822 : HeapTuple indTup;
19823 : Form_pg_index indexForm;
19824 :
1906 alvherre 19825 CBC 261 : indTup = SearchSysCache1(INDEXRELID,
19826 : ObjectIdGetDatum(inhForm->inhrelid));
1435 tgl 19827 GIC 261 : if (!HeapTupleIsValid(indTup))
1435 tgl 19828 UIC 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
1906 alvherre 19829 GIC 261 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
1564 peter_e 19830 261 : if (indexForm->indisvalid)
1906 alvherre 19831 235 : tuples += 1;
19832 261 : ReleaseSysCache(indTup);
19833 : }
19834 :
19835 : /* Done with pg_inherits */
19836 159 : systable_endscan(scan);
1539 andres 19837 CBC 159 : table_close(inheritsRel, AccessShareLock);
19838 :
1906 alvherre 19839 ECB : /*
19840 : * If we found as many inherited indexes as the partitioned table has
19841 : * partitions, we're good; update pg_index to set indisvalid.
19842 : */
717 alvherre 19843 GIC 159 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
19844 : {
1906 alvherre 19845 ECB : Relation idxRel;
19846 : HeapTuple newtup;
19847 :
1539 andres 19848 GIC 77 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
1906 alvherre 19849 ECB :
1906 alvherre 19850 GIC 77 : newtup = heap_copytuple(partedIdx->rd_indextuple);
19851 77 : ((Form_pg_index) GETSTRUCT(newtup))->indisvalid = true;
19852 77 : updated = true;
19853 :
19854 77 : CatalogTupleUpdate(idxRel, &partedIdx->rd_indextuple->t_self, newtup);
1906 alvherre 19855 ECB :
1539 andres 19856 CBC 77 : table_close(idxRel, RowExclusiveLock);
1906 alvherre 19857 ECB : }
19858 :
19859 : /*
19860 : * If this index is in turn a partition of a larger index, validating it
19861 : * might cause the parent to become valid also. Try that.
19862 : */
1824 alvherre 19863 GIC 159 : if (updated && partedIdx->rd_rel->relispartition)
19864 : {
19865 : Oid parentIdxId,
19866 : parentTblId;
1906 alvherre 19867 ECB : Relation parentIdx,
19868 : parentTbl;
1906 alvherre 19869 EUB :
1906 alvherre 19870 ECB : /* make sure we see the validation we just did */
1906 alvherre 19871 GIC 18 : CommandCounterIncrement();
19872 :
745 alvherre 19873 CBC 18 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
19874 18 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
1906 alvherre 19875 GIC 18 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
1906 alvherre 19876 CBC 18 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
19877 18 : Assert(!parentIdx->rd_index->indisvalid);
19878 :
1906 alvherre 19879 GIC 18 : validatePartitionedIndex(parentIdx, parentTbl);
19880 :
1906 alvherre 19881 CBC 18 : relation_close(parentIdx, AccessExclusiveLock);
1906 alvherre 19882 GIC 18 : relation_close(parentTbl, AccessExclusiveLock);
19883 : }
19884 159 : }
19885 :
19886 : /*
19887 : * When attaching an index as a partition of a partitioned index which is a
19888 : * primary key, verify that all the columns in the partition are marked NOT
19889 : * NULL.
19890 : */
19891 : static void
2 alvherre 19892 GNC 49 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
19893 : {
19894 96 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
19895 : {
19896 50 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
19897 : iinfo->ii_IndexAttrNumbers[i] - 1);
19898 :
19899 50 : if (!att->attnotnull)
19900 3 : ereport(ERROR,
19901 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
19902 : errmsg("invalid primary key definition"),
19903 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
19904 : NameStr(att->attname),
19905 : RelationGetRelationName(partition)));
19906 : }
19907 46 : }
19908 :
19909 : /*
1467 alvherre 19910 ECB : * Return an OID list of constraints that reference the given relation
19911 : * that are marked as having a parent constraints.
19912 : */
19913 : static List *
1467 alvherre 19914 CBC 425 : GetParentedForeignKeyRefs(Relation partition)
19915 : {
1467 alvherre 19916 ECB : Relation pg_constraint;
19917 : HeapTuple tuple;
19918 : SysScanDesc scan;
19919 : ScanKeyData key[2];
1467 alvherre 19920 GIC 425 : List *constraints = NIL;
19921 :
19922 : /*
19923 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
19924 : * scan.
1467 alvherre 19925 ECB : */
1467 alvherre 19926 CBC 591 : if (RelationGetIndexList(partition) == NIL ||
19927 166 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
1467 alvherre 19928 ECB : INDEX_ATTR_BITMAP_KEY)))
1467 alvherre 19929 CBC 337 : return NIL;
1467 alvherre 19930 ECB :
19931 : /* Search for constraints referencing this table */
1467 alvherre 19932 CBC 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
19933 88 : ScanKeyInit(&key[0],
1467 alvherre 19934 ECB : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
19935 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
1467 alvherre 19936 CBC 88 : ScanKeyInit(&key[1],
1467 alvherre 19937 ECB : Anum_pg_constraint_contype, BTEqualStrategyNumber,
19938 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
19939 :
19940 : /* XXX This is a seqscan, as we don't have a usable index */
1467 alvherre 19941 CBC 88 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
1467 alvherre 19942 GIC 150 : while ((tuple = systable_getnext(scan)) != NULL)
1467 alvherre 19943 ECB : {
1467 alvherre 19944 GIC 62 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19945 :
19946 : /*
19947 : * We only need to process constraints that are part of larger ones.
19948 : */
1467 alvherre 19949 CBC 62 : if (!OidIsValid(constrForm->conparentid))
1467 alvherre 19950 UIC 0 : continue;
1467 alvherre 19951 ECB :
1467 alvherre 19952 CBC 62 : constraints = lappend_oid(constraints, constrForm->oid);
1467 alvherre 19953 ECB : }
19954 :
1467 alvherre 19955 GIC 88 : systable_endscan(scan);
19956 88 : table_close(pg_constraint, AccessShareLock);
19957 :
19958 88 : return constraints;
19959 : }
1467 alvherre 19960 ECB :
19961 : /*
19962 : * During DETACH PARTITION, verify that any foreign keys pointing to the
19963 : * partitioned table would not become invalid. An error is raised if any
19964 : * referenced values exist.
19965 : */
19966 : static void
1467 alvherre 19967 GIC 230 : ATDetachCheckNoForeignKeyRefs(Relation partition)
19968 : {
19969 : List *constraints;
1467 alvherre 19970 ECB : ListCell *cell;
19971 :
1467 alvherre 19972 CBC 230 : constraints = GetParentedForeignKeyRefs(partition);
1467 alvherre 19973 ECB :
1467 alvherre 19974 GIC 256 : foreach(cell, constraints)
19975 : {
19976 43 : Oid constrOid = lfirst_oid(cell);
1467 alvherre 19977 ECB : HeapTuple tuple;
19978 : Form_pg_constraint constrForm;
19979 : Relation rel;
267 peter 19980 GNC 43 : Trigger trig = {0};
19981 :
1467 alvherre 19982 GIC 43 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
19983 43 : if (!HeapTupleIsValid(tuple))
1467 alvherre 19984 LBC 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
1467 alvherre 19985 CBC 43 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19986 :
19987 43 : Assert(OidIsValid(constrForm->conparentid));
1467 alvherre 19988 GIC 43 : Assert(constrForm->confrelid == RelationGetRelid(partition));
19989 :
1467 alvherre 19990 ECB : /* prevent data changes into the referencing table until commit */
1467 alvherre 19991 CBC 43 : rel = table_open(constrForm->conrelid, ShareLock);
19992 :
19993 43 : trig.tgoid = InvalidOid;
1467 alvherre 19994 GIC 43 : trig.tgname = NameStr(constrForm->conname);
1467 alvherre 19995 CBC 43 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
19996 43 : trig.tgisinternal = true;
1467 alvherre 19997 GIC 43 : trig.tgconstrrelid = RelationGetRelid(partition);
1467 alvherre 19998 CBC 43 : trig.tgconstrindid = constrForm->conindid;
1467 alvherre 19999 GIC 43 : trig.tgconstraint = constrForm->oid;
20000 43 : trig.tgdeferrable = false;
20001 43 : trig.tginitdeferred = false;
1467 alvherre 20002 ECB : /* we needn't fill in remaining fields */
20003 :
1467 alvherre 20004 GIC 43 : RI_PartitionRemove_Check(&trig, rel, partition);
1467 alvherre 20005 ECB :
1467 alvherre 20006 GBC 26 : ReleaseSysCache(tuple);
20007 :
1467 alvherre 20008 CBC 26 : table_close(rel, NoLock);
20009 : }
1467 alvherre 20010 GIC 213 : }
888 tmunro 20011 ECB :
751 rhaas 20012 : /*
20013 : * resolve column compression specification to compression method.
20014 : */
20015 : static char
682 tgl 20016 CBC 1195 : GetAttributeCompression(Oid atttypid, char *compression)
751 rhaas 20017 ECB : {
20018 : char cmethod;
20019 :
682 tgl 20020 CBC 1195 : if (compression == NULL || strcmp(compression, "default") == 0)
20021 1124 : return InvalidCompressionMethod;
682 tgl 20022 ECB :
751 rhaas 20023 : /*
20024 : * To specify a nondefault method, the column data type must be toastable.
682 tgl 20025 : * Note this says nothing about whether the column's attstorage setting
20026 : * permits compression; we intentionally allow attstorage and
20027 : * attcompression to be independent. But with a non-toastable type,
20028 : * attstorage could not be set to a value that would permit compression.
20029 : *
20030 : * We don't actually need to enforce this, since nothing bad would happen
20031 : * if attcompression were non-default; it would never be consulted. But
20032 : * it seems more user-friendly to complain about a certainly-useless
20033 : * attempt to set the property.
20034 : */
682 tgl 20035 CBC 71 : if (!TypeIsToastable(atttypid))
751 rhaas 20036 3 : ereport(ERROR,
20037 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
751 rhaas 20038 ECB : errmsg("column data type %s does not support compression",
20039 : format_type_be(atttypid))));
20040 :
748 rhaas 20041 GIC 68 : cmethod = CompressionNameToMethod(compression);
20042 68 : if (!CompressionMethodIsValid(cmethod))
20043 6 : ereport(ERROR,
20044 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
748 rhaas 20045 ECB : errmsg("invalid compression method \"%s\"", compression)));
20046 :
751 rhaas 20047 GIC 62 : return cmethod;
20048 : }
20049 :
20050 : /*
20051 : * resolve column storage specification
20052 : */
20053 : static char
270 peter 20054 GNC 115 : GetAttributeStorage(Oid atttypid, const char *storagemode)
20055 : {
20056 115 : char cstorage = 0;
20057 :
20058 115 : if (pg_strcasecmp(storagemode, "plain") == 0)
20059 23 : cstorage = TYPSTORAGE_PLAIN;
20060 92 : else if (pg_strcasecmp(storagemode, "external") == 0)
20061 75 : cstorage = TYPSTORAGE_EXTERNAL;
20062 17 : else if (pg_strcasecmp(storagemode, "extended") == 0)
20063 7 : cstorage = TYPSTORAGE_EXTENDED;
20064 10 : else if (pg_strcasecmp(storagemode, "main") == 0)
20065 7 : cstorage = TYPSTORAGE_MAIN;
150 tgl 20066 3 : else if (pg_strcasecmp(storagemode, "default") == 0)
20067 3 : cstorage = get_typstorage(atttypid);
20068 : else
270 peter 20069 UNC 0 : ereport(ERROR,
20070 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20071 : errmsg("invalid storage type \"%s\"",
20072 : storagemode)));
20073 :
20074 : /*
20075 : * safety check: do not allow toasted storage modes unless column datatype
20076 : * is TOAST-aware.
20077 : */
270 peter 20078 GNC 115 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20079 3 : ereport(ERROR,
20080 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20081 : errmsg("column data type %s can only have storage PLAIN",
20082 : format_type_be(atttypid))));
20083 :
20084 112 : return cstorage;
20085 : }
|