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-2024, 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/gist.h"
20 : #include "access/heapam.h"
21 : #include "access/heapam_xlog.h"
22 : #include "access/multixact.h"
23 : #include "access/reloptions.h"
24 : #include "access/relscan.h"
25 : #include "access/sysattr.h"
26 : #include "access/tableam.h"
27 : #include "access/toast_compression.h"
28 : #include "access/xact.h"
29 : #include "access/xlog.h"
30 : #include "access/xloginsert.h"
31 : #include "catalog/catalog.h"
32 : #include "catalog/heap.h"
33 : #include "catalog/index.h"
34 : #include "catalog/namespace.h"
35 : #include "catalog/objectaccess.h"
36 : #include "catalog/partition.h"
37 : #include "catalog/pg_am.h"
38 : #include "catalog/pg_attrdef.h"
39 : #include "catalog/pg_collation.h"
40 : #include "catalog/pg_constraint.h"
41 : #include "catalog/pg_depend.h"
42 : #include "catalog/pg_foreign_table.h"
43 : #include "catalog/pg_inherits.h"
44 : #include "catalog/pg_largeobject.h"
45 : #include "catalog/pg_namespace.h"
46 : #include "catalog/pg_opclass.h"
47 : #include "catalog/pg_policy.h"
48 : #include "catalog/pg_proc.h"
49 : #include "catalog/pg_publication_rel.h"
50 : #include "catalog/pg_rewrite.h"
51 : #include "catalog/pg_statistic_ext.h"
52 : #include "catalog/pg_tablespace.h"
53 : #include "catalog/pg_trigger.h"
54 : #include "catalog/pg_type.h"
55 : #include "catalog/storage.h"
56 : #include "catalog/storage_xlog.h"
57 : #include "catalog/toasting.h"
58 : #include "commands/cluster.h"
59 : #include "commands/comment.h"
60 : #include "commands/defrem.h"
61 : #include "commands/event_trigger.h"
62 : #include "commands/sequence.h"
63 : #include "commands/tablecmds.h"
64 : #include "commands/tablespace.h"
65 : #include "commands/trigger.h"
66 : #include "commands/typecmds.h"
67 : #include "commands/user.h"
68 : #include "commands/vacuum.h"
69 : #include "executor/executor.h"
70 : #include "foreign/fdwapi.h"
71 : #include "foreign/foreign.h"
72 : #include "miscadmin.h"
73 : #include "nodes/makefuncs.h"
74 : #include "nodes/nodeFuncs.h"
75 : #include "nodes/parsenodes.h"
76 : #include "optimizer/optimizer.h"
77 : #include "parser/parse_coerce.h"
78 : #include "parser/parse_collate.h"
79 : #include "parser/parse_expr.h"
80 : #include "parser/parse_relation.h"
81 : #include "parser/parse_type.h"
82 : #include "parser/parse_utilcmd.h"
83 : #include "parser/parser.h"
84 : #include "partitioning/partbounds.h"
85 : #include "partitioning/partdesc.h"
86 : #include "pgstat.h"
87 : #include "rewrite/rewriteDefine.h"
88 : #include "rewrite/rewriteHandler.h"
89 : #include "rewrite/rewriteManip.h"
90 : #include "storage/bufmgr.h"
91 : #include "storage/lmgr.h"
92 : #include "storage/lock.h"
93 : #include "storage/predicate.h"
94 : #include "storage/smgr.h"
95 : #include "tcop/utility.h"
96 : #include "utils/acl.h"
97 : #include "utils/builtins.h"
98 : #include "utils/fmgroids.h"
99 : #include "utils/inval.h"
100 : #include "utils/lsyscache.h"
101 : #include "utils/memutils.h"
102 : #include "utils/partcache.h"
103 : #include "utils/relcache.h"
104 : #include "utils/ruleutils.h"
105 : #include "utils/snapmgr.h"
106 : #include "utils/syscache.h"
107 : #include "utils/timestamp.h"
108 : #include "utils/typcache.h"
109 : #include "utils/usercontext.h"
110 :
111 : /*
112 : * ON COMMIT action list
113 : */
114 : typedef struct OnCommitItem
115 : {
116 : Oid relid; /* relid of relation */
117 : OnCommitAction oncommit; /* what to do at end of xact */
118 :
119 : /*
120 : * If this entry was created during the current transaction,
121 : * creating_subid is the ID of the creating subxact; if created in a prior
122 : * transaction, creating_subid is zero. If deleted during the current
123 : * transaction, deleting_subid is the ID of the deleting subxact; if no
124 : * deletion request is pending, deleting_subid is zero.
125 : */
126 : SubTransactionId creating_subid;
127 : SubTransactionId deleting_subid;
128 : } OnCommitItem;
129 :
130 : static List *on_commits = NIL;
131 :
132 :
133 : /*
134 : * State information for ALTER TABLE
135 : *
136 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
137 : * structs, one for each table modified by the operation (the named table
138 : * plus any child tables that are affected). We save lists of subcommands
139 : * to apply to this table (possibly modified by parse transformation steps);
140 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
141 : * necessary information is stored in the constraints and newvals lists.
142 : *
143 : * Phase 2 is divided into multiple passes; subcommands are executed in
144 : * a pass determined by subcommand type.
145 : */
146 :
147 : typedef enum AlterTablePass
148 : {
149 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
150 : AT_PASS_DROP, /* DROP (all flavors) */
151 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
152 : AT_PASS_ADD_COL, /* ADD COLUMN */
153 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
154 : AT_PASS_OLD_COL_ATTRS, /* re-install attnotnull */
155 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
156 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
157 : /* We could support a RENAME COLUMN pass here, but not currently used */
158 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
159 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
160 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
161 : AT_PASS_ADD_INDEX, /* ADD indexes */
162 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
163 : AT_PASS_MISC, /* other stuff */
164 : } AlterTablePass;
165 :
166 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
167 :
168 : typedef struct AlteredTableInfo
169 : {
170 : /* Information saved before any work commences: */
171 : Oid relid; /* Relation to work on */
172 : char relkind; /* Its relkind */
173 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
174 :
175 : /*
176 : * Transiently set during Phase 2, normally set to NULL.
177 : *
178 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
179 : * returns control. This can be exploited by ATExecCmd subroutines to
180 : * close/reopen across transaction boundaries.
181 : */
182 : Relation rel;
183 :
184 : /* Information saved by Phase 1 for Phase 2: */
185 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
186 : /* Information saved by Phases 1/2 for Phase 3: */
187 : List *constraints; /* List of NewConstraint */
188 : List *newvals; /* List of NewColumnValue */
189 : List *afterStmts; /* List of utility command parsetrees */
190 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
191 : int rewrite; /* Reason for forced rewrite, if any */
192 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
193 : Oid newAccessMethod; /* new access method; 0 means no change,
194 : * if above is true */
195 : Oid newTableSpace; /* new tablespace; 0 means no change */
196 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
197 : char newrelpersistence; /* if above is true */
198 : Expr *partition_constraint; /* for attach partition validation */
199 : /* true, if validating default due to some other attach/detach */
200 : bool validate_default;
201 : /* Objects to rebuild after completing ALTER TYPE operations */
202 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
203 : List *changedConstraintDefs; /* string definitions of same */
204 : List *changedIndexOids; /* OIDs of indexes to rebuild */
205 : List *changedIndexDefs; /* string definitions of same */
206 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
207 : char *clusterOnIndex; /* index to use for CLUSTER */
208 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
209 : List *changedStatisticsDefs; /* string definitions of same */
210 : } AlteredTableInfo;
211 :
212 : /* Struct describing one new constraint to check in Phase 3 scan */
213 : /* Note: new not-null constraints are handled elsewhere */
214 : typedef struct NewConstraint
215 : {
216 : char *name; /* Constraint name, or NULL if none */
217 : ConstrType contype; /* CHECK or FOREIGN */
218 : Oid refrelid; /* PK rel, if FOREIGN */
219 : Oid refindid; /* OID of PK's index, if FOREIGN */
220 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
221 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
222 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
223 : ExprState *qualstate; /* Execution state for CHECK expr */
224 : } NewConstraint;
225 :
226 : /*
227 : * Struct describing one new column value that needs to be computed during
228 : * Phase 3 copy (this could be either a new column with a non-null default, or
229 : * a column that we're changing the type of). Columns without such an entry
230 : * are just copied from the old table during ATRewriteTable. Note that the
231 : * expr is an expression over *old* table values, except when is_generated
232 : * is true; then it is an expression over columns of the *new* tuple.
233 : */
234 : typedef struct NewColumnValue
235 : {
236 : AttrNumber attnum; /* which column */
237 : Expr *expr; /* expression to compute */
238 : ExprState *exprstate; /* execution state */
239 : bool is_generated; /* is it a GENERATED expression? */
240 : } NewColumnValue;
241 :
242 : /*
243 : * Error-reporting support for RemoveRelations
244 : */
245 : struct dropmsgstrings
246 : {
247 : char kind;
248 : int nonexistent_code;
249 : const char *nonexistent_msg;
250 : const char *skipping_msg;
251 : const char *nota_msg;
252 : const char *drophint_msg;
253 : };
254 :
255 : static const struct dropmsgstrings dropmsgstringarray[] = {
256 : {RELKIND_RELATION,
257 : ERRCODE_UNDEFINED_TABLE,
258 : gettext_noop("table \"%s\" does not exist"),
259 : gettext_noop("table \"%s\" does not exist, skipping"),
260 : gettext_noop("\"%s\" is not a table"),
261 : gettext_noop("Use DROP TABLE to remove a table.")},
262 : {RELKIND_SEQUENCE,
263 : ERRCODE_UNDEFINED_TABLE,
264 : gettext_noop("sequence \"%s\" does not exist"),
265 : gettext_noop("sequence \"%s\" does not exist, skipping"),
266 : gettext_noop("\"%s\" is not a sequence"),
267 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
268 : {RELKIND_VIEW,
269 : ERRCODE_UNDEFINED_TABLE,
270 : gettext_noop("view \"%s\" does not exist"),
271 : gettext_noop("view \"%s\" does not exist, skipping"),
272 : gettext_noop("\"%s\" is not a view"),
273 : gettext_noop("Use DROP VIEW to remove a view.")},
274 : {RELKIND_MATVIEW,
275 : ERRCODE_UNDEFINED_TABLE,
276 : gettext_noop("materialized view \"%s\" does not exist"),
277 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
278 : gettext_noop("\"%s\" is not a materialized view"),
279 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
280 : {RELKIND_INDEX,
281 : ERRCODE_UNDEFINED_OBJECT,
282 : gettext_noop("index \"%s\" does not exist"),
283 : gettext_noop("index \"%s\" does not exist, skipping"),
284 : gettext_noop("\"%s\" is not an index"),
285 : gettext_noop("Use DROP INDEX to remove an index.")},
286 : {RELKIND_COMPOSITE_TYPE,
287 : ERRCODE_UNDEFINED_OBJECT,
288 : gettext_noop("type \"%s\" does not exist"),
289 : gettext_noop("type \"%s\" does not exist, skipping"),
290 : gettext_noop("\"%s\" is not a type"),
291 : gettext_noop("Use DROP TYPE to remove a type.")},
292 : {RELKIND_FOREIGN_TABLE,
293 : ERRCODE_UNDEFINED_OBJECT,
294 : gettext_noop("foreign table \"%s\" does not exist"),
295 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
296 : gettext_noop("\"%s\" is not a foreign table"),
297 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
298 : {RELKIND_PARTITIONED_TABLE,
299 : ERRCODE_UNDEFINED_TABLE,
300 : gettext_noop("table \"%s\" does not exist"),
301 : gettext_noop("table \"%s\" does not exist, skipping"),
302 : gettext_noop("\"%s\" is not a table"),
303 : gettext_noop("Use DROP TABLE to remove a table.")},
304 : {RELKIND_PARTITIONED_INDEX,
305 : ERRCODE_UNDEFINED_OBJECT,
306 : gettext_noop("index \"%s\" does not exist"),
307 : gettext_noop("index \"%s\" does not exist, skipping"),
308 : gettext_noop("\"%s\" is not an index"),
309 : gettext_noop("Use DROP INDEX to remove an index.")},
310 : {'\0', 0, NULL, NULL, NULL, NULL}
311 : };
312 :
313 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
314 : struct DropRelationCallbackState
315 : {
316 : /* These fields are set by RemoveRelations: */
317 : char expected_relkind;
318 : LOCKMODE heap_lockmode;
319 : /* These fields are state to track which subsidiary locks are held: */
320 : Oid heapOid;
321 : Oid partParentOid;
322 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
323 : char actual_relkind;
324 : char actual_relpersistence;
325 : };
326 :
327 : /* Alter table target-type flags for ATSimplePermissions */
328 : #define ATT_TABLE 0x0001
329 : #define ATT_VIEW 0x0002
330 : #define ATT_MATVIEW 0x0004
331 : #define ATT_INDEX 0x0008
332 : #define ATT_COMPOSITE_TYPE 0x0010
333 : #define ATT_FOREIGN_TABLE 0x0020
334 : #define ATT_PARTITIONED_INDEX 0x0040
335 : #define ATT_SEQUENCE 0x0080
336 :
337 : /*
338 : * ForeignTruncateInfo
339 : *
340 : * Information related to truncation of foreign tables. This is used for
341 : * the elements in a hash table. It uses the server OID as lookup key,
342 : * and includes a per-server list of all foreign tables involved in the
343 : * truncation.
344 : */
345 : typedef struct ForeignTruncateInfo
346 : {
347 : Oid serverid;
348 : List *rels;
349 : } ForeignTruncateInfo;
350 :
351 : /*
352 : * Partition tables are expected to be dropped when the parent partitioned
353 : * table gets dropped. Hence for partitioning we use AUTO dependency.
354 : * Otherwise, for regular inheritance use NORMAL dependency.
355 : */
356 : #define child_dependency_type(child_is_partition) \
357 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
358 :
359 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
360 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
361 : static void truncate_check_activity(Relation rel);
362 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
363 : Oid relId, Oid oldRelId, void *arg);
364 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
365 : bool is_partition, List **supconstr,
366 : List **supnotnulls);
367 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
368 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
369 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
370 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
371 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
372 : static void StoreCatalogInheritance(Oid relationId, List *supers,
373 : bool child_is_partition);
374 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
375 : int32 seqNumber, Relation inhRelation,
376 : bool child_is_partition);
377 : static int findAttrByName(const char *attributeName, const List *columns);
378 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
379 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
380 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
381 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
382 : LOCKMODE lockmode);
383 : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
384 : bool recurse, bool recursing, LOCKMODE lockmode);
385 : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
386 : Relation rel, HeapTuple contuple, List **otherrelids,
387 : LOCKMODE lockmode);
388 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
389 : Relation rel, char *constrName,
390 : bool recurse, bool recursing, LOCKMODE lockmode);
391 : static int transformColumnNameList(Oid relId, List *colList,
392 : int16 *attnums, Oid *atttypids);
393 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
394 : List **attnamelist,
395 : int16 *attnums, Oid *atttypids,
396 : Oid *opclasses, bool *pk_has_without_overlaps);
397 : static Oid transformFkeyCheckAttrs(Relation pkrel,
398 : int numattrs, int16 *attnums,
399 : bool with_period, Oid *opclasses,
400 : bool *pk_has_without_overlaps);
401 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
402 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
403 : Oid *funcid);
404 : static void validateForeignKeyConstraint(char *conname,
405 : Relation rel, Relation pkrel,
406 : Oid pkindOid, Oid constraintOid, bool hasperiod);
407 : static void ATController(AlterTableStmt *parsetree,
408 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
409 : AlterTableUtilityContext *context);
410 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
411 : bool recurse, bool recursing, LOCKMODE lockmode,
412 : AlterTableUtilityContext *context);
413 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
414 : AlterTableUtilityContext *context);
415 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
416 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
417 : AlterTableUtilityContext *context);
418 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
419 : Relation rel, AlterTableCmd *cmd,
420 : bool recurse, LOCKMODE lockmode,
421 : AlterTablePass cur_pass,
422 : AlterTableUtilityContext *context);
423 : static void ATRewriteTables(AlterTableStmt *parsetree,
424 : List **wqueue, LOCKMODE lockmode,
425 : AlterTableUtilityContext *context);
426 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
427 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
428 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
429 : static void ATSimpleRecursion(List **wqueue, Relation rel,
430 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
431 : AlterTableUtilityContext *context);
432 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
433 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
434 : LOCKMODE lockmode,
435 : AlterTableUtilityContext *context);
436 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
437 : DropBehavior behavior);
438 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
439 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
440 : AlterTableUtilityContext *context);
441 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
442 : Relation rel, AlterTableCmd **cmd,
443 : bool recurse, bool recursing,
444 : LOCKMODE lockmode, AlterTablePass cur_pass,
445 : AlterTableUtilityContext *context);
446 : static bool check_for_column_name_collision(Relation rel, const char *colname,
447 : bool if_not_exists);
448 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
449 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
450 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
451 : LOCKMODE lockmode);
452 : static bool set_attnotnull(List **wqueue, Relation rel,
453 : AttrNumber attnum, bool recurse, LOCKMODE lockmode);
454 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
455 : char *constrname, char *colName,
456 : bool recurse, bool recursing,
457 : List **readyRels, LOCKMODE lockmode);
458 : static ObjectAddress ATExecSetAttNotNull(List **wqueue, Relation rel,
459 : const char *colName, LOCKMODE lockmode);
460 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
461 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
462 : List *testConstraint, List *provenConstraint);
463 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
464 : Node *newDefault, LOCKMODE lockmode);
465 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
466 : Node *newDefault);
467 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
468 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
469 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
470 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
471 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
472 : bool recurse, bool recursing);
473 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
474 : Node *newExpr, LOCKMODE lockmode);
475 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
476 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
477 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
478 : Node *newValue, LOCKMODE lockmode);
479 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
480 : Node *options, bool isReset, LOCKMODE lockmode);
481 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
482 : Node *newValue, LOCKMODE lockmode);
483 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
484 : AlterTableCmd *cmd, LOCKMODE lockmode,
485 : AlterTableUtilityContext *context);
486 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
487 : DropBehavior behavior,
488 : bool recurse, bool recursing,
489 : bool missing_ok, LOCKMODE lockmode,
490 : ObjectAddresses *addrs);
491 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
492 : LOCKMODE lockmode, AlterTableUtilityContext *context);
493 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
494 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
495 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
496 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
497 : static ObjectAddress ATExecAddConstraint(List **wqueue,
498 : AlteredTableInfo *tab, Relation rel,
499 : Constraint *newConstraint, bool recurse, bool is_readd,
500 : LOCKMODE lockmode);
501 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
502 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
503 : IndexStmt *stmt, LOCKMODE lockmode);
504 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
505 : AlteredTableInfo *tab, Relation rel,
506 : Constraint *constr,
507 : bool recurse, bool recursing, bool is_readd,
508 : LOCKMODE lockmode);
509 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
510 : Relation rel, Constraint *fkconstraint,
511 : bool recurse, bool recursing,
512 : LOCKMODE lockmode);
513 : static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
514 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
515 : int numfks, int16 *pkattnum, int16 *fkattnum,
516 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
517 : int numfkdelsetcols, int16 *fkdelsetcols,
518 : bool old_check_ok,
519 : Oid parentDelTrigger, Oid parentUpdTrigger,
520 : bool with_period);
521 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
522 : int numfksetcols, const int16 *fksetcolsattnums,
523 : List *fksetcols);
524 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
525 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
526 : int numfks, int16 *pkattnum, int16 *fkattnum,
527 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
528 : int numfkdelsetcols, int16 *fkdelsetcols,
529 : bool old_check_ok, LOCKMODE lockmode,
530 : Oid parentInsTrigger, Oid parentUpdTrigger,
531 : bool with_period);
532 :
533 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
534 : Relation partitionRel);
535 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
536 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
537 : Relation partRel);
538 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
539 : Constraint *fkconstraint, Oid constraintOid,
540 : Oid indexOid,
541 : Oid parentInsTrigger, Oid parentUpdTrigger,
542 : Oid *insertTrigOid, Oid *updateTrigOid);
543 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
544 : Constraint *fkconstraint, Oid constraintOid,
545 : Oid indexOid,
546 : Oid parentDelTrigger, Oid parentUpdTrigger,
547 : Oid *deleteTrigOid, Oid *updateTrigOid);
548 : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
549 : Oid partRelid,
550 : Oid parentConstrOid, int numfks,
551 : AttrNumber *mapped_conkey, AttrNumber *confkey,
552 : Oid *conpfeqop,
553 : Oid parentInsTrigger,
554 : Oid parentUpdTrigger,
555 : Relation trigrel);
556 : static void GetForeignKeyActionTriggers(Relation trigrel,
557 : Oid conoid, Oid confrelid, Oid conrelid,
558 : Oid *deleteTriggerOid,
559 : Oid *updateTriggerOid);
560 : static void GetForeignKeyCheckTriggers(Relation trigrel,
561 : Oid conoid, Oid confrelid, Oid conrelid,
562 : Oid *insertTriggerOid,
563 : Oid *updateTriggerOid);
564 : static void ATExecDropConstraint(Relation rel, const char *constrName,
565 : DropBehavior behavior, bool recurse,
566 : bool missing_ok, LOCKMODE lockmode);
567 : static ObjectAddress dropconstraint_internal(Relation rel,
568 : HeapTuple constraintTup, DropBehavior behavior,
569 : bool recurse, bool recursing,
570 : bool missing_ok, List **readyRels,
571 : LOCKMODE lockmode);
572 : static void ATPrepAlterColumnType(List **wqueue,
573 : AlteredTableInfo *tab, Relation rel,
574 : bool recurse, bool recursing,
575 : AlterTableCmd *cmd, LOCKMODE lockmode,
576 : AlterTableUtilityContext *context);
577 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
578 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
579 : AlterTableCmd *cmd, LOCKMODE lockmode);
580 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
581 : Relation rel, AttrNumber attnum, const char *colName);
582 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
583 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
584 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
585 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
586 : LOCKMODE lockmode);
587 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
588 : char *cmd, List **wqueue, LOCKMODE lockmode,
589 : bool rewrite);
590 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
591 : Oid objid, Relation rel, List *domname,
592 : const char *conname);
593 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
594 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
595 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
596 : List *options, LOCKMODE lockmode);
597 : static void change_owner_fix_column_acls(Oid relationOid,
598 : Oid oldOwnerId, Oid newOwnerId);
599 : static void change_owner_recurse_to_sequences(Oid relationOid,
600 : Oid newOwnerId, LOCKMODE lockmode);
601 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
602 : LOCKMODE lockmode);
603 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
604 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
605 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethod);
606 : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
607 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
608 : const char *tablespacename, LOCKMODE lockmode);
609 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
610 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
611 : static void ATExecSetRelOptions(Relation rel, List *defList,
612 : AlterTableType operation,
613 : LOCKMODE lockmode);
614 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
615 : char fires_when, bool skip_system, bool recurse,
616 : LOCKMODE lockmode);
617 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
618 : char fires_when, LOCKMODE lockmode);
619 : static void ATPrepAddInherit(Relation child_rel);
620 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
621 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
622 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
623 : DependencyType deptype);
624 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
625 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
626 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
627 : static void ATExecGenericOptions(Relation rel, List *options);
628 : static void ATExecSetRowSecurity(Relation rel, bool rls);
629 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
630 : static ObjectAddress ATExecSetCompression(Relation rel,
631 : const char *column, Node *newValue, LOCKMODE lockmode);
632 :
633 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
634 : static const char *storage_name(char c);
635 :
636 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
637 : Oid oldRelOid, void *arg);
638 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
639 : Oid oldrelid, void *arg);
640 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
641 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
642 : List **partexprs, Oid *partopclass, Oid *partcollation,
643 : PartitionStrategy strategy);
644 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
645 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
646 : bool expect_detached);
647 : static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
648 : int inhcount);
649 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
650 : PartitionCmd *cmd,
651 : AlterTableUtilityContext *context);
652 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
653 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
654 : List *partConstraint,
655 : bool validate_default);
656 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
657 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
658 : static void DropClonedTriggersFromPartition(Oid partitionId);
659 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
660 : Relation rel, RangeVar *name,
661 : bool concurrent);
662 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
663 : bool concurrent, Oid defaultPartOid);
664 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
665 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
666 : RangeVar *name);
667 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
668 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
669 : Relation partitionTbl);
670 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
671 : static List *GetParentedForeignKeyRefs(Relation partition);
672 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
673 : static char GetAttributeCompression(Oid atttypid, const char *compression);
674 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
675 :
676 : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
677 : Relation rel, PartitionCmd *cmd,
678 : AlterTableUtilityContext *context);
679 : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
680 : PartitionCmd *cmd, AlterTableUtilityContext *context);
681 :
682 : /* ----------------------------------------------------------------
683 : * DefineRelation
684 : * Creates a new relation.
685 : *
686 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
687 : * The other arguments are used to extend the behavior for other cases:
688 : * relkind: relkind to assign to the new relation
689 : * ownerId: if not InvalidOid, use this as the new relation's owner.
690 : * typaddress: if not null, it's set to the pg_type entry's address.
691 : * queryString: for error reporting
692 : *
693 : * Note that permissions checks are done against current user regardless of
694 : * ownerId. A nonzero ownerId is used when someone is creating a relation
695 : * "on behalf of" someone else, so we still want to see that the current user
696 : * has permissions to do it.
697 : *
698 : * If successful, returns the address of the new relation.
699 : * ----------------------------------------------------------------
700 : */
701 : ObjectAddress
702 53398 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
703 : ObjectAddress *typaddress, const char *queryString)
704 : {
705 : char relname[NAMEDATALEN];
706 : Oid namespaceId;
707 : Oid relationId;
708 : Oid tablespaceId;
709 : Relation rel;
710 : TupleDesc descriptor;
711 : List *inheritOids;
712 : List *old_constraints;
713 : List *old_notnulls;
714 : List *rawDefaults;
715 : List *cookedDefaults;
716 : List *nncols;
717 : Datum reloptions;
718 : ListCell *listptr;
719 : AttrNumber attnum;
720 : bool partitioned;
721 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
722 : Oid ofTypeId;
723 : ObjectAddress address;
724 : LOCKMODE parentLockmode;
725 53398 : Oid accessMethodId = InvalidOid;
726 :
727 : /*
728 : * Truncate relname to appropriate length (probably a waste of time, as
729 : * parser should have done this already).
730 : */
731 53398 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
732 :
733 : /*
734 : * Check consistency of arguments
735 : */
736 53398 : if (stmt->oncommit != ONCOMMIT_NOOP
737 178 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
738 12 : ereport(ERROR,
739 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
740 : errmsg("ON COMMIT can only be used on temporary tables")));
741 :
742 53386 : if (stmt->partspec != NULL)
743 : {
744 4924 : if (relkind != RELKIND_RELATION)
745 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
746 :
747 4924 : relkind = RELKIND_PARTITIONED_TABLE;
748 4924 : partitioned = true;
749 : }
750 : else
751 48462 : partitioned = false;
752 :
753 : /*
754 : * Look up the namespace in which we are supposed to create the relation,
755 : * check we have permission to create there, lock it against concurrent
756 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
757 : * namespace is selected.
758 : */
759 : namespaceId =
760 53386 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
761 :
762 : /*
763 : * Security check: disallow creating temp tables from security-restricted
764 : * code. This is needed because calling code might not expect untrusted
765 : * tables to appear in pg_temp at the front of its search path.
766 : */
767 53386 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
768 3044 : && InSecurityRestrictedOperation())
769 0 : ereport(ERROR,
770 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
771 : errmsg("cannot create temporary table within security-restricted operation")));
772 :
773 : /*
774 : * Determine the lockmode to use when scanning parents. A self-exclusive
775 : * lock is needed here.
776 : *
777 : * For regular inheritance, if two backends attempt to add children to the
778 : * same parent simultaneously, and that parent has no pre-existing
779 : * children, then both will attempt to update the parent's relhassubclass
780 : * field, leading to a "tuple concurrently updated" error. Also, this
781 : * interlocks against a concurrent ANALYZE on the parent table, which
782 : * might otherwise be attempting to clear the parent's relhassubclass
783 : * field, if its previous children were recently dropped.
784 : *
785 : * If the child table is a partition, then we instead grab an exclusive
786 : * lock on the parent because its partition descriptor will be changed by
787 : * addition of the new partition.
788 : */
789 53386 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
790 : ShareUpdateExclusiveLock);
791 :
792 : /* Determine the list of OIDs of the parents. */
793 53386 : inheritOids = NIL;
794 63870 : foreach(listptr, stmt->inhRelations)
795 : {
796 10484 : RangeVar *rv = (RangeVar *) lfirst(listptr);
797 : Oid parentOid;
798 :
799 10484 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
800 :
801 : /*
802 : * Reject duplications in the list of parents.
803 : */
804 10484 : if (list_member_oid(inheritOids, parentOid))
805 0 : ereport(ERROR,
806 : (errcode(ERRCODE_DUPLICATE_TABLE),
807 : errmsg("relation \"%s\" would be inherited from more than once",
808 : get_rel_name(parentOid))));
809 :
810 10484 : inheritOids = lappend_oid(inheritOids, parentOid);
811 : }
812 :
813 : /*
814 : * Select tablespace to use: an explicitly indicated one, or (in the case
815 : * of a partitioned table) the parent's, if it has one.
816 : */
817 53386 : if (stmt->tablespacename)
818 : {
819 116 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
820 :
821 110 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
822 6 : ereport(ERROR,
823 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
824 : errmsg("cannot specify default tablespace for partitioned relations")));
825 : }
826 53270 : else if (stmt->partbound)
827 : {
828 : Assert(list_length(inheritOids) == 1);
829 8340 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
830 : }
831 : else
832 44930 : tablespaceId = InvalidOid;
833 :
834 : /* still nothing? use the default */
835 53374 : if (!OidIsValid(tablespaceId))
836 53248 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
837 : partitioned);
838 :
839 : /* Check permissions except when using database's default */
840 53368 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
841 : {
842 : AclResult aclresult;
843 :
844 144 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
845 : ACL_CREATE);
846 144 : if (aclresult != ACLCHECK_OK)
847 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
848 6 : get_tablespace_name(tablespaceId));
849 : }
850 :
851 : /* In all cases disallow placing user relations in pg_global */
852 53362 : if (tablespaceId == GLOBALTABLESPACE_OID)
853 18 : ereport(ERROR,
854 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
855 : errmsg("only shared relations can be placed in pg_global tablespace")));
856 :
857 : /* Identify user ID that will own the table */
858 53344 : if (!OidIsValid(ownerId))
859 53112 : ownerId = GetUserId();
860 :
861 : /*
862 : * Parse and validate reloptions, if any.
863 : */
864 53344 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
865 : true, false);
866 :
867 53326 : switch (relkind)
868 : {
869 13370 : case RELKIND_VIEW:
870 13370 : (void) view_reloptions(reloptions, true);
871 13352 : break;
872 4906 : case RELKIND_PARTITIONED_TABLE:
873 4906 : (void) partitioned_table_reloptions(reloptions, true);
874 4900 : break;
875 35050 : default:
876 35050 : (void) heap_reloptions(relkind, reloptions, true);
877 : }
878 :
879 53206 : if (stmt->ofTypename)
880 : {
881 : AclResult aclresult;
882 :
883 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
884 :
885 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
886 86 : if (aclresult != ACLCHECK_OK)
887 6 : aclcheck_error_type(aclresult, ofTypeId);
888 : }
889 : else
890 53120 : ofTypeId = InvalidOid;
891 :
892 : /*
893 : * Look up inheritance ancestors and generate relation schema, including
894 : * inherited attributes. (Note that stmt->tableElts is destructively
895 : * modified by MergeAttributes.)
896 : */
897 53032 : stmt->tableElts =
898 53200 : MergeAttributes(stmt->tableElts, inheritOids,
899 53200 : stmt->relation->relpersistence,
900 53200 : stmt->partbound != NULL,
901 : &old_constraints, &old_notnulls);
902 :
903 : /*
904 : * Create a tuple descriptor from the relation schema. Note that this
905 : * deals with column names, types, and in-descriptor NOT NULL flags, but
906 : * not default values, NOT NULL or CHECK constraints; we handle those
907 : * below.
908 : */
909 53032 : descriptor = BuildDescForRelation(stmt->tableElts);
910 :
911 : /*
912 : * Find columns with default values and prepare for insertion of the
913 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
914 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
915 : * while raw defaults go into a list of RawColumnDefault structs that will
916 : * be processed by AddRelationNewConstraints. (We can't deal with raw
917 : * expressions until we can do transformExpr.)
918 : *
919 : * We can set the atthasdef flags now in the tuple descriptor; this just
920 : * saves StoreAttrDefault from having to do an immediate update of the
921 : * pg_attribute rows.
922 : */
923 52984 : rawDefaults = NIL;
924 52984 : cookedDefaults = NIL;
925 52984 : attnum = 0;
926 :
927 267654 : foreach(listptr, stmt->tableElts)
928 : {
929 214670 : ColumnDef *colDef = lfirst(listptr);
930 : Form_pg_attribute attr;
931 :
932 214670 : attnum++;
933 214670 : attr = TupleDescAttr(descriptor, attnum - 1);
934 :
935 214670 : if (colDef->raw_default != NULL)
936 : {
937 : RawColumnDefault *rawEnt;
938 :
939 : Assert(colDef->cooked_default == NULL);
940 :
941 2370 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
942 2370 : rawEnt->attnum = attnum;
943 2370 : rawEnt->raw_default = colDef->raw_default;
944 2370 : rawEnt->missingMode = false;
945 2370 : rawEnt->generated = colDef->generated;
946 2370 : rawDefaults = lappend(rawDefaults, rawEnt);
947 2370 : attr->atthasdef = true;
948 : }
949 212300 : else if (colDef->cooked_default != NULL)
950 : {
951 : CookedConstraint *cooked;
952 :
953 442 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
954 442 : cooked->contype = CONSTR_DEFAULT;
955 442 : cooked->conoid = InvalidOid; /* until created */
956 442 : cooked->name = NULL;
957 442 : cooked->attnum = attnum;
958 442 : cooked->expr = colDef->cooked_default;
959 442 : cooked->skip_validation = false;
960 442 : cooked->is_local = true; /* not used for defaults */
961 442 : cooked->inhcount = 0; /* ditto */
962 442 : cooked->is_no_inherit = false;
963 442 : cookedDefaults = lappend(cookedDefaults, cooked);
964 442 : attr->atthasdef = true;
965 : }
966 : }
967 :
968 : /*
969 : * For relations with table AM and partitioned tables, select access
970 : * method to use: an explicitly indicated one, or (in the case of a
971 : * partitioned table) the parent's, if it has one.
972 : */
973 52984 : if (stmt->accessMethod != NULL)
974 : {
975 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
976 146 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
977 : }
978 52838 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
979 : {
980 36726 : if (stmt->partbound)
981 : {
982 : Assert(list_length(inheritOids) == 1);
983 8182 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
984 : }
985 :
986 36726 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
987 31800 : accessMethodId = get_table_am_oid(default_table_access_method, false);
988 : }
989 :
990 : /*
991 : * Create the relation. Inherited defaults and constraints are passed in
992 : * for immediate handling --- since they don't need parsing, they can be
993 : * stored immediately.
994 : */
995 52966 : relationId = heap_create_with_catalog(relname,
996 : namespaceId,
997 : tablespaceId,
998 : InvalidOid,
999 : InvalidOid,
1000 : ofTypeId,
1001 : ownerId,
1002 : accessMethodId,
1003 : descriptor,
1004 : list_concat(cookedDefaults,
1005 : old_constraints),
1006 : relkind,
1007 52966 : stmt->relation->relpersistence,
1008 : false,
1009 : false,
1010 : stmt->oncommit,
1011 : reloptions,
1012 : true,
1013 : allowSystemTableMods,
1014 : false,
1015 : InvalidOid,
1016 : typaddress);
1017 :
1018 : /*
1019 : * We must bump the command counter to make the newly-created relation
1020 : * tuple visible for opening.
1021 : */
1022 52942 : CommandCounterIncrement();
1023 :
1024 : /*
1025 : * Open the new relation and acquire exclusive lock on it. This isn't
1026 : * really necessary for locking out other backends (since they can't see
1027 : * the new rel anyway until we commit), but it keeps the lock manager from
1028 : * complaining about deadlock risks.
1029 : */
1030 52942 : rel = relation_open(relationId, AccessExclusiveLock);
1031 :
1032 : /*
1033 : * Now add any newly specified column default and generation expressions
1034 : * to the new relation. These are passed to us in the form of raw
1035 : * parsetrees; we need to transform them to executable expression trees
1036 : * before they can be added. The most convenient way to do that is to
1037 : * apply the parser's transformExpr routine, but transformExpr doesn't
1038 : * work unless we have a pre-existing relation. So, the transformation has
1039 : * to be postponed to this final step of CREATE TABLE.
1040 : *
1041 : * This needs to be before processing the partitioning clauses because
1042 : * those could refer to generated columns.
1043 : */
1044 52942 : if (rawDefaults)
1045 2006 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1046 : true, true, false, queryString);
1047 :
1048 : /*
1049 : * Make column generation expressions visible for use by partitioning.
1050 : */
1051 52840 : CommandCounterIncrement();
1052 :
1053 : /* Process and store partition bound, if any. */
1054 52840 : if (stmt->partbound)
1055 : {
1056 : PartitionBoundSpec *bound;
1057 : ParseState *pstate;
1058 8286 : Oid parentId = linitial_oid(inheritOids),
1059 : defaultPartOid;
1060 : Relation parent,
1061 8286 : defaultRel = NULL;
1062 : ParseNamespaceItem *nsitem;
1063 :
1064 : /* Already have strong enough lock on the parent */
1065 8286 : parent = table_open(parentId, NoLock);
1066 :
1067 : /*
1068 : * We are going to try to validate the partition bound specification
1069 : * against the partition key of parentRel, so it better have one.
1070 : */
1071 8286 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1072 18 : ereport(ERROR,
1073 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1074 : errmsg("\"%s\" is not partitioned",
1075 : RelationGetRelationName(parent))));
1076 :
1077 : /*
1078 : * The partition constraint of the default partition depends on the
1079 : * partition bounds of every other partition. It is possible that
1080 : * another backend might be about to execute a query on the default
1081 : * partition table, and that the query relies on previously cached
1082 : * default partition constraints. We must therefore take a table lock
1083 : * strong enough to prevent all queries on the default partition from
1084 : * proceeding until we commit and send out a shared-cache-inval notice
1085 : * that will make them update their index lists.
1086 : *
1087 : * Order of locking: The relation being added won't be visible to
1088 : * other backends until it is committed, hence here in
1089 : * DefineRelation() the order of locking the default partition and the
1090 : * relation being added does not matter. But at all other places we
1091 : * need to lock the default relation before we lock the relation being
1092 : * added or removed i.e. we should take the lock in same order at all
1093 : * the places such that lock parent, lock default partition and then
1094 : * lock the partition so as to avoid a deadlock.
1095 : */
1096 : defaultPartOid =
1097 8268 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1098 : true));
1099 8268 : if (OidIsValid(defaultPartOid))
1100 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1101 :
1102 : /* Transform the bound values */
1103 8268 : pstate = make_parsestate(NULL);
1104 8268 : pstate->p_sourcetext = queryString;
1105 :
1106 : /*
1107 : * Add an nsitem containing this relation, so that transformExpr
1108 : * called on partition bound expressions is able to report errors
1109 : * using a proper context.
1110 : */
1111 8268 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1112 : NULL, false, false);
1113 8268 : addNSItemToQuery(pstate, nsitem, false, true, true);
1114 :
1115 8268 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1116 :
1117 : /*
1118 : * Check first that the new partition's bound is valid and does not
1119 : * overlap with any of existing partitions of the parent.
1120 : */
1121 8064 : check_new_partition_bound(relname, parent, bound, pstate);
1122 :
1123 : /*
1124 : * If the default partition exists, its partition constraints will
1125 : * change after the addition of this new partition such that it won't
1126 : * allow any row that qualifies for this new partition. So, check that
1127 : * the existing data in the default partition satisfies the constraint
1128 : * as it will exist after adding this partition.
1129 : */
1130 7950 : if (OidIsValid(defaultPartOid))
1131 : {
1132 348 : check_default_partition_contents(parent, defaultRel, bound);
1133 : /* Keep the lock until commit. */
1134 330 : table_close(defaultRel, NoLock);
1135 : }
1136 :
1137 : /* Update the pg_class entry. */
1138 7932 : StorePartitionBound(rel, parent, bound);
1139 :
1140 7932 : table_close(parent, NoLock);
1141 : }
1142 :
1143 : /* Store inheritance information for new rel. */
1144 52486 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1145 :
1146 : /*
1147 : * Process the partitioning specification (if any) and store the partition
1148 : * key information into the catalog.
1149 : */
1150 52486 : if (partitioned)
1151 : {
1152 : ParseState *pstate;
1153 : int partnatts;
1154 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1155 : Oid partopclass[PARTITION_MAX_KEYS];
1156 : Oid partcollation[PARTITION_MAX_KEYS];
1157 4900 : List *partexprs = NIL;
1158 :
1159 4900 : pstate = make_parsestate(NULL);
1160 4900 : pstate->p_sourcetext = queryString;
1161 :
1162 4900 : partnatts = list_length(stmt->partspec->partParams);
1163 :
1164 : /* Protect fixed-size arrays here and in executor */
1165 4900 : if (partnatts > PARTITION_MAX_KEYS)
1166 0 : ereport(ERROR,
1167 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1168 : errmsg("cannot partition using more than %d columns",
1169 : PARTITION_MAX_KEYS)));
1170 :
1171 : /*
1172 : * We need to transform the raw parsetrees corresponding to partition
1173 : * expressions into executable expression trees. Like column defaults
1174 : * and CHECK constraints, we could not have done the transformation
1175 : * earlier.
1176 : */
1177 4900 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1178 :
1179 4870 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1180 : partattrs, &partexprs, partopclass,
1181 4870 : partcollation, stmt->partspec->strategy);
1182 :
1183 4786 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1184 : partexprs,
1185 : partopclass, partcollation);
1186 :
1187 : /* make it all visible */
1188 4786 : CommandCounterIncrement();
1189 : }
1190 :
1191 : /*
1192 : * If we're creating a partition, create now all the indexes, triggers,
1193 : * FKs defined in the parent.
1194 : *
1195 : * We can't do it earlier, because DefineIndex wants to know the partition
1196 : * key which we just stored.
1197 : */
1198 52372 : if (stmt->partbound)
1199 : {
1200 7926 : Oid parentId = linitial_oid(inheritOids);
1201 : Relation parent;
1202 : List *idxlist;
1203 : ListCell *cell;
1204 :
1205 : /* Already have strong enough lock on the parent */
1206 7926 : parent = table_open(parentId, NoLock);
1207 7926 : idxlist = RelationGetIndexList(parent);
1208 :
1209 : /*
1210 : * For each index in the parent table, create one in the partition
1211 : */
1212 9366 : foreach(cell, idxlist)
1213 : {
1214 1458 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1215 : AttrMap *attmap;
1216 : IndexStmt *idxstmt;
1217 : Oid constraintOid;
1218 :
1219 1458 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1220 : {
1221 36 : if (idxRel->rd_index->indisunique)
1222 12 : ereport(ERROR,
1223 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1224 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1225 : RelationGetRelationName(parent)),
1226 : errdetail("Table \"%s\" contains indexes that are unique.",
1227 : RelationGetRelationName(parent))));
1228 : else
1229 : {
1230 24 : index_close(idxRel, AccessShareLock);
1231 24 : continue;
1232 : }
1233 : }
1234 :
1235 1422 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1236 : RelationGetDescr(parent),
1237 : false);
1238 : idxstmt =
1239 1422 : generateClonedIndexStmt(NULL, idxRel,
1240 : attmap, &constraintOid);
1241 1422 : DefineIndex(RelationGetRelid(rel),
1242 : idxstmt,
1243 : InvalidOid,
1244 : RelationGetRelid(idxRel),
1245 : constraintOid,
1246 : -1,
1247 : false, false, false, false, false);
1248 :
1249 1416 : index_close(idxRel, AccessShareLock);
1250 : }
1251 :
1252 7908 : list_free(idxlist);
1253 :
1254 : /*
1255 : * If there are any row-level triggers, clone them to the new
1256 : * partition.
1257 : */
1258 7908 : if (parent->trigdesc != NULL)
1259 426 : CloneRowTriggersToPartition(parent, rel);
1260 :
1261 : /*
1262 : * And foreign keys too. Note that because we're freshly creating the
1263 : * table, there is no need to verify these new constraints.
1264 : */
1265 7908 : CloneForeignKeyConstraints(NULL, parent, rel);
1266 :
1267 7908 : table_close(parent, NoLock);
1268 : }
1269 :
1270 : /*
1271 : * Now add any newly specified CHECK constraints to the new relation. Same
1272 : * as for defaults above, but these need to come after partitioning is set
1273 : * up.
1274 : */
1275 52354 : if (stmt->constraints)
1276 634 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1277 : true, true, false, queryString);
1278 :
1279 : /*
1280 : * Finally, merge the not-null constraints that are declared directly with
1281 : * those that come from parent relations (making sure to count inheritance
1282 : * appropriately for each), create them, and set the attnotnull flag on
1283 : * columns that don't yet have it.
1284 : */
1285 52336 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1286 : old_notnulls);
1287 59812 : foreach(listptr, nncols)
1288 7488 : set_attnotnull(NULL, rel, lfirst_int(listptr), false, NoLock);
1289 :
1290 52324 : ObjectAddressSet(address, RelationRelationId, relationId);
1291 :
1292 : /*
1293 : * Clean up. We keep lock on new relation (although it shouldn't be
1294 : * visible to anyone else anyway, until commit).
1295 : */
1296 52324 : relation_close(rel, NoLock);
1297 :
1298 52324 : return address;
1299 : }
1300 :
1301 : /*
1302 : * BuildDescForRelation
1303 : *
1304 : * Given a list of ColumnDef nodes, build a TupleDesc.
1305 : *
1306 : * Note: tdtypeid will need to be filled in later on.
1307 : */
1308 : TupleDesc
1309 55710 : BuildDescForRelation(const List *columns)
1310 : {
1311 : int natts;
1312 : AttrNumber attnum;
1313 : ListCell *l;
1314 : TupleDesc desc;
1315 : bool has_not_null;
1316 : char *attname;
1317 : Oid atttypid;
1318 : int32 atttypmod;
1319 : Oid attcollation;
1320 : int attdim;
1321 :
1322 : /*
1323 : * allocate a new tuple descriptor
1324 : */
1325 55710 : natts = list_length(columns);
1326 55710 : desc = CreateTemplateTupleDesc(natts);
1327 55710 : has_not_null = false;
1328 :
1329 55710 : attnum = 0;
1330 :
1331 273322 : foreach(l, columns)
1332 : {
1333 217672 : ColumnDef *entry = lfirst(l);
1334 : AclResult aclresult;
1335 : Form_pg_attribute att;
1336 :
1337 : /*
1338 : * for each entry in the list, get the name and type information from
1339 : * the list and have TupleDescInitEntry fill in the attribute
1340 : * information we need.
1341 : */
1342 217672 : attnum++;
1343 :
1344 217672 : attname = entry->colname;
1345 217672 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1346 :
1347 217672 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1348 217672 : if (aclresult != ACLCHECK_OK)
1349 42 : aclcheck_error_type(aclresult, atttypid);
1350 :
1351 217630 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1352 217630 : attdim = list_length(entry->typeName->arrayBounds);
1353 217630 : if (attdim > PG_INT16_MAX)
1354 0 : ereport(ERROR,
1355 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1356 : errmsg("too many array dimensions"));
1357 :
1358 217630 : if (entry->typeName->setof)
1359 0 : ereport(ERROR,
1360 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1361 : errmsg("column \"%s\" cannot be declared SETOF",
1362 : attname)));
1363 :
1364 217630 : TupleDescInitEntry(desc, attnum, attname,
1365 : atttypid, atttypmod, attdim);
1366 217630 : att = TupleDescAttr(desc, attnum - 1);
1367 :
1368 : /* Override TupleDescInitEntry's settings as requested */
1369 217630 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1370 :
1371 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1372 217630 : att->attnotnull = entry->is_not_null;
1373 217630 : has_not_null |= entry->is_not_null;
1374 217630 : att->attislocal = entry->is_local;
1375 217630 : att->attinhcount = entry->inhcount;
1376 217630 : att->attidentity = entry->identity;
1377 217630 : att->attgenerated = entry->generated;
1378 217630 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1379 217618 : if (entry->storage)
1380 22940 : att->attstorage = entry->storage;
1381 194678 : else if (entry->storage_name)
1382 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1383 : }
1384 :
1385 55650 : if (has_not_null)
1386 : {
1387 12058 : TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
1388 :
1389 12058 : constr->has_not_null = true;
1390 12058 : constr->has_generated_stored = false;
1391 12058 : constr->defval = NULL;
1392 12058 : constr->missing = NULL;
1393 12058 : constr->num_defval = 0;
1394 12058 : constr->check = NULL;
1395 12058 : constr->num_check = 0;
1396 12058 : desc->constr = constr;
1397 : }
1398 : else
1399 : {
1400 43592 : desc->constr = NULL;
1401 : }
1402 :
1403 55650 : return desc;
1404 : }
1405 :
1406 : /*
1407 : * Emit the right error or warning message for a "DROP" command issued on a
1408 : * non-existent relation
1409 : */
1410 : static void
1411 1080 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1412 : {
1413 : const struct dropmsgstrings *rentry;
1414 :
1415 1200 : if (rel->schemaname != NULL &&
1416 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1417 : {
1418 42 : if (!missing_ok)
1419 : {
1420 0 : ereport(ERROR,
1421 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1422 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1423 : }
1424 : else
1425 : {
1426 42 : ereport(NOTICE,
1427 : (errmsg("schema \"%s\" does not exist, skipping",
1428 : rel->schemaname)));
1429 : }
1430 42 : return;
1431 : }
1432 :
1433 1358 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1434 : {
1435 1358 : if (rentry->kind == rightkind)
1436 : {
1437 1038 : if (!missing_ok)
1438 : {
1439 132 : ereport(ERROR,
1440 : (errcode(rentry->nonexistent_code),
1441 : errmsg(rentry->nonexistent_msg, rel->relname)));
1442 : }
1443 : else
1444 : {
1445 906 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1446 906 : break;
1447 : }
1448 : }
1449 : }
1450 :
1451 : Assert(rentry->kind != '\0'); /* Should be impossible */
1452 : }
1453 :
1454 : /*
1455 : * Emit the right error message for a "DROP" command issued on a
1456 : * relation of the wrong type
1457 : */
1458 : static void
1459 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1460 : {
1461 : const struct dropmsgstrings *rentry;
1462 : const struct dropmsgstrings *wentry;
1463 :
1464 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1465 0 : if (rentry->kind == rightkind)
1466 0 : break;
1467 : Assert(rentry->kind != '\0');
1468 :
1469 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1470 0 : if (wentry->kind == wrongkind)
1471 0 : break;
1472 : /* wrongkind could be something we don't have in our table... */
1473 :
1474 0 : ereport(ERROR,
1475 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1476 : errmsg(rentry->nota_msg, relname),
1477 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1478 : }
1479 :
1480 : /*
1481 : * RemoveRelations
1482 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1483 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1484 : */
1485 : void
1486 16270 : RemoveRelations(DropStmt *drop)
1487 : {
1488 : ObjectAddresses *objects;
1489 : char relkind;
1490 : ListCell *cell;
1491 16270 : int flags = 0;
1492 16270 : LOCKMODE lockmode = AccessExclusiveLock;
1493 :
1494 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1495 16270 : if (drop->concurrent)
1496 : {
1497 : /*
1498 : * Note that for temporary relations this lock may get upgraded later
1499 : * on, but as no other session can access a temporary relation, this
1500 : * is actually fine.
1501 : */
1502 138 : lockmode = ShareUpdateExclusiveLock;
1503 : Assert(drop->removeType == OBJECT_INDEX);
1504 138 : if (list_length(drop->objects) != 1)
1505 6 : ereport(ERROR,
1506 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1507 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1508 132 : if (drop->behavior == DROP_CASCADE)
1509 0 : ereport(ERROR,
1510 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1511 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1512 : }
1513 :
1514 : /*
1515 : * First we identify all the relations, then we delete them in a single
1516 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1517 : * RESTRICT errors if one of the relations depends on another.
1518 : */
1519 :
1520 : /* Determine required relkind */
1521 16264 : switch (drop->removeType)
1522 : {
1523 14166 : case OBJECT_TABLE:
1524 14166 : relkind = RELKIND_RELATION;
1525 14166 : break;
1526 :
1527 778 : case OBJECT_INDEX:
1528 778 : relkind = RELKIND_INDEX;
1529 778 : break;
1530 :
1531 172 : case OBJECT_SEQUENCE:
1532 172 : relkind = RELKIND_SEQUENCE;
1533 172 : break;
1534 :
1535 880 : case OBJECT_VIEW:
1536 880 : relkind = RELKIND_VIEW;
1537 880 : break;
1538 :
1539 120 : case OBJECT_MATVIEW:
1540 120 : relkind = RELKIND_MATVIEW;
1541 120 : break;
1542 :
1543 148 : case OBJECT_FOREIGN_TABLE:
1544 148 : relkind = RELKIND_FOREIGN_TABLE;
1545 148 : break;
1546 :
1547 0 : default:
1548 0 : elog(ERROR, "unrecognized drop object type: %d",
1549 : (int) drop->removeType);
1550 : relkind = 0; /* keep compiler quiet */
1551 : break;
1552 : }
1553 :
1554 : /* Lock and validate each relation; build a list of object addresses */
1555 16264 : objects = new_object_addresses();
1556 :
1557 36040 : foreach(cell, drop->objects)
1558 : {
1559 19934 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1560 : Oid relOid;
1561 : ObjectAddress obj;
1562 : struct DropRelationCallbackState state;
1563 :
1564 : /*
1565 : * These next few steps are a great deal like relation_openrv, but we
1566 : * don't bother building a relcache entry since we don't need it.
1567 : *
1568 : * Check for shared-cache-inval messages before trying to access the
1569 : * relation. This is needed to cover the case where the name
1570 : * identifies a rel that has been dropped and recreated since the
1571 : * start of our transaction: if we don't flush the old syscache entry,
1572 : * then we'll latch onto that entry and suffer an error later.
1573 : */
1574 19934 : AcceptInvalidationMessages();
1575 :
1576 : /* Look up the appropriate relation using namespace search. */
1577 19934 : state.expected_relkind = relkind;
1578 39868 : state.heap_lockmode = drop->concurrent ?
1579 19934 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1580 : /* We must initialize these fields to show that no locks are held: */
1581 19934 : state.heapOid = InvalidOid;
1582 19934 : state.partParentOid = InvalidOid;
1583 :
1584 19934 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1585 : RangeVarCallbackForDropRelation,
1586 : (void *) &state);
1587 :
1588 : /* Not there? */
1589 19914 : if (!OidIsValid(relOid))
1590 : {
1591 1080 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1592 948 : continue;
1593 : }
1594 :
1595 : /*
1596 : * Decide if concurrent mode needs to be used here or not. The
1597 : * callback retrieved the rel's persistence for us.
1598 : */
1599 18834 : if (drop->concurrent &&
1600 126 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1601 : {
1602 : Assert(list_length(drop->objects) == 1 &&
1603 : drop->removeType == OBJECT_INDEX);
1604 108 : flags |= PERFORM_DELETION_CONCURRENTLY;
1605 : }
1606 :
1607 : /*
1608 : * Concurrent index drop cannot be used with partitioned indexes,
1609 : * either.
1610 : */
1611 18834 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1612 108 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1613 6 : ereport(ERROR,
1614 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1615 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1616 : rel->relname)));
1617 :
1618 : /*
1619 : * If we're told to drop a partitioned index, we must acquire lock on
1620 : * all the children of its parent partitioned table before proceeding.
1621 : * Otherwise we'd try to lock the child index partitions before their
1622 : * tables, leading to potential deadlock against other sessions that
1623 : * will lock those objects in the other order.
1624 : */
1625 18828 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1626 70 : (void) find_all_inheritors(state.heapOid,
1627 : state.heap_lockmode,
1628 : NULL);
1629 :
1630 : /* OK, we're ready to delete this one */
1631 18828 : obj.classId = RelationRelationId;
1632 18828 : obj.objectId = relOid;
1633 18828 : obj.objectSubId = 0;
1634 :
1635 18828 : add_exact_object_address(&obj, objects);
1636 : }
1637 :
1638 16106 : performMultipleDeletions(objects, drop->behavior, flags);
1639 :
1640 15970 : free_object_addresses(objects);
1641 15970 : }
1642 :
1643 : /*
1644 : * Before acquiring a table lock, check whether we have sufficient rights.
1645 : * In the case of DROP INDEX, also try to lock the table before the index.
1646 : * Also, if the table to be dropped is a partition, we try to lock the parent
1647 : * first.
1648 : */
1649 : static void
1650 20096 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1651 : void *arg)
1652 : {
1653 : HeapTuple tuple;
1654 : struct DropRelationCallbackState *state;
1655 : char expected_relkind;
1656 : bool is_partition;
1657 : Form_pg_class classform;
1658 : LOCKMODE heap_lockmode;
1659 20096 : bool invalid_system_index = false;
1660 :
1661 20096 : state = (struct DropRelationCallbackState *) arg;
1662 20096 : heap_lockmode = state->heap_lockmode;
1663 :
1664 : /*
1665 : * If we previously locked some other index's heap, and the name we're
1666 : * looking up no longer refers to that relation, release the now-useless
1667 : * lock.
1668 : */
1669 20096 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1670 : {
1671 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1672 0 : state->heapOid = InvalidOid;
1673 : }
1674 :
1675 : /*
1676 : * Similarly, if we previously locked some other partition's heap, and the
1677 : * name we're looking up no longer refers to that relation, release the
1678 : * now-useless lock.
1679 : */
1680 20096 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1681 : {
1682 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1683 0 : state->partParentOid = InvalidOid;
1684 : }
1685 :
1686 : /* Didn't find a relation, so no need for locking or permission checks. */
1687 20096 : if (!OidIsValid(relOid))
1688 1086 : return;
1689 :
1690 19010 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1691 19010 : if (!HeapTupleIsValid(tuple))
1692 0 : return; /* concurrently dropped, so nothing to do */
1693 19010 : classform = (Form_pg_class) GETSTRUCT(tuple);
1694 19010 : is_partition = classform->relispartition;
1695 :
1696 : /* Pass back some data to save lookups in RemoveRelations */
1697 19010 : state->actual_relkind = classform->relkind;
1698 19010 : state->actual_relpersistence = classform->relpersistence;
1699 :
1700 : /*
1701 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1702 : * but RemoveRelations() can only pass one relkind for a given relation.
1703 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1704 : * That means we must be careful before giving the wrong type error when
1705 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1706 : * exists with indexes.
1707 : */
1708 19010 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1709 3002 : expected_relkind = RELKIND_RELATION;
1710 16008 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1711 78 : expected_relkind = RELKIND_INDEX;
1712 : else
1713 15930 : expected_relkind = classform->relkind;
1714 :
1715 19010 : if (state->expected_relkind != expected_relkind)
1716 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1717 0 : state->expected_relkind);
1718 :
1719 : /* Allow DROP to either table owner or schema owner */
1720 19010 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1721 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1722 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1723 18 : get_relkind_objtype(classform->relkind),
1724 18 : rel->relname);
1725 :
1726 : /*
1727 : * Check the case of a system index that might have been invalidated by a
1728 : * failed concurrent process and allow its drop. For the time being, this
1729 : * only concerns indexes of toast relations that became invalid during a
1730 : * REINDEX CONCURRENTLY process.
1731 : */
1732 18992 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1733 : {
1734 : HeapTuple locTuple;
1735 : Form_pg_index indexform;
1736 : bool indisvalid;
1737 :
1738 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1739 0 : if (!HeapTupleIsValid(locTuple))
1740 : {
1741 0 : ReleaseSysCache(tuple);
1742 0 : return;
1743 : }
1744 :
1745 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1746 0 : indisvalid = indexform->indisvalid;
1747 0 : ReleaseSysCache(locTuple);
1748 :
1749 : /* Mark object as being an invalid index of system catalogs */
1750 0 : if (!indisvalid)
1751 0 : invalid_system_index = true;
1752 : }
1753 :
1754 : /* In the case of an invalid index, it is fine to bypass this check */
1755 18992 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1756 2 : ereport(ERROR,
1757 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1758 : errmsg("permission denied: \"%s\" is a system catalog",
1759 : rel->relname)));
1760 :
1761 18990 : ReleaseSysCache(tuple);
1762 :
1763 : /*
1764 : * In DROP INDEX, attempt to acquire lock on the parent table before
1765 : * locking the index. index_drop() will need this anyway, and since
1766 : * regular queries lock tables before their indexes, we risk deadlock if
1767 : * we do it the other way around. No error if we don't find a pg_index
1768 : * entry, though --- the relation may have been dropped. Note that this
1769 : * code will execute for either plain or partitioned indexes.
1770 : */
1771 18990 : if (expected_relkind == RELKIND_INDEX &&
1772 : relOid != oldRelOid)
1773 : {
1774 754 : state->heapOid = IndexGetRelation(relOid, true);
1775 754 : if (OidIsValid(state->heapOid))
1776 754 : LockRelationOid(state->heapOid, heap_lockmode);
1777 : }
1778 :
1779 : /*
1780 : * Similarly, if the relation is a partition, we must acquire lock on its
1781 : * parent before locking the partition. That's because queries lock the
1782 : * parent before its partitions, so we risk deadlock if we do it the other
1783 : * way around.
1784 : */
1785 18990 : if (is_partition && relOid != oldRelOid)
1786 : {
1787 594 : state->partParentOid = get_partition_parent(relOid, true);
1788 594 : if (OidIsValid(state->partParentOid))
1789 594 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1790 : }
1791 : }
1792 :
1793 : /*
1794 : * ExecuteTruncate
1795 : * Executes a TRUNCATE command.
1796 : *
1797 : * This is a multi-relation truncate. We first open and grab exclusive
1798 : * lock on all relations involved, checking permissions and otherwise
1799 : * verifying that the relation is OK for truncation. Note that if relations
1800 : * are foreign tables, at this stage, we have not yet checked that their
1801 : * foreign data in external data sources are OK for truncation. These are
1802 : * checked when foreign data are actually truncated later. In CASCADE mode,
1803 : * relations having FK references to the targeted relations are automatically
1804 : * added to the group; in RESTRICT mode, we check that all FK references are
1805 : * internal to the group that's being truncated. Finally all the relations
1806 : * are truncated and reindexed.
1807 : */
1808 : void
1809 1438 : ExecuteTruncate(TruncateStmt *stmt)
1810 : {
1811 1438 : List *rels = NIL;
1812 1438 : List *relids = NIL;
1813 1438 : List *relids_logged = NIL;
1814 : ListCell *cell;
1815 :
1816 : /*
1817 : * Open, exclusive-lock, and check all the explicitly-specified relations
1818 : */
1819 3102 : foreach(cell, stmt->relations)
1820 : {
1821 1712 : RangeVar *rv = lfirst(cell);
1822 : Relation rel;
1823 1712 : bool recurse = rv->inh;
1824 : Oid myrelid;
1825 1712 : LOCKMODE lockmode = AccessExclusiveLock;
1826 :
1827 1712 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1828 : 0, RangeVarCallbackForTruncate,
1829 : NULL);
1830 :
1831 : /* don't throw error for "TRUNCATE foo, foo" */
1832 1676 : if (list_member_oid(relids, myrelid))
1833 2 : continue;
1834 :
1835 : /* open the relation, we already hold a lock on it */
1836 1674 : rel = table_open(myrelid, NoLock);
1837 :
1838 : /*
1839 : * RangeVarGetRelidExtended() has done most checks with its callback,
1840 : * but other checks with the now-opened Relation remain.
1841 : */
1842 1674 : truncate_check_activity(rel);
1843 :
1844 1674 : rels = lappend(rels, rel);
1845 1674 : relids = lappend_oid(relids, myrelid);
1846 :
1847 : /* Log this relation only if needed for logical decoding */
1848 1674 : if (RelationIsLogicallyLogged(rel))
1849 64 : relids_logged = lappend_oid(relids_logged, myrelid);
1850 :
1851 1674 : if (recurse)
1852 : {
1853 : ListCell *child;
1854 : List *children;
1855 :
1856 1612 : children = find_all_inheritors(myrelid, lockmode, NULL);
1857 :
1858 4956 : foreach(child, children)
1859 : {
1860 3344 : Oid childrelid = lfirst_oid(child);
1861 :
1862 3344 : if (list_member_oid(relids, childrelid))
1863 1612 : continue;
1864 :
1865 : /* find_all_inheritors already got lock */
1866 1732 : rel = table_open(childrelid, NoLock);
1867 :
1868 : /*
1869 : * It is possible that the parent table has children that are
1870 : * temp tables of other backends. We cannot safely access
1871 : * such tables (because of buffering issues), and the best
1872 : * thing to do is to silently ignore them. Note that this
1873 : * check is the same as one of the checks done in
1874 : * truncate_check_activity() called below, still it is kept
1875 : * here for simplicity.
1876 : */
1877 1732 : if (RELATION_IS_OTHER_TEMP(rel))
1878 : {
1879 8 : table_close(rel, lockmode);
1880 8 : continue;
1881 : }
1882 :
1883 : /*
1884 : * Inherited TRUNCATE commands perform access permission
1885 : * checks on the parent table only. So we skip checking the
1886 : * children's permissions and don't call
1887 : * truncate_check_perms() here.
1888 : */
1889 1724 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1890 1724 : truncate_check_activity(rel);
1891 :
1892 1724 : rels = lappend(rels, rel);
1893 1724 : relids = lappend_oid(relids, childrelid);
1894 :
1895 : /* Log this relation only if needed for logical decoding */
1896 1724 : if (RelationIsLogicallyLogged(rel))
1897 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1898 : }
1899 : }
1900 62 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1901 12 : ereport(ERROR,
1902 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1903 : errmsg("cannot truncate only a partitioned table"),
1904 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1905 : }
1906 :
1907 1390 : ExecuteTruncateGuts(rels, relids, relids_logged,
1908 1390 : stmt->behavior, stmt->restart_seqs, false);
1909 :
1910 : /* And close the rels */
1911 4540 : foreach(cell, rels)
1912 : {
1913 3232 : Relation rel = (Relation) lfirst(cell);
1914 :
1915 3232 : table_close(rel, NoLock);
1916 : }
1917 1308 : }
1918 :
1919 : /*
1920 : * ExecuteTruncateGuts
1921 : *
1922 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1923 : * command (see above) as well as replication subscribers that execute a
1924 : * replicated TRUNCATE action.
1925 : *
1926 : * explicit_rels is the list of Relations to truncate that the command
1927 : * specified. relids is the list of Oids corresponding to explicit_rels.
1928 : * relids_logged is the list of Oids (a subset of relids) that require
1929 : * WAL-logging. This is all a bit redundant, but the existing callers have
1930 : * this information handy in this form.
1931 : */
1932 : void
1933 1428 : ExecuteTruncateGuts(List *explicit_rels,
1934 : List *relids,
1935 : List *relids_logged,
1936 : DropBehavior behavior, bool restart_seqs,
1937 : bool run_as_table_owner)
1938 : {
1939 : List *rels;
1940 1428 : List *seq_relids = NIL;
1941 1428 : HTAB *ft_htab = NULL;
1942 : EState *estate;
1943 : ResultRelInfo *resultRelInfos;
1944 : ResultRelInfo *resultRelInfo;
1945 : SubTransactionId mySubid;
1946 : ListCell *cell;
1947 : Oid *logrelids;
1948 :
1949 : /*
1950 : * Check the explicitly-specified relations.
1951 : *
1952 : * In CASCADE mode, suck in all referencing relations as well. This
1953 : * requires multiple iterations to find indirectly-dependent relations. At
1954 : * each phase, we need to exclusive-lock new rels before looking for their
1955 : * dependencies, else we might miss something. Also, we check each rel as
1956 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1957 : * time on a rel we have no permissions for.
1958 : */
1959 1428 : rels = list_copy(explicit_rels);
1960 1428 : if (behavior == DROP_CASCADE)
1961 : {
1962 : for (;;)
1963 40 : {
1964 : List *newrelids;
1965 :
1966 80 : newrelids = heap_truncate_find_FKs(relids);
1967 80 : if (newrelids == NIL)
1968 40 : break; /* nothing else to add */
1969 :
1970 134 : foreach(cell, newrelids)
1971 : {
1972 94 : Oid relid = lfirst_oid(cell);
1973 : Relation rel;
1974 :
1975 94 : rel = table_open(relid, AccessExclusiveLock);
1976 94 : ereport(NOTICE,
1977 : (errmsg("truncate cascades to table \"%s\"",
1978 : RelationGetRelationName(rel))));
1979 94 : truncate_check_rel(relid, rel->rd_rel);
1980 94 : truncate_check_perms(relid, rel->rd_rel);
1981 94 : truncate_check_activity(rel);
1982 94 : rels = lappend(rels, rel);
1983 94 : relids = lappend_oid(relids, relid);
1984 :
1985 : /* Log this relation only if needed for logical decoding */
1986 94 : if (RelationIsLogicallyLogged(rel))
1987 0 : relids_logged = lappend_oid(relids_logged, relid);
1988 : }
1989 : }
1990 : }
1991 :
1992 : /*
1993 : * Check foreign key references. In CASCADE mode, this should be
1994 : * unnecessary since we just pulled in all the references; but as a
1995 : * cross-check, do it anyway if in an Assert-enabled build.
1996 : */
1997 : #ifdef USE_ASSERT_CHECKING
1998 : heap_truncate_check_FKs(rels, false);
1999 : #else
2000 1428 : if (behavior == DROP_RESTRICT)
2001 1388 : heap_truncate_check_FKs(rels, false);
2002 : #endif
2003 :
2004 : /*
2005 : * If we are asked to restart sequences, find all the sequences, lock them
2006 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2007 : * We want to do this early since it's pointless to do all the truncation
2008 : * work only to fail on sequence permissions.
2009 : */
2010 1354 : if (restart_seqs)
2011 : {
2012 52 : foreach(cell, rels)
2013 : {
2014 26 : Relation rel = (Relation) lfirst(cell);
2015 26 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2016 : ListCell *seqcell;
2017 :
2018 62 : foreach(seqcell, seqlist)
2019 : {
2020 36 : Oid seq_relid = lfirst_oid(seqcell);
2021 : Relation seq_rel;
2022 :
2023 36 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2024 :
2025 : /* This check must match AlterSequence! */
2026 36 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2027 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2028 0 : RelationGetRelationName(seq_rel));
2029 :
2030 36 : seq_relids = lappend_oid(seq_relids, seq_relid);
2031 :
2032 36 : relation_close(seq_rel, NoLock);
2033 : }
2034 : }
2035 : }
2036 :
2037 : /* Prepare to catch AFTER triggers. */
2038 1354 : AfterTriggerBeginQuery();
2039 :
2040 : /*
2041 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2042 : * each relation. We don't need to call ExecOpenIndices, though.
2043 : *
2044 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2045 : * though we don't have a range table and don't populate the
2046 : * es_result_relations array. That's a bit bogus, but it's enough to make
2047 : * ExecGetTriggerResultRel() find them.
2048 : */
2049 1354 : estate = CreateExecutorState();
2050 : resultRelInfos = (ResultRelInfo *)
2051 1354 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2052 1354 : resultRelInfo = resultRelInfos;
2053 4758 : foreach(cell, rels)
2054 : {
2055 3404 : Relation rel = (Relation) lfirst(cell);
2056 :
2057 3404 : InitResultRelInfo(resultRelInfo,
2058 : rel,
2059 : 0, /* dummy rangetable index */
2060 : NULL,
2061 : 0);
2062 3404 : estate->es_opened_result_relations =
2063 3404 : lappend(estate->es_opened_result_relations, resultRelInfo);
2064 3404 : resultRelInfo++;
2065 : }
2066 :
2067 : /*
2068 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2069 : * truncating (this is because one of them might throw an error). Also, if
2070 : * we were to allow them to prevent statement execution, that would need
2071 : * to be handled here.
2072 : */
2073 1354 : resultRelInfo = resultRelInfos;
2074 4758 : foreach(cell, rels)
2075 : {
2076 : UserContext ucxt;
2077 :
2078 3404 : if (run_as_table_owner)
2079 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2080 : &ucxt);
2081 3404 : ExecBSTruncateTriggers(estate, resultRelInfo);
2082 3404 : if (run_as_table_owner)
2083 70 : RestoreUserContext(&ucxt);
2084 3404 : resultRelInfo++;
2085 : }
2086 :
2087 : /*
2088 : * OK, truncate each table.
2089 : */
2090 1354 : mySubid = GetCurrentSubTransactionId();
2091 :
2092 4758 : foreach(cell, rels)
2093 : {
2094 3404 : Relation rel = (Relation) lfirst(cell);
2095 :
2096 : /* Skip partitioned tables as there is nothing to do */
2097 3404 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2098 692 : continue;
2099 :
2100 : /*
2101 : * Build the lists of foreign tables belonging to each foreign server
2102 : * and pass each list to the foreign data wrapper's callback function,
2103 : * so that each server can truncate its all foreign tables in bulk.
2104 : * Each list is saved as a single entry in a hash table that uses the
2105 : * server OID as lookup key.
2106 : */
2107 2712 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2108 : {
2109 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2110 : bool found;
2111 : ForeignTruncateInfo *ft_info;
2112 :
2113 : /* First time through, initialize hashtable for foreign tables */
2114 34 : if (!ft_htab)
2115 : {
2116 : HASHCTL hctl;
2117 :
2118 30 : memset(&hctl, 0, sizeof(HASHCTL));
2119 30 : hctl.keysize = sizeof(Oid);
2120 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2121 30 : hctl.hcxt = CurrentMemoryContext;
2122 :
2123 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2124 : 32, /* start small and extend */
2125 : &hctl,
2126 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2127 : }
2128 :
2129 : /* Find or create cached entry for the foreign table */
2130 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2131 34 : if (!found)
2132 30 : ft_info->rels = NIL;
2133 :
2134 : /*
2135 : * Save the foreign table in the entry of the server that the
2136 : * foreign table belongs to.
2137 : */
2138 34 : ft_info->rels = lappend(ft_info->rels, rel);
2139 34 : continue;
2140 : }
2141 :
2142 : /*
2143 : * Normally, we need a transaction-safe truncation here. However, if
2144 : * the table was either created in the current (sub)transaction or has
2145 : * a new relfilenumber in the current (sub)transaction, then we can
2146 : * just truncate it in-place, because a rollback would cause the whole
2147 : * table or the current physical file to be thrown away anyway.
2148 : */
2149 2678 : if (rel->rd_createSubid == mySubid ||
2150 2652 : rel->rd_newRelfilelocatorSubid == mySubid)
2151 : {
2152 : /* Immediate, non-rollbackable truncation is OK */
2153 90 : heap_truncate_one_rel(rel);
2154 : }
2155 : else
2156 : {
2157 : Oid heap_relid;
2158 : Oid toast_relid;
2159 2588 : ReindexParams reindex_params = {0};
2160 :
2161 : /*
2162 : * This effectively deletes all rows in the table, and may be done
2163 : * in a serializable transaction. In that case we must record a
2164 : * rw-conflict in to this transaction from each transaction
2165 : * holding a predicate lock on the table.
2166 : */
2167 2588 : CheckTableForSerializableConflictIn(rel);
2168 :
2169 : /*
2170 : * Need the full transaction-safe pushups.
2171 : *
2172 : * Create a new empty storage file for the relation, and assign it
2173 : * as the relfilenumber value. The old storage file is scheduled
2174 : * for deletion at commit.
2175 : */
2176 2588 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2177 :
2178 2588 : heap_relid = RelationGetRelid(rel);
2179 :
2180 : /*
2181 : * The same for the toast table, if any.
2182 : */
2183 2588 : toast_relid = rel->rd_rel->reltoastrelid;
2184 2588 : if (OidIsValid(toast_relid))
2185 : {
2186 1596 : Relation toastrel = relation_open(toast_relid,
2187 : AccessExclusiveLock);
2188 :
2189 1596 : RelationSetNewRelfilenumber(toastrel,
2190 1596 : toastrel->rd_rel->relpersistence);
2191 1596 : table_close(toastrel, NoLock);
2192 : }
2193 :
2194 : /*
2195 : * Reconstruct the indexes to match, and we're done.
2196 : */
2197 2588 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2198 : &reindex_params);
2199 : }
2200 :
2201 2678 : pgstat_count_truncate(rel);
2202 : }
2203 :
2204 : /* Now go through the hash table, and truncate foreign tables */
2205 1354 : if (ft_htab)
2206 : {
2207 : ForeignTruncateInfo *ft_info;
2208 : HASH_SEQ_STATUS seq;
2209 :
2210 30 : hash_seq_init(&seq, ft_htab);
2211 :
2212 30 : PG_TRY();
2213 : {
2214 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2215 : {
2216 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2217 :
2218 : /* truncate_check_rel() has checked that already */
2219 : Assert(routine->ExecForeignTruncate != NULL);
2220 :
2221 30 : routine->ExecForeignTruncate(ft_info->rels,
2222 : behavior,
2223 : restart_seqs);
2224 : }
2225 : }
2226 8 : PG_FINALLY();
2227 : {
2228 30 : hash_destroy(ft_htab);
2229 : }
2230 30 : PG_END_TRY();
2231 : }
2232 :
2233 : /*
2234 : * Restart owned sequences if we were asked to.
2235 : */
2236 1382 : foreach(cell, seq_relids)
2237 : {
2238 36 : Oid seq_relid = lfirst_oid(cell);
2239 :
2240 36 : ResetSequence(seq_relid);
2241 : }
2242 :
2243 : /*
2244 : * Write a WAL record to allow this set of actions to be logically
2245 : * decoded.
2246 : *
2247 : * Assemble an array of relids so we can write a single WAL record for the
2248 : * whole action.
2249 : */
2250 1346 : if (relids_logged != NIL)
2251 : {
2252 : xl_heap_truncate xlrec;
2253 50 : int i = 0;
2254 :
2255 : /* should only get here if wal_level >= logical */
2256 : Assert(XLogLogicalInfoActive());
2257 :
2258 50 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2259 136 : foreach(cell, relids_logged)
2260 86 : logrelids[i++] = lfirst_oid(cell);
2261 :
2262 50 : xlrec.dbId = MyDatabaseId;
2263 50 : xlrec.nrelids = list_length(relids_logged);
2264 50 : xlrec.flags = 0;
2265 50 : if (behavior == DROP_CASCADE)
2266 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2267 50 : if (restart_seqs)
2268 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2269 :
2270 50 : XLogBeginInsert();
2271 50 : XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2272 50 : XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2273 :
2274 50 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2275 :
2276 50 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2277 : }
2278 :
2279 : /*
2280 : * Process all AFTER STATEMENT TRUNCATE triggers.
2281 : */
2282 1346 : resultRelInfo = resultRelInfos;
2283 4742 : foreach(cell, rels)
2284 : {
2285 : UserContext ucxt;
2286 :
2287 3396 : if (run_as_table_owner)
2288 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2289 : &ucxt);
2290 3396 : ExecASTruncateTriggers(estate, resultRelInfo);
2291 3396 : if (run_as_table_owner)
2292 70 : RestoreUserContext(&ucxt);
2293 3396 : resultRelInfo++;
2294 : }
2295 :
2296 : /* Handle queued AFTER triggers */
2297 1346 : AfterTriggerEndQuery(estate);
2298 :
2299 : /* We can clean up the EState now */
2300 1346 : FreeExecutorState(estate);
2301 :
2302 : /*
2303 : * Close any rels opened by CASCADE (can't do this while EState still
2304 : * holds refs)
2305 : */
2306 1346 : rels = list_difference_ptr(rels, explicit_rels);
2307 1440 : foreach(cell, rels)
2308 : {
2309 94 : Relation rel = (Relation) lfirst(cell);
2310 :
2311 94 : table_close(rel, NoLock);
2312 : }
2313 1346 : }
2314 :
2315 : /*
2316 : * Check that a given relation is safe to truncate. Subroutine for
2317 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2318 : */
2319 : static void
2320 3608 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2321 : {
2322 3608 : char *relname = NameStr(reltuple->relname);
2323 :
2324 : /*
2325 : * Only allow truncate on regular tables, foreign tables using foreign
2326 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2327 : * latter are only being included here for the following checks; no
2328 : * physical truncation will occur in their case.).
2329 : */
2330 3608 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2331 : {
2332 36 : Oid serverid = GetForeignServerIdByRelId(relid);
2333 36 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2334 :
2335 36 : if (!fdwroutine->ExecForeignTruncate)
2336 2 : ereport(ERROR,
2337 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2338 : errmsg("cannot truncate foreign table \"%s\"",
2339 : relname)));
2340 : }
2341 3572 : else if (reltuple->relkind != RELKIND_RELATION &&
2342 704 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2343 0 : ereport(ERROR,
2344 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2345 : errmsg("\"%s\" is not a table", relname)));
2346 :
2347 : /*
2348 : * Most system catalogs can't be truncated at all, or at least not unless
2349 : * allow_system_table_mods=on. As an exception, however, we allow
2350 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2351 : * to change its relfilenode to match the old cluster, and allowing a
2352 : * TRUNCATE command to be executed is the easiest way of doing that.
2353 : */
2354 3606 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2355 22 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2356 2 : ereport(ERROR,
2357 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2358 : errmsg("permission denied: \"%s\" is a system catalog",
2359 : relname)));
2360 :
2361 3604 : InvokeObjectTruncateHook(relid);
2362 3604 : }
2363 :
2364 : /*
2365 : * Check that current user has the permission to truncate given relation.
2366 : */
2367 : static void
2368 1880 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2369 : {
2370 1880 : char *relname = NameStr(reltuple->relname);
2371 : AclResult aclresult;
2372 :
2373 : /* Permissions checks */
2374 1880 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2375 1880 : if (aclresult != ACLCHECK_OK)
2376 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2377 : relname);
2378 1848 : }
2379 :
2380 : /*
2381 : * Set of extra sanity checks to check if a given relation is safe to
2382 : * truncate. This is split with truncate_check_rel() as
2383 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2384 : */
2385 : static void
2386 3492 : truncate_check_activity(Relation rel)
2387 : {
2388 : /*
2389 : * Don't allow truncate on temp tables of other backends ... their local
2390 : * buffer manager is not going to cope.
2391 : */
2392 3492 : if (RELATION_IS_OTHER_TEMP(rel))
2393 0 : ereport(ERROR,
2394 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2395 : errmsg("cannot truncate temporary tables of other sessions")));
2396 :
2397 : /*
2398 : * Also check for active uses of the relation in the current transaction,
2399 : * including open scans and pending AFTER trigger events.
2400 : */
2401 3492 : CheckTableNotInUse(rel, "TRUNCATE");
2402 3492 : }
2403 :
2404 : /*
2405 : * storage_name
2406 : * returns the name corresponding to a typstorage/attstorage enum value
2407 : */
2408 : static const char *
2409 24 : storage_name(char c)
2410 : {
2411 24 : switch (c)
2412 : {
2413 0 : case TYPSTORAGE_PLAIN:
2414 0 : return "PLAIN";
2415 0 : case TYPSTORAGE_EXTERNAL:
2416 0 : return "EXTERNAL";
2417 12 : case TYPSTORAGE_EXTENDED:
2418 12 : return "EXTENDED";
2419 12 : case TYPSTORAGE_MAIN:
2420 12 : return "MAIN";
2421 0 : default:
2422 0 : return "???";
2423 : }
2424 : }
2425 :
2426 : /*----------
2427 : * MergeAttributes
2428 : * Returns new schema given initial schema and superclasses.
2429 : *
2430 : * Input arguments:
2431 : * 'columns' is the column/attribute definition for the table. (It's a list
2432 : * of ColumnDef's.) It is destructively changed.
2433 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2434 : * 'relpersistence' is the persistence type of the table.
2435 : * 'is_partition' tells if the table is a partition.
2436 : *
2437 : * Output arguments:
2438 : * 'supconstr' receives a list of constraints belonging to the parents,
2439 : * updated as necessary to be valid for the child.
2440 : * 'supnotnulls' receives a list of CookedConstraints that corresponds to
2441 : * constraints coming from inheritance parents.
2442 : *
2443 : * Return value:
2444 : * Completed schema list.
2445 : *
2446 : * Notes:
2447 : * The order in which the attributes are inherited is very important.
2448 : * Intuitively, the inherited attributes should come first. If a table
2449 : * inherits from multiple parents, the order of those attributes are
2450 : * according to the order of the parents specified in CREATE TABLE.
2451 : *
2452 : * Here's an example:
2453 : *
2454 : * create table person (name text, age int4, location point);
2455 : * create table emp (salary int4, manager text) inherits(person);
2456 : * create table student (gpa float8) inherits (person);
2457 : * create table stud_emp (percent int4) inherits (emp, student);
2458 : *
2459 : * The order of the attributes of stud_emp is:
2460 : *
2461 : * person {1:name, 2:age, 3:location}
2462 : * / \
2463 : * {6:gpa} student emp {4:salary, 5:manager}
2464 : * \ /
2465 : * stud_emp {7:percent}
2466 : *
2467 : * If the same attribute name appears multiple times, then it appears
2468 : * in the result table in the proper location for its first appearance.
2469 : *
2470 : * Constraints (including not-null constraints) for the child table
2471 : * are the union of all relevant constraints, from both the child schema
2472 : * and parent tables. In addition, in legacy inheritance, each column that
2473 : * appears in a primary key in any of the parents also gets a NOT NULL
2474 : * constraint (partitioning doesn't need this, because the PK itself gets
2475 : * inherited.)
2476 : *
2477 : * The default value for a child column is defined as:
2478 : * (1) If the child schema specifies a default, that value is used.
2479 : * (2) If neither the child nor any parent specifies a default, then
2480 : * the column will not have a default.
2481 : * (3) If conflicting defaults are inherited from different parents
2482 : * (and not overridden by the child), an error is raised.
2483 : * (4) Otherwise the inherited default is used.
2484 : *
2485 : * Note that the default-value infrastructure is used for generated
2486 : * columns' expressions too, so most of the preceding paragraph applies
2487 : * to generation expressions too. We insist that a child column be
2488 : * generated if and only if its parent(s) are, but it need not have
2489 : * the same generation expression.
2490 : *----------
2491 : */
2492 : static List *
2493 53200 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2494 : bool is_partition, List **supconstr, List **supnotnulls)
2495 : {
2496 53200 : List *inh_columns = NIL;
2497 53200 : List *constraints = NIL;
2498 53200 : List *nnconstraints = NIL;
2499 53200 : bool have_bogus_defaults = false;
2500 : int child_attno;
2501 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2502 53200 : List *saved_columns = NIL;
2503 : ListCell *lc;
2504 :
2505 : /*
2506 : * Check for and reject tables with too many columns. We perform this
2507 : * check relatively early for two reasons: (a) we don't run the risk of
2508 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2509 : * okay if we're processing <= 1600 columns, but could take minutes to
2510 : * execute if the user attempts to create a table with hundreds of
2511 : * thousands of columns.
2512 : *
2513 : * Note that we also need to check that we do not exceed this figure after
2514 : * including columns from inherited relations.
2515 : */
2516 53200 : if (list_length(columns) > MaxHeapAttributeNumber)
2517 0 : ereport(ERROR,
2518 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2519 : errmsg("tables can have at most %d columns",
2520 : MaxHeapAttributeNumber)));
2521 :
2522 : /*
2523 : * Check for duplicate names in the explicit list of attributes.
2524 : *
2525 : * Although we might consider merging such entries in the same way that we
2526 : * handle name conflicts for inherited attributes, it seems to make more
2527 : * sense to assume such conflicts are errors.
2528 : *
2529 : * We don't use foreach() here because we have two nested loops over the
2530 : * columns list, with possible element deletions in the inner one. If we
2531 : * used foreach_delete_current() it could only fix up the state of one of
2532 : * the loops, so it seems cleaner to use looping over list indexes for
2533 : * both loops. Note that any deletion will happen beyond where the outer
2534 : * loop is, so its index never needs adjustment.
2535 : */
2536 247218 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2537 : {
2538 194042 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2539 :
2540 194042 : if (!is_partition && coldef->typeName == NULL)
2541 : {
2542 : /*
2543 : * Typed table column option that does not belong to a column from
2544 : * the type. This works because the columns from the type come
2545 : * first in the list. (We omit this check for partition column
2546 : * lists; those are processed separately below.)
2547 : */
2548 6 : ereport(ERROR,
2549 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2550 : errmsg("column \"%s\" does not exist",
2551 : coldef->colname)));
2552 : }
2553 :
2554 : /* restpos scans all entries beyond coldef; incr is in loop body */
2555 6160804 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2556 : {
2557 5966786 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2558 :
2559 5966786 : if (strcmp(coldef->colname, restdef->colname) == 0)
2560 : {
2561 50 : if (coldef->is_from_type)
2562 : {
2563 : /*
2564 : * merge the column options into the column from the type
2565 : */
2566 32 : coldef->is_not_null = restdef->is_not_null;
2567 32 : coldef->raw_default = restdef->raw_default;
2568 32 : coldef->cooked_default = restdef->cooked_default;
2569 32 : coldef->constraints = restdef->constraints;
2570 32 : coldef->is_from_type = false;
2571 32 : columns = list_delete_nth_cell(columns, restpos);
2572 : }
2573 : else
2574 18 : ereport(ERROR,
2575 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2576 : errmsg("column \"%s\" specified more than once",
2577 : coldef->colname)));
2578 : }
2579 : else
2580 5966736 : restpos++;
2581 : }
2582 : }
2583 :
2584 : /*
2585 : * In case of a partition, there are no new column definitions, only dummy
2586 : * ColumnDefs created for column constraints. Set them aside for now and
2587 : * process them at the end.
2588 : */
2589 53176 : if (is_partition)
2590 : {
2591 8328 : saved_columns = columns;
2592 8328 : columns = NIL;
2593 : }
2594 :
2595 : /*
2596 : * Scan the parents left-to-right, and merge their attributes to form a
2597 : * list of inherited columns (inh_columns).
2598 : */
2599 53176 : child_attno = 0;
2600 63564 : foreach(lc, supers)
2601 : {
2602 10460 : Oid parent = lfirst_oid(lc);
2603 : Relation relation;
2604 : TupleDesc tupleDesc;
2605 : TupleConstr *constr;
2606 : AttrMap *newattmap;
2607 : List *inherited_defaults;
2608 : List *cols_with_defaults;
2609 : List *nnconstrs;
2610 : ListCell *lc1;
2611 : ListCell *lc2;
2612 : Bitmapset *pkattrs;
2613 10460 : Bitmapset *nncols = NULL;
2614 :
2615 : /* caller already got lock */
2616 10460 : relation = table_open(parent, NoLock);
2617 :
2618 : /*
2619 : * Check for active uses of the parent partitioned table in the
2620 : * current transaction, such as being used in some manner by an
2621 : * enclosing command.
2622 : */
2623 10460 : if (is_partition)
2624 8328 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2625 :
2626 : /*
2627 : * We do not allow partitioned tables and partitions to participate in
2628 : * regular inheritance.
2629 : */
2630 10454 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2631 6 : ereport(ERROR,
2632 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2633 : errmsg("cannot inherit from partitioned table \"%s\"",
2634 : RelationGetRelationName(relation))));
2635 10448 : if (relation->rd_rel->relispartition && !is_partition)
2636 6 : ereport(ERROR,
2637 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2638 : errmsg("cannot inherit from partition \"%s\"",
2639 : RelationGetRelationName(relation))));
2640 :
2641 10442 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2642 8324 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2643 8304 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2644 0 : ereport(ERROR,
2645 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2646 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2647 : RelationGetRelationName(relation))));
2648 :
2649 : /*
2650 : * If the parent is permanent, so must be all of its partitions. Note
2651 : * that inheritance allows that case.
2652 : */
2653 10442 : if (is_partition &&
2654 8322 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2655 : relpersistence == RELPERSISTENCE_TEMP)
2656 6 : ereport(ERROR,
2657 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2658 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2659 : RelationGetRelationName(relation))));
2660 :
2661 : /* Permanent rels cannot inherit from temporary ones */
2662 10436 : if (relpersistence != RELPERSISTENCE_TEMP &&
2663 10064 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2664 24 : ereport(ERROR,
2665 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2666 : errmsg(!is_partition
2667 : ? "cannot inherit from temporary relation \"%s\""
2668 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2669 : RelationGetRelationName(relation))));
2670 :
2671 : /* If existing rel is temp, it must belong to this session */
2672 10412 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2673 324 : !relation->rd_islocaltemp)
2674 0 : ereport(ERROR,
2675 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2676 : errmsg(!is_partition
2677 : ? "cannot inherit from temporary relation of another session"
2678 : : "cannot create as partition of temporary relation of another session")));
2679 :
2680 : /*
2681 : * We should have an UNDER permission flag for this, but for now,
2682 : * demand that creator of a child table own the parent.
2683 : */
2684 10412 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2685 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2686 0 : RelationGetRelationName(relation));
2687 :
2688 10412 : tupleDesc = RelationGetDescr(relation);
2689 10412 : constr = tupleDesc->constr;
2690 :
2691 : /*
2692 : * newattmap->attnums[] will contain the child-table attribute numbers
2693 : * for the attributes of this parent table. (They are not the same
2694 : * for parents after the first one, nor if we have dropped columns.)
2695 : */
2696 10412 : newattmap = make_attrmap(tupleDesc->natts);
2697 :
2698 : /* We can't process inherited defaults until newattmap is complete. */
2699 10412 : inherited_defaults = cols_with_defaults = NIL;
2700 :
2701 : /*
2702 : * All columns that are part of the parent's primary key need to be
2703 : * NOT NULL; if partition just the attnotnull bit, otherwise a full
2704 : * constraint (if they don't have one already). Also, we request
2705 : * attnotnull on columns that have a not-null constraint that's not
2706 : * marked NO INHERIT.
2707 : */
2708 10412 : pkattrs = RelationGetIndexAttrBitmap(relation,
2709 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
2710 10412 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation), true);
2711 11260 : foreach(lc1, nnconstrs)
2712 848 : nncols = bms_add_member(nncols,
2713 848 : ((CookedConstraint *) lfirst(lc1))->attnum);
2714 :
2715 32352 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2716 21940 : parent_attno++)
2717 : {
2718 21964 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2719 : parent_attno - 1);
2720 21964 : char *attributeName = NameStr(attribute->attname);
2721 : int exist_attno;
2722 : ColumnDef *newdef;
2723 : ColumnDef *mergeddef;
2724 :
2725 : /*
2726 : * Ignore dropped columns in the parent.
2727 : */
2728 21964 : if (attribute->attisdropped)
2729 192 : continue; /* leave newattmap->attnums entry as zero */
2730 :
2731 : /*
2732 : * Create new column definition
2733 : */
2734 21772 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2735 : attribute->atttypmod, attribute->attcollation);
2736 21772 : newdef->storage = attribute->attstorage;
2737 21772 : newdef->generated = attribute->attgenerated;
2738 21772 : if (CompressionMethodIsValid(attribute->attcompression))
2739 30 : newdef->compression =
2740 30 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2741 :
2742 : /*
2743 : * Regular inheritance children are independent enough not to
2744 : * inherit identity columns. But partitions are integral part of
2745 : * a partitioned table and inherit identity column.
2746 : */
2747 21772 : if (is_partition)
2748 17810 : newdef->identity = attribute->attidentity;
2749 :
2750 : /*
2751 : * Does it match some previously considered column from another
2752 : * parent?
2753 : */
2754 21772 : exist_attno = findAttrByName(attributeName, inh_columns);
2755 21772 : if (exist_attno > 0)
2756 : {
2757 : /*
2758 : * Yes, try to merge the two column definitions.
2759 : */
2760 302 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2761 :
2762 278 : newattmap->attnums[parent_attno - 1] = exist_attno;
2763 :
2764 : /*
2765 : * Partitions have only one parent, so conflict should never
2766 : * occur.
2767 : */
2768 : Assert(!is_partition);
2769 : }
2770 : else
2771 : {
2772 : /*
2773 : * No, create a new inherited column
2774 : */
2775 21470 : newdef->inhcount = 1;
2776 21470 : newdef->is_local = false;
2777 21470 : inh_columns = lappend(inh_columns, newdef);
2778 :
2779 21470 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2780 :
2781 21470 : mergeddef = newdef;
2782 : }
2783 :
2784 : /*
2785 : * mark attnotnull if parent has it and it's not NO INHERIT
2786 : */
2787 42648 : if (bms_is_member(parent_attno, nncols) ||
2788 20900 : bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2789 : pkattrs))
2790 2146 : mergeddef->is_not_null = true;
2791 :
2792 : /*
2793 : * In regular inheritance, columns in the parent's primary key get
2794 : * an extra not-null constraint. Partitioning doesn't need this,
2795 : * because the PK itself is going to be cloned to the partition.
2796 : */
2797 25686 : if (!is_partition &&
2798 3938 : bms_is_member(parent_attno -
2799 : FirstLowInvalidHeapAttributeNumber,
2800 : pkattrs))
2801 : {
2802 : CookedConstraint *nn;
2803 :
2804 256 : nn = palloc(sizeof(CookedConstraint));
2805 256 : nn->contype = CONSTR_NOTNULL;
2806 256 : nn->conoid = InvalidOid;
2807 256 : nn->name = NULL;
2808 256 : nn->attnum = newattmap->attnums[parent_attno - 1];
2809 256 : nn->expr = NULL;
2810 256 : nn->skip_validation = false;
2811 256 : nn->is_local = false;
2812 256 : nn->inhcount = 1;
2813 256 : nn->is_no_inherit = false;
2814 :
2815 256 : nnconstraints = lappend(nnconstraints, nn);
2816 : }
2817 :
2818 : /*
2819 : * Locate default/generation expression if any
2820 : */
2821 21748 : if (attribute->atthasdef)
2822 : {
2823 : Node *this_default;
2824 :
2825 650 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2826 650 : if (this_default == NULL)
2827 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2828 : parent_attno, RelationGetRelationName(relation));
2829 :
2830 : /*
2831 : * If it's a GENERATED default, it might contain Vars that
2832 : * need to be mapped to the inherited column(s)' new numbers.
2833 : * We can't do that till newattmap is ready, so just remember
2834 : * all the inherited default expressions for the moment.
2835 : */
2836 650 : inherited_defaults = lappend(inherited_defaults, this_default);
2837 650 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2838 : }
2839 : }
2840 :
2841 : /*
2842 : * Now process any inherited default expressions, adjusting attnos
2843 : * using the completed newattmap map.
2844 : */
2845 11038 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2846 : {
2847 650 : Node *this_default = (Node *) lfirst(lc1);
2848 650 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2849 : bool found_whole_row;
2850 :
2851 : /* Adjust Vars to match new table's column numbering */
2852 650 : this_default = map_variable_attnos(this_default,
2853 : 1, 0,
2854 : newattmap,
2855 : InvalidOid, &found_whole_row);
2856 :
2857 : /*
2858 : * For the moment we have to reject whole-row variables. We could
2859 : * convert them, if we knew the new table's rowtype OID, but that
2860 : * hasn't been assigned yet. (A variable could only appear in a
2861 : * generation expression, so the error message is correct.)
2862 : */
2863 650 : if (found_whole_row)
2864 0 : ereport(ERROR,
2865 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2866 : errmsg("cannot convert whole-row table reference"),
2867 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2868 : def->colname,
2869 : RelationGetRelationName(relation))));
2870 :
2871 : /*
2872 : * If we already had a default from some prior parent, check to
2873 : * see if they are the same. If so, no problem; if not, mark the
2874 : * column as having a bogus default. Below, we will complain if
2875 : * the bogus default isn't overridden by the child columns.
2876 : */
2877 : Assert(def->raw_default == NULL);
2878 650 : if (def->cooked_default == NULL)
2879 620 : def->cooked_default = this_default;
2880 30 : else if (!equal(def->cooked_default, this_default))
2881 : {
2882 24 : def->cooked_default = &bogus_marker;
2883 24 : have_bogus_defaults = true;
2884 : }
2885 : }
2886 :
2887 : /*
2888 : * Now copy the CHECK constraints of this parent, adjusting attnos
2889 : * using the completed newattmap map. Identically named constraints
2890 : * are merged if possible, else we throw error.
2891 : */
2892 10388 : if (constr && constr->num_check > 0)
2893 : {
2894 304 : ConstrCheck *check = constr->check;
2895 :
2896 638 : for (int i = 0; i < constr->num_check; i++)
2897 : {
2898 334 : char *name = check[i].ccname;
2899 : Node *expr;
2900 : bool found_whole_row;
2901 :
2902 : /* ignore if the constraint is non-inheritable */
2903 334 : if (check[i].ccnoinherit)
2904 48 : continue;
2905 :
2906 : /* Adjust Vars to match new table's column numbering */
2907 286 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2908 : 1, 0,
2909 : newattmap,
2910 : InvalidOid, &found_whole_row);
2911 :
2912 : /*
2913 : * For the moment we have to reject whole-row variables. We
2914 : * could convert them, if we knew the new table's rowtype OID,
2915 : * but that hasn't been assigned yet.
2916 : */
2917 286 : if (found_whole_row)
2918 0 : ereport(ERROR,
2919 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2920 : errmsg("cannot convert whole-row table reference"),
2921 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2922 : name,
2923 : RelationGetRelationName(relation))));
2924 :
2925 286 : constraints = MergeCheckConstraint(constraints, name, expr);
2926 : }
2927 : }
2928 :
2929 : /*
2930 : * Also copy the not-null constraints from this parent. The
2931 : * attnotnull markings were already installed above.
2932 : */
2933 11236 : foreach(lc1, nnconstrs)
2934 : {
2935 848 : CookedConstraint *nn = lfirst(lc1);
2936 :
2937 : Assert(nn->contype == CONSTR_NOTNULL);
2938 :
2939 848 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2940 848 : nn->is_local = false;
2941 848 : nn->inhcount = 1;
2942 :
2943 848 : nnconstraints = lappend(nnconstraints, nn);
2944 : }
2945 :
2946 10388 : free_attrmap(newattmap);
2947 :
2948 : /*
2949 : * Close the parent rel, but keep our lock on it until xact commit.
2950 : * That will prevent someone else from deleting or ALTERing the parent
2951 : * before the child is committed.
2952 : */
2953 10388 : table_close(relation, NoLock);
2954 : }
2955 :
2956 : /*
2957 : * If we had no inherited attributes, the result columns are just the
2958 : * explicitly declared columns. Otherwise, we need to merge the declared
2959 : * columns into the inherited column list. Although, we never have any
2960 : * explicitly declared columns if the table is a partition.
2961 : */
2962 53104 : if (inh_columns != NIL)
2963 : {
2964 10044 : int newcol_attno = 0;
2965 :
2966 10834 : foreach(lc, columns)
2967 : {
2968 838 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2969 838 : char *attributeName = newdef->colname;
2970 : int exist_attno;
2971 :
2972 : /*
2973 : * Partitions have only one parent and have no column definitions
2974 : * of their own, so conflict should never occur.
2975 : */
2976 : Assert(!is_partition);
2977 :
2978 838 : newcol_attno++;
2979 :
2980 : /*
2981 : * Does it match some inherited column?
2982 : */
2983 838 : exist_attno = findAttrByName(attributeName, inh_columns);
2984 838 : if (exist_attno > 0)
2985 : {
2986 : /*
2987 : * Yes, try to merge the two column definitions.
2988 : */
2989 280 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2990 : }
2991 : else
2992 : {
2993 : /*
2994 : * No, attach new column unchanged to result columns.
2995 : */
2996 558 : inh_columns = lappend(inh_columns, newdef);
2997 : }
2998 : }
2999 :
3000 9996 : columns = inh_columns;
3001 :
3002 : /*
3003 : * Check that we haven't exceeded the legal # of columns after merging
3004 : * in inherited columns.
3005 : */
3006 9996 : if (list_length(columns) > MaxHeapAttributeNumber)
3007 0 : ereport(ERROR,
3008 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3009 : errmsg("tables can have at most %d columns",
3010 : MaxHeapAttributeNumber)));
3011 : }
3012 :
3013 : /*
3014 : * Now that we have the column definition list for a partition, we can
3015 : * check whether the columns referenced in the column constraint specs
3016 : * actually exist. Also, merge column defaults.
3017 : */
3018 53056 : if (is_partition)
3019 : {
3020 8494 : foreach(lc, saved_columns)
3021 : {
3022 208 : ColumnDef *restdef = lfirst(lc);
3023 208 : bool found = false;
3024 : ListCell *l;
3025 :
3026 784 : foreach(l, columns)
3027 : {
3028 588 : ColumnDef *coldef = lfirst(l);
3029 :
3030 588 : if (strcmp(coldef->colname, restdef->colname) == 0)
3031 : {
3032 208 : found = true;
3033 :
3034 : /*
3035 : * Check for conflicts related to generated columns.
3036 : *
3037 : * Same rules as above: generated-ness has to match the
3038 : * parent, but the contents of the generation expression
3039 : * can be different.
3040 : */
3041 208 : if (coldef->generated)
3042 : {
3043 106 : if (restdef->raw_default && !restdef->generated)
3044 6 : ereport(ERROR,
3045 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3046 : errmsg("column \"%s\" inherits from generated column but specifies default",
3047 : restdef->colname)));
3048 100 : if (restdef->identity)
3049 0 : ereport(ERROR,
3050 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3051 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3052 : restdef->colname)));
3053 : }
3054 : else
3055 : {
3056 102 : if (restdef->generated)
3057 6 : ereport(ERROR,
3058 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3059 : errmsg("child column \"%s\" specifies generation expression",
3060 : restdef->colname),
3061 : errhint("A child table column cannot be generated unless its parent column is.")));
3062 : }
3063 :
3064 : /*
3065 : * Override the parent's default value for this column
3066 : * (coldef->cooked_default) with the partition's local
3067 : * definition (restdef->raw_default), if there's one. It
3068 : * should be physically impossible to get a cooked default
3069 : * in the local definition or a raw default in the
3070 : * inherited definition, but make sure they're nulls, for
3071 : * future-proofing.
3072 : */
3073 : Assert(restdef->cooked_default == NULL);
3074 : Assert(coldef->raw_default == NULL);
3075 196 : if (restdef->raw_default)
3076 : {
3077 124 : coldef->raw_default = restdef->raw_default;
3078 124 : coldef->cooked_default = NULL;
3079 : }
3080 : }
3081 : }
3082 :
3083 : /* complain for constraints on columns not in parent */
3084 196 : if (!found)
3085 0 : ereport(ERROR,
3086 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3087 : errmsg("column \"%s\" does not exist",
3088 : restdef->colname)));
3089 : }
3090 : }
3091 :
3092 : /*
3093 : * If we found any conflicting parent default values, check to make sure
3094 : * they were overridden by the child.
3095 : */
3096 53044 : if (have_bogus_defaults)
3097 : {
3098 54 : foreach(lc, columns)
3099 : {
3100 42 : ColumnDef *def = lfirst(lc);
3101 :
3102 42 : if (def->cooked_default == &bogus_marker)
3103 : {
3104 12 : if (def->generated)
3105 6 : ereport(ERROR,
3106 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3107 : errmsg("column \"%s\" inherits conflicting generation expressions",
3108 : def->colname),
3109 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3110 : else
3111 6 : ereport(ERROR,
3112 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3113 : errmsg("column \"%s\" inherits conflicting default values",
3114 : def->colname),
3115 : errhint("To resolve the conflict, specify a default explicitly.")));
3116 : }
3117 : }
3118 : }
3119 :
3120 53032 : *supconstr = constraints;
3121 53032 : *supnotnulls = nnconstraints;
3122 :
3123 53032 : return columns;
3124 : }
3125 :
3126 :
3127 : /*
3128 : * MergeCheckConstraint
3129 : * Try to merge an inherited CHECK constraint with previous ones
3130 : *
3131 : * If we inherit identically-named constraints from multiple parents, we must
3132 : * merge them, or throw an error if they don't have identical definitions.
3133 : *
3134 : * constraints is a list of CookedConstraint structs for previous constraints.
3135 : *
3136 : * If the new constraint matches an existing one, then the existing
3137 : * constraint's inheritance count is updated. If there is a conflict (same
3138 : * name but different expression), throw an error. If the constraint neither
3139 : * matches nor conflicts with an existing one, a new constraint is appended to
3140 : * the list.
3141 : */
3142 : static List *
3143 286 : MergeCheckConstraint(List *constraints, const char *name, Node *expr)
3144 : {
3145 : ListCell *lc;
3146 : CookedConstraint *newcon;
3147 :
3148 316 : foreach(lc, constraints)
3149 : {
3150 72 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3151 :
3152 : Assert(ccon->contype == CONSTR_CHECK);
3153 :
3154 : /* Non-matching names never conflict */
3155 72 : if (strcmp(ccon->name, name) != 0)
3156 30 : continue;
3157 :
3158 42 : if (equal(expr, ccon->expr))
3159 : {
3160 : /* OK to merge constraint with existing */
3161 42 : ccon->inhcount++;
3162 42 : if (ccon->inhcount < 0)
3163 0 : ereport(ERROR,
3164 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3165 : errmsg("too many inheritance parents"));
3166 42 : return constraints;
3167 : }
3168 :
3169 0 : ereport(ERROR,
3170 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3171 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3172 : name)));
3173 : }
3174 :
3175 : /*
3176 : * Constraint couldn't be merged with an existing one and also didn't
3177 : * conflict with an existing one, so add it as a new one to the list.
3178 : */
3179 244 : newcon = palloc0_object(CookedConstraint);
3180 244 : newcon->contype = CONSTR_CHECK;
3181 244 : newcon->name = pstrdup(name);
3182 244 : newcon->expr = expr;
3183 244 : newcon->inhcount = 1;
3184 244 : return lappend(constraints, newcon);
3185 : }
3186 :
3187 : /*
3188 : * MergeChildAttribute
3189 : * Merge given child attribute definition into given inherited attribute.
3190 : *
3191 : * Input arguments:
3192 : * 'inh_columns' is the list of inherited ColumnDefs.
3193 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3194 : * 'newcol_attno' is the attribute number in child table's schema definition
3195 : * 'newdef' is the column/attribute definition from the child table.
3196 : *
3197 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3198 : * ColumnDef remains unchanged.
3199 : *
3200 : * Notes:
3201 : * - The attribute is merged according to the rules laid out in the prologue
3202 : * of MergeAttributes().
3203 : * - If matching inherited attribute exists but the child attribute can not be
3204 : * merged into it, the function throws respective errors.
3205 : * - A partition can not have its own column definitions. Hence this function
3206 : * is applicable only to a regular inheritance child.
3207 : */
3208 : static void
3209 280 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3210 : {
3211 280 : char *attributeName = newdef->colname;
3212 : ColumnDef *inhdef;
3213 : Oid inhtypeid,
3214 : newtypeid;
3215 : int32 inhtypmod,
3216 : newtypmod;
3217 : Oid inhcollid,
3218 : newcollid;
3219 :
3220 280 : if (exist_attno == newcol_attno)
3221 252 : ereport(NOTICE,
3222 : (errmsg("merging column \"%s\" with inherited definition",
3223 : attributeName)));
3224 : else
3225 28 : ereport(NOTICE,
3226 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3227 : errdetail("User-specified column moved to the position of the inherited column.")));
3228 :
3229 280 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3230 :
3231 : /*
3232 : * Must have the same type and typmod
3233 : */
3234 280 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3235 280 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3236 280 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3237 12 : ereport(ERROR,
3238 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3239 : errmsg("column \"%s\" has a type conflict",
3240 : attributeName),
3241 : errdetail("%s versus %s",
3242 : format_type_with_typemod(inhtypeid, inhtypmod),
3243 : format_type_with_typemod(newtypeid, newtypmod))));
3244 :
3245 : /*
3246 : * Must have the same collation
3247 : */
3248 268 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3249 268 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3250 268 : if (inhcollid != newcollid)
3251 6 : ereport(ERROR,
3252 : (errcode(ERRCODE_COLLATION_MISMATCH),
3253 : errmsg("column \"%s\" has a collation conflict",
3254 : attributeName),
3255 : errdetail("\"%s\" versus \"%s\"",
3256 : get_collation_name(inhcollid),
3257 : get_collation_name(newcollid))));
3258 :
3259 : /*
3260 : * Identity is never inherited by a regular inheritance child. Pick
3261 : * child's identity definition if there's one.
3262 : */
3263 262 : inhdef->identity = newdef->identity;
3264 :
3265 : /*
3266 : * Copy storage parameter
3267 : */
3268 262 : if (inhdef->storage == 0)
3269 0 : inhdef->storage = newdef->storage;
3270 262 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3271 6 : ereport(ERROR,
3272 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3273 : errmsg("column \"%s\" has a storage parameter conflict",
3274 : attributeName),
3275 : errdetail("%s versus %s",
3276 : storage_name(inhdef->storage),
3277 : storage_name(newdef->storage))));
3278 :
3279 : /*
3280 : * Copy compression parameter
3281 : */
3282 256 : if (inhdef->compression == NULL)
3283 250 : inhdef->compression = newdef->compression;
3284 6 : else if (newdef->compression != NULL)
3285 : {
3286 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3287 6 : ereport(ERROR,
3288 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3289 : errmsg("column \"%s\" has a compression method conflict",
3290 : attributeName),
3291 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3292 : }
3293 :
3294 : /*
3295 : * Merge of not-null constraints = OR 'em together
3296 : */
3297 250 : inhdef->is_not_null |= newdef->is_not_null;
3298 :
3299 : /*
3300 : * Check for conflicts related to generated columns.
3301 : *
3302 : * If the parent column is generated, the child column will be made a
3303 : * generated column if it isn't already. If it is a generated column,
3304 : * we'll take its generation expression in preference to the parent's. We
3305 : * must check that the child column doesn't specify a default value or
3306 : * identity, which matches the rules for a single column in
3307 : * parse_utilcmd.c.
3308 : *
3309 : * Conversely, if the parent column is not generated, the child column
3310 : * can't be either. (We used to allow that, but it results in being able
3311 : * to override the generation expression via UPDATEs through the parent.)
3312 : */
3313 250 : if (inhdef->generated)
3314 : {
3315 26 : if (newdef->raw_default && !newdef->generated)
3316 6 : ereport(ERROR,
3317 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3318 : errmsg("column \"%s\" inherits from generated column but specifies default",
3319 : inhdef->colname)));
3320 20 : if (newdef->identity)
3321 6 : ereport(ERROR,
3322 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3323 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3324 : inhdef->colname)));
3325 : }
3326 : else
3327 : {
3328 224 : if (newdef->generated)
3329 6 : ereport(ERROR,
3330 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3331 : errmsg("child column \"%s\" specifies generation expression",
3332 : inhdef->colname),
3333 : errhint("A child table column cannot be generated unless its parent column is.")));
3334 : }
3335 :
3336 : /*
3337 : * If new def has a default, override previous default
3338 : */
3339 232 : if (newdef->raw_default != NULL)
3340 : {
3341 18 : inhdef->raw_default = newdef->raw_default;
3342 18 : inhdef->cooked_default = newdef->cooked_default;
3343 : }
3344 :
3345 : /* Mark the column as locally defined */
3346 232 : inhdef->is_local = true;
3347 232 : }
3348 :
3349 : /*
3350 : * MergeInheritedAttribute
3351 : * Merge given parent attribute definition into specified attribute
3352 : * inherited from the previous parents.
3353 : *
3354 : * Input arguments:
3355 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3356 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3357 : * 'newdef' is the new parent column/attribute definition to be merged.
3358 : *
3359 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3360 : *
3361 : * Notes:
3362 : * - The attribute is merged according to the rules laid out in the prologue
3363 : * of MergeAttributes().
3364 : * - If matching inherited attribute exists but the new attribute can not be
3365 : * merged into it, the function throws respective errors.
3366 : * - A partition inherits from only a single parent. Hence this function is
3367 : * applicable only to a regular inheritance.
3368 : */
3369 : static ColumnDef *
3370 302 : MergeInheritedAttribute(List *inh_columns,
3371 : int exist_attno,
3372 : const ColumnDef *newdef)
3373 : {
3374 302 : char *attributeName = newdef->colname;
3375 : ColumnDef *prevdef;
3376 : Oid prevtypeid,
3377 : newtypeid;
3378 : int32 prevtypmod,
3379 : newtypmod;
3380 : Oid prevcollid,
3381 : newcollid;
3382 :
3383 302 : ereport(NOTICE,
3384 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3385 : attributeName)));
3386 302 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3387 :
3388 : /*
3389 : * Must have the same type and typmod
3390 : */
3391 302 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3392 302 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3393 302 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3394 0 : ereport(ERROR,
3395 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3396 : errmsg("inherited column \"%s\" has a type conflict",
3397 : attributeName),
3398 : errdetail("%s versus %s",
3399 : format_type_with_typemod(prevtypeid, prevtypmod),
3400 : format_type_with_typemod(newtypeid, newtypmod))));
3401 :
3402 : /*
3403 : * Must have the same collation
3404 : */
3405 302 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3406 302 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3407 302 : if (prevcollid != newcollid)
3408 0 : ereport(ERROR,
3409 : (errcode(ERRCODE_COLLATION_MISMATCH),
3410 : errmsg("inherited column \"%s\" has a collation conflict",
3411 : attributeName),
3412 : errdetail("\"%s\" versus \"%s\"",
3413 : get_collation_name(prevcollid),
3414 : get_collation_name(newcollid))));
3415 :
3416 : /*
3417 : * Copy/check storage parameter
3418 : */
3419 302 : if (prevdef->storage == 0)
3420 0 : prevdef->storage = newdef->storage;
3421 302 : else if (prevdef->storage != newdef->storage)
3422 6 : ereport(ERROR,
3423 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3424 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3425 : attributeName),
3426 : errdetail("%s versus %s",
3427 : storage_name(prevdef->storage),
3428 : storage_name(newdef->storage))));
3429 :
3430 : /*
3431 : * Copy/check compression parameter
3432 : */
3433 296 : if (prevdef->compression == NULL)
3434 284 : prevdef->compression = newdef->compression;
3435 12 : else if (newdef->compression != NULL)
3436 : {
3437 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3438 6 : ereport(ERROR,
3439 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3440 : errmsg("column \"%s\" has a compression method conflict",
3441 : attributeName),
3442 : errdetail("%s versus %s",
3443 : prevdef->compression, newdef->compression)));
3444 : }
3445 :
3446 : /*
3447 : * Check for GENERATED conflicts
3448 : */
3449 290 : if (prevdef->generated != newdef->generated)
3450 12 : ereport(ERROR,
3451 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3452 : errmsg("inherited column \"%s\" has a generation conflict",
3453 : attributeName)));
3454 :
3455 : /*
3456 : * Default and other constraints are handled by the caller.
3457 : */
3458 :
3459 278 : prevdef->inhcount++;
3460 278 : if (prevdef->inhcount < 0)
3461 0 : ereport(ERROR,
3462 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3463 : errmsg("too many inheritance parents"));
3464 :
3465 278 : return prevdef;
3466 : }
3467 :
3468 : /*
3469 : * StoreCatalogInheritance
3470 : * Updates the system catalogs with proper inheritance information.
3471 : *
3472 : * supers is a list of the OIDs of the new relation's direct ancestors.
3473 : */
3474 : static void
3475 52486 : StoreCatalogInheritance(Oid relationId, List *supers,
3476 : bool child_is_partition)
3477 : {
3478 : Relation relation;
3479 : int32 seqNumber;
3480 : ListCell *entry;
3481 :
3482 : /*
3483 : * sanity checks
3484 : */
3485 : Assert(OidIsValid(relationId));
3486 :
3487 52486 : if (supers == NIL)
3488 42832 : return;
3489 :
3490 : /*
3491 : * Store INHERITS information in pg_inherits using direct ancestors only.
3492 : * Also enter dependencies on the direct ancestors, and make sure they are
3493 : * marked with relhassubclass = true.
3494 : *
3495 : * (Once upon a time, both direct and indirect ancestors were found here
3496 : * and then entered into pg_ipl. Since that catalog doesn't exist
3497 : * anymore, there's no need to look for indirect ancestors.)
3498 : */
3499 9654 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3500 :
3501 9654 : seqNumber = 1;
3502 19580 : foreach(entry, supers)
3503 : {
3504 9926 : Oid parentOid = lfirst_oid(entry);
3505 :
3506 9926 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3507 : child_is_partition);
3508 9926 : seqNumber++;
3509 : }
3510 :
3511 9654 : table_close(relation, RowExclusiveLock);
3512 : }
3513 :
3514 : /*
3515 : * Make catalog entries showing relationId as being an inheritance child
3516 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3517 : */
3518 : static void
3519 12612 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3520 : int32 seqNumber, Relation inhRelation,
3521 : bool child_is_partition)
3522 : {
3523 : ObjectAddress childobject,
3524 : parentobject;
3525 :
3526 : /* store the pg_inherits row */
3527 12612 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3528 :
3529 : /*
3530 : * Store a dependency too
3531 : */
3532 12612 : parentobject.classId = RelationRelationId;
3533 12612 : parentobject.objectId = parentOid;
3534 12612 : parentobject.objectSubId = 0;
3535 12612 : childobject.classId = RelationRelationId;
3536 12612 : childobject.objectId = relationId;
3537 12612 : childobject.objectSubId = 0;
3538 :
3539 12612 : recordDependencyOn(&childobject, &parentobject,
3540 : child_dependency_type(child_is_partition));
3541 :
3542 : /*
3543 : * Post creation hook of this inheritance. Since object_access_hook
3544 : * doesn't take multiple object identifiers, we relay oid of parent
3545 : * relation using auxiliary_id argument.
3546 : */
3547 12612 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3548 : relationId, 0,
3549 : parentOid, false);
3550 :
3551 : /*
3552 : * Mark the parent as having subclasses.
3553 : */
3554 12612 : SetRelationHasSubclass(parentOid, true);
3555 12612 : }
3556 :
3557 : /*
3558 : * Look for an existing column entry with the given name.
3559 : *
3560 : * Returns the index (starting with 1) if attribute already exists in columns,
3561 : * 0 if it doesn't.
3562 : */
3563 : static int
3564 22610 : findAttrByName(const char *attributeName, const List *columns)
3565 : {
3566 : ListCell *lc;
3567 22610 : int i = 1;
3568 :
3569 42122 : foreach(lc, columns)
3570 : {
3571 20094 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3572 582 : return i;
3573 :
3574 19512 : i++;
3575 : }
3576 22028 : return 0;
3577 : }
3578 :
3579 :
3580 : /*
3581 : * SetRelationHasSubclass
3582 : * Set the value of the relation's relhassubclass field in pg_class.
3583 : *
3584 : * NOTE: caller must be holding an appropriate lock on the relation.
3585 : * ShareUpdateExclusiveLock is sufficient.
3586 : *
3587 : * NOTE: an important side-effect of this operation is that an SI invalidation
3588 : * message is sent out to all backends --- including me --- causing plans
3589 : * referencing the relation to be rebuilt with the new list of children.
3590 : * This must happen even if we find that no change is needed in the pg_class
3591 : * row.
3592 : */
3593 : void
3594 15958 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3595 : {
3596 : Relation relationRelation;
3597 : HeapTuple tuple;
3598 : Form_pg_class classtuple;
3599 :
3600 : /*
3601 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3602 : */
3603 15958 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3604 15958 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3605 15958 : if (!HeapTupleIsValid(tuple))
3606 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3607 15958 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3608 :
3609 15958 : if (classtuple->relhassubclass != relhassubclass)
3610 : {
3611 7504 : classtuple->relhassubclass = relhassubclass;
3612 7504 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3613 : }
3614 : else
3615 : {
3616 : /* no need to change tuple, but force relcache rebuild anyway */
3617 8454 : CacheInvalidateRelcacheByTuple(tuple);
3618 : }
3619 :
3620 15958 : heap_freetuple(tuple);
3621 15958 : table_close(relationRelation, RowExclusiveLock);
3622 15958 : }
3623 :
3624 : /*
3625 : * CheckRelationTableSpaceMove
3626 : * Check if relation can be moved to new tablespace.
3627 : *
3628 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3629 : *
3630 : * Returns true if the relation can be moved to the new tablespace; raises
3631 : * an error if it is not possible to do the move; returns false if the move
3632 : * would have no effect.
3633 : */
3634 : bool
3635 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3636 : {
3637 : Oid oldTableSpaceId;
3638 :
3639 : /*
3640 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3641 : * stored as 0.
3642 : */
3643 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3644 226 : if (newTableSpaceId == oldTableSpaceId ||
3645 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3646 10 : return false;
3647 :
3648 : /*
3649 : * We cannot support moving mapped relations into different tablespaces.
3650 : * (In particular this eliminates all shared catalogs.)
3651 : */
3652 216 : if (RelationIsMapped(rel))
3653 0 : ereport(ERROR,
3654 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3655 : errmsg("cannot move system relation \"%s\"",
3656 : RelationGetRelationName(rel))));
3657 :
3658 : /* Cannot move a non-shared relation into pg_global */
3659 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3660 12 : ereport(ERROR,
3661 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3662 : errmsg("only shared relations can be placed in pg_global tablespace")));
3663 :
3664 : /*
3665 : * Do not allow moving temp tables of other backends ... their local
3666 : * buffer manager is not going to cope.
3667 : */
3668 204 : if (RELATION_IS_OTHER_TEMP(rel))
3669 0 : ereport(ERROR,
3670 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3671 : errmsg("cannot move temporary tables of other sessions")));
3672 :
3673 204 : return true;
3674 : }
3675 :
3676 : /*
3677 : * SetRelationTableSpace
3678 : * Set new reltablespace and relfilenumber in pg_class entry.
3679 : *
3680 : * newTableSpaceId is the new tablespace for the relation, and
3681 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3682 : * InvalidRelFileNumber, this field is not updated.
3683 : *
3684 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3685 : *
3686 : * The caller of this routine had better check if a relation can be
3687 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3688 : * first, and is responsible for making the change visible with
3689 : * CommandCounterIncrement().
3690 : */
3691 : void
3692 204 : SetRelationTableSpace(Relation rel,
3693 : Oid newTableSpaceId,
3694 : RelFileNumber newRelFilenumber)
3695 : {
3696 : Relation pg_class;
3697 : HeapTuple tuple;
3698 : Form_pg_class rd_rel;
3699 204 : Oid reloid = RelationGetRelid(rel);
3700 :
3701 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3702 :
3703 : /* Get a modifiable copy of the relation's pg_class row. */
3704 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3705 :
3706 204 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3707 204 : if (!HeapTupleIsValid(tuple))
3708 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3709 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3710 :
3711 : /* Update the pg_class row. */
3712 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3713 204 : InvalidOid : newTableSpaceId;
3714 204 : if (RelFileNumberIsValid(newRelFilenumber))
3715 160 : rd_rel->relfilenode = newRelFilenumber;
3716 204 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3717 :
3718 : /*
3719 : * Record dependency on tablespace. This is only required for relations
3720 : * that have no physical storage.
3721 : */
3722 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3723 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3724 : rd_rel->reltablespace);
3725 :
3726 204 : heap_freetuple(tuple);
3727 204 : table_close(pg_class, RowExclusiveLock);
3728 204 : }
3729 :
3730 : /*
3731 : * renameatt_check - basic sanity checks before attribute rename
3732 : */
3733 : static void
3734 976 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3735 : {
3736 976 : char relkind = classform->relkind;
3737 :
3738 976 : if (classform->reloftype && !recursing)
3739 6 : ereport(ERROR,
3740 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3741 : errmsg("cannot rename column of typed table")));
3742 :
3743 : /*
3744 : * Renaming the columns of sequences or toast tables doesn't actually
3745 : * break anything from the system's point of view, since internal
3746 : * references are by attnum. But it doesn't seem right to allow users to
3747 : * change names that are hardcoded into the system, hence the following
3748 : * restriction.
3749 : */
3750 970 : if (relkind != RELKIND_RELATION &&
3751 84 : relkind != RELKIND_VIEW &&
3752 84 : relkind != RELKIND_MATVIEW &&
3753 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3754 36 : relkind != RELKIND_INDEX &&
3755 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3756 0 : relkind != RELKIND_FOREIGN_TABLE &&
3757 : relkind != RELKIND_PARTITIONED_TABLE)
3758 0 : ereport(ERROR,
3759 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3760 : errmsg("cannot rename columns of relation \"%s\"",
3761 : NameStr(classform->relname)),
3762 : errdetail_relkind_not_supported(relkind)));
3763 :
3764 : /*
3765 : * permissions checking. only the owner of a class can change its schema.
3766 : */
3767 970 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3768 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3769 0 : NameStr(classform->relname));
3770 970 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3771 2 : ereport(ERROR,
3772 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3773 : errmsg("permission denied: \"%s\" is a system catalog",
3774 : NameStr(classform->relname))));
3775 968 : }
3776 :
3777 : /*
3778 : * renameatt_internal - workhorse for renameatt
3779 : *
3780 : * Return value is the attribute number in the 'myrelid' relation.
3781 : */
3782 : static AttrNumber
3783 540 : renameatt_internal(Oid myrelid,
3784 : const char *oldattname,
3785 : const char *newattname,
3786 : bool recurse,
3787 : bool recursing,
3788 : int expected_parents,
3789 : DropBehavior behavior)
3790 : {
3791 : Relation targetrelation;
3792 : Relation attrelation;
3793 : HeapTuple atttup;
3794 : Form_pg_attribute attform;
3795 : AttrNumber attnum;
3796 :
3797 : /*
3798 : * Grab an exclusive lock on the target table, which we will NOT release
3799 : * until end of transaction.
3800 : */
3801 540 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3802 540 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3803 :
3804 : /*
3805 : * if the 'recurse' flag is set then we are supposed to rename this
3806 : * attribute in all classes that inherit from 'relname' (as well as in
3807 : * 'relname').
3808 : *
3809 : * any permissions or problems with duplicate attributes will cause the
3810 : * whole transaction to abort, which is what we want -- all or nothing.
3811 : */
3812 540 : if (recurse)
3813 : {
3814 : List *child_oids,
3815 : *child_numparents;
3816 : ListCell *lo,
3817 : *li;
3818 :
3819 : /*
3820 : * we need the number of parents for each child so that the recursive
3821 : * calls to renameatt() can determine whether there are any parents
3822 : * outside the inheritance hierarchy being processed.
3823 : */
3824 236 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3825 : &child_numparents);
3826 :
3827 : /*
3828 : * find_all_inheritors does the recursive search of the inheritance
3829 : * hierarchy, so all we have to do is process all of the relids in the
3830 : * list that it returns.
3831 : */
3832 710 : forboth(lo, child_oids, li, child_numparents)
3833 : {
3834 504 : Oid childrelid = lfirst_oid(lo);
3835 504 : int numparents = lfirst_int(li);
3836 :
3837 504 : if (childrelid == myrelid)
3838 236 : continue;
3839 : /* note we need not recurse again */
3840 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3841 : }
3842 : }
3843 : else
3844 : {
3845 : /*
3846 : * If we are told not to recurse, there had better not be any child
3847 : * tables; else the rename would put them out of step.
3848 : *
3849 : * expected_parents will only be 0 if we are not already recursing.
3850 : */
3851 340 : if (expected_parents == 0 &&
3852 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3853 12 : ereport(ERROR,
3854 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3855 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3856 : oldattname)));
3857 : }
3858 :
3859 : /* rename attributes in typed tables of composite type */
3860 498 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3861 : {
3862 : List *child_oids;
3863 : ListCell *lo;
3864 :
3865 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3866 24 : RelationGetRelationName(targetrelation),
3867 : behavior);
3868 :
3869 24 : foreach(lo, child_oids)
3870 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3871 : }
3872 :
3873 492 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3874 :
3875 492 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3876 492 : if (!HeapTupleIsValid(atttup))
3877 24 : ereport(ERROR,
3878 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3879 : errmsg("column \"%s\" does not exist",
3880 : oldattname)));
3881 468 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3882 :
3883 468 : attnum = attform->attnum;
3884 468 : if (attnum <= 0)
3885 0 : ereport(ERROR,
3886 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3887 : errmsg("cannot rename system column \"%s\"",
3888 : oldattname)));
3889 :
3890 : /*
3891 : * if the attribute is inherited, forbid the renaming. if this is a
3892 : * top-level call to renameatt(), then expected_parents will be 0, so the
3893 : * effect of this code will be to prohibit the renaming if the attribute
3894 : * is inherited at all. if this is a recursive call to renameatt(),
3895 : * expected_parents will be the number of parents the current relation has
3896 : * within the inheritance hierarchy being processed, so we'll prohibit the
3897 : * renaming only if there are additional parents from elsewhere.
3898 : */
3899 468 : if (attform->attinhcount > expected_parents)
3900 30 : ereport(ERROR,
3901 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3902 : errmsg("cannot rename inherited column \"%s\"",
3903 : oldattname)));
3904 :
3905 : /* new name should not already exist */
3906 438 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3907 :
3908 : /* apply the update */
3909 426 : namestrcpy(&(attform->attname), newattname);
3910 :
3911 426 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3912 :
3913 426 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3914 :
3915 426 : heap_freetuple(atttup);
3916 :
3917 426 : table_close(attrelation, RowExclusiveLock);
3918 :
3919 426 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3920 :
3921 426 : return attnum;
3922 : }
3923 :
3924 : /*
3925 : * Perform permissions and integrity checks before acquiring a relation lock.
3926 : */
3927 : static void
3928 398 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3929 : void *arg)
3930 : {
3931 : HeapTuple tuple;
3932 : Form_pg_class form;
3933 :
3934 398 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3935 398 : if (!HeapTupleIsValid(tuple))
3936 40 : return; /* concurrently dropped */
3937 358 : form = (Form_pg_class) GETSTRUCT(tuple);
3938 358 : renameatt_check(relid, form, false);
3939 350 : ReleaseSysCache(tuple);
3940 : }
3941 :
3942 : /*
3943 : * renameatt - changes the name of an attribute in a relation
3944 : *
3945 : * The returned ObjectAddress is that of the renamed column.
3946 : */
3947 : ObjectAddress
3948 304 : renameatt(RenameStmt *stmt)
3949 : {
3950 : Oid relid;
3951 : AttrNumber attnum;
3952 : ObjectAddress address;
3953 :
3954 : /* lock level taken here should match renameatt_internal */
3955 304 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3956 304 : stmt->missing_ok ? RVR_MISSING_OK : 0,
3957 : RangeVarCallbackForRenameAttribute,
3958 : NULL);
3959 :
3960 290 : if (!OidIsValid(relid))
3961 : {
3962 24 : ereport(NOTICE,
3963 : (errmsg("relation \"%s\" does not exist, skipping",
3964 : stmt->relation->relname)));
3965 24 : return InvalidObjectAddress;
3966 : }
3967 :
3968 : attnum =
3969 266 : renameatt_internal(relid,
3970 266 : stmt->subname, /* old att name */
3971 266 : stmt->newname, /* new att name */
3972 266 : stmt->relation->inh, /* recursive? */
3973 : false, /* recursing? */
3974 : 0, /* expected inhcount */
3975 : stmt->behavior);
3976 :
3977 182 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3978 :
3979 182 : return address;
3980 : }
3981 :
3982 : /*
3983 : * same logic as renameatt_internal
3984 : */
3985 : static ObjectAddress
3986 84 : rename_constraint_internal(Oid myrelid,
3987 : Oid mytypid,
3988 : const char *oldconname,
3989 : const char *newconname,
3990 : bool recurse,
3991 : bool recursing,
3992 : int expected_parents)
3993 : {
3994 84 : Relation targetrelation = NULL;
3995 : Oid constraintOid;
3996 : HeapTuple tuple;
3997 : Form_pg_constraint con;
3998 : ObjectAddress address;
3999 :
4000 : Assert(!myrelid || !mytypid);
4001 :
4002 84 : if (mytypid)
4003 : {
4004 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4005 : }
4006 : else
4007 : {
4008 78 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4009 :
4010 : /*
4011 : * don't tell it whether we're recursing; we allow changing typed
4012 : * tables here
4013 : */
4014 78 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4015 :
4016 78 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4017 : }
4018 :
4019 84 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4020 84 : if (!HeapTupleIsValid(tuple))
4021 0 : elog(ERROR, "cache lookup failed for constraint %u",
4022 : constraintOid);
4023 84 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4024 :
4025 84 : if (myrelid &&
4026 78 : (con->contype == CONSTRAINT_CHECK ||
4027 18 : con->contype == CONSTRAINT_NOTNULL) &&
4028 60 : !con->connoinherit)
4029 : {
4030 48 : if (recurse)
4031 : {
4032 : List *child_oids,
4033 : *child_numparents;
4034 : ListCell *lo,
4035 : *li;
4036 :
4037 30 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4038 : &child_numparents);
4039 :
4040 72 : forboth(lo, child_oids, li, child_numparents)
4041 : {
4042 42 : Oid childrelid = lfirst_oid(lo);
4043 42 : int numparents = lfirst_int(li);
4044 :
4045 42 : if (childrelid == myrelid)
4046 30 : continue;
4047 :
4048 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4049 : }
4050 : }
4051 : else
4052 : {
4053 24 : if (expected_parents == 0 &&
4054 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4055 6 : ereport(ERROR,
4056 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4057 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4058 : oldconname)));
4059 : }
4060 :
4061 42 : if (con->coninhcount > expected_parents)
4062 6 : ereport(ERROR,
4063 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4064 : errmsg("cannot rename inherited constraint \"%s\"",
4065 : oldconname)));
4066 : }
4067 :
4068 72 : if (con->conindid
4069 18 : && (con->contype == CONSTRAINT_PRIMARY
4070 6 : || con->contype == CONSTRAINT_UNIQUE
4071 0 : || con->contype == CONSTRAINT_EXCLUSION))
4072 : /* rename the index; this renames the constraint as well */
4073 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4074 : else
4075 54 : RenameConstraintById(constraintOid, newconname);
4076 :
4077 72 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4078 :
4079 72 : ReleaseSysCache(tuple);
4080 :
4081 72 : if (targetrelation)
4082 : {
4083 : /*
4084 : * Invalidate relcache so as others can see the new constraint name.
4085 : */
4086 66 : CacheInvalidateRelcache(targetrelation);
4087 :
4088 66 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4089 : }
4090 :
4091 72 : return address;
4092 : }
4093 :
4094 : ObjectAddress
4095 78 : RenameConstraint(RenameStmt *stmt)
4096 : {
4097 78 : Oid relid = InvalidOid;
4098 78 : Oid typid = InvalidOid;
4099 :
4100 78 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4101 : {
4102 : Relation rel;
4103 : HeapTuple tup;
4104 :
4105 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4106 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4107 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4108 6 : if (!HeapTupleIsValid(tup))
4109 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4110 6 : checkDomainOwner(tup);
4111 6 : ReleaseSysCache(tup);
4112 6 : table_close(rel, NoLock);
4113 : }
4114 : else
4115 : {
4116 : /* lock level taken here should match rename_constraint_internal */
4117 72 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4118 72 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4119 : RangeVarCallbackForRenameAttribute,
4120 : NULL);
4121 72 : if (!OidIsValid(relid))
4122 : {
4123 6 : ereport(NOTICE,
4124 : (errmsg("relation \"%s\" does not exist, skipping",
4125 : stmt->relation->relname)));
4126 6 : return InvalidObjectAddress;
4127 : }
4128 : }
4129 :
4130 : return
4131 72 : rename_constraint_internal(relid, typid,
4132 72 : stmt->subname,
4133 72 : stmt->newname,
4134 138 : (stmt->relation &&
4135 66 : stmt->relation->inh), /* recursive? */
4136 : false, /* recursing? */
4137 : 0 /* expected inhcount */ );
4138 : }
4139 :
4140 : /*
4141 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4142 : * RENAME
4143 : */
4144 : ObjectAddress
4145 510 : RenameRelation(RenameStmt *stmt)
4146 : {
4147 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4148 : Oid relid;
4149 : ObjectAddress address;
4150 :
4151 : /*
4152 : * Grab an exclusive lock on the target table, index, sequence, view,
4153 : * materialized view, or foreign table, which we will NOT release until
4154 : * end of transaction.
4155 : *
4156 : * Lock level used here should match RenameRelationInternal, to avoid lock
4157 : * escalation. However, because ALTER INDEX can be used with any relation
4158 : * type, we mustn't believe without verification.
4159 : */
4160 : for (;;)
4161 12 : {
4162 : LOCKMODE lockmode;
4163 : char relkind;
4164 : bool obj_is_index;
4165 :
4166 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4167 :
4168 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4169 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4170 : RangeVarCallbackForAlterRelation,
4171 : (void *) stmt);
4172 :
4173 472 : if (!OidIsValid(relid))
4174 : {
4175 18 : ereport(NOTICE,
4176 : (errmsg("relation \"%s\" does not exist, skipping",
4177 : stmt->relation->relname)));
4178 18 : return InvalidObjectAddress;
4179 : }
4180 :
4181 : /*
4182 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4183 : * to rename a table), but we might've used the wrong lock level. If
4184 : * that happens, retry with the correct lock level. We don't bother
4185 : * if we already acquired AccessExclusiveLock with an index, however.
4186 : */
4187 454 : relkind = get_rel_relkind(relid);
4188 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4189 : relkind == RELKIND_PARTITIONED_INDEX);
4190 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4191 : break;
4192 :
4193 12 : UnlockRelationOid(relid, lockmode);
4194 12 : is_index_stmt = obj_is_index;
4195 : }
4196 :
4197 : /* Do the work */
4198 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4199 :
4200 430 : ObjectAddressSet(address, RelationRelationId, relid);
4201 :
4202 430 : return address;
4203 : }
4204 :
4205 : /*
4206 : * RenameRelationInternal - change the name of a relation
4207 : */
4208 : void
4209 1336 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4210 : {
4211 : Relation targetrelation;
4212 : Relation relrelation; /* for RELATION relation */
4213 : HeapTuple reltup;
4214 : Form_pg_class relform;
4215 : Oid namespaceId;
4216 :
4217 : /*
4218 : * Grab a lock on the target relation, which we will NOT release until end
4219 : * of transaction. We need at least a self-exclusive lock so that
4220 : * concurrent DDL doesn't overwrite the rename if they start updating
4221 : * while still seeing the old version. The lock also guards against
4222 : * triggering relcache reloads in concurrent sessions, which might not
4223 : * handle this information changing under them. For indexes, we can use a
4224 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4225 : * specially.
4226 : */
4227 1336 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4228 1336 : namespaceId = RelationGetNamespace(targetrelation);
4229 :
4230 : /*
4231 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4232 : */
4233 1336 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4234 :
4235 1336 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4236 1336 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4237 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4238 1336 : relform = (Form_pg_class) GETSTRUCT(reltup);
4239 :
4240 1336 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4241 12 : ereport(ERROR,
4242 : (errcode(ERRCODE_DUPLICATE_TABLE),
4243 : errmsg("relation \"%s\" already exists",
4244 : newrelname)));
4245 :
4246 : /*
4247 : * RenameRelation is careful not to believe the caller's idea of the
4248 : * relation kind being handled. We don't have to worry about this, but
4249 : * let's not be totally oblivious to it. We can process an index as
4250 : * not-an-index, but not the other way around.
4251 : */
4252 : Assert(!is_index ||
4253 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4254 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4255 :
4256 : /*
4257 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4258 : * because it's a copy...)
4259 : */
4260 1324 : namestrcpy(&(relform->relname), newrelname);
4261 :
4262 1324 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4263 :
4264 1324 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4265 : InvalidOid, is_internal);
4266 :
4267 1324 : heap_freetuple(reltup);
4268 1324 : table_close(relrelation, RowExclusiveLock);
4269 :
4270 : /*
4271 : * Also rename the associated type, if any.
4272 : */
4273 1324 : if (OidIsValid(targetrelation->rd_rel->reltype))
4274 160 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4275 : newrelname, namespaceId);
4276 :
4277 : /*
4278 : * Also rename the associated constraint, if any.
4279 : */
4280 1324 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4281 724 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4282 : {
4283 618 : Oid constraintId = get_index_constraint(myrelid);
4284 :
4285 618 : if (OidIsValid(constraintId))
4286 36 : RenameConstraintById(constraintId, newrelname);
4287 : }
4288 :
4289 : /*
4290 : * Close rel, but keep lock!
4291 : */
4292 1324 : relation_close(targetrelation, NoLock);
4293 1324 : }
4294 :
4295 : /*
4296 : * ResetRelRewrite - reset relrewrite
4297 : */
4298 : void
4299 412 : ResetRelRewrite(Oid myrelid)
4300 : {
4301 : Relation relrelation; /* for RELATION relation */
4302 : HeapTuple reltup;
4303 : Form_pg_class relform;
4304 :
4305 : /*
4306 : * Find relation's pg_class tuple.
4307 : */
4308 412 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4309 :
4310 412 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4311 412 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4312 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4313 412 : relform = (Form_pg_class) GETSTRUCT(reltup);
4314 :
4315 : /*
4316 : * Update pg_class tuple.
4317 : */
4318 412 : relform->relrewrite = InvalidOid;
4319 :
4320 412 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4321 :
4322 412 : heap_freetuple(reltup);
4323 412 : table_close(relrelation, RowExclusiveLock);
4324 412 : }
4325 :
4326 : /*
4327 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4328 : * any open reference to the target table besides the one just acquired by
4329 : * the calling command; this implies there's an open cursor or active plan.
4330 : * We need this check because our lock doesn't protect us against stomping
4331 : * on our own foot, only other people's feet!
4332 : *
4333 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4334 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4335 : * possibly be relaxed to only error out for certain types of alterations.
4336 : * But the use-case for allowing any of these things is not obvious, so we
4337 : * won't work hard at it for now.
4338 : *
4339 : * We also reject these commands if there are any pending AFTER trigger events
4340 : * for the rel. This is certainly necessary for the rewriting variants of
4341 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4342 : * events would try to fetch the wrong tuples. It might be overly cautious
4343 : * in other cases, but again it seems better to err on the side of paranoia.
4344 : *
4345 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4346 : * we are worried about active indexscans on the index. The trigger-event
4347 : * check can be skipped, since we are doing no damage to the parent table.
4348 : *
4349 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4350 : */
4351 : void
4352 137850 : CheckTableNotInUse(Relation rel, const char *stmt)
4353 : {
4354 : int expected_refcnt;
4355 :
4356 137850 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4357 137850 : if (rel->rd_refcnt != expected_refcnt)
4358 24 : ereport(ERROR,
4359 : (errcode(ERRCODE_OBJECT_IN_USE),
4360 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4361 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4362 : stmt, RelationGetRelationName(rel))));
4363 :
4364 137826 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4365 215680 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4366 106800 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4367 18 : ereport(ERROR,
4368 : (errcode(ERRCODE_OBJECT_IN_USE),
4369 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4370 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4371 : stmt, RelationGetRelationName(rel))));
4372 137808 : }
4373 :
4374 : /*
4375 : * AlterTableLookupRelation
4376 : * Look up, and lock, the OID for the relation named by an alter table
4377 : * statement.
4378 : */
4379 : Oid
4380 34678 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4381 : {
4382 69272 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4383 34678 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4384 : RangeVarCallbackForAlterRelation,
4385 : (void *) stmt);
4386 : }
4387 :
4388 : /*
4389 : * AlterTable
4390 : * Execute ALTER TABLE, which can be a list of subcommands
4391 : *
4392 : * ALTER TABLE is performed in three phases:
4393 : * 1. Examine subcommands and perform pre-transformation checking.
4394 : * 2. Validate and transform subcommands, and update system catalogs.
4395 : * 3. Scan table(s) to check new constraints, and optionally recopy
4396 : * the data into new table(s).
4397 : * Phase 3 is not performed unless one or more of the subcommands requires
4398 : * it. The intention of this design is to allow multiple independent
4399 : * updates of the table schema to be performed with only one pass over the
4400 : * data.
4401 : *
4402 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4403 : * each table to be affected (there may be multiple affected tables if the
4404 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4405 : * validation of the subcommands. Because earlier subcommands may change
4406 : * the catalog state seen by later commands, there are limits to what can
4407 : * be done in this phase. Generally, this phase acquires table locks,
4408 : * checks permissions and relkind, and recurses to find child tables.
4409 : *
4410 : * ATRewriteCatalogs performs phase 2 for each affected table.
4411 : * Certain subcommands need to be performed before others to avoid
4412 : * unnecessary conflicts; for example, DROP COLUMN should come before
4413 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4414 : * lists, one for each logical "pass" of phase 2.
4415 : *
4416 : * ATRewriteTables performs phase 3 for those tables that need it.
4417 : *
4418 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4419 : * since phase 1 already does it. However, for certain subcommand types
4420 : * it is only possible to determine how to recurse at phase 2 time; for
4421 : * those cases, phase 1 sets the cmd->recurse flag.
4422 : *
4423 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4424 : * the whole operation; we don't have to do anything special to clean up.
4425 : *
4426 : * The caller must lock the relation, with an appropriate lock level
4427 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4428 : * or higher. We pass the lock level down
4429 : * so that we can apply it recursively to inherited tables. Note that the
4430 : * lock level we want as we recurse might well be higher than required for
4431 : * that specific subcommand. So we pass down the overall lock requirement,
4432 : * rather than reassess it at lower levels.
4433 : *
4434 : * The caller also provides a "context" which is to be passed back to
4435 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4436 : * Some of the fields therein, such as the relid, are used here as well.
4437 : */
4438 : void
4439 34456 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4440 : AlterTableUtilityContext *context)
4441 : {
4442 : Relation rel;
4443 :
4444 : /* Caller is required to provide an adequate lock. */
4445 34456 : rel = relation_open(context->relid, NoLock);
4446 :
4447 34456 : CheckTableNotInUse(rel, "ALTER TABLE");
4448 :
4449 34438 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4450 31152 : }
4451 :
4452 : /*
4453 : * AlterTableInternal
4454 : *
4455 : * ALTER TABLE with target specified by OID
4456 : *
4457 : * We do not reject if the relation is already open, because it's quite
4458 : * likely that one or more layers of caller have it open. That means it
4459 : * is unsafe to use this entry point for alterations that could break
4460 : * existing query plans. On the assumption it's not used for such, we
4461 : * don't have to reject pending AFTER triggers, either.
4462 : *
4463 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4464 : * used for any subcommand types that require parse transformation or
4465 : * could generate subcommands that have to be passed to ProcessUtility.
4466 : */
4467 : void
4468 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4469 : {
4470 : Relation rel;
4471 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4472 :
4473 278 : rel = relation_open(relid, lockmode);
4474 :
4475 278 : EventTriggerAlterTableRelid(relid);
4476 :
4477 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4478 278 : }
4479 :
4480 : /*
4481 : * AlterTableGetLockLevel
4482 : *
4483 : * Sets the overall lock level required for the supplied list of subcommands.
4484 : * Policy for doing this set according to needs of AlterTable(), see
4485 : * comments there for overall explanation.
4486 : *
4487 : * Function is called before and after parsing, so it must give same
4488 : * answer each time it is called. Some subcommands are transformed
4489 : * into other subcommand types, so the transform must never be made to a
4490 : * lower lock level than previously assigned. All transforms are noted below.
4491 : *
4492 : * Since this is called before we lock the table we cannot use table metadata
4493 : * to influence the type of lock we acquire.
4494 : *
4495 : * There should be no lockmodes hardcoded into the subcommand functions. All
4496 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4497 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4498 : * and does not travel through this section of code and cannot be combined with
4499 : * any of the subcommands given here.
4500 : *
4501 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4502 : * so any changes that might affect SELECTs running on standbys need to use
4503 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4504 : * have a solution for that also.
4505 : *
4506 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4507 : * that takes a lock less than AccessExclusiveLock can change object definitions
4508 : * while pg_dump is running. Be careful to check that the appropriate data is
4509 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4510 : * otherwise we might end up with an inconsistent dump that can't restore.
4511 : */
4512 : LOCKMODE
4513 34956 : AlterTableGetLockLevel(List *cmds)
4514 : {
4515 : /*
4516 : * This only works if we read catalog tables using MVCC snapshots.
4517 : */
4518 : ListCell *lcmd;
4519 34956 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4520 :
4521 71922 : foreach(lcmd, cmds)
4522 : {
4523 36966 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4524 36966 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4525 :
4526 36966 : switch (cmd->subtype)
4527 : {
4528 : /*
4529 : * These subcommands rewrite the heap, so require full locks.
4530 : */
4531 3228 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4532 : * to SELECT */
4533 : case AT_SetAccessMethod: /* must rewrite heap */
4534 : case AT_SetTableSpace: /* must rewrite heap */
4535 : case AT_AlterColumnType: /* must rewrite heap */
4536 3228 : cmd_lockmode = AccessExclusiveLock;
4537 3228 : break;
4538 :
4539 : /*
4540 : * These subcommands may require addition of toast tables. If
4541 : * we add a toast table to a table currently being scanned, we
4542 : * might miss data added to the new toast table by concurrent
4543 : * insert transactions.
4544 : */
4545 212 : case AT_SetStorage: /* may add toast tables, see
4546 : * ATRewriteCatalogs() */
4547 212 : cmd_lockmode = AccessExclusiveLock;
4548 212 : break;
4549 :
4550 : /*
4551 : * Removing constraints can affect SELECTs that have been
4552 : * optimized assuming the constraint holds true. See also
4553 : * CloneFkReferenced.
4554 : */
4555 1242 : case AT_DropConstraint: /* as DROP INDEX */
4556 : case AT_DropNotNull: /* may change some SQL plans */
4557 1242 : cmd_lockmode = AccessExclusiveLock;
4558 1242 : break;
4559 :
4560 : /*
4561 : * Subcommands that may be visible to concurrent SELECTs
4562 : */
4563 1718 : case AT_DropColumn: /* change visible to SELECT */
4564 : case AT_AddColumnToView: /* CREATE VIEW */
4565 : case AT_DropOids: /* used to equiv to DropColumn */
4566 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4567 : case AT_EnableReplicaRule: /* may change SELECT rules */
4568 : case AT_EnableRule: /* may change SELECT rules */
4569 : case AT_DisableRule: /* may change SELECT rules */
4570 1718 : cmd_lockmode = AccessExclusiveLock;
4571 1718 : break;
4572 :
4573 : /*
4574 : * Changing owner may remove implicit SELECT privileges
4575 : */
4576 1806 : case AT_ChangeOwner: /* change visible to SELECT */
4577 1806 : cmd_lockmode = AccessExclusiveLock;
4578 1806 : break;
4579 :
4580 : /*
4581 : * Changing foreign table options may affect optimization.
4582 : */
4583 238 : case AT_GenericOptions:
4584 : case AT_AlterColumnGenericOptions:
4585 238 : cmd_lockmode = AccessExclusiveLock;
4586 238 : break;
4587 :
4588 : /*
4589 : * These subcommands affect write operations only.
4590 : */
4591 340 : case AT_EnableTrig:
4592 : case AT_EnableAlwaysTrig:
4593 : case AT_EnableReplicaTrig:
4594 : case AT_EnableTrigAll:
4595 : case AT_EnableTrigUser:
4596 : case AT_DisableTrig:
4597 : case AT_DisableTrigAll:
4598 : case AT_DisableTrigUser:
4599 340 : cmd_lockmode = ShareRowExclusiveLock;
4600 340 : break;
4601 :
4602 : /*
4603 : * These subcommands affect write operations only. XXX
4604 : * Theoretically, these could be ShareRowExclusiveLock.
4605 : */
4606 9658 : case AT_ColumnDefault:
4607 : case AT_CookedColumnDefault:
4608 : case AT_AlterConstraint:
4609 : case AT_AddIndex: /* from ADD CONSTRAINT */
4610 : case AT_AddIndexConstraint:
4611 : case AT_ReplicaIdentity:
4612 : case AT_SetNotNull:
4613 : case AT_SetAttNotNull:
4614 : case AT_EnableRowSecurity:
4615 : case AT_DisableRowSecurity:
4616 : case AT_ForceRowSecurity:
4617 : case AT_NoForceRowSecurity:
4618 : case AT_AddIdentity:
4619 : case AT_DropIdentity:
4620 : case AT_SetIdentity:
4621 : case AT_SetExpression:
4622 : case AT_DropExpression:
4623 : case AT_SetCompression:
4624 9658 : cmd_lockmode = AccessExclusiveLock;
4625 9658 : break;
4626 :
4627 12846 : case AT_AddConstraint:
4628 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4629 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4630 12846 : if (IsA(cmd->def, Constraint))
4631 : {
4632 12846 : Constraint *con = (Constraint *) cmd->def;
4633 :
4634 12846 : switch (con->contype)
4635 : {
4636 9508 : case CONSTR_EXCLUSION:
4637 : case CONSTR_PRIMARY:
4638 : case CONSTR_UNIQUE:
4639 :
4640 : /*
4641 : * Cases essentially the same as CREATE INDEX. We
4642 : * could reduce the lock strength to ShareLock if
4643 : * we can work out how to allow concurrent catalog
4644 : * updates. XXX Might be set down to
4645 : * ShareRowExclusiveLock but requires further
4646 : * analysis.
4647 : */
4648 9508 : cmd_lockmode = AccessExclusiveLock;
4649 9508 : break;
4650 2386 : case CONSTR_FOREIGN:
4651 :
4652 : /*
4653 : * We add triggers to both tables when we add a
4654 : * Foreign Key, so the lock level must be at least
4655 : * as strong as CREATE TRIGGER.
4656 : */
4657 2386 : cmd_lockmode = ShareRowExclusiveLock;
4658 2386 : break;
4659 :
4660 952 : default:
4661 952 : cmd_lockmode = AccessExclusiveLock;
4662 : }
4663 0 : }
4664 12846 : break;
4665 :
4666 : /*
4667 : * These subcommands affect inheritance behaviour. Queries
4668 : * started before us will continue to see the old inheritance
4669 : * behaviour, while queries started after we commit will see
4670 : * new behaviour. No need to prevent reads or writes to the
4671 : * subtable while we hook it up though. Changing the TupDesc
4672 : * may be a problem, so keep highest lock.
4673 : */
4674 386 : case AT_AddInherit:
4675 : case AT_DropInherit:
4676 386 : cmd_lockmode = AccessExclusiveLock;
4677 386 : break;
4678 :
4679 : /*
4680 : * These subcommands affect implicit row type conversion. They
4681 : * have affects similar to CREATE/DROP CAST on queries. don't
4682 : * provide for invalidating parse trees as a result of such
4683 : * changes, so we keep these at AccessExclusiveLock.
4684 : */
4685 72 : case AT_AddOf:
4686 : case AT_DropOf:
4687 72 : cmd_lockmode = AccessExclusiveLock;
4688 72 : break;
4689 :
4690 : /*
4691 : * Only used by CREATE OR REPLACE VIEW which must conflict
4692 : * with an SELECTs currently using the view.
4693 : */
4694 194 : case AT_ReplaceRelOptions:
4695 194 : cmd_lockmode = AccessExclusiveLock;
4696 194 : break;
4697 :
4698 : /*
4699 : * These subcommands affect general strategies for performance
4700 : * and maintenance, though don't change the semantic results
4701 : * from normal data reads and writes. Delaying an ALTER TABLE
4702 : * behind currently active writes only delays the point where
4703 : * the new strategy begins to take effect, so there is no
4704 : * benefit in waiting. In this case the minimum restriction
4705 : * applies: we don't currently allow concurrent catalog
4706 : * updates.
4707 : */
4708 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4709 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4710 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4711 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4712 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4713 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4714 234 : break;
4715 :
4716 88 : case AT_SetLogged:
4717 : case AT_SetUnLogged:
4718 88 : cmd_lockmode = AccessExclusiveLock;
4719 88 : break;
4720 :
4721 388 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4722 388 : cmd_lockmode = ShareUpdateExclusiveLock;
4723 388 : break;
4724 :
4725 : /*
4726 : * Rel options are more complex than first appears. Options
4727 : * are set here for tables, views and indexes; for historical
4728 : * reasons these can all be used with ALTER TABLE, so we can't
4729 : * decide between them using the basic grammar.
4730 : */
4731 740 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4732 : * getTables() */
4733 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4734 : * getTables() */
4735 740 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4736 740 : break;
4737 :
4738 2608 : case AT_AttachPartition:
4739 2608 : cmd_lockmode = ShareUpdateExclusiveLock;
4740 2608 : break;
4741 :
4742 528 : case AT_DetachPartition:
4743 528 : if (((PartitionCmd *) cmd->def)->concurrent)
4744 158 : cmd_lockmode = ShareUpdateExclusiveLock;
4745 : else
4746 370 : cmd_lockmode = AccessExclusiveLock;
4747 528 : break;
4748 :
4749 14 : case AT_DetachPartitionFinalize:
4750 14 : cmd_lockmode = ShareUpdateExclusiveLock;
4751 14 : break;
4752 :
4753 270 : case AT_SplitPartition:
4754 270 : cmd_lockmode = AccessExclusiveLock;
4755 270 : break;
4756 :
4757 156 : case AT_MergePartitions:
4758 156 : cmd_lockmode = AccessExclusiveLock;
4759 156 : break;
4760 :
4761 0 : default: /* oops */
4762 0 : elog(ERROR, "unrecognized alter table type: %d",
4763 : (int) cmd->subtype);
4764 : break;
4765 : }
4766 :
4767 : /*
4768 : * Take the greatest lockmode from any subcommand
4769 : */
4770 36966 : if (cmd_lockmode > lockmode)
4771 31000 : lockmode = cmd_lockmode;
4772 : }
4773 :
4774 34956 : return lockmode;
4775 : }
4776 :
4777 : /*
4778 : * ATController provides top level control over the phases.
4779 : *
4780 : * parsetree is passed in to allow it to be passed to event triggers
4781 : * when requested.
4782 : */
4783 : static void
4784 34716 : ATController(AlterTableStmt *parsetree,
4785 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4786 : AlterTableUtilityContext *context)
4787 : {
4788 34716 : List *wqueue = NIL;
4789 : ListCell *lcmd;
4790 :
4791 : /* Phase 1: preliminary examination of commands, create work queue */
4792 71164 : foreach(lcmd, cmds)
4793 : {
4794 36720 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4795 :
4796 36720 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4797 : }
4798 :
4799 : /* Close the relation, but keep lock until commit */
4800 34444 : relation_close(rel, NoLock);
4801 :
4802 : /* Phase 2: update system catalogs */
4803 34444 : ATRewriteCatalogs(&wqueue, lockmode, context);
4804 :
4805 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4806 31778 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4807 31430 : }
4808 :
4809 : /*
4810 : * ATPrepCmd
4811 : *
4812 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4813 : * recursion and permission checks.
4814 : *
4815 : * Caller must have acquired appropriate lock type on relation already.
4816 : * This lock should be held until commit.
4817 : */
4818 : static void
4819 37420 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4820 : bool recurse, bool recursing, LOCKMODE lockmode,
4821 : AlterTableUtilityContext *context)
4822 : {
4823 : AlteredTableInfo *tab;
4824 37420 : AlterTablePass pass = AT_PASS_UNSET;
4825 :
4826 : /* Find or create work queue entry for this table */
4827 37420 : tab = ATGetQueueEntry(wqueue, rel);
4828 :
4829 : /*
4830 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4831 : * partitions that are pending detach.
4832 : */
4833 37420 : if (rel->rd_rel->relispartition &&
4834 2608 : cmd->subtype != AT_DetachPartitionFinalize &&
4835 1304 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4836 2 : ereport(ERROR,
4837 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4838 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4839 : RelationGetRelationName(rel)),
4840 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4841 :
4842 : /*
4843 : * Copy the original subcommand for each table, so we can scribble on it.
4844 : * This avoids conflicts when different child tables need to make
4845 : * different parse transformations (for example, the same column may have
4846 : * different column numbers in different children).
4847 : */
4848 37418 : cmd = copyObject(cmd);
4849 :
4850 : /*
4851 : * Do permissions and relkind checking, recursion to child tables if
4852 : * needed, and any additional phase-1 processing needed. (But beware of
4853 : * adding any processing that looks at table details that another
4854 : * subcommand could change. In some cases we reject multiple subcommands
4855 : * that could try to change the same state in contrary ways.)
4856 : */
4857 37418 : switch (cmd->subtype)
4858 : {
4859 1962 : case AT_AddColumn: /* ADD COLUMN */
4860 1962 : ATSimplePermissions(cmd->subtype, rel,
4861 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4862 1962 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4863 : lockmode, context);
4864 : /* Recursion occurs during execution phase */
4865 1950 : pass = AT_PASS_ADD_COL;
4866 1950 : break;
4867 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4868 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4869 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4870 : lockmode, context);
4871 : /* Recursion occurs during execution phase */
4872 24 : pass = AT_PASS_ADD_COL;
4873 24 : break;
4874 580 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4875 :
4876 : /*
4877 : * We allow defaults on views so that INSERT into a view can have
4878 : * default-ish behavior. This works because the rewriter
4879 : * substitutes default values into INSERTs before it expands
4880 : * rules.
4881 : */
4882 580 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4883 580 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4884 : /* No command-specific prep needed */
4885 580 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4886 580 : break;
4887 110 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4888 : /* This is currently used only in CREATE TABLE */
4889 : /* (so the permission check really isn't necessary) */
4890 110 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4891 : /* This command never recurses */
4892 110 : pass = AT_PASS_ADD_OTHERCONSTR;
4893 110 : break;
4894 158 : case AT_AddIdentity:
4895 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4896 : /* Set up recursion for phase 2; no other prep needed */
4897 158 : if (recurse)
4898 152 : cmd->recurse = true;
4899 158 : pass = AT_PASS_ADD_OTHERCONSTR;
4900 158 : break;
4901 62 : case AT_SetIdentity:
4902 62 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4903 : /* Set up recursion for phase 2; no other prep needed */
4904 62 : if (recurse)
4905 56 : cmd->recurse = true;
4906 : /* This should run after AddIdentity, so do it in MISC pass */
4907 62 : pass = AT_PASS_MISC;
4908 62 : break;
4909 62 : case AT_DropIdentity:
4910 62 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4911 : /* Set up recursion for phase 2; no other prep needed */
4912 62 : if (recurse)
4913 56 : cmd->recurse = true;
4914 62 : pass = AT_PASS_DROP;
4915 62 : break;
4916 262 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4917 262 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4918 : /* Set up recursion for phase 2; no other prep needed */
4919 256 : if (recurse)
4920 244 : cmd->recurse = true;
4921 256 : pass = AT_PASS_DROP;
4922 256 : break;
4923 378 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4924 378 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4925 : /* Set up recursion for phase 2; no other prep needed */
4926 372 : if (recurse)
4927 342 : cmd->recurse = true;
4928 372 : pass = AT_PASS_COL_ATTRS;
4929 372 : break;
4930 7386 : case AT_SetAttNotNull: /* set pg_attribute.attnotnull without adding
4931 : * a constraint */
4932 7386 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4933 : /* Need command-specific recursion decision */
4934 7386 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4935 7386 : pass = AT_PASS_COL_ATTRS;
4936 7386 : break;
4937 84 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4938 84 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4939 84 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4940 84 : pass = AT_PASS_SET_EXPRESSION;
4941 84 : break;
4942 44 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4943 44 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4944 44 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4945 44 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4946 32 : pass = AT_PASS_DROP;
4947 32 : break;
4948 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4949 164 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4950 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4951 : /* No command-specific prep needed */
4952 164 : pass = AT_PASS_MISC;
4953 164 : break;
4954 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4955 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4956 44 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4957 : /* This command never recurses */
4958 32 : pass = AT_PASS_MISC;
4959 32 : break;
4960 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4961 234 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4962 234 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4963 : /* No command-specific prep needed */
4964 234 : pass = AT_PASS_MISC;
4965 234 : break;
4966 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4967 68 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4968 : /* This command never recurses */
4969 : /* No command-specific prep needed */
4970 68 : pass = AT_PASS_MISC;
4971 68 : break;
4972 1624 : case AT_DropColumn: /* DROP COLUMN */
4973 1624 : ATSimplePermissions(cmd->subtype, rel,
4974 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4975 1618 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4976 : lockmode, context);
4977 : /* Recursion occurs during execution phase */
4978 1606 : pass = AT_PASS_DROP;
4979 1606 : break;
4980 0 : case AT_AddIndex: /* ADD INDEX */
4981 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4982 : /* This command never recurses */
4983 : /* No command-specific prep needed */
4984 0 : pass = AT_PASS_ADD_INDEX;
4985 0 : break;
4986 12912 : case AT_AddConstraint: /* ADD CONSTRAINT */
4987 12912 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4988 12912 : if (recurse)
4989 : {
4990 : /* recurses at exec time; lock descendants and set flag */
4991 12568 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
4992 12568 : cmd->recurse = true;
4993 : }
4994 12912 : pass = AT_PASS_ADD_CONSTR;
4995 12912 : break;
4996 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4997 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4998 : /* This command never recurses */
4999 : /* No command-specific prep needed */
5000 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5001 0 : break;
5002 942 : case AT_DropConstraint: /* DROP CONSTRAINT */
5003 942 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5004 942 : ATCheckPartitionsNotInUse(rel, lockmode);
5005 : /* Other recursion occurs during execution phase */
5006 : /* No command-specific prep needed except saving recurse flag */
5007 936 : if (recurse)
5008 732 : cmd->recurse = true;
5009 936 : pass = AT_PASS_DROP;
5010 936 : break;
5011 1146 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5012 1146 : ATSimplePermissions(cmd->subtype, rel,
5013 : ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5014 : /* See comments for ATPrepAlterColumnType */
5015 1146 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5016 : AT_PASS_UNSET, context);
5017 : Assert(cmd != NULL);
5018 : /* Performs own recursion */
5019 1140 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5020 : lockmode, context);
5021 996 : pass = AT_PASS_ALTER_TYPE;
5022 996 : break;
5023 164 : case AT_AlterColumnGenericOptions:
5024 164 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5025 : /* This command never recurses */
5026 : /* No command-specific prep needed */
5027 164 : pass = AT_PASS_MISC;
5028 164 : break;
5029 1782 : case AT_ChangeOwner: /* ALTER OWNER */
5030 : /* This command never recurses */
5031 : /* No command-specific prep needed */
5032 1782 : pass = AT_PASS_MISC;
5033 1782 : break;
5034 64 : case AT_ClusterOn: /* CLUSTER ON */
5035 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5036 64 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5037 : /* These commands never recurse */
5038 : /* No command-specific prep needed */
5039 64 : pass = AT_PASS_MISC;
5040 64 : break;
5041 38 : case AT_SetLogged: /* SET LOGGED */
5042 38 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5043 38 : if (tab->chgPersistence)
5044 0 : ereport(ERROR,
5045 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5046 : errmsg("cannot change persistence setting twice")));
5047 38 : tab->chgPersistence = ATPrepChangePersistence(rel, true);
5048 : /* force rewrite if necessary; see comment in ATRewriteTables */
5049 32 : if (tab->chgPersistence)
5050 : {
5051 26 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
5052 26 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5053 : }
5054 32 : pass = AT_PASS_MISC;
5055 32 : break;
5056 50 : case AT_SetUnLogged: /* SET UNLOGGED */
5057 50 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5058 50 : if (tab->chgPersistence)
5059 0 : ereport(ERROR,
5060 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5061 : errmsg("cannot change persistence setting twice")));
5062 50 : tab->chgPersistence = ATPrepChangePersistence(rel, false);
5063 : /* force rewrite if necessary; see comment in ATRewriteTables */
5064 44 : if (tab->chgPersistence)
5065 : {
5066 38 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
5067 38 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
5068 : }
5069 44 : pass = AT_PASS_MISC;
5070 44 : break;
5071 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5072 6 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5073 6 : pass = AT_PASS_DROP;
5074 6 : break;
5075 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5076 128 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5077 :
5078 : /* check if another access method change was already requested */
5079 128 : if (tab->chgAccessMethod)
5080 18 : ereport(ERROR,
5081 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5082 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5083 :
5084 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5085 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5086 110 : break;
5087 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5088 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
5089 : ATT_PARTITIONED_INDEX);
5090 : /* This command never recurses */
5091 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5092 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5093 158 : break;
5094 934 : case AT_SetRelOptions: /* SET (...) */
5095 : case AT_ResetRelOptions: /* RESET (...) */
5096 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5097 934 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
5098 : /* This command never recurses */
5099 : /* No command-specific prep needed */
5100 934 : pass = AT_PASS_MISC;
5101 934 : break;
5102 342 : case AT_AddInherit: /* INHERIT */
5103 342 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5104 : /* This command never recurses */
5105 342 : ATPrepAddInherit(rel);
5106 324 : pass = AT_PASS_MISC;
5107 324 : break;
5108 44 : case AT_DropInherit: /* NO INHERIT */
5109 44 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5110 : /* This command never recurses */
5111 : /* No command-specific prep needed */
5112 44 : pass = AT_PASS_MISC;
5113 44 : break;
5114 132 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5115 132 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5116 : /* Recursion occurs during execution phase */
5117 126 : pass = AT_PASS_MISC;
5118 126 : break;
5119 388 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5120 388 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5121 : /* Recursion occurs during execution phase */
5122 : /* No command-specific prep needed except saving recurse flag */
5123 388 : if (recurse)
5124 388 : cmd->recurse = true;
5125 388 : pass = AT_PASS_MISC;
5126 388 : break;
5127 456 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5128 456 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5129 456 : pass = AT_PASS_MISC;
5130 : /* This command never recurses */
5131 : /* No command-specific prep needed */
5132 456 : break;
5133 340 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5134 : case AT_EnableAlwaysTrig:
5135 : case AT_EnableReplicaTrig:
5136 : case AT_EnableTrigAll:
5137 : case AT_EnableTrigUser:
5138 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5139 : case AT_DisableTrigAll:
5140 : case AT_DisableTrigUser:
5141 340 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5142 : /* Set up recursion for phase 2; no other prep needed */
5143 340 : if (recurse)
5144 312 : cmd->recurse = true;
5145 340 : pass = AT_PASS_MISC;
5146 340 : break;
5147 532 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5148 : case AT_EnableAlwaysRule:
5149 : case AT_EnableReplicaRule:
5150 : case AT_DisableRule:
5151 : case AT_AddOf: /* OF */
5152 : case AT_DropOf: /* NOT OF */
5153 : case AT_EnableRowSecurity:
5154 : case AT_DisableRowSecurity:
5155 : case AT_ForceRowSecurity:
5156 : case AT_NoForceRowSecurity:
5157 532 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5158 : /* These commands never recurse */
5159 : /* No command-specific prep needed */
5160 532 : pass = AT_PASS_MISC;
5161 532 : break;
5162 50 : case AT_GenericOptions:
5163 50 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5164 : /* No command-specific prep needed */
5165 50 : pass = AT_PASS_MISC;
5166 50 : break;
5167 2596 : case AT_AttachPartition:
5168 2596 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
5169 : /* No command-specific prep needed */
5170 2596 : pass = AT_PASS_MISC;
5171 2596 : break;
5172 528 : case AT_DetachPartition:
5173 528 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5174 : /* No command-specific prep needed */
5175 522 : pass = AT_PASS_MISC;
5176 522 : break;
5177 14 : case AT_DetachPartitionFinalize:
5178 14 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5179 : /* No command-specific prep needed */
5180 14 : pass = AT_PASS_MISC;
5181 14 : break;
5182 270 : case AT_SplitPartition:
5183 270 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5184 : /* No command-specific prep needed */
5185 270 : pass = AT_PASS_MISC;
5186 270 : break;
5187 156 : case AT_MergePartitions:
5188 156 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5189 : /* No command-specific prep needed */
5190 156 : pass = AT_PASS_MISC;
5191 156 : break;
5192 0 : default: /* oops */
5193 0 : elog(ERROR, "unrecognized alter table type: %d",
5194 : (int) cmd->subtype);
5195 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5196 : break;
5197 : }
5198 : Assert(pass > AT_PASS_UNSET);
5199 :
5200 : /* Add the subcommand to the appropriate list for phase 2 */
5201 37136 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5202 37136 : }
5203 :
5204 : /*
5205 : * ATRewriteCatalogs
5206 : *
5207 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5208 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5209 : * conflicts).
5210 : */
5211 : static void
5212 34444 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5213 : AlterTableUtilityContext *context)
5214 : {
5215 : ListCell *ltab;
5216 :
5217 : /*
5218 : * We process all the tables "in parallel", one pass at a time. This is
5219 : * needed because we may have to propagate work from one table to another
5220 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5221 : * re-adding of the foreign key constraint to the other table). Work can
5222 : * only be propagated into later passes, however.
5223 : */
5224 470208 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5225 : {
5226 : /* Go through each table that needs to be processed */
5227 889458 : foreach(ltab, *wqueue)
5228 : {
5229 453694 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5230 453694 : List *subcmds = tab->subcmds[pass];
5231 : ListCell *lcmd;
5232 :
5233 453694 : if (subcmds == NIL)
5234 399094 : continue;
5235 :
5236 : /*
5237 : * Open the relation and store it in tab. This allows subroutines
5238 : * close and reopen, if necessary. Appropriate lock was obtained
5239 : * by phase 1, needn't get it again.
5240 : */
5241 54600 : tab->rel = relation_open(tab->relid, NoLock);
5242 :
5243 110212 : foreach(lcmd, subcmds)
5244 58278 : ATExecCmd(wqueue, tab,
5245 58278 : lfirst_node(AlterTableCmd, lcmd),
5246 : lockmode, pass, context);
5247 :
5248 : /*
5249 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5250 : * (this is not done in ATExecAlterColumnType since it should be
5251 : * done only once if multiple columns of a table are altered).
5252 : */
5253 51934 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5254 978 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5255 :
5256 51934 : if (tab->rel)
5257 : {
5258 51934 : relation_close(tab->rel, NoLock);
5259 51934 : tab->rel = NULL;
5260 : }
5261 : }
5262 : }
5263 :
5264 : /* Check to see if a toast table must be added. */
5265 67374 : foreach(ltab, *wqueue)
5266 : {
5267 35596 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5268 :
5269 : /*
5270 : * If the table is source table of ATTACH PARTITION command, we did
5271 : * not modify anything about it that will change its toasting
5272 : * requirement, so no need to check.
5273 : */
5274 35596 : if (((tab->relkind == RELKIND_RELATION ||
5275 6458 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5276 33768 : tab->partition_constraint == NULL) ||
5277 3760 : tab->relkind == RELKIND_MATVIEW)
5278 31886 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5279 : }
5280 31778 : }
5281 :
5282 : /*
5283 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5284 : */
5285 : static void
5286 58278 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5287 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5288 : AlterTableUtilityContext *context)
5289 : {
5290 58278 : ObjectAddress address = InvalidObjectAddress;
5291 58278 : Relation rel = tab->rel;
5292 :
5293 58278 : switch (cmd->subtype)
5294 : {
5295 1968 : case AT_AddColumn: /* ADD COLUMN */
5296 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5297 1968 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5298 1968 : cmd->recurse, false,
5299 : lockmode, cur_pass, context);
5300 1854 : break;
5301 556 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5302 556 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5303 496 : break;
5304 110 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5305 110 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5306 110 : break;
5307 152 : case AT_AddIdentity:
5308 152 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5309 : cur_pass, context);
5310 : Assert(cmd != NULL);
5311 146 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5312 104 : break;
5313 62 : case AT_SetIdentity:
5314 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5315 : cur_pass, context);
5316 : Assert(cmd != NULL);
5317 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5318 38 : break;
5319 62 : case AT_DropIdentity:
5320 62 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5321 44 : break;
5322 256 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5323 256 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5324 142 : break;
5325 372 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5326 372 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5327 372 : cmd->recurse, false, NULL, lockmode);
5328 336 : break;
5329 14702 : case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
5330 14702 : address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
5331 14684 : break;
5332 84 : case AT_SetExpression:
5333 84 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5334 78 : break;
5335 32 : case AT_DropExpression:
5336 32 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5337 26 : break;
5338 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5339 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5340 116 : break;
5341 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5342 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5343 26 : break;
5344 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5345 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5346 6 : break;
5347 234 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5348 234 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5349 222 : break;
5350 68 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5351 68 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5352 : lockmode);
5353 62 : break;
5354 1606 : case AT_DropColumn: /* DROP COLUMN */
5355 1606 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5356 1606 : cmd->behavior, cmd->recurse, false,
5357 1606 : cmd->missing_ok, lockmode,
5358 : NULL);
5359 1432 : break;
5360 1030 : case AT_AddIndex: /* ADD INDEX */
5361 1030 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5362 : lockmode);
5363 902 : break;
5364 434 : case AT_ReAddIndex: /* ADD INDEX */
5365 434 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5366 : lockmode);
5367 434 : break;
5368 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5369 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5370 : true, lockmode);
5371 14 : break;
5372 16612 : case AT_AddConstraint: /* ADD CONSTRAINT */
5373 : /* Transform the command only during initial examination */
5374 16612 : if (cur_pass == AT_PASS_ADD_CONSTR)
5375 12882 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5376 12912 : cmd->recurse, lockmode,
5377 : cur_pass, context);
5378 : /* Depending on constraint type, might be no more work to do now */
5379 16582 : if (cmd != NULL)
5380 : address =
5381 3700 : ATExecAddConstraint(wqueue, tab, rel,
5382 3700 : (Constraint *) cmd->def,
5383 3700 : cmd->recurse, false, lockmode);
5384 16022 : break;
5385 194 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5386 : address =
5387 194 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5388 : true, true, lockmode);
5389 182 : break;
5390 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5391 : * constraint */
5392 : address =
5393 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5394 14 : ((AlterDomainStmt *) cmd->def)->def,
5395 : NULL);
5396 8 : break;
5397 54 : case AT_ReAddComment: /* Re-add existing comment */
5398 54 : address = CommentObject((CommentStmt *) cmd->def);
5399 54 : break;
5400 8434 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5401 8434 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5402 : lockmode);
5403 8422 : break;
5404 126 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5405 126 : address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5406 114 : break;
5407 388 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5408 388 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5409 : false, lockmode);
5410 388 : break;
5411 936 : case AT_DropConstraint: /* DROP CONSTRAINT */
5412 936 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5413 936 : cmd->recurse,
5414 936 : cmd->missing_ok, lockmode);
5415 738 : break;
5416 966 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5417 : /* parse transformation was done earlier */
5418 966 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5419 930 : break;
5420 164 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5421 : address =
5422 164 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5423 164 : (List *) cmd->def, lockmode);
5424 158 : break;
5425 1782 : case AT_ChangeOwner: /* ALTER OWNER */
5426 1776 : ATExecChangeOwner(RelationGetRelid(rel),
5427 1782 : get_rolespec_oid(cmd->newowner, false),
5428 : false, lockmode);
5429 1764 : break;
5430 64 : case AT_ClusterOn: /* CLUSTER ON */
5431 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5432 58 : break;
5433 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5434 18 : ATExecDropCluster(rel, lockmode);
5435 12 : break;
5436 76 : case AT_SetLogged: /* SET LOGGED */
5437 : case AT_SetUnLogged: /* SET UNLOGGED */
5438 76 : break;
5439 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5440 : /* nothing to do here, oid columns don't exist anymore */
5441 6 : break;
5442 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5443 :
5444 : /*
5445 : * Only do this for partitioned tables, for which this is just a
5446 : * catalog change. Tables with storage are handled by Phase 3.
5447 : */
5448 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5449 50 : tab->chgAccessMethod)
5450 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5451 92 : break;
5452 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5453 :
5454 : /*
5455 : * Only do this for partitioned tables and indexes, for which this
5456 : * is just a catalog change. Other relation types which have
5457 : * storage are handled by Phase 3.
5458 : */
5459 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5460 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5461 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5462 :
5463 152 : break;
5464 934 : case AT_SetRelOptions: /* SET (...) */
5465 : case AT_ResetRelOptions: /* RESET (...) */
5466 : case AT_ReplaceRelOptions: /* replace entire option list */
5467 934 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5468 882 : break;
5469 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5470 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5471 : TRIGGER_FIRES_ON_ORIGIN, false,
5472 122 : cmd->recurse,
5473 : lockmode);
5474 122 : break;
5475 40 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5476 40 : ATExecEnableDisableTrigger(rel, cmd->name,
5477 : TRIGGER_FIRES_ALWAYS, false,
5478 40 : cmd->recurse,
5479 : lockmode);
5480 40 : break;
5481 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5482 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5483 : TRIGGER_FIRES_ON_REPLICA, false,
5484 16 : cmd->recurse,
5485 : lockmode);
5486 16 : break;
5487 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5488 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5489 : TRIGGER_DISABLED, false,
5490 138 : cmd->recurse,
5491 : lockmode);
5492 138 : break;
5493 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5494 0 : ATExecEnableDisableTrigger(rel, NULL,
5495 : TRIGGER_FIRES_ON_ORIGIN, false,
5496 0 : cmd->recurse,
5497 : lockmode);
5498 0 : break;
5499 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5500 12 : ATExecEnableDisableTrigger(rel, NULL,
5501 : TRIGGER_DISABLED, false,
5502 12 : cmd->recurse,
5503 : lockmode);
5504 12 : break;
5505 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5506 0 : ATExecEnableDisableTrigger(rel, NULL,
5507 : TRIGGER_FIRES_ON_ORIGIN, true,
5508 0 : cmd->recurse,
5509 : lockmode);
5510 0 : break;
5511 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5512 12 : ATExecEnableDisableTrigger(rel, NULL,
5513 : TRIGGER_DISABLED, true,
5514 12 : cmd->recurse,
5515 : lockmode);
5516 12 : break;
5517 :
5518 8 : case AT_EnableRule: /* ENABLE RULE name */
5519 8 : ATExecEnableDisableRule(rel, cmd->name,
5520 : RULE_FIRES_ON_ORIGIN, lockmode);
5521 8 : break;
5522 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5523 0 : ATExecEnableDisableRule(rel, cmd->name,
5524 : RULE_FIRES_ALWAYS, lockmode);
5525 0 : break;
5526 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5527 6 : ATExecEnableDisableRule(rel, cmd->name,
5528 : RULE_FIRES_ON_REPLICA, lockmode);
5529 6 : break;
5530 32 : case AT_DisableRule: /* DISABLE RULE name */
5531 32 : ATExecEnableDisableRule(rel, cmd->name,
5532 : RULE_DISABLED, lockmode);
5533 32 : break;
5534 :
5535 324 : case AT_AddInherit:
5536 324 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5537 234 : break;
5538 44 : case AT_DropInherit:
5539 44 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5540 38 : break;
5541 66 : case AT_AddOf:
5542 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5543 30 : break;
5544 6 : case AT_DropOf:
5545 6 : ATExecDropOf(rel, lockmode);
5546 6 : break;
5547 474 : case AT_ReplicaIdentity:
5548 474 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5549 420 : break;
5550 284 : case AT_EnableRowSecurity:
5551 284 : ATExecSetRowSecurity(rel, true);
5552 284 : break;
5553 10 : case AT_DisableRowSecurity:
5554 10 : ATExecSetRowSecurity(rel, false);
5555 10 : break;
5556 88 : case AT_ForceRowSecurity:
5557 88 : ATExecForceNoForceRowSecurity(rel, true);
5558 88 : break;
5559 32 : case AT_NoForceRowSecurity:
5560 32 : ATExecForceNoForceRowSecurity(rel, false);
5561 32 : break;
5562 50 : case AT_GenericOptions:
5563 50 : ATExecGenericOptions(rel, (List *) cmd->def);
5564 48 : break;
5565 2596 : case AT_AttachPartition:
5566 2596 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5567 : cur_pass, context);
5568 : Assert(cmd != NULL);
5569 2566 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5570 2182 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5571 : context);
5572 : else
5573 384 : address = ATExecAttachPartitionIdx(wqueue, rel,
5574 384 : ((PartitionCmd *) cmd->def)->name);
5575 2230 : break;
5576 522 : case AT_DetachPartition:
5577 522 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5578 : cur_pass, context);
5579 : Assert(cmd != NULL);
5580 : /* ATPrepCmd ensures it must be a table */
5581 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5582 516 : address = ATExecDetachPartition(wqueue, tab, rel,
5583 516 : ((PartitionCmd *) cmd->def)->name,
5584 516 : ((PartitionCmd *) cmd->def)->concurrent);
5585 386 : break;
5586 14 : case AT_DetachPartitionFinalize:
5587 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5588 14 : break;
5589 270 : case AT_SplitPartition:
5590 270 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5591 : cur_pass, context);
5592 : Assert(cmd != NULL);
5593 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5594 138 : ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5595 : context);
5596 132 : break;
5597 156 : case AT_MergePartitions:
5598 156 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5599 : cur_pass, context);
5600 : Assert(cmd != NULL);
5601 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5602 102 : ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5603 : context);
5604 90 : break;
5605 0 : default: /* oops */
5606 0 : elog(ERROR, "unrecognized alter table type: %d",
5607 : (int) cmd->subtype);
5608 : break;
5609 : }
5610 :
5611 : /*
5612 : * Report the subcommand to interested event triggers.
5613 : */
5614 55612 : if (cmd)
5615 42730 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5616 :
5617 : /*
5618 : * Bump the command counter to ensure the next subcommand in the sequence
5619 : * can see the changes so far
5620 : */
5621 55612 : CommandCounterIncrement();
5622 55612 : }
5623 :
5624 : /*
5625 : * ATParseTransformCmd: perform parse transformation for one subcommand
5626 : *
5627 : * Returns the transformed subcommand tree, if there is one, else NULL.
5628 : *
5629 : * The parser may hand back additional AlterTableCmd(s) and/or other
5630 : * utility statements, either before or after the original subcommand.
5631 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5632 : * AlteredTableInfo (they had better be for later passes than the current one).
5633 : * Utility statements that are supposed to happen before the AlterTableCmd
5634 : * are executed immediately. Those that are supposed to happen afterwards
5635 : * are added to the tab->afterStmts list to be done at the very end.
5636 : */
5637 : static AlterTableCmd *
5638 19664 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5639 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5640 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5641 : {
5642 19664 : AlterTableCmd *newcmd = NULL;
5643 19664 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5644 : List *beforeStmts;
5645 : List *afterStmts;
5646 : ListCell *lc;
5647 :
5648 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5649 19664 : atstmt->relation =
5650 19664 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5651 19664 : pstrdup(RelationGetRelationName(rel)),
5652 : -1);
5653 19664 : atstmt->relation->inh = recurse;
5654 19664 : atstmt->cmds = list_make1(cmd);
5655 19664 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5656 19664 : atstmt->missing_ok = false;
5657 :
5658 : /* Transform the AlterTableStmt */
5659 19664 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5660 : atstmt,
5661 : context->queryString,
5662 : &beforeStmts,
5663 : &afterStmts);
5664 :
5665 : /* Execute any statements that should happen before these subcommand(s) */
5666 19870 : foreach(lc, beforeStmts)
5667 : {
5668 470 : Node *stmt = (Node *) lfirst(lc);
5669 :
5670 470 : ProcessUtilityForAlterTable(stmt, context);
5671 458 : CommandCounterIncrement();
5672 : }
5673 :
5674 : /* Examine the transformed subcommands and schedule them appropriately */
5675 46278 : foreach(lc, atstmt->cmds)
5676 : {
5677 26878 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5678 : AlterTablePass pass;
5679 :
5680 : /*
5681 : * This switch need only cover the subcommand types that can be added
5682 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5683 : * executing the subcommand immediately, as a substitute for the
5684 : * original subcommand. (Note, however, that this does cause
5685 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5686 : * which is important for index and foreign key constraints.)
5687 : *
5688 : * We assume we needn't do any phase-1 checks for added subcommands.
5689 : */
5690 26878 : switch (cmd2->subtype)
5691 : {
5692 7160 : case AT_SetAttNotNull:
5693 7160 : ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
5694 7160 : pass = AT_PASS_COL_ATTRS;
5695 7160 : break;
5696 1048 : case AT_AddIndex:
5697 :
5698 : /*
5699 : * A primary key on an inheritance parent needs supporting NOT
5700 : * NULL constraint on its children; enqueue commands to create
5701 : * those or mark them inherited if they already exist.
5702 : */
5703 1048 : ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5704 1048 : pass = AT_PASS_ADD_INDEX;
5705 1048 : break;
5706 8434 : case AT_AddIndexConstraint:
5707 : /* as above */
5708 8434 : ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5709 8434 : pass = AT_PASS_ADD_INDEXCONSTR;
5710 8434 : break;
5711 3718 : case AT_AddConstraint:
5712 : /* Recursion occurs during execution phase */
5713 3718 : if (recurse)
5714 3668 : cmd2->recurse = true;
5715 3718 : switch (castNode(Constraint, cmd2->def)->contype)
5716 : {
5717 0 : case CONSTR_PRIMARY:
5718 : case CONSTR_UNIQUE:
5719 : case CONSTR_EXCLUSION:
5720 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5721 0 : break;
5722 3718 : default:
5723 3718 : pass = AT_PASS_ADD_OTHERCONSTR;
5724 3718 : break;
5725 : }
5726 3718 : break;
5727 0 : case AT_AlterColumnGenericOptions:
5728 : /* This command never recurses */
5729 : /* No command-specific prep needed */
5730 0 : pass = AT_PASS_MISC;
5731 0 : break;
5732 6518 : default:
5733 6518 : pass = cur_pass;
5734 6518 : break;
5735 : }
5736 :
5737 26878 : if (pass < cur_pass)
5738 : {
5739 : /* Cannot schedule into a pass we already finished */
5740 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5741 : pass);
5742 : }
5743 26878 : else if (pass > cur_pass)
5744 : {
5745 : /* OK, queue it up for later */
5746 20360 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5747 : }
5748 : else
5749 : {
5750 : /*
5751 : * We should see at most one subcommand for the current pass,
5752 : * which is the transformed version of the original subcommand.
5753 : */
5754 6518 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5755 : {
5756 : /* Found the transformed version of our subcommand */
5757 6518 : newcmd = cmd2;
5758 : }
5759 : else
5760 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5761 : pass);
5762 : }
5763 : }
5764 :
5765 : /* Queue up any after-statements to happen at the end */
5766 19400 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5767 :
5768 19400 : return newcmd;
5769 : }
5770 :
5771 : /*
5772 : * ATRewriteTables: ALTER TABLE phase 3
5773 : */
5774 : static void
5775 31778 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5776 : AlterTableUtilityContext *context)
5777 : {
5778 : ListCell *ltab;
5779 :
5780 : /* Go through each table that needs to be checked or rewritten */
5781 67070 : foreach(ltab, *wqueue)
5782 : {
5783 35560 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5784 :
5785 : /* Relations without storage may be ignored here */
5786 35560 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5787 6182 : continue;
5788 :
5789 : /*
5790 : * If we change column data types, the operation has to be propagated
5791 : * to tables that use this table's rowtype as a column type.
5792 : * tab->newvals will also be non-NULL in the case where we're adding a
5793 : * column with a default. We choose to forbid that case as well,
5794 : * since composite types might eventually support defaults.
5795 : *
5796 : * (Eventually we'll probably need to check for composite type
5797 : * dependencies even when we're just scanning the table without a
5798 : * rewrite, but at the moment a composite type does not enforce any
5799 : * constraints, so it's not necessary/appropriate to enforce them just
5800 : * during ALTER.)
5801 : */
5802 29378 : if (tab->newvals != NIL || tab->rewrite > 0)
5803 : {
5804 : Relation rel;
5805 :
5806 1468 : rel = table_open(tab->relid, NoLock);
5807 1468 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5808 1456 : table_close(rel, NoLock);
5809 : }
5810 :
5811 : /*
5812 : * We only need to rewrite the table if at least one column needs to
5813 : * be recomputed, or we are changing its persistence or access method.
5814 : *
5815 : * There are two reasons for requiring a rewrite when changing
5816 : * persistence: on one hand, we need to ensure that the buffers
5817 : * belonging to each of the two relations are marked with or without
5818 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5819 : * and assigns a new relfilenumber, we automatically create or drop an
5820 : * init fork for the relation as appropriate.
5821 : */
5822 29366 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5823 848 : {
5824 : /* Build a temporary relation and copy data */
5825 : Relation OldHeap;
5826 : Oid OIDNewHeap;
5827 : Oid NewAccessMethod;
5828 : Oid NewTableSpace;
5829 : char persistence;
5830 :
5831 886 : OldHeap = table_open(tab->relid, NoLock);
5832 :
5833 : /*
5834 : * We don't support rewriting of system catalogs; there are too
5835 : * many corner cases and too little benefit. In particular this
5836 : * is certainly not going to work for mapped catalogs.
5837 : */
5838 886 : if (IsSystemRelation(OldHeap))
5839 0 : ereport(ERROR,
5840 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5841 : errmsg("cannot rewrite system relation \"%s\"",
5842 : RelationGetRelationName(OldHeap))));
5843 :
5844 886 : if (RelationIsUsedAsCatalogTable(OldHeap))
5845 2 : ereport(ERROR,
5846 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5847 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5848 : RelationGetRelationName(OldHeap))));
5849 :
5850 : /*
5851 : * Don't allow rewrite on temp tables of other backends ... their
5852 : * local buffer manager is not going to cope.
5853 : */
5854 884 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5855 0 : ereport(ERROR,
5856 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5857 : errmsg("cannot rewrite temporary tables of other sessions")));
5858 :
5859 : /*
5860 : * Select destination tablespace (same as original unless user
5861 : * requested a change)
5862 : */
5863 884 : if (tab->newTableSpace)
5864 0 : NewTableSpace = tab->newTableSpace;
5865 : else
5866 884 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5867 :
5868 : /*
5869 : * Select destination access method (same as original unless user
5870 : * requested a change)
5871 : */
5872 884 : if (tab->chgAccessMethod)
5873 36 : NewAccessMethod = tab->newAccessMethod;
5874 : else
5875 848 : NewAccessMethod = OldHeap->rd_rel->relam;
5876 :
5877 : /*
5878 : * Select persistence of transient table (same as original unless
5879 : * user requested a change)
5880 : */
5881 884 : persistence = tab->chgPersistence ?
5882 832 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5883 :
5884 884 : table_close(OldHeap, NoLock);
5885 :
5886 : /*
5887 : * Fire off an Event Trigger now, before actually rewriting the
5888 : * table.
5889 : *
5890 : * We don't support Event Trigger for nested commands anywhere,
5891 : * here included, and parsetree is given NULL when coming from
5892 : * AlterTableInternal.
5893 : *
5894 : * And fire it only once.
5895 : */
5896 884 : if (parsetree)
5897 884 : EventTriggerTableRewrite((Node *) parsetree,
5898 : tab->relid,
5899 : tab->rewrite);
5900 :
5901 : /*
5902 : * Create transient table that will receive the modified data.
5903 : *
5904 : * Ensure it is marked correctly as logged or unlogged. We have
5905 : * to do this here so that buffers for the new relfilenumber will
5906 : * have the right persistence set, and at the same time ensure
5907 : * that the original filenumbers's buffers will get read in with
5908 : * the correct setting (i.e. the original one). Otherwise a
5909 : * rollback after the rewrite would possibly result with buffers
5910 : * for the original filenumbers having the wrong persistence
5911 : * setting.
5912 : *
5913 : * NB: This relies on swap_relation_files() also swapping the
5914 : * persistence. That wouldn't work for pg_class, but that can't be
5915 : * unlogged anyway.
5916 : */
5917 878 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5918 : persistence, lockmode);
5919 :
5920 : /*
5921 : * Copy the heap data into the new table with the desired
5922 : * modifications, and test the current data within the table
5923 : * against new constraints generated by ALTER TABLE commands.
5924 : */
5925 878 : ATRewriteTable(tab, OIDNewHeap, lockmode);
5926 :
5927 : /*
5928 : * Swap the physical files of the old and new heaps, then rebuild
5929 : * indexes and discard the old heap. We can use RecentXmin for
5930 : * the table's new relfrozenxid because we rewrote all the tuples
5931 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5932 : * we never try to swap toast tables by content, since we have no
5933 : * interest in letting this code work on system catalogs.
5934 : */
5935 854 : finish_heap_swap(tab->relid, OIDNewHeap,
5936 : false, false, true,
5937 854 : !OidIsValid(tab->newTableSpace),
5938 : RecentXmin,
5939 : ReadNextMultiXactId(),
5940 : persistence);
5941 :
5942 848 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5943 : }
5944 28480 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5945 : {
5946 12 : if (tab->chgPersistence)
5947 12 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5948 : }
5949 : else
5950 : {
5951 : /*
5952 : * If required, test the current data within the table against new
5953 : * constraints generated by ALTER TABLE commands, but don't
5954 : * rebuild data.
5955 : */
5956 28468 : if (tab->constraints != NIL || tab->verify_new_notnull ||
5957 25670 : tab->partition_constraint != NULL)
5958 4582 : ATRewriteTable(tab, InvalidOid, lockmode);
5959 :
5960 : /*
5961 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
5962 : * just do a block-by-block copy.
5963 : */
5964 28250 : if (tab->newTableSpace)
5965 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5966 : }
5967 :
5968 : /*
5969 : * Also change persistence of owned sequences, so that it matches the
5970 : * table persistence.
5971 : */
5972 29110 : if (tab->chgPersistence)
5973 : {
5974 64 : List *seqlist = getOwnedSequences(tab->relid);
5975 : ListCell *lc;
5976 :
5977 112 : foreach(lc, seqlist)
5978 : {
5979 48 : Oid seq_relid = lfirst_oid(lc);
5980 :
5981 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5982 : }
5983 : }
5984 : }
5985 :
5986 : /*
5987 : * Foreign key constraints are checked in a final pass, since (a) it's
5988 : * generally best to examine each one separately, and (b) it's at least
5989 : * theoretically possible that we have changed both relations of the
5990 : * foreign key, and we'd better have finished both rewrites before we try
5991 : * to read the tables.
5992 : */
5993 66556 : foreach(ltab, *wqueue)
5994 : {
5995 35126 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5996 35126 : Relation rel = NULL;
5997 : ListCell *lcon;
5998 :
5999 : /* Relations without storage may be ignored here too */
6000 35126 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6001 6102 : continue;
6002 :
6003 30680 : foreach(lcon, tab->constraints)
6004 : {
6005 1736 : NewConstraint *con = lfirst(lcon);
6006 :
6007 1736 : if (con->contype == CONSTR_FOREIGN)
6008 : {
6009 1084 : Constraint *fkconstraint = (Constraint *) con->qual;
6010 : Relation refrel;
6011 :
6012 1084 : if (rel == NULL)
6013 : {
6014 : /* Long since locked, no need for another */
6015 1072 : rel = table_open(tab->relid, NoLock);
6016 : }
6017 :
6018 1084 : refrel = table_open(con->refrelid, RowShareLock);
6019 :
6020 1084 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6021 : con->refindid,
6022 : con->conid,
6023 1084 : con->conwithperiod);
6024 :
6025 : /*
6026 : * No need to mark the constraint row as validated, we did
6027 : * that when we inserted the row earlier.
6028 : */
6029 :
6030 1004 : table_close(refrel, NoLock);
6031 : }
6032 : }
6033 :
6034 28944 : if (rel)
6035 992 : table_close(rel, NoLock);
6036 : }
6037 :
6038 : /* Finally, run any afterStmts that were queued up */
6039 66438 : foreach(ltab, *wqueue)
6040 : {
6041 35008 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6042 : ListCell *lc;
6043 :
6044 35094 : foreach(lc, tab->afterStmts)
6045 : {
6046 86 : Node *stmt = (Node *) lfirst(lc);
6047 :
6048 86 : ProcessUtilityForAlterTable(stmt, context);
6049 86 : CommandCounterIncrement();
6050 : }
6051 : }
6052 31430 : }
6053 :
6054 : /*
6055 : * ATRewriteTable: scan or rewrite one table
6056 : *
6057 : * OIDNewHeap is InvalidOid if we don't need to rewrite
6058 : */
6059 : static void
6060 5460 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
6061 : {
6062 : Relation oldrel;
6063 : Relation newrel;
6064 : TupleDesc oldTupDesc;
6065 : TupleDesc newTupDesc;
6066 5460 : bool needscan = false;
6067 : List *notnull_attrs;
6068 : int i;
6069 : ListCell *l;
6070 : EState *estate;
6071 : CommandId mycid;
6072 : BulkInsertState bistate;
6073 : int ti_options;
6074 5460 : ExprState *partqualstate = NULL;
6075 :
6076 : /*
6077 : * Open the relation(s). We have surely already locked the existing
6078 : * table.
6079 : */
6080 5460 : oldrel = table_open(tab->relid, NoLock);
6081 5460 : oldTupDesc = tab->oldDesc;
6082 5460 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6083 :
6084 5460 : if (OidIsValid(OIDNewHeap))
6085 878 : newrel = table_open(OIDNewHeap, lockmode);
6086 : else
6087 4582 : newrel = NULL;
6088 :
6089 : /*
6090 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6091 : * is empty, so don't bother using it.
6092 : */
6093 5460 : if (newrel)
6094 : {
6095 878 : mycid = GetCurrentCommandId(true);
6096 878 : bistate = GetBulkInsertState();
6097 878 : ti_options = TABLE_INSERT_SKIP_FSM;
6098 : }
6099 : else
6100 : {
6101 : /* keep compiler quiet about using these uninitialized */
6102 4582 : mycid = 0;
6103 4582 : bistate = NULL;
6104 4582 : ti_options = 0;
6105 : }
6106 :
6107 : /*
6108 : * Generate the constraint and default execution states
6109 : */
6110 :
6111 5460 : estate = CreateExecutorState();
6112 :
6113 : /* Build the needed expression execution states */
6114 7292 : foreach(l, tab->constraints)
6115 : {
6116 1832 : NewConstraint *con = lfirst(l);
6117 :
6118 1832 : switch (con->contype)
6119 : {
6120 742 : case CONSTR_CHECK:
6121 742 : needscan = true;
6122 742 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6123 742 : break;
6124 1090 : case CONSTR_FOREIGN:
6125 : /* Nothing to do here */
6126 1090 : break;
6127 0 : default:
6128 0 : elog(ERROR, "unrecognized constraint type: %d",
6129 : (int) con->contype);
6130 : }
6131 : }
6132 :
6133 : /* Build expression execution states for partition check quals */
6134 5460 : if (tab->partition_constraint)
6135 : {
6136 1926 : needscan = true;
6137 1926 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6138 : }
6139 :
6140 6364 : foreach(l, tab->newvals)
6141 : {
6142 904 : NewColumnValue *ex = lfirst(l);
6143 :
6144 : /* expr already planned */
6145 904 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6146 : }
6147 :
6148 5460 : notnull_attrs = NIL;
6149 5460 : if (newrel || tab->verify_new_notnull)
6150 : {
6151 : /*
6152 : * If we are rebuilding the tuples OR if we added any new but not
6153 : * verified not-null constraints, check all not-null constraints. This
6154 : * is a bit of overkill but it minimizes risk of bugs, and
6155 : * heap_attisnull is a pretty cheap test anyway.
6156 : */
6157 7270 : for (i = 0; i < newTupDesc->natts; i++)
6158 : {
6159 5280 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6160 :
6161 5280 : if (attr->attnotnull && !attr->attisdropped)
6162 2148 : notnull_attrs = lappend_int(notnull_attrs, i);
6163 : }
6164 1990 : if (notnull_attrs)
6165 1582 : needscan = true;
6166 : }
6167 :
6168 5460 : if (newrel || needscan)
6169 : {
6170 : ExprContext *econtext;
6171 : TupleTableSlot *oldslot;
6172 : TupleTableSlot *newslot;
6173 : TableScanDesc scan;
6174 : MemoryContext oldCxt;
6175 4554 : List *dropped_attrs = NIL;
6176 : ListCell *lc;
6177 : Snapshot snapshot;
6178 :
6179 4554 : if (newrel)
6180 878 : ereport(DEBUG1,
6181 : (errmsg_internal("rewriting table \"%s\"",
6182 : RelationGetRelationName(oldrel))));
6183 : else
6184 3676 : ereport(DEBUG1,
6185 : (errmsg_internal("verifying table \"%s\"",
6186 : RelationGetRelationName(oldrel))));
6187 :
6188 4554 : if (newrel)
6189 : {
6190 : /*
6191 : * All predicate locks on the tuples or pages are about to be made
6192 : * invalid, because we move tuples around. Promote them to
6193 : * relation locks.
6194 : */
6195 878 : TransferPredicateLocksToHeapRelation(oldrel);
6196 : }
6197 :
6198 4554 : econtext = GetPerTupleExprContext(estate);
6199 :
6200 : /*
6201 : * Create necessary tuple slots. When rewriting, two slots are needed,
6202 : * otherwise one suffices. In the case where one slot suffices, we
6203 : * need to use the new tuple descriptor, otherwise some constraints
6204 : * can't be evaluated. Note that even when the tuple layout is the
6205 : * same and no rewrite is required, the tupDescs might not be
6206 : * (consider ADD COLUMN without a default).
6207 : */
6208 4554 : if (tab->rewrite)
6209 : {
6210 : Assert(newrel != NULL);
6211 878 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6212 : table_slot_callbacks(oldrel));
6213 878 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6214 : table_slot_callbacks(newrel));
6215 :
6216 : /*
6217 : * Set all columns in the new slot to NULL initially, to ensure
6218 : * columns added as part of the rewrite are initialized to NULL.
6219 : * That is necessary as tab->newvals will not contain an
6220 : * expression for columns with a NULL default, e.g. when adding a
6221 : * column without a default together with a column with a default
6222 : * requiring an actual rewrite.
6223 : */
6224 878 : ExecStoreAllNullTuple(newslot);
6225 : }
6226 : else
6227 : {
6228 3676 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6229 : table_slot_callbacks(oldrel));
6230 3676 : newslot = NULL;
6231 : }
6232 :
6233 : /*
6234 : * Any attributes that are dropped according to the new tuple
6235 : * descriptor can be set to NULL. We precompute the list of dropped
6236 : * attributes to avoid needing to do so in the per-tuple loop.
6237 : */
6238 16104 : for (i = 0; i < newTupDesc->natts; i++)
6239 : {
6240 11550 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6241 790 : dropped_attrs = lappend_int(dropped_attrs, i);
6242 : }
6243 :
6244 : /*
6245 : * Scan through the rows, generating a new row if needed and then
6246 : * checking all the constraints.
6247 : */
6248 4554 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6249 4554 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6250 :
6251 : /*
6252 : * Switch to per-tuple memory context and reset it for each tuple
6253 : * produced, so we don't leak memory.
6254 : */
6255 4554 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6256 :
6257 767134 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6258 : {
6259 : TupleTableSlot *insertslot;
6260 :
6261 762822 : if (tab->rewrite > 0)
6262 : {
6263 : /* Extract data from old tuple */
6264 97564 : slot_getallattrs(oldslot);
6265 97564 : ExecClearTuple(newslot);
6266 :
6267 : /* copy attributes */
6268 97564 : memcpy(newslot->tts_values, oldslot->tts_values,
6269 97564 : sizeof(Datum) * oldslot->tts_nvalid);
6270 97564 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6271 97564 : sizeof(bool) * oldslot->tts_nvalid);
6272 :
6273 : /* Set dropped attributes to null in new tuple */
6274 97650 : foreach(lc, dropped_attrs)
6275 86 : newslot->tts_isnull[lfirst_int(lc)] = true;
6276 :
6277 : /*
6278 : * Constraints and GENERATED expressions might reference the
6279 : * tableoid column, so fill tts_tableOid with the desired
6280 : * value. (We must do this each time, because it gets
6281 : * overwritten with newrel's OID during storing.)
6282 : */
6283 97564 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6284 :
6285 : /*
6286 : * Process supplied expressions to replace selected columns.
6287 : *
6288 : * First, evaluate expressions whose inputs come from the old
6289 : * tuple.
6290 : */
6291 97564 : econtext->ecxt_scantuple = oldslot;
6292 :
6293 200948 : foreach(l, tab->newvals)
6294 : {
6295 103396 : NewColumnValue *ex = lfirst(l);
6296 :
6297 103396 : if (ex->is_generated)
6298 150 : continue;
6299 :
6300 103246 : newslot->tts_values[ex->attnum - 1]
6301 103234 : = ExecEvalExpr(ex->exprstate,
6302 : econtext,
6303 103246 : &newslot->tts_isnull[ex->attnum - 1]);
6304 : }
6305 :
6306 97552 : ExecStoreVirtualTuple(newslot);
6307 :
6308 : /*
6309 : * Now, evaluate any expressions whose inputs come from the
6310 : * new tuple. We assume these columns won't reference each
6311 : * other, so that there's no ordering dependency.
6312 : */
6313 97552 : econtext->ecxt_scantuple = newslot;
6314 :
6315 200936 : foreach(l, tab->newvals)
6316 : {
6317 103384 : NewColumnValue *ex = lfirst(l);
6318 :
6319 103384 : if (!ex->is_generated)
6320 103234 : continue;
6321 :
6322 150 : newslot->tts_values[ex->attnum - 1]
6323 150 : = ExecEvalExpr(ex->exprstate,
6324 : econtext,
6325 150 : &newslot->tts_isnull[ex->attnum - 1]);
6326 : }
6327 :
6328 97552 : insertslot = newslot;
6329 : }
6330 : else
6331 : {
6332 : /*
6333 : * If there's no rewrite, old and new table are guaranteed to
6334 : * have the same AM, so we can just use the old slot to verify
6335 : * new constraints etc.
6336 : */
6337 665258 : insertslot = oldslot;
6338 : }
6339 :
6340 : /* Now check any constraints on the possibly-changed tuple */
6341 762810 : econtext->ecxt_scantuple = insertslot;
6342 :
6343 3339124 : foreach(l, notnull_attrs)
6344 : {
6345 2576398 : int attn = lfirst_int(l);
6346 :
6347 2576398 : if (slot_attisnull(insertslot, attn + 1))
6348 : {
6349 84 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6350 :
6351 84 : ereport(ERROR,
6352 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6353 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6354 : NameStr(attr->attname),
6355 : RelationGetRelationName(oldrel)),
6356 : errtablecol(oldrel, attn + 1)));
6357 : }
6358 : }
6359 :
6360 770826 : foreach(l, tab->constraints)
6361 : {
6362 8172 : NewConstraint *con = lfirst(l);
6363 :
6364 8172 : switch (con->contype)
6365 : {
6366 8072 : case CONSTR_CHECK:
6367 8072 : if (!ExecCheck(con->qualstate, econtext))
6368 72 : ereport(ERROR,
6369 : (errcode(ERRCODE_CHECK_VIOLATION),
6370 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6371 : con->name,
6372 : RelationGetRelationName(oldrel)),
6373 : errtableconstraint(oldrel, con->name)));
6374 8000 : break;
6375 100 : case CONSTR_NOTNULL:
6376 : case CONSTR_FOREIGN:
6377 : /* Nothing to do here */
6378 100 : break;
6379 0 : default:
6380 0 : elog(ERROR, "unrecognized constraint type: %d",
6381 : (int) con->contype);
6382 : }
6383 : }
6384 :
6385 762654 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6386 : {
6387 74 : if (tab->validate_default)
6388 26 : ereport(ERROR,
6389 : (errcode(ERRCODE_CHECK_VIOLATION),
6390 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6391 : RelationGetRelationName(oldrel)),
6392 : errtable(oldrel)));
6393 : else
6394 48 : ereport(ERROR,
6395 : (errcode(ERRCODE_CHECK_VIOLATION),
6396 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6397 : RelationGetRelationName(oldrel)),
6398 : errtable(oldrel)));
6399 : }
6400 :
6401 : /* Write the tuple out to the new relation */
6402 762580 : if (newrel)
6403 97540 : table_tuple_insert(newrel, insertslot, mycid,
6404 : ti_options, bistate);
6405 :
6406 762580 : ResetExprContext(econtext);
6407 :
6408 762580 : CHECK_FOR_INTERRUPTS();
6409 : }
6410 :
6411 4312 : MemoryContextSwitchTo(oldCxt);
6412 4312 : table_endscan(scan);
6413 4312 : UnregisterSnapshot(snapshot);
6414 :
6415 4312 : ExecDropSingleTupleTableSlot(oldslot);
6416 4312 : if (newslot)
6417 854 : ExecDropSingleTupleTableSlot(newslot);
6418 : }
6419 :
6420 5218 : FreeExecutorState(estate);
6421 :
6422 5218 : table_close(oldrel, NoLock);
6423 5218 : if (newrel)
6424 : {
6425 854 : FreeBulkInsertState(bistate);
6426 :
6427 854 : table_finish_bulk_insert(newrel, ti_options);
6428 :
6429 854 : table_close(newrel, NoLock);
6430 : }
6431 5218 : }
6432 :
6433 : /*
6434 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6435 : */
6436 : static AlteredTableInfo *
6437 44124 : ATGetQueueEntry(List **wqueue, Relation rel)
6438 : {
6439 44124 : Oid relid = RelationGetRelid(rel);
6440 : AlteredTableInfo *tab;
6441 : ListCell *ltab;
6442 :
6443 53570 : foreach(ltab, *wqueue)
6444 : {
6445 14864 : tab = (AlteredTableInfo *) lfirst(ltab);
6446 14864 : if (tab->relid == relid)
6447 5418 : return tab;
6448 : }
6449 :
6450 : /*
6451 : * Not there, so add it. Note that we make a copy of the relation's
6452 : * existing descriptor before anything interesting can happen to it.
6453 : */
6454 38706 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6455 38706 : tab->relid = relid;
6456 38706 : tab->rel = NULL; /* set later */
6457 38706 : tab->relkind = rel->rd_rel->relkind;
6458 38706 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6459 38706 : tab->newAccessMethod = InvalidOid;
6460 38706 : tab->chgAccessMethod = false;
6461 38706 : tab->newTableSpace = InvalidOid;
6462 38706 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6463 38706 : tab->chgPersistence = false;
6464 :
6465 38706 : *wqueue = lappend(*wqueue, tab);
6466 :
6467 38706 : return tab;
6468 : }
6469 :
6470 : static const char *
6471 42 : alter_table_type_to_string(AlterTableType cmdtype)
6472 : {
6473 42 : switch (cmdtype)
6474 : {
6475 0 : case AT_AddColumn:
6476 : case AT_AddColumnToView:
6477 0 : return "ADD COLUMN";
6478 0 : case AT_ColumnDefault:
6479 : case AT_CookedColumnDefault:
6480 0 : return "ALTER COLUMN ... SET DEFAULT";
6481 6 : case AT_DropNotNull:
6482 6 : return "ALTER COLUMN ... DROP NOT NULL";
6483 6 : case AT_SetNotNull:
6484 6 : return "ALTER COLUMN ... SET NOT NULL";
6485 0 : case AT_SetAttNotNull:
6486 0 : return NULL; /* not real grammar */
6487 0 : case AT_SetExpression:
6488 0 : return "ALTER COLUMN ... SET EXPRESSION";
6489 0 : case AT_DropExpression:
6490 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6491 0 : case AT_SetStatistics:
6492 0 : return "ALTER COLUMN ... SET STATISTICS";
6493 12 : case AT_SetOptions:
6494 12 : return "ALTER COLUMN ... SET";
6495 0 : case AT_ResetOptions:
6496 0 : return "ALTER COLUMN ... RESET";
6497 0 : case AT_SetStorage:
6498 0 : return "ALTER COLUMN ... SET STORAGE";
6499 0 : case AT_SetCompression:
6500 0 : return "ALTER COLUMN ... SET COMPRESSION";
6501 6 : case AT_DropColumn:
6502 6 : return "DROP COLUMN";
6503 0 : case AT_AddIndex:
6504 : case AT_ReAddIndex:
6505 0 : return NULL; /* not real grammar */
6506 0 : case AT_AddConstraint:
6507 : case AT_ReAddConstraint:
6508 : case AT_ReAddDomainConstraint:
6509 : case AT_AddIndexConstraint:
6510 0 : return "ADD CONSTRAINT";
6511 6 : case AT_AlterConstraint:
6512 6 : return "ALTER CONSTRAINT";
6513 0 : case AT_ValidateConstraint:
6514 0 : return "VALIDATE CONSTRAINT";
6515 0 : case AT_DropConstraint:
6516 0 : return "DROP CONSTRAINT";
6517 0 : case AT_ReAddComment:
6518 0 : return NULL; /* not real grammar */
6519 0 : case AT_AlterColumnType:
6520 0 : return "ALTER COLUMN ... SET DATA TYPE";
6521 0 : case AT_AlterColumnGenericOptions:
6522 0 : return "ALTER COLUMN ... OPTIONS";
6523 0 : case AT_ChangeOwner:
6524 0 : return "OWNER TO";
6525 0 : case AT_ClusterOn:
6526 0 : return "CLUSTER ON";
6527 0 : case AT_DropCluster:
6528 0 : return "SET WITHOUT CLUSTER";
6529 0 : case AT_SetAccessMethod:
6530 0 : return "SET ACCESS METHOD";
6531 0 : case AT_SetLogged:
6532 0 : return "SET LOGGED";
6533 0 : case AT_SetUnLogged:
6534 0 : return "SET UNLOGGED";
6535 0 : case AT_DropOids:
6536 0 : return "SET WITHOUT OIDS";
6537 0 : case AT_SetTableSpace:
6538 0 : return "SET TABLESPACE";
6539 0 : case AT_SetRelOptions:
6540 0 : return "SET";
6541 0 : case AT_ResetRelOptions:
6542 0 : return "RESET";
6543 0 : case AT_ReplaceRelOptions:
6544 0 : return NULL; /* not real grammar */
6545 0 : case AT_EnableTrig:
6546 0 : return "ENABLE TRIGGER";
6547 0 : case AT_EnableAlwaysTrig:
6548 0 : return "ENABLE ALWAYS TRIGGER";
6549 0 : case AT_EnableReplicaTrig:
6550 0 : return "ENABLE REPLICA TRIGGER";
6551 0 : case AT_DisableTrig:
6552 0 : return "DISABLE TRIGGER";
6553 0 : case AT_EnableTrigAll:
6554 0 : return "ENABLE TRIGGER ALL";
6555 0 : case AT_DisableTrigAll:
6556 0 : return "DISABLE TRIGGER ALL";
6557 0 : case AT_EnableTrigUser:
6558 0 : return "ENABLE TRIGGER USER";
6559 0 : case AT_DisableTrigUser:
6560 0 : return "DISABLE TRIGGER USER";
6561 0 : case AT_EnableRule:
6562 0 : return "ENABLE RULE";
6563 0 : case AT_EnableAlwaysRule:
6564 0 : return "ENABLE ALWAYS RULE";
6565 0 : case AT_EnableReplicaRule:
6566 0 : return "ENABLE REPLICA RULE";
6567 0 : case AT_DisableRule:
6568 0 : return "DISABLE RULE";
6569 0 : case AT_AddInherit:
6570 0 : return "INHERIT";
6571 0 : case AT_DropInherit:
6572 0 : return "NO INHERIT";
6573 0 : case AT_AddOf:
6574 0 : return "OF";
6575 0 : case AT_DropOf:
6576 0 : return "NOT OF";
6577 0 : case AT_ReplicaIdentity:
6578 0 : return "REPLICA IDENTITY";
6579 0 : case AT_EnableRowSecurity:
6580 0 : return "ENABLE ROW SECURITY";
6581 0 : case AT_DisableRowSecurity:
6582 0 : return "DISABLE ROW SECURITY";
6583 0 : case AT_ForceRowSecurity:
6584 0 : return "FORCE ROW SECURITY";
6585 0 : case AT_NoForceRowSecurity:
6586 0 : return "NO FORCE ROW SECURITY";
6587 0 : case AT_GenericOptions:
6588 0 : return "OPTIONS";
6589 0 : case AT_AttachPartition:
6590 0 : return "ATTACH PARTITION";
6591 6 : case AT_DetachPartition:
6592 6 : return "DETACH PARTITION";
6593 0 : case AT_DetachPartitionFinalize:
6594 0 : return "DETACH PARTITION ... FINALIZE";
6595 0 : case AT_SplitPartition:
6596 0 : return "SPLIT PARTITION";
6597 0 : case AT_MergePartitions:
6598 0 : return "MERGE PARTITIONS";
6599 0 : case AT_AddIdentity:
6600 0 : return "ALTER COLUMN ... ADD IDENTITY";
6601 0 : case AT_SetIdentity:
6602 0 : return "ALTER COLUMN ... SET";
6603 0 : case AT_DropIdentity:
6604 0 : return "ALTER COLUMN ... DROP IDENTITY";
6605 0 : case AT_ReAddStatistics:
6606 0 : return NULL; /* not real grammar */
6607 : }
6608 :
6609 0 : return NULL;
6610 : }
6611 :
6612 : /*
6613 : * ATSimplePermissions
6614 : *
6615 : * - Ensure that it is a relation (or possibly a view)
6616 : * - Ensure this user is the owner
6617 : * - Ensure that it is not a system table
6618 : */
6619 : static void
6620 40214 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6621 : {
6622 : int actual_target;
6623 :
6624 40214 : switch (rel->rd_rel->relkind)
6625 : {
6626 38008 : case RELKIND_RELATION:
6627 : case RELKIND_PARTITIONED_TABLE:
6628 38008 : actual_target = ATT_TABLE;
6629 38008 : break;
6630 396 : case RELKIND_VIEW:
6631 396 : actual_target = ATT_VIEW;
6632 396 : break;
6633 46 : case RELKIND_MATVIEW:
6634 46 : actual_target = ATT_MATVIEW;
6635 46 : break;
6636 226 : case RELKIND_INDEX:
6637 226 : actual_target = ATT_INDEX;
6638 226 : break;
6639 426 : case RELKIND_PARTITIONED_INDEX:
6640 426 : actual_target = ATT_PARTITIONED_INDEX;
6641 426 : break;
6642 214 : case RELKIND_COMPOSITE_TYPE:
6643 214 : actual_target = ATT_COMPOSITE_TYPE;
6644 214 : break;
6645 886 : case RELKIND_FOREIGN_TABLE:
6646 886 : actual_target = ATT_FOREIGN_TABLE;
6647 886 : break;
6648 12 : case RELKIND_SEQUENCE:
6649 12 : actual_target = ATT_SEQUENCE;
6650 12 : break;
6651 0 : default:
6652 0 : actual_target = 0;
6653 0 : break;
6654 : }
6655 :
6656 : /* Wrong target type? */
6657 40214 : if ((actual_target & allowed_targets) == 0)
6658 : {
6659 42 : const char *action_str = alter_table_type_to_string(cmdtype);
6660 :
6661 42 : if (action_str)
6662 42 : ereport(ERROR,
6663 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6664 : /* translator: %s is a group of some SQL keywords */
6665 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6666 : action_str, RelationGetRelationName(rel)),
6667 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6668 : else
6669 : /* internal error? */
6670 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6671 : RelationGetRelationName(rel));
6672 : }
6673 :
6674 : /* Permissions checks */
6675 40172 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6676 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6677 12 : RelationGetRelationName(rel));
6678 :
6679 40160 : if (!allowSystemTableMods && IsSystemRelation(rel))
6680 0 : ereport(ERROR,
6681 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6682 : errmsg("permission denied: \"%s\" is a system catalog",
6683 : RelationGetRelationName(rel))));
6684 40160 : }
6685 :
6686 : /*
6687 : * ATSimpleRecursion
6688 : *
6689 : * Simple table recursion sufficient for most ALTER TABLE operations.
6690 : * All direct and indirect children are processed in an unspecified order.
6691 : * Note that if a child inherits from the original table via multiple
6692 : * inheritance paths, it will be visited just once.
6693 : */
6694 : static void
6695 15652 : ATSimpleRecursion(List **wqueue, Relation rel,
6696 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6697 : AlterTableUtilityContext *context)
6698 : {
6699 : /*
6700 : * Propagate to children, if desired and if there are (or might be) any
6701 : * children.
6702 : */
6703 15652 : if (recurse && rel->rd_rel->relhassubclass)
6704 : {
6705 160 : Oid relid = RelationGetRelid(rel);
6706 : ListCell *child;
6707 : List *children;
6708 :
6709 160 : children = find_all_inheritors(relid, lockmode, NULL);
6710 :
6711 : /*
6712 : * find_all_inheritors does the recursive search of the inheritance
6713 : * hierarchy, so all we have to do is process all of the relids in the
6714 : * list that it returns.
6715 : */
6716 684 : foreach(child, children)
6717 : {
6718 524 : Oid childrelid = lfirst_oid(child);
6719 : Relation childrel;
6720 :
6721 524 : if (childrelid == relid)
6722 160 : continue;
6723 : /* find_all_inheritors already got lock */
6724 364 : childrel = relation_open(childrelid, NoLock);
6725 364 : CheckTableNotInUse(childrel, "ALTER TABLE");
6726 364 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6727 364 : relation_close(childrel, NoLock);
6728 : }
6729 : }
6730 15652 : }
6731 :
6732 : /*
6733 : * Obtain list of partitions of the given table, locking them all at the given
6734 : * lockmode and ensuring that they all pass CheckTableNotInUse.
6735 : *
6736 : * This function is a no-op if the given relation is not a partitioned table;
6737 : * in particular, nothing is done if it's a legacy inheritance parent.
6738 : */
6739 : static void
6740 942 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6741 : {
6742 942 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6743 : {
6744 : List *inh;
6745 : ListCell *cell;
6746 :
6747 146 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6748 : /* first element is the parent rel; must ignore it */
6749 484 : for_each_from(cell, inh, 1)
6750 : {
6751 : Relation childrel;
6752 :
6753 : /* find_all_inheritors already got lock */
6754 344 : childrel = table_open(lfirst_oid(cell), NoLock);
6755 344 : CheckTableNotInUse(childrel, "ALTER TABLE");
6756 338 : table_close(childrel, NoLock);
6757 : }
6758 140 : list_free(inh);
6759 : }
6760 936 : }
6761 :
6762 : /*
6763 : * ATTypedTableRecursion
6764 : *
6765 : * Propagate ALTER TYPE operations to the typed tables of that type.
6766 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6767 : * recursion to inheritance children of the typed tables.
6768 : */
6769 : static void
6770 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6771 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6772 : {
6773 : ListCell *child;
6774 : List *children;
6775 :
6776 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6777 :
6778 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6779 190 : RelationGetRelationName(rel),
6780 : cmd->behavior);
6781 :
6782 202 : foreach(child, children)
6783 : {
6784 30 : Oid childrelid = lfirst_oid(child);
6785 : Relation childrel;
6786 :
6787 30 : childrel = relation_open(childrelid, lockmode);
6788 30 : CheckTableNotInUse(childrel, "ALTER TABLE");
6789 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6790 30 : relation_close(childrel, NoLock);
6791 : }
6792 172 : }
6793 :
6794 :
6795 : /*
6796 : * find_composite_type_dependencies
6797 : *
6798 : * Check to see if the type "typeOid" is being used as a column in some table
6799 : * (possibly nested several levels deep in composite types, arrays, etc!).
6800 : * Eventually, we'd like to propagate the check or rewrite operation
6801 : * into such tables, but for now, just error out if we find any.
6802 : *
6803 : * Caller should provide either the associated relation of a rowtype,
6804 : * or a type name (not both) for use in the error message, if any.
6805 : *
6806 : * Note that "typeOid" is not necessarily a composite type; it could also be
6807 : * another container type such as an array or range, or a domain over one of
6808 : * these things. The name of this function is therefore somewhat historical,
6809 : * but it's not worth changing.
6810 : *
6811 : * We assume that functions and views depending on the type are not reasons
6812 : * to reject the ALTER. (How safe is this really?)
6813 : */
6814 : void
6815 3902 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6816 : const char *origTypeName)
6817 : {
6818 : Relation depRel;
6819 : ScanKeyData key[2];
6820 : SysScanDesc depScan;
6821 : HeapTuple depTup;
6822 :
6823 : /* since this function recurses, it could be driven to stack overflow */
6824 3902 : check_stack_depth();
6825 :
6826 : /*
6827 : * We scan pg_depend to find those things that depend on the given type.
6828 : * (We assume we can ignore refobjsubid for a type.)
6829 : */
6830 3902 : depRel = table_open(DependRelationId, AccessShareLock);
6831 :
6832 3902 : ScanKeyInit(&key[0],
6833 : Anum_pg_depend_refclassid,
6834 : BTEqualStrategyNumber, F_OIDEQ,
6835 : ObjectIdGetDatum(TypeRelationId));
6836 3902 : ScanKeyInit(&key[1],
6837 : Anum_pg_depend_refobjid,
6838 : BTEqualStrategyNumber, F_OIDEQ,
6839 : ObjectIdGetDatum(typeOid));
6840 :
6841 3902 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6842 : NULL, 2, key);
6843 :
6844 6030 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6845 : {
6846 2224 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6847 : Relation rel;
6848 : TupleDesc tupleDesc;
6849 : Form_pg_attribute att;
6850 :
6851 : /* Check for directly dependent types */
6852 2224 : if (pg_depend->classid == TypeRelationId)
6853 : {
6854 : /*
6855 : * This must be an array, domain, or range containing the given
6856 : * type, so recursively check for uses of this type. Note that
6857 : * any error message will mention the original type not the
6858 : * container; this is intentional.
6859 : */
6860 1878 : find_composite_type_dependencies(pg_depend->objid,
6861 : origRelation, origTypeName);
6862 1854 : continue;
6863 : }
6864 :
6865 : /* Else, ignore dependees that aren't relations */
6866 346 : if (pg_depend->classid != RelationRelationId)
6867 122 : continue;
6868 :
6869 224 : rel = relation_open(pg_depend->objid, AccessShareLock);
6870 224 : tupleDesc = RelationGetDescr(rel);
6871 :
6872 : /*
6873 : * If objsubid identifies a specific column, refer to that in error
6874 : * messages. Otherwise, search to see if there's a user column of the
6875 : * type. (We assume system columns are never of interesting types.)
6876 : * The search is needed because an index containing an expression
6877 : * column of the target type will just be recorded as a whole-relation
6878 : * dependency. If we do not find a column of the type, the dependency
6879 : * must indicate that the type is transiently referenced in an index
6880 : * expression but not stored on disk, which we assume is OK, just as
6881 : * we do for references in views. (It could also be that the target
6882 : * type is embedded in some container type that is stored in an index
6883 : * column, but the previous recursion should catch such cases.)
6884 : */
6885 224 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6886 66 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6887 : else
6888 : {
6889 158 : att = NULL;
6890 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
6891 : {
6892 254 : att = TupleDescAttr(tupleDesc, attno - 1);
6893 254 : if (att->atttypid == typeOid && !att->attisdropped)
6894 6 : break;
6895 248 : att = NULL;
6896 : }
6897 158 : if (att == NULL)
6898 : {
6899 : /* No such column, so assume OK */
6900 152 : relation_close(rel, AccessShareLock);
6901 152 : continue;
6902 : }
6903 : }
6904 :
6905 : /*
6906 : * We definitely should reject if the relation has storage. If it's
6907 : * partitioned, then perhaps we don't have to reject: if there are
6908 : * partitions then we'll fail when we find one, else there is no
6909 : * stored data to worry about. However, it's possible that the type
6910 : * change would affect conclusions about whether the type is sortable
6911 : * or hashable and thus (if it's a partitioning column) break the
6912 : * partitioning rule. For now, reject for partitioned rels too.
6913 : */
6914 72 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6915 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6916 : {
6917 72 : if (origTypeName)
6918 30 : ereport(ERROR,
6919 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6920 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6921 : origTypeName,
6922 : RelationGetRelationName(rel),
6923 : NameStr(att->attname))));
6924 42 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6925 18 : ereport(ERROR,
6926 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6927 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6928 : RelationGetRelationName(origRelation),
6929 : RelationGetRelationName(rel),
6930 : NameStr(att->attname))));
6931 24 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6932 6 : ereport(ERROR,
6933 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6934 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6935 : RelationGetRelationName(origRelation),
6936 : RelationGetRelationName(rel),
6937 : NameStr(att->attname))));
6938 : else
6939 18 : ereport(ERROR,
6940 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6941 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6942 : RelationGetRelationName(origRelation),
6943 : RelationGetRelationName(rel),
6944 : NameStr(att->attname))));
6945 : }
6946 0 : else if (OidIsValid(rel->rd_rel->reltype))
6947 : {
6948 : /*
6949 : * A view or composite type itself isn't a problem, but we must
6950 : * recursively check for indirect dependencies via its rowtype.
6951 : */
6952 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
6953 : origRelation, origTypeName);
6954 : }
6955 :
6956 0 : relation_close(rel, AccessShareLock);
6957 : }
6958 :
6959 3806 : systable_endscan(depScan);
6960 :
6961 3806 : relation_close(depRel, AccessShareLock);
6962 3806 : }
6963 :
6964 :
6965 : /*
6966 : * find_typed_table_dependencies
6967 : *
6968 : * Check to see if a composite type is being used as the type of a
6969 : * typed table. Abort if any are found and behavior is RESTRICT.
6970 : * Else return the list of tables.
6971 : */
6972 : static List *
6973 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6974 : {
6975 : Relation classRel;
6976 : ScanKeyData key[1];
6977 : TableScanDesc scan;
6978 : HeapTuple tuple;
6979 214 : List *result = NIL;
6980 :
6981 214 : classRel = table_open(RelationRelationId, AccessShareLock);
6982 :
6983 214 : ScanKeyInit(&key[0],
6984 : Anum_pg_class_reloftype,
6985 : BTEqualStrategyNumber, F_OIDEQ,
6986 : ObjectIdGetDatum(typeOid));
6987 :
6988 214 : scan = table_beginscan_catalog(classRel, 1, key);
6989 :
6990 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6991 : {
6992 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6993 :
6994 60 : if (behavior == DROP_RESTRICT)
6995 24 : ereport(ERROR,
6996 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6997 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6998 : typeName),
6999 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7000 : else
7001 36 : result = lappend_oid(result, classform->oid);
7002 : }
7003 :
7004 190 : table_endscan(scan);
7005 190 : table_close(classRel, AccessShareLock);
7006 :
7007 190 : return result;
7008 : }
7009 :
7010 :
7011 : /*
7012 : * check_of_type
7013 : *
7014 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7015 : * isn't suitable, throw an error. Currently, we require that the type
7016 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7017 : * would require handling a number of extra corner cases in the DDL commands.
7018 : * (Also, allowing domain-over-composite would open up a can of worms about
7019 : * whether and how the domain's constraints should apply to derived tables.)
7020 : */
7021 : void
7022 170 : check_of_type(HeapTuple typetuple)
7023 : {
7024 170 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7025 170 : bool typeOk = false;
7026 :
7027 170 : if (typ->typtype == TYPTYPE_COMPOSITE)
7028 : {
7029 : Relation typeRelation;
7030 :
7031 : Assert(OidIsValid(typ->typrelid));
7032 170 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7033 170 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7034 :
7035 : /*
7036 : * Close the parent rel, but keep our AccessShareLock on it until xact
7037 : * commit. That will prevent someone else from deleting or ALTERing
7038 : * the type before the typed table creation/conversion commits.
7039 : */
7040 170 : relation_close(typeRelation, NoLock);
7041 : }
7042 170 : if (!typeOk)
7043 6 : ereport(ERROR,
7044 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7045 : errmsg("type %s is not a composite type",
7046 : format_type_be(typ->oid))));
7047 164 : }
7048 :
7049 :
7050 : /*
7051 : * ALTER TABLE ADD COLUMN
7052 : *
7053 : * Adds an additional attribute to a relation making the assumption that
7054 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7055 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7056 : * AlterTableCmd's.
7057 : *
7058 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7059 : * have to decide at runtime whether to recurse or not depending on whether we
7060 : * actually add a column or merely merge with an existing column. (We can't
7061 : * check this in a static pre-pass because it won't handle multiple inheritance
7062 : * situations correctly.)
7063 : */
7064 : static void
7065 1986 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7066 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7067 : AlterTableUtilityContext *context)
7068 : {
7069 1986 : if (rel->rd_rel->reloftype && !recursing)
7070 6 : ereport(ERROR,
7071 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7072 : errmsg("cannot add column to typed table")));
7073 :
7074 1980 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7075 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7076 :
7077 1974 : if (recurse && !is_view)
7078 1874 : cmd->recurse = true;
7079 1974 : }
7080 :
7081 : /*
7082 : * Add a column to a table. The return value is the address of the
7083 : * new column in the parent relation.
7084 : *
7085 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7086 : * copy (but that happens only after we check for IF NOT EXISTS).
7087 : */
7088 : static ObjectAddress
7089 2604 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7090 : AlterTableCmd **cmd, bool recurse, bool recursing,
7091 : LOCKMODE lockmode, AlterTablePass cur_pass,
7092 : AlterTableUtilityContext *context)
7093 : {
7094 2604 : Oid myrelid = RelationGetRelid(rel);
7095 2604 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7096 2604 : bool if_not_exists = (*cmd)->missing_ok;
7097 : Relation pgclass,
7098 : attrdesc;
7099 : HeapTuple reltup;
7100 : Form_pg_attribute attribute;
7101 : int newattnum;
7102 : char relkind;
7103 : Expr *defval;
7104 : List *children;
7105 : ListCell *child;
7106 : AlterTableCmd *childcmd;
7107 : ObjectAddress address;
7108 : TupleDesc tupdesc;
7109 :
7110 : /* since this function recurses, it could be driven to stack overflow */
7111 2604 : check_stack_depth();
7112 :
7113 : /* At top level, permission check was done in ATPrepCmd, else do it */
7114 2604 : if (recursing)
7115 636 : ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7116 :
7117 2604 : if (rel->rd_rel->relispartition && !recursing)
7118 12 : ereport(ERROR,
7119 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7120 : errmsg("cannot add column to a partition")));
7121 :
7122 2592 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7123 :
7124 : /*
7125 : * Are we adding the column to a recursion child? If so, check whether to
7126 : * merge with an existing definition for the column. If we do merge, we
7127 : * must not recurse. Children will already have the column, and recursing
7128 : * into them would mess up attinhcount.
7129 : */
7130 2592 : if (colDef->inhcount > 0)
7131 : {
7132 : HeapTuple tuple;
7133 :
7134 : /* Does child already have a column by this name? */
7135 636 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7136 636 : if (HeapTupleIsValid(tuple))
7137 : {
7138 48 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7139 : Oid ctypeId;
7140 : int32 ctypmod;
7141 : Oid ccollid;
7142 :
7143 : /* Child column must match on type, typmod, and collation */
7144 48 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7145 48 : if (ctypeId != childatt->atttypid ||
7146 48 : ctypmod != childatt->atttypmod)
7147 0 : ereport(ERROR,
7148 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7149 : errmsg("child table \"%s\" has different type for column \"%s\"",
7150 : RelationGetRelationName(rel), colDef->colname)));
7151 48 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7152 48 : if (ccollid != childatt->attcollation)
7153 0 : ereport(ERROR,
7154 : (errcode(ERRCODE_COLLATION_MISMATCH),
7155 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7156 : RelationGetRelationName(rel), colDef->colname),
7157 : errdetail("\"%s\" versus \"%s\"",
7158 : get_collation_name(ccollid),
7159 : get_collation_name(childatt->attcollation))));
7160 :
7161 : /* Bump the existing child att's inhcount */
7162 48 : childatt->attinhcount++;
7163 48 : if (childatt->attinhcount < 0)
7164 0 : ereport(ERROR,
7165 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7166 : errmsg("too many inheritance parents"));
7167 48 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7168 :
7169 48 : heap_freetuple(tuple);
7170 :
7171 : /* Inform the user about the merge */
7172 48 : ereport(NOTICE,
7173 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7174 : colDef->colname, RelationGetRelationName(rel))));
7175 :
7176 48 : table_close(attrdesc, RowExclusiveLock);
7177 :
7178 : /* Make the child column change visible */
7179 48 : CommandCounterIncrement();
7180 :
7181 48 : return InvalidObjectAddress;
7182 : }
7183 : }
7184 :
7185 : /* skip if the name already exists and if_not_exists is true */
7186 2544 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7187 : {
7188 54 : table_close(attrdesc, RowExclusiveLock);
7189 54 : return InvalidObjectAddress;
7190 : }
7191 :
7192 : /*
7193 : * Okay, we need to add the column, so go ahead and do parse
7194 : * transformation. This can result in queueing up, or even immediately
7195 : * executing, subsidiary operations (such as creation of unique indexes);
7196 : * so we mustn't do it until we have made the if_not_exists check.
7197 : *
7198 : * When recursing, the command was already transformed and we needn't do
7199 : * so again. Also, if context isn't given we can't transform. (That
7200 : * currently happens only for AT_AddColumnToView; we expect that view.c
7201 : * passed us a ColumnDef that doesn't need work.)
7202 : */
7203 2460 : if (context != NULL && !recursing)
7204 : {
7205 1848 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7206 : cur_pass, context);
7207 : Assert(*cmd != NULL);
7208 1848 : colDef = castNode(ColumnDef, (*cmd)->def);
7209 : }
7210 :
7211 : /*
7212 : * Regular inheritance children are independent enough not to inherit the
7213 : * identity column from parent hence cannot recursively add identity
7214 : * column if the table has inheritance children.
7215 : *
7216 : * Partitions, on the other hand, are integral part of a partitioned table
7217 : * and inherit identity column. Hence propagate identity column down the
7218 : * partition hierarchy.
7219 : */
7220 2460 : if (colDef->identity &&
7221 54 : recurse &&
7222 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7223 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7224 6 : ereport(ERROR,
7225 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7226 : errmsg("cannot recursively add identity column to table that has child tables")));
7227 :
7228 2454 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7229 :
7230 2454 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7231 2454 : if (!HeapTupleIsValid(reltup))
7232 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7233 2454 : relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7234 :
7235 : /* Determine the new attribute's number */
7236 2454 : newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7237 2454 : if (newattnum > MaxHeapAttributeNumber)
7238 0 : ereport(ERROR,
7239 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7240 : errmsg("tables can have at most %d columns",
7241 : MaxHeapAttributeNumber)));
7242 :
7243 : /*
7244 : * Construct new attribute's pg_attribute entry.
7245 : */
7246 2454 : tupdesc = BuildDescForRelation(list_make1(colDef));
7247 :
7248 2442 : attribute = TupleDescAttr(tupdesc, 0);
7249 :
7250 : /* Fix up attribute number */
7251 2442 : attribute->attnum = newattnum;
7252 :
7253 : /* make sure datatype is legal for a column */
7254 2442 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7255 2442 : list_make1_oid(rel->rd_rel->reltype),
7256 : 0);
7257 :
7258 2412 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7259 :
7260 2412 : table_close(attrdesc, RowExclusiveLock);
7261 :
7262 : /*
7263 : * Update pg_class tuple as appropriate
7264 : */
7265 2412 : ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7266 :
7267 2412 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7268 :
7269 2412 : heap_freetuple(reltup);
7270 :
7271 : /* Post creation hook for new attribute */
7272 2412 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7273 :
7274 2412 : table_close(pgclass, RowExclusiveLock);
7275 :
7276 : /* Make the attribute's catalog entry visible */
7277 2412 : CommandCounterIncrement();
7278 :
7279 : /*
7280 : * Store the DEFAULT, if any, in the catalogs
7281 : */
7282 2412 : if (colDef->raw_default)
7283 : {
7284 : RawColumnDefault *rawEnt;
7285 :
7286 700 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7287 700 : rawEnt->attnum = attribute->attnum;
7288 700 : rawEnt->raw_default = copyObject(colDef->raw_default);
7289 :
7290 : /*
7291 : * Attempt to skip a complete table rewrite by storing the specified
7292 : * DEFAULT value outside of the heap. This may be disabled inside
7293 : * AddRelationNewConstraints if the optimization cannot be applied.
7294 : */
7295 700 : rawEnt->missingMode = (!colDef->generated);
7296 :
7297 700 : rawEnt->generated = colDef->generated;
7298 :
7299 : /*
7300 : * This function is intended for CREATE TABLE, so it processes a
7301 : * _list_ of defaults, but we just do one.
7302 : */
7303 700 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7304 : false, true, false, NULL);
7305 :
7306 : /* Make the additional catalog changes visible */
7307 688 : CommandCounterIncrement();
7308 :
7309 : /*
7310 : * Did the request for a missing value work? If not we'll have to do a
7311 : * rewrite
7312 : */
7313 688 : if (!rawEnt->missingMode)
7314 108 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7315 : }
7316 :
7317 : /*
7318 : * Tell Phase 3 to fill in the default expression, if there is one.
7319 : *
7320 : * If there is no default, Phase 3 doesn't have to do anything, because
7321 : * that effectively means that the default is NULL. The heap tuple access
7322 : * routines always check for attnum > # of attributes in tuple, and return
7323 : * NULL if so, so without any modification of the tuple data we will get
7324 : * the effect of NULL values in the new column.
7325 : *
7326 : * An exception occurs when the new column is of a domain type: the domain
7327 : * might have a not-null constraint, or a check constraint that indirectly
7328 : * rejects nulls. If there are any domain constraints then we construct
7329 : * an explicit NULL default value that will be passed through
7330 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7331 : * rewriting the table which we really don't have to do, but the present
7332 : * design of domain processing doesn't offer any simple way of checking
7333 : * the constraints more directly.)
7334 : *
7335 : * Note: we use build_column_default, and not just the cooked default
7336 : * returned by AddRelationNewConstraints, so that the right thing happens
7337 : * when a datatype's default applies.
7338 : *
7339 : * Note: it might seem that this should happen at the end of Phase 2, so
7340 : * that the effects of subsequent subcommands can be taken into account.
7341 : * It's intentional that we do it now, though. The new column should be
7342 : * filled according to what is said in the ADD COLUMN subcommand, so that
7343 : * the effects are the same as if this subcommand had been run by itself
7344 : * and the later subcommands had been issued in new ALTER TABLE commands.
7345 : *
7346 : * We can skip this entirely for relations without storage, since Phase 3
7347 : * is certainly not going to touch them. System attributes don't have
7348 : * interesting defaults, either.
7349 : */
7350 2400 : if (RELKIND_HAS_STORAGE(relkind))
7351 : {
7352 : /*
7353 : * For an identity column, we can't use build_column_default(),
7354 : * because the sequence ownership isn't set yet. So do it manually.
7355 : */
7356 2048 : if (colDef->identity)
7357 : {
7358 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7359 :
7360 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7361 42 : nve->typeId = attribute->atttypid;
7362 :
7363 42 : defval = (Expr *) nve;
7364 :
7365 : /* must do a rewrite for identity columns */
7366 42 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7367 : }
7368 : else
7369 2006 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7370 :
7371 2048 : if (!defval && DomainHasConstraints(attribute->atttypid))
7372 : {
7373 : Oid baseTypeId;
7374 : int32 baseTypeMod;
7375 : Oid baseTypeColl;
7376 :
7377 6 : baseTypeMod = attribute->atttypmod;
7378 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7379 6 : baseTypeColl = get_typcollation(baseTypeId);
7380 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7381 6 : defval = (Expr *) coerce_to_target_type(NULL,
7382 : (Node *) defval,
7383 : baseTypeId,
7384 : attribute->atttypid,
7385 : attribute->atttypmod,
7386 : COERCION_ASSIGNMENT,
7387 : COERCE_IMPLICIT_CAST,
7388 : -1);
7389 6 : if (defval == NULL) /* should not happen */
7390 0 : elog(ERROR, "failed to coerce base type to domain");
7391 : }
7392 :
7393 2048 : if (defval)
7394 : {
7395 : NewColumnValue *newval;
7396 :
7397 606 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7398 606 : newval->attnum = attribute->attnum;
7399 606 : newval->expr = expression_planner(defval);
7400 606 : newval->is_generated = (colDef->generated != '\0');
7401 :
7402 606 : tab->newvals = lappend(tab->newvals, newval);
7403 : }
7404 :
7405 2048 : if (DomainHasConstraints(attribute->atttypid))
7406 12 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7407 :
7408 2048 : if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7409 : {
7410 : /*
7411 : * If the new column is NOT NULL, and there is no missing value,
7412 : * tell Phase 3 it needs to check for NULLs.
7413 : */
7414 1598 : tab->verify_new_notnull |= colDef->is_not_null;
7415 : }
7416 : }
7417 :
7418 : /*
7419 : * Add needed dependency entries for the new column.
7420 : */
7421 2400 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7422 2400 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7423 :
7424 : /*
7425 : * Propagate to children as appropriate. Unlike most other ALTER
7426 : * routines, we have to do this one level of recursion at a time; we can't
7427 : * use find_all_inheritors to do it in one pass.
7428 : */
7429 : children =
7430 2400 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7431 :
7432 : /*
7433 : * If we are told not to recurse, there had better not be any child
7434 : * tables; else the addition would put them out of step.
7435 : */
7436 2400 : if (children && !recurse)
7437 12 : ereport(ERROR,
7438 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7439 : errmsg("column must be added to child tables too")));
7440 :
7441 : /* Children should see column as singly inherited */
7442 2388 : if (!recursing)
7443 : {
7444 1800 : childcmd = copyObject(*cmd);
7445 1800 : colDef = castNode(ColumnDef, childcmd->def);
7446 1800 : colDef->inhcount = 1;
7447 1800 : colDef->is_local = false;
7448 : }
7449 : else
7450 588 : childcmd = *cmd; /* no need to copy again */
7451 :
7452 3024 : foreach(child, children)
7453 : {
7454 636 : Oid childrelid = lfirst_oid(child);
7455 : Relation childrel;
7456 : AlteredTableInfo *childtab;
7457 :
7458 : /* find_inheritance_children already got lock */
7459 636 : childrel = table_open(childrelid, NoLock);
7460 636 : CheckTableNotInUse(childrel, "ALTER TABLE");
7461 :
7462 : /* Find or create work queue entry for this table */
7463 636 : childtab = ATGetQueueEntry(wqueue, childrel);
7464 :
7465 : /* Recurse to child; return value is ignored */
7466 636 : ATExecAddColumn(wqueue, childtab, childrel,
7467 : &childcmd, recurse, true,
7468 : lockmode, cur_pass, context);
7469 :
7470 636 : table_close(childrel, NoLock);
7471 : }
7472 :
7473 2388 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7474 2388 : return address;
7475 : }
7476 :
7477 : /*
7478 : * If a new or renamed column will collide with the name of an existing
7479 : * column and if_not_exists is false then error out, else do nothing.
7480 : */
7481 : static bool
7482 2982 : check_for_column_name_collision(Relation rel, const char *colname,
7483 : bool if_not_exists)
7484 : {
7485 : HeapTuple attTuple;
7486 : int attnum;
7487 :
7488 : /*
7489 : * this test is deliberately not attisdropped-aware, since if one tries to
7490 : * add a column matching a dropped column name, it's gonna fail anyway.
7491 : */
7492 2982 : attTuple = SearchSysCache2(ATTNAME,
7493 : ObjectIdGetDatum(RelationGetRelid(rel)),
7494 : PointerGetDatum(colname));
7495 2982 : if (!HeapTupleIsValid(attTuple))
7496 2886 : return true;
7497 :
7498 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7499 96 : ReleaseSysCache(attTuple);
7500 :
7501 : /*
7502 : * We throw a different error message for conflicts with system column
7503 : * names, since they are normally not shown and the user might otherwise
7504 : * be confused about the reason for the conflict.
7505 : */
7506 96 : if (attnum <= 0)
7507 12 : ereport(ERROR,
7508 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7509 : errmsg("column name \"%s\" conflicts with a system column name",
7510 : colname)));
7511 : else
7512 : {
7513 84 : if (if_not_exists)
7514 : {
7515 54 : ereport(NOTICE,
7516 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7517 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7518 : colname, RelationGetRelationName(rel))));
7519 54 : return false;
7520 : }
7521 :
7522 30 : ereport(ERROR,
7523 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7524 : errmsg("column \"%s\" of relation \"%s\" already exists",
7525 : colname, RelationGetRelationName(rel))));
7526 : }
7527 :
7528 : return true;
7529 : }
7530 :
7531 : /*
7532 : * Install a column's dependency on its datatype.
7533 : */
7534 : static void
7535 3330 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7536 : {
7537 : ObjectAddress myself,
7538 : referenced;
7539 :
7540 3330 : myself.classId = RelationRelationId;
7541 3330 : myself.objectId = relid;
7542 3330 : myself.objectSubId = attnum;
7543 3330 : referenced.classId = TypeRelationId;
7544 3330 : referenced.objectId = typid;
7545 3330 : referenced.objectSubId = 0;
7546 3330 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7547 3330 : }
7548 :
7549 : /*
7550 : * Install a column's dependency on its collation.
7551 : */
7552 : static void
7553 3330 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7554 : {
7555 : ObjectAddress myself,
7556 : referenced;
7557 :
7558 : /* We know the default collation is pinned, so don't bother recording it */
7559 3330 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7560 : {
7561 18 : myself.classId = RelationRelationId;
7562 18 : myself.objectId = relid;
7563 18 : myself.objectSubId = attnum;
7564 18 : referenced.classId = CollationRelationId;
7565 18 : referenced.objectId = collid;
7566 18 : referenced.objectSubId = 0;
7567 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7568 : }
7569 3330 : }
7570 :
7571 : /*
7572 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7573 : *
7574 : * Return the address of the modified column. If the column was already
7575 : * nullable, InvalidObjectAddress is returned.
7576 : */
7577 : static ObjectAddress
7578 256 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7579 : LOCKMODE lockmode)
7580 : {
7581 : HeapTuple tuple;
7582 : HeapTuple conTup;
7583 : Form_pg_attribute attTup;
7584 : AttrNumber attnum;
7585 : Relation attr_rel;
7586 : ObjectAddress address;
7587 : List *readyRels;
7588 :
7589 : /*
7590 : * lookup the attribute
7591 : */
7592 256 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7593 :
7594 256 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7595 256 : if (!HeapTupleIsValid(tuple))
7596 18 : ereport(ERROR,
7597 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7598 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7599 : colName, RelationGetRelationName(rel))));
7600 238 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7601 238 : attnum = attTup->attnum;
7602 238 : ObjectAddressSubSet(address, RelationRelationId,
7603 : RelationGetRelid(rel), attnum);
7604 :
7605 : /* If the column is already nullable there's nothing to do. */
7606 238 : if (!attTup->attnotnull)
7607 : {
7608 6 : table_close(attr_rel, RowExclusiveLock);
7609 6 : return InvalidObjectAddress;
7610 : }
7611 :
7612 : /* Prevent them from altering a system attribute */
7613 232 : if (attnum <= 0)
7614 0 : ereport(ERROR,
7615 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7616 : errmsg("cannot alter system column \"%s\"",
7617 : colName)));
7618 :
7619 232 : if (attTup->attidentity)
7620 18 : ereport(ERROR,
7621 : (errcode(ERRCODE_SYNTAX_ERROR),
7622 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7623 : colName, RelationGetRelationName(rel))));
7624 :
7625 : /*
7626 : * It's not OK to remove a constraint only for the parent and leave it in
7627 : * the children, so disallow that.
7628 : */
7629 214 : if (!recurse)
7630 : {
7631 12 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7632 : {
7633 : PartitionDesc partdesc;
7634 :
7635 12 : partdesc = RelationGetPartitionDesc(rel, true);
7636 :
7637 12 : if (partdesc->nparts > 0)
7638 6 : ereport(ERROR,
7639 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7640 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7641 : errhint("Do not specify the ONLY keyword."));
7642 : }
7643 0 : else if (rel->rd_rel->relhassubclass &&
7644 0 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
7645 : {
7646 0 : ereport(ERROR,
7647 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7648 : errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7649 : colName),
7650 : errhint("Do not specify the ONLY keyword."));
7651 : }
7652 : }
7653 :
7654 : /*
7655 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7656 : */
7657 208 : if (rel->rd_rel->relispartition)
7658 : {
7659 18 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7660 18 : Relation parent = table_open(parentId, AccessShareLock);
7661 18 : TupleDesc tupDesc = RelationGetDescr(parent);
7662 : AttrNumber parent_attnum;
7663 :
7664 18 : parent_attnum = get_attnum(parentId, colName);
7665 18 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7666 18 : ereport(ERROR,
7667 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7668 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7669 : colName)));
7670 0 : table_close(parent, AccessShareLock);
7671 : }
7672 :
7673 : /*
7674 : * Find the constraint that makes this column NOT NULL, and drop it if we
7675 : * see one. dropconstraint_internal() will do necessary consistency
7676 : * checking. If there isn't one, there are two possibilities: either the
7677 : * column is marked attnotnull because it's part of the primary key, and
7678 : * then we just throw an appropriate error; or it's a leftover marking
7679 : * that we can remove. However, before doing the latter, to avoid
7680 : * breaking consistency any further, prevent this if the column is part of
7681 : * the replica identity.
7682 : */
7683 190 : conTup = findNotNullConstraint(RelationGetRelid(rel), colName);
7684 190 : if (conTup == NULL)
7685 : {
7686 : Bitmapset *pkcols;
7687 : Bitmapset *ircols;
7688 :
7689 : /*
7690 : * If the column is in a primary key, throw a specific error message.
7691 : */
7692 30 : pkcols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
7693 30 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
7694 : pkcols))
7695 12 : ereport(ERROR,
7696 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7697 : errmsg("column \"%s\" is in a primary key", colName));
7698 :
7699 : /* Also throw an error if the column is in the replica identity */
7700 18 : ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
7701 18 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, ircols))
7702 6 : ereport(ERROR,
7703 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7704 : errmsg("column \"%s\" is in index used as replica identity",
7705 : get_attname(RelationGetRelid(rel), attnum, false)));
7706 :
7707 : /* Otherwise, just remove the attnotnull marking and do nothing else. */
7708 12 : attTup->attnotnull = false;
7709 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7710 : }
7711 : else
7712 : {
7713 : /* The normal case: we have a pg_constraint row, remove it */
7714 160 : readyRels = NIL;
7715 160 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7716 : false, &readyRels, lockmode);
7717 :
7718 124 : heap_freetuple(conTup);
7719 : }
7720 :
7721 136 : InvokeObjectPostAlterHook(RelationRelationId,
7722 : RelationGetRelid(rel), attnum);
7723 :
7724 136 : table_close(attr_rel, RowExclusiveLock);
7725 :
7726 136 : return address;
7727 : }
7728 :
7729 : /*
7730 : * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7731 : * to verify it; recurses to apply the same to children.
7732 : *
7733 : * When called to alter an existing table, 'wqueue' must be given so that we can
7734 : * queue a check that existing tuples pass the constraint. When called from
7735 : * table creation, 'wqueue' should be passed as NULL.
7736 : *
7737 : * Returns true if the flag was set in any table, otherwise false.
7738 : */
7739 : static bool
7740 23764 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
7741 : LOCKMODE lockmode)
7742 : {
7743 : HeapTuple tuple;
7744 : Form_pg_attribute attForm;
7745 23764 : bool retval = false;
7746 :
7747 : /* Guard against stack overflow due to overly deep inheritance tree. */
7748 23764 : check_stack_depth();
7749 :
7750 23764 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7751 23764 : if (!HeapTupleIsValid(tuple))
7752 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7753 : attnum, RelationGetRelid(rel));
7754 23764 : attForm = (Form_pg_attribute) GETSTRUCT(tuple);
7755 23764 : if (!attForm->attnotnull)
7756 : {
7757 : Relation attr_rel;
7758 :
7759 1890 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7760 :
7761 1890 : attForm->attnotnull = true;
7762 1890 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7763 :
7764 1890 : table_close(attr_rel, RowExclusiveLock);
7765 :
7766 : /*
7767 : * And set up for existing values to be checked, unless another
7768 : * constraint already proves this.
7769 : */
7770 1890 : if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
7771 : {
7772 : AlteredTableInfo *tab;
7773 :
7774 1602 : tab = ATGetQueueEntry(wqueue, rel);
7775 1602 : tab->verify_new_notnull = true;
7776 : }
7777 :
7778 1890 : retval = true;
7779 : }
7780 :
7781 23764 : if (recurse)
7782 : {
7783 : List *children;
7784 : ListCell *lc;
7785 :
7786 : /* Make above update visible, for multiple inheritance cases */
7787 1134 : if (retval)
7788 296 : CommandCounterIncrement();
7789 :
7790 1134 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7791 1374 : foreach(lc, children)
7792 : {
7793 240 : Oid childrelid = lfirst_oid(lc);
7794 : Relation childrel;
7795 : AttrNumber childattno;
7796 :
7797 : /* find_inheritance_children already got lock */
7798 240 : childrel = table_open(childrelid, NoLock);
7799 240 : CheckTableNotInUse(childrel, "ALTER TABLE");
7800 :
7801 240 : childattno = get_attnum(RelationGetRelid(childrel),
7802 240 : get_attname(RelationGetRelid(rel), attnum,
7803 : false));
7804 240 : retval |= set_attnotnull(wqueue, childrel, childattno,
7805 : recurse, lockmode);
7806 240 : table_close(childrel, NoLock);
7807 : }
7808 : }
7809 :
7810 23764 : return retval;
7811 : }
7812 :
7813 : /*
7814 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7815 : *
7816 : * Add a not-null constraint to a single table and its children. Returns
7817 : * the address of the constraint added to the parent relation, if one gets
7818 : * added, or InvalidObjectAddress otherwise.
7819 : *
7820 : * We must recurse to child tables during execution, rather than using
7821 : * ALTER TABLE's normal prep-time recursion.
7822 : */
7823 : static ObjectAddress
7824 502 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7825 : bool recurse, bool recursing, List **readyRels,
7826 : LOCKMODE lockmode)
7827 : {
7828 : HeapTuple tuple;
7829 : Relation constr_rel;
7830 : ScanKeyData skey;
7831 : SysScanDesc conscan;
7832 : AttrNumber attnum;
7833 : ObjectAddress address;
7834 : Constraint *constraint;
7835 : CookedConstraint *ccon;
7836 : List *cooked;
7837 502 : bool is_no_inherit = false;
7838 502 : List *ready = NIL;
7839 :
7840 : /* Guard against stack overflow due to overly deep inheritance tree. */
7841 502 : check_stack_depth();
7842 :
7843 : /*
7844 : * In cases of multiple inheritance, we might visit the same child more
7845 : * than once. In the topmost call, set up a list that we fill with all
7846 : * visited relations, to skip those.
7847 : */
7848 502 : if (readyRels == NULL)
7849 : {
7850 : Assert(!recursing);
7851 372 : readyRels = &ready;
7852 : }
7853 502 : if (list_member_oid(*readyRels, RelationGetRelid(rel)))
7854 6 : return InvalidObjectAddress;
7855 496 : *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
7856 :
7857 : /* At top level, permission check was done in ATPrepCmd, else do it */
7858 496 : if (recursing)
7859 : {
7860 124 : ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7861 : Assert(conName != NULL);
7862 : }
7863 :
7864 496 : attnum = get_attnum(RelationGetRelid(rel), colName);
7865 496 : if (attnum == InvalidAttrNumber)
7866 18 : ereport(ERROR,
7867 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7868 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7869 : colName, RelationGetRelationName(rel))));
7870 :
7871 : /* Prevent them from altering a system attribute */
7872 478 : if (attnum <= 0)
7873 0 : ereport(ERROR,
7874 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7875 : errmsg("cannot alter system column \"%s\"",
7876 : colName)));
7877 :
7878 : /* See if there's already a constraint */
7879 478 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7880 478 : ScanKeyInit(&skey,
7881 : Anum_pg_constraint_conrelid,
7882 : BTEqualStrategyNumber, F_OIDEQ,
7883 : ObjectIdGetDatum(RelationGetRelid(rel)));
7884 478 : conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
7885 : NULL, 1, &skey);
7886 :
7887 904 : while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
7888 : {
7889 452 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7890 452 : bool changed = false;
7891 : HeapTuple copytup;
7892 :
7893 452 : if (conForm->contype != CONSTRAINT_NOTNULL)
7894 206 : continue;
7895 :
7896 246 : if (extractNotNullColumn(tuple) != attnum)
7897 220 : continue;
7898 :
7899 26 : copytup = heap_copytuple(tuple);
7900 26 : conForm = (Form_pg_constraint) GETSTRUCT(copytup);
7901 :
7902 : /*
7903 : * Don't let a NO INHERIT constraint be changed into inherit.
7904 : */
7905 26 : if (conForm->connoinherit && recurse)
7906 6 : ereport(ERROR,
7907 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7908 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" in relation \"%s\"",
7909 : NameStr(conForm->conname),
7910 : RelationGetRelationName(rel)));
7911 :
7912 :
7913 : /*
7914 : * If we find an appropriate constraint, we're almost done, but just
7915 : * need to change some properties on it: if we're recursing, increment
7916 : * coninhcount; if not, set conislocal if not already set.
7917 : */
7918 20 : if (recursing)
7919 : {
7920 6 : conForm->coninhcount++;
7921 6 : changed = true;
7922 : }
7923 14 : else if (!conForm->conislocal)
7924 : {
7925 0 : conForm->conislocal = true;
7926 0 : changed = true;
7927 : }
7928 :
7929 20 : if (changed)
7930 : {
7931 6 : CatalogTupleUpdate(constr_rel, ©tup->t_self, copytup);
7932 6 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7933 : }
7934 :
7935 20 : systable_endscan(conscan);
7936 20 : table_close(constr_rel, RowExclusiveLock);
7937 :
7938 20 : if (changed)
7939 6 : return address;
7940 : else
7941 14 : return InvalidObjectAddress;
7942 : }
7943 :
7944 452 : systable_endscan(conscan);
7945 452 : table_close(constr_rel, RowExclusiveLock);
7946 :
7947 : /*
7948 : * If we're asked not to recurse, and children exist, raise an error for
7949 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
7950 : * specified.
7951 : */
7952 482 : if (!recurse &&
7953 30 : find_inheritance_children(RelationGetRelid(rel),
7954 : NoLock) != NIL)
7955 : {
7956 24 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7957 12 : ereport(ERROR,
7958 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7959 : errmsg("constraint must be added to child tables too"),
7960 : errhint("Do not specify the ONLY keyword."));
7961 : else
7962 12 : is_no_inherit = true;
7963 : }
7964 :
7965 : /*
7966 : * No constraint exists; we must add one. First determine a name to use,
7967 : * if we haven't already.
7968 : */
7969 440 : if (!recursing)
7970 : {
7971 : Assert(conName == NULL);
7972 322 : conName = ChooseConstraintName(RelationGetRelationName(rel),
7973 : colName, "not_null",
7974 322 : RelationGetNamespace(rel),
7975 : NIL);
7976 : }
7977 440 : constraint = makeNode(Constraint);
7978 440 : constraint->contype = CONSTR_NOTNULL;
7979 440 : constraint->conname = conName;
7980 440 : constraint->deferrable = false;
7981 440 : constraint->initdeferred = false;
7982 440 : constraint->location = -1;
7983 440 : constraint->keys = list_make1(makeString(colName));
7984 440 : constraint->is_no_inherit = is_no_inherit;
7985 440 : constraint->inhcount = recursing ? 1 : 0;
7986 440 : constraint->skip_validation = false;
7987 440 : constraint->initially_valid = true;
7988 :
7989 : /* and do it */
7990 440 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7991 440 : false, !recursing, false, NULL);
7992 440 : ccon = linitial(cooked);
7993 440 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7994 :
7995 440 : InvokeObjectPostAlterHook(RelationRelationId,
7996 : RelationGetRelid(rel), attnum);
7997 :
7998 : /*
7999 : * Mark pg_attribute.attnotnull for the column. Tell that function not to
8000 : * recurse, because we're going to do it here.
8001 : */
8002 440 : set_attnotnull(wqueue, rel, attnum, false, lockmode);
8003 :
8004 : /*
8005 : * Recurse to propagate the constraint to children that don't have one.
8006 : */
8007 440 : if (recurse)
8008 : {
8009 : List *children;
8010 : ListCell *lc;
8011 :
8012 422 : children = find_inheritance_children(RelationGetRelid(rel),
8013 : lockmode);
8014 :
8015 552 : foreach(lc, children)
8016 : {
8017 : Relation childrel;
8018 :
8019 130 : childrel = table_open(lfirst_oid(lc), NoLock);
8020 :
8021 130 : ATExecSetNotNull(wqueue, childrel,
8022 : conName, colName, recurse, true,
8023 : readyRels, lockmode);
8024 :
8025 130 : table_close(childrel, NoLock);
8026 : }
8027 : }
8028 :
8029 440 : return address;
8030 : }
8031 :
8032 : /*
8033 : * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
8034 : *
8035 : * This doesn't exist in the grammar; it's used when creating a
8036 : * primary key and the column is not already marked attnotnull.
8037 : */
8038 : static ObjectAddress
8039 14702 : ATExecSetAttNotNull(List **wqueue, Relation rel,
8040 : const char *colName, LOCKMODE lockmode)
8041 : {
8042 : AttrNumber attnum;
8043 14702 : ObjectAddress address = InvalidObjectAddress;
8044 :
8045 14702 : attnum = get_attnum(RelationGetRelid(rel), colName);
8046 14702 : if (attnum == InvalidAttrNumber)
8047 18 : ereport(ERROR,
8048 : errcode(ERRCODE_UNDEFINED_COLUMN),
8049 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8050 : colName, RelationGetRelationName(rel)));
8051 :
8052 : /*
8053 : * Make the change, if necessary, and only if so report the column as
8054 : * changed
8055 : */
8056 14684 : if (set_attnotnull(wqueue, rel, attnum, false, lockmode))
8057 918 : ObjectAddressSubSet(address, RelationRelationId,
8058 : RelationGetRelid(rel), attnum);
8059 :
8060 14684 : return address;
8061 : }
8062 :
8063 : /*
8064 : * NotNullImpliedByRelConstraints
8065 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8066 : */
8067 : static bool
8068 1652 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8069 : {
8070 1652 : NullTest *nnulltest = makeNode(NullTest);
8071 :
8072 3304 : nnulltest->arg = (Expr *) makeVar(1,
8073 1652 : attr->attnum,
8074 : attr->atttypid,
8075 : attr->atttypmod,
8076 : attr->attcollation,
8077 : 0);
8078 1652 : nnulltest->nulltesttype = IS_NOT_NULL;
8079 :
8080 : /*
8081 : * argisrow = false is correct even for a composite column, because
8082 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8083 : * case, just IS DISTINCT FROM NULL.
8084 : */
8085 1652 : nnulltest->argisrow = false;
8086 1652 : nnulltest->location = -1;
8087 :
8088 1652 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8089 : {
8090 50 : ereport(DEBUG1,
8091 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8092 : RelationGetRelationName(rel), NameStr(attr->attname))));
8093 50 : return true;
8094 : }
8095 :
8096 1602 : return false;
8097 : }
8098 :
8099 : /*
8100 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8101 : *
8102 : * Return the address of the affected column.
8103 : */
8104 : static ObjectAddress
8105 556 : ATExecColumnDefault(Relation rel, const char *colName,
8106 : Node *newDefault, LOCKMODE lockmode)
8107 : {
8108 556 : TupleDesc tupdesc = RelationGetDescr(rel);
8109 : AttrNumber attnum;
8110 : ObjectAddress address;
8111 :
8112 : /*
8113 : * get the number of the attribute
8114 : */
8115 556 : attnum = get_attnum(RelationGetRelid(rel), colName);
8116 556 : if (attnum == InvalidAttrNumber)
8117 30 : ereport(ERROR,
8118 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8119 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8120 : colName, RelationGetRelationName(rel))));
8121 :
8122 : /* Prevent them from altering a system attribute */
8123 526 : if (attnum <= 0)
8124 0 : ereport(ERROR,
8125 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8126 : errmsg("cannot alter system column \"%s\"",
8127 : colName)));
8128 :
8129 526 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8130 18 : ereport(ERROR,
8131 : (errcode(ERRCODE_SYNTAX_ERROR),
8132 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8133 : colName, RelationGetRelationName(rel)),
8134 : /* translator: %s is an SQL ALTER command */
8135 : newDefault ? 0 : errhint("Use %s instead.",
8136 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8137 :
8138 508 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8139 6 : ereport(ERROR,
8140 : (errcode(ERRCODE_SYNTAX_ERROR),
8141 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8142 : colName, RelationGetRelationName(rel)),
8143 : newDefault ?
8144 : /* translator: %s is an SQL ALTER command */
8145 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8146 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8147 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8148 :
8149 : /*
8150 : * Remove any old default for the column. We use RESTRICT here for
8151 : * safety, but at present we do not expect anything to depend on the
8152 : * default.
8153 : *
8154 : * We treat removing the existing default as an internal operation when it
8155 : * is preparatory to adding a new default, but as a user-initiated
8156 : * operation when the user asked for a drop.
8157 : */
8158 502 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8159 : newDefault != NULL);
8160 :
8161 502 : if (newDefault)
8162 : {
8163 : /* SET DEFAULT */
8164 : RawColumnDefault *rawEnt;
8165 :
8166 328 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8167 328 : rawEnt->attnum = attnum;
8168 328 : rawEnt->raw_default = newDefault;
8169 328 : rawEnt->missingMode = false;
8170 328 : rawEnt->generated = '\0';
8171 :
8172 : /*
8173 : * This function is intended for CREATE TABLE, so it processes a
8174 : * _list_ of defaults, but we just do one.
8175 : */
8176 328 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8177 : false, true, false, NULL);
8178 : }
8179 :
8180 496 : ObjectAddressSubSet(address, RelationRelationId,
8181 : RelationGetRelid(rel), attnum);
8182 496 : return address;
8183 : }
8184 :
8185 : /*
8186 : * Add a pre-cooked default expression.
8187 : *
8188 : * Return the address of the affected column.
8189 : */
8190 : static ObjectAddress
8191 110 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8192 : Node *newDefault)
8193 : {
8194 : ObjectAddress address;
8195 :
8196 : /* We assume no checking is required */
8197 :
8198 : /*
8199 : * Remove any old default for the column. We use RESTRICT here for
8200 : * safety, but at present we do not expect anything to depend on the
8201 : * default. (In ordinary cases, there could not be a default in place
8202 : * anyway, but it's possible when combining LIKE with inheritance.)
8203 : */
8204 110 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8205 : true);
8206 :
8207 110 : (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8208 :
8209 110 : ObjectAddressSubSet(address, RelationRelationId,
8210 : RelationGetRelid(rel), attnum);
8211 110 : return address;
8212 : }
8213 :
8214 : /*
8215 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8216 : *
8217 : * Return the address of the affected column.
8218 : */
8219 : static ObjectAddress
8220 152 : ATExecAddIdentity(Relation rel, const char *colName,
8221 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8222 : {
8223 : Relation attrelation;
8224 : HeapTuple tuple;
8225 : Form_pg_attribute attTup;
8226 : AttrNumber attnum;
8227 : ObjectAddress address;
8228 152 : ColumnDef *cdef = castNode(ColumnDef, def);
8229 : bool ispartitioned;
8230 :
8231 152 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8232 152 : if (ispartitioned && !recurse)
8233 0 : ereport(ERROR,
8234 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8235 : errmsg("cannot add identity to a column of only the partitioned table"),
8236 : errhint("Do not specify the ONLY keyword.")));
8237 :
8238 152 : if (rel->rd_rel->relispartition && !recursing)
8239 12 : ereport(ERROR,
8240 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8241 : errmsg("cannot add identity to a column of a partition"));
8242 :
8243 140 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8244 :
8245 140 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8246 140 : if (!HeapTupleIsValid(tuple))
8247 0 : ereport(ERROR,
8248 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8249 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8250 : colName, RelationGetRelationName(rel))));
8251 140 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8252 140 : attnum = attTup->attnum;
8253 :
8254 : /* Can't alter a system attribute */
8255 140 : if (attnum <= 0)
8256 0 : ereport(ERROR,
8257 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8258 : errmsg("cannot alter system column \"%s\"",
8259 : colName)));
8260 :
8261 : /*
8262 : * Creating a column as identity implies NOT NULL, so adding the identity
8263 : * to an existing column that is not NOT NULL would create a state that
8264 : * cannot be reproduced without contortions.
8265 : */
8266 140 : if (!attTup->attnotnull)
8267 6 : ereport(ERROR,
8268 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8269 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8270 : colName, RelationGetRelationName(rel))));
8271 :
8272 134 : if (attTup->attidentity)
8273 18 : ereport(ERROR,
8274 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8275 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8276 : colName, RelationGetRelationName(rel))));
8277 :
8278 116 : if (attTup->atthasdef)
8279 6 : ereport(ERROR,
8280 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8281 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8282 : colName, RelationGetRelationName(rel))));
8283 :
8284 110 : attTup->attidentity = cdef->identity;
8285 110 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8286 :
8287 110 : InvokeObjectPostAlterHook(RelationRelationId,
8288 : RelationGetRelid(rel),
8289 : attTup->attnum);
8290 110 : ObjectAddressSubSet(address, RelationRelationId,
8291 : RelationGetRelid(rel), attnum);
8292 110 : heap_freetuple(tuple);
8293 :
8294 110 : table_close(attrelation, RowExclusiveLock);
8295 :
8296 : /*
8297 : * Recurse to propagate the identity column to partitions. Identity is
8298 : * not inherited in regular inheritance children.
8299 : */
8300 110 : if (recurse && ispartitioned)
8301 : {
8302 : List *children;
8303 : ListCell *lc;
8304 :
8305 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8306 :
8307 16 : foreach(lc, children)
8308 : {
8309 : Relation childrel;
8310 :
8311 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8312 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8313 6 : table_close(childrel, NoLock);
8314 : }
8315 : }
8316 :
8317 110 : return address;
8318 : }
8319 :
8320 : /*
8321 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8322 : *
8323 : * Return the address of the affected column.
8324 : */
8325 : static ObjectAddress
8326 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8327 : LOCKMODE lockmode, bool recurse, bool recursing)
8328 : {
8329 : ListCell *option;
8330 74 : DefElem *generatedEl = NULL;
8331 : HeapTuple tuple;
8332 : Form_pg_attribute attTup;
8333 : AttrNumber attnum;
8334 : Relation attrelation;
8335 : ObjectAddress address;
8336 : bool ispartitioned;
8337 :
8338 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8339 74 : if (ispartitioned && !recurse)
8340 6 : ereport(ERROR,
8341 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8342 : errmsg("cannot change identity column of only the partitioned table"),
8343 : errhint("Do not specify the ONLY keyword.")));
8344 :
8345 68 : if (rel->rd_rel->relispartition && !recursing)
8346 12 : ereport(ERROR,
8347 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8348 : errmsg("cannot change identity column of a partition"));
8349 :
8350 100 : foreach(option, castNode(List, def))
8351 : {
8352 44 : DefElem *defel = lfirst_node(DefElem, option);
8353 :
8354 44 : if (strcmp(defel->defname, "generated") == 0)
8355 : {
8356 44 : if (generatedEl)
8357 0 : ereport(ERROR,
8358 : (errcode(ERRCODE_SYNTAX_ERROR),
8359 : errmsg("conflicting or redundant options")));
8360 44 : generatedEl = defel;
8361 : }
8362 : else
8363 0 : elog(ERROR, "option \"%s\" not recognized",
8364 : defel->defname);
8365 : }
8366 :
8367 : /*
8368 : * Even if there is nothing to change here, we run all the checks. There
8369 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8370 : * there.
8371 : */
8372 :
8373 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8374 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8375 56 : if (!HeapTupleIsValid(tuple))
8376 0 : ereport(ERROR,
8377 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8378 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8379 : colName, RelationGetRelationName(rel))));
8380 :
8381 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8382 56 : attnum = attTup->attnum;
8383 :
8384 56 : if (attnum <= 0)
8385 0 : ereport(ERROR,
8386 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8387 : errmsg("cannot alter system column \"%s\"",
8388 : colName)));
8389 :
8390 56 : if (!attTup->attidentity)
8391 6 : ereport(ERROR,
8392 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8393 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8394 : colName, RelationGetRelationName(rel))));
8395 :
8396 50 : if (generatedEl)
8397 : {
8398 44 : attTup->attidentity = defGetInt32(generatedEl);
8399 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8400 :
8401 44 : InvokeObjectPostAlterHook(RelationRelationId,
8402 : RelationGetRelid(rel),
8403 : attTup->attnum);
8404 44 : ObjectAddressSubSet(address, RelationRelationId,
8405 : RelationGetRelid(rel), attnum);
8406 : }
8407 : else
8408 6 : address = InvalidObjectAddress;
8409 :
8410 50 : heap_freetuple(tuple);
8411 50 : table_close(attrelation, RowExclusiveLock);
8412 :
8413 : /*
8414 : * Recurse to propagate the identity change to partitions. Identity is not
8415 : * inherited in regular inheritance children.
8416 : */
8417 50 : if (generatedEl && recurse && ispartitioned)
8418 : {
8419 : List *children;
8420 : ListCell *lc;
8421 :
8422 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8423 :
8424 18 : foreach(lc, children)
8425 : {
8426 : Relation childrel;
8427 :
8428 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8429 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8430 12 : table_close(childrel, NoLock);
8431 : }
8432 : }
8433 :
8434 50 : return address;
8435 : }
8436 :
8437 : /*
8438 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8439 : *
8440 : * Return the address of the affected column.
8441 : */
8442 : static ObjectAddress
8443 98 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8444 : bool recurse, bool recursing)
8445 : {
8446 : HeapTuple tuple;
8447 : Form_pg_attribute attTup;
8448 : AttrNumber attnum;
8449 : Relation attrelation;
8450 : ObjectAddress address;
8451 : Oid seqid;
8452 : ObjectAddress seqaddress;
8453 : bool ispartitioned;
8454 :
8455 98 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8456 98 : if (ispartitioned && !recurse)
8457 6 : ereport(ERROR,
8458 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8459 : errmsg("cannot drop identity from a column of only the partitioned table"),
8460 : errhint("Do not specify the ONLY keyword.")));
8461 :
8462 92 : if (rel->rd_rel->relispartition && !recursing)
8463 6 : ereport(ERROR,
8464 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8465 : errmsg("cannot drop identity from a column of a partition"));
8466 :
8467 86 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8468 86 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8469 86 : if (!HeapTupleIsValid(tuple))
8470 0 : ereport(ERROR,
8471 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8472 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8473 : colName, RelationGetRelationName(rel))));
8474 :
8475 86 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8476 86 : attnum = attTup->attnum;
8477 :
8478 86 : if (attnum <= 0)
8479 0 : ereport(ERROR,
8480 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8481 : errmsg("cannot alter system column \"%s\"",
8482 : colName)));
8483 :
8484 86 : if (!attTup->attidentity)
8485 : {
8486 12 : if (!missing_ok)
8487 6 : ereport(ERROR,
8488 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8489 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8490 : colName, RelationGetRelationName(rel))));
8491 : else
8492 : {
8493 6 : ereport(NOTICE,
8494 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8495 : colName, RelationGetRelationName(rel))));
8496 6 : heap_freetuple(tuple);
8497 6 : table_close(attrelation, RowExclusiveLock);
8498 6 : return InvalidObjectAddress;
8499 : }
8500 : }
8501 :
8502 74 : attTup->attidentity = '\0';
8503 74 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8504 :
8505 74 : InvokeObjectPostAlterHook(RelationRelationId,
8506 : RelationGetRelid(rel),
8507 : attTup->attnum);
8508 74 : ObjectAddressSubSet(address, RelationRelationId,
8509 : RelationGetRelid(rel), attnum);
8510 74 : heap_freetuple(tuple);
8511 :
8512 74 : table_close(attrelation, RowExclusiveLock);
8513 :
8514 : /*
8515 : * Recurse to drop the identity from column in partitions. Identity is
8516 : * not inherited in regular inheritance children so ignore them.
8517 : */
8518 74 : if (recurse && ispartitioned)
8519 : {
8520 : List *children;
8521 : ListCell *lc;
8522 :
8523 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8524 :
8525 12 : foreach(lc, children)
8526 : {
8527 : Relation childrel;
8528 :
8529 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8530 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8531 6 : table_close(childrel, NoLock);
8532 : }
8533 : }
8534 :
8535 74 : if (!recursing)
8536 : {
8537 : /* drop the internal sequence */
8538 38 : seqid = getIdentitySequence(rel, attnum, false);
8539 38 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8540 : RelationRelationId, DEPENDENCY_INTERNAL);
8541 38 : CommandCounterIncrement();
8542 38 : seqaddress.classId = RelationRelationId;
8543 38 : seqaddress.objectId = seqid;
8544 38 : seqaddress.objectSubId = 0;
8545 38 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8546 : }
8547 :
8548 74 : return address;
8549 : }
8550 :
8551 : /*
8552 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8553 : *
8554 : * Return the address of the affected column.
8555 : */
8556 : static ObjectAddress
8557 84 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8558 : Node *newExpr, LOCKMODE lockmode)
8559 : {
8560 : HeapTuple tuple;
8561 : Form_pg_attribute attTup;
8562 : AttrNumber attnum;
8563 : Oid attrdefoid;
8564 : ObjectAddress address;
8565 : Expr *defval;
8566 : NewColumnValue *newval;
8567 : RawColumnDefault *rawEnt;
8568 :
8569 84 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8570 84 : if (!HeapTupleIsValid(tuple))
8571 0 : ereport(ERROR,
8572 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8573 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8574 : colName, RelationGetRelationName(rel))));
8575 :
8576 84 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8577 84 : attnum = attTup->attnum;
8578 :
8579 84 : if (attnum <= 0)
8580 0 : ereport(ERROR,
8581 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8582 : errmsg("cannot alter system column \"%s\"",
8583 : colName)));
8584 :
8585 84 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8586 6 : ereport(ERROR,
8587 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8588 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8589 : colName, RelationGetRelationName(rel))));
8590 78 : ReleaseSysCache(tuple);
8591 :
8592 : /*
8593 : * Clear all the missing values if we're rewriting the table, since this
8594 : * renders them pointless.
8595 : */
8596 78 : RelationClearMissing(rel);
8597 :
8598 : /* make sure we don't conflict with later attribute modifications */
8599 78 : CommandCounterIncrement();
8600 :
8601 : /*
8602 : * Find everything that depends on the column (constraints, indexes, etc),
8603 : * and record enough information to let us recreate the objects after
8604 : * rewrite.
8605 : */
8606 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8607 :
8608 : /*
8609 : * Drop the dependency records of the GENERATED expression, in particular
8610 : * its INTERNAL dependency on the column, which would otherwise cause
8611 : * dependency.c to refuse to perform the deletion.
8612 : */
8613 78 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8614 78 : if (!OidIsValid(attrdefoid))
8615 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8616 : RelationGetRelid(rel), attnum);
8617 78 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8618 :
8619 : /* Make above changes visible */
8620 78 : CommandCounterIncrement();
8621 :
8622 : /*
8623 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8624 : * safety, but at present we do not expect anything to depend on the
8625 : * expression.
8626 : */
8627 78 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8628 : false, false);
8629 :
8630 : /* Prepare to store the new expression, in the catalogs */
8631 78 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8632 78 : rawEnt->attnum = attnum;
8633 78 : rawEnt->raw_default = newExpr;
8634 78 : rawEnt->missingMode = false;
8635 78 : rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8636 :
8637 : /* Store the generated expression */
8638 78 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8639 : false, true, false, NULL);
8640 :
8641 : /* Make above new expression visible */
8642 78 : CommandCounterIncrement();
8643 :
8644 : /* Prepare for table rewrite */
8645 78 : defval = (Expr *) build_column_default(rel, attnum);
8646 :
8647 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8648 78 : newval->attnum = attnum;
8649 78 : newval->expr = expression_planner(defval);
8650 78 : newval->is_generated = true;
8651 :
8652 78 : tab->newvals = lappend(tab->newvals, newval);
8653 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8654 :
8655 : /* Drop any pg_statistic entry for the column */
8656 78 : RemoveStatistics(RelationGetRelid(rel), attnum);
8657 :
8658 78 : InvokeObjectPostAlterHook(RelationRelationId,
8659 : RelationGetRelid(rel), attnum);
8660 :
8661 78 : ObjectAddressSubSet(address, RelationRelationId,
8662 : RelationGetRelid(rel), attnum);
8663 78 : return address;
8664 : }
8665 :
8666 : /*
8667 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8668 : */
8669 : static void
8670 44 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8671 : {
8672 : /*
8673 : * Reject ONLY if there are child tables. We could implement this, but it
8674 : * is a bit complicated. GENERATED clauses must be attached to the column
8675 : * definition and cannot be added later like DEFAULT, so if a child table
8676 : * has a generation expression that the parent does not have, the child
8677 : * column will necessarily be an attislocal column. So to implement ONLY
8678 : * here, we'd need extra code to update attislocal of the direct child
8679 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8680 : * resulting state can be properly dumped and restored.
8681 : */
8682 56 : if (!recurse &&
8683 12 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8684 6 : ereport(ERROR,
8685 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8686 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8687 :
8688 : /*
8689 : * Cannot drop generation expression from inherited columns.
8690 : */
8691 38 : if (!recursing)
8692 : {
8693 : HeapTuple tuple;
8694 : Form_pg_attribute attTup;
8695 :
8696 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8697 32 : if (!HeapTupleIsValid(tuple))
8698 0 : ereport(ERROR,
8699 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8700 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8701 : cmd->name, RelationGetRelationName(rel))));
8702 :
8703 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8704 :
8705 32 : if (attTup->attinhcount > 0)
8706 6 : ereport(ERROR,
8707 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8708 : errmsg("cannot drop generation expression from inherited column")));
8709 : }
8710 32 : }
8711 :
8712 : /*
8713 : * Return the address of the affected column.
8714 : */
8715 : static ObjectAddress
8716 32 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8717 : {
8718 : HeapTuple tuple;
8719 : Form_pg_attribute attTup;
8720 : AttrNumber attnum;
8721 : Relation attrelation;
8722 : Oid attrdefoid;
8723 : ObjectAddress address;
8724 :
8725 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8726 32 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8727 32 : if (!HeapTupleIsValid(tuple))
8728 0 : ereport(ERROR,
8729 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8730 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8731 : colName, RelationGetRelationName(rel))));
8732 :
8733 32 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8734 32 : attnum = attTup->attnum;
8735 :
8736 32 : if (attnum <= 0)
8737 0 : ereport(ERROR,
8738 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8739 : errmsg("cannot alter system column \"%s\"",
8740 : colName)));
8741 :
8742 32 : if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8743 : {
8744 12 : if (!missing_ok)
8745 6 : ereport(ERROR,
8746 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8747 : errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8748 : colName, RelationGetRelationName(rel))));
8749 : else
8750 : {
8751 6 : ereport(NOTICE,
8752 : (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8753 : colName, RelationGetRelationName(rel))));
8754 6 : heap_freetuple(tuple);
8755 6 : table_close(attrelation, RowExclusiveLock);
8756 6 : return InvalidObjectAddress;
8757 : }
8758 : }
8759 :
8760 : /*
8761 : * Mark the column as no longer generated. (The atthasdef flag needs to
8762 : * get cleared too, but RemoveAttrDefault will handle that.)
8763 : */
8764 20 : attTup->attgenerated = '\0';
8765 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8766 :
8767 20 : InvokeObjectPostAlterHook(RelationRelationId,
8768 : RelationGetRelid(rel),
8769 : attnum);
8770 20 : heap_freetuple(tuple);
8771 :
8772 20 : table_close(attrelation, RowExclusiveLock);
8773 :
8774 : /*
8775 : * Drop the dependency records of the GENERATED expression, in particular
8776 : * its INTERNAL dependency on the column, which would otherwise cause
8777 : * dependency.c to refuse to perform the deletion.
8778 : */
8779 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8780 20 : if (!OidIsValid(attrdefoid))
8781 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8782 : RelationGetRelid(rel), attnum);
8783 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8784 :
8785 : /* Make above changes visible */
8786 20 : CommandCounterIncrement();
8787 :
8788 : /*
8789 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8790 : * safety, but at present we do not expect anything to depend on the
8791 : * default.
8792 : */
8793 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8794 : false, false);
8795 :
8796 20 : ObjectAddressSubSet(address, RelationRelationId,
8797 : RelationGetRelid(rel), attnum);
8798 20 : return address;
8799 : }
8800 :
8801 : /*
8802 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8803 : *
8804 : * Return value is the address of the modified column
8805 : */
8806 : static ObjectAddress
8807 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8808 : {
8809 164 : int newtarget = 0;
8810 : bool newtarget_default;
8811 : Relation attrelation;
8812 : HeapTuple tuple,
8813 : newtuple;
8814 : Form_pg_attribute attrtuple;
8815 : AttrNumber attnum;
8816 : ObjectAddress address;
8817 : Datum repl_val[Natts_pg_attribute];
8818 : bool repl_null[Natts_pg_attribute];
8819 : bool repl_repl[Natts_pg_attribute];
8820 :
8821 : /*
8822 : * We allow referencing columns by numbers only for indexes, since table
8823 : * column numbers could contain gaps if columns are later dropped.
8824 : */
8825 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8826 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8827 : !colName)
8828 0 : ereport(ERROR,
8829 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8830 : errmsg("cannot refer to non-index column by number")));
8831 :
8832 : /* -1 was used in previous versions for the default setting */
8833 164 : if (newValue && intVal(newValue) != -1)
8834 : {
8835 120 : newtarget = intVal(newValue);
8836 120 : newtarget_default = false;
8837 : }
8838 : else
8839 44 : newtarget_default = true;
8840 :
8841 164 : if (!newtarget_default)
8842 : {
8843 : /*
8844 : * Limit target to a sane range
8845 : */
8846 120 : if (newtarget < 0)
8847 : {
8848 0 : ereport(ERROR,
8849 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8850 : errmsg("statistics target %d is too low",
8851 : newtarget)));
8852 : }
8853 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8854 : {
8855 0 : newtarget = MAX_STATISTICS_TARGET;
8856 0 : ereport(WARNING,
8857 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8858 : errmsg("lowering statistics target to %d",
8859 : newtarget)));
8860 : }
8861 : }
8862 :
8863 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8864 :
8865 164 : if (colName)
8866 : {
8867 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8868 :
8869 100 : if (!HeapTupleIsValid(tuple))
8870 12 : ereport(ERROR,
8871 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8872 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8873 : colName, RelationGetRelationName(rel))));
8874 : }
8875 : else
8876 : {
8877 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8878 :
8879 64 : if (!HeapTupleIsValid(tuple))
8880 12 : ereport(ERROR,
8881 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8882 : errmsg("column number %d of relation \"%s\" does not exist",
8883 : colNum, RelationGetRelationName(rel))));
8884 : }
8885 :
8886 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8887 :
8888 140 : attnum = attrtuple->attnum;
8889 140 : if (attnum <= 0)
8890 0 : ereport(ERROR,
8891 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8892 : errmsg("cannot alter system column \"%s\"",
8893 : colName)));
8894 :
8895 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8896 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8897 : {
8898 52 : if (attnum > rel->rd_index->indnkeyatts)
8899 6 : ereport(ERROR,
8900 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8901 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8902 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8903 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8904 18 : ereport(ERROR,
8905 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8906 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8907 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8908 : errhint("Alter statistics on table column instead.")));
8909 : }
8910 :
8911 : /* Build new tuple. */
8912 116 : memset(repl_null, false, sizeof(repl_null));
8913 116 : memset(repl_repl, false, sizeof(repl_repl));
8914 116 : if (!newtarget_default)
8915 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8916 : else
8917 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8918 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8919 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8920 : repl_val, repl_null, repl_repl);
8921 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8922 :
8923 116 : InvokeObjectPostAlterHook(RelationRelationId,
8924 : RelationGetRelid(rel),
8925 : attrtuple->attnum);
8926 116 : ObjectAddressSubSet(address, RelationRelationId,
8927 : RelationGetRelid(rel), attnum);
8928 :
8929 116 : heap_freetuple(newtuple);
8930 :
8931 116 : ReleaseSysCache(tuple);
8932 :
8933 116 : table_close(attrelation, RowExclusiveLock);
8934 :
8935 116 : return address;
8936 : }
8937 :
8938 : /*
8939 : * Return value is the address of the modified column
8940 : */
8941 : static ObjectAddress
8942 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
8943 : bool isReset, LOCKMODE lockmode)
8944 : {
8945 : Relation attrelation;
8946 : HeapTuple tuple,
8947 : newtuple;
8948 : Form_pg_attribute attrtuple;
8949 : AttrNumber attnum;
8950 : Datum datum,
8951 : newOptions;
8952 : bool isnull;
8953 : ObjectAddress address;
8954 : Datum repl_val[Natts_pg_attribute];
8955 : bool repl_null[Natts_pg_attribute];
8956 : bool repl_repl[Natts_pg_attribute];
8957 :
8958 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8959 :
8960 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8961 :
8962 32 : if (!HeapTupleIsValid(tuple))
8963 0 : ereport(ERROR,
8964 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8965 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8966 : colName, RelationGetRelationName(rel))));
8967 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8968 :
8969 32 : attnum = attrtuple->attnum;
8970 32 : if (attnum <= 0)
8971 0 : ereport(ERROR,
8972 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8973 : errmsg("cannot alter system column \"%s\"",
8974 : colName)));
8975 :
8976 : /* Generate new proposed attoptions (text array) */
8977 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8978 : &isnull);
8979 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8980 : castNode(List, options), NULL, NULL,
8981 : false, isReset);
8982 : /* Validate new options */
8983 32 : (void) attribute_reloptions(newOptions, true);
8984 :
8985 : /* Build new tuple. */
8986 32 : memset(repl_null, false, sizeof(repl_null));
8987 32 : memset(repl_repl, false, sizeof(repl_repl));
8988 32 : if (newOptions != (Datum) 0)
8989 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8990 : else
8991 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
8992 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8993 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8994 : repl_val, repl_null, repl_repl);
8995 :
8996 : /* Update system catalog. */
8997 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8998 :
8999 32 : InvokeObjectPostAlterHook(RelationRelationId,
9000 : RelationGetRelid(rel),
9001 : attrtuple->attnum);
9002 32 : ObjectAddressSubSet(address, RelationRelationId,
9003 : RelationGetRelid(rel), attnum);
9004 :
9005 32 : heap_freetuple(newtuple);
9006 :
9007 32 : ReleaseSysCache(tuple);
9008 :
9009 32 : table_close(attrelation, RowExclusiveLock);
9010 :
9011 32 : return address;
9012 : }
9013 :
9014 : /*
9015 : * Helper function for ATExecSetStorage and ATExecSetCompression
9016 : *
9017 : * Set the attstorage and/or attcompression fields for index columns
9018 : * associated with the specified table column.
9019 : */
9020 : static void
9021 284 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9022 : AttrNumber attnum,
9023 : bool setstorage, char newstorage,
9024 : bool setcompression, char newcompression,
9025 : LOCKMODE lockmode)
9026 : {
9027 : ListCell *lc;
9028 :
9029 356 : foreach(lc, RelationGetIndexList(rel))
9030 : {
9031 72 : Oid indexoid = lfirst_oid(lc);
9032 : Relation indrel;
9033 72 : AttrNumber indattnum = 0;
9034 : HeapTuple tuple;
9035 :
9036 72 : indrel = index_open(indexoid, lockmode);
9037 :
9038 120 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9039 : {
9040 78 : if (indrel->rd_index->indkey.values[i] == attnum)
9041 : {
9042 30 : indattnum = i + 1;
9043 30 : break;
9044 : }
9045 : }
9046 :
9047 72 : if (indattnum == 0)
9048 : {
9049 42 : index_close(indrel, lockmode);
9050 42 : continue;
9051 : }
9052 :
9053 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9054 :
9055 30 : if (HeapTupleIsValid(tuple))
9056 : {
9057 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9058 :
9059 30 : if (setstorage)
9060 24 : attrtuple->attstorage = newstorage;
9061 :
9062 30 : if (setcompression)
9063 6 : attrtuple->attcompression = newcompression;
9064 :
9065 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9066 :
9067 30 : InvokeObjectPostAlterHook(RelationRelationId,
9068 : RelationGetRelid(rel),
9069 : attrtuple->attnum);
9070 :
9071 30 : heap_freetuple(tuple);
9072 : }
9073 :
9074 30 : index_close(indrel, lockmode);
9075 : }
9076 284 : }
9077 :
9078 : /*
9079 : * ALTER TABLE ALTER COLUMN SET STORAGE
9080 : *
9081 : * Return value is the address of the modified column
9082 : */
9083 : static ObjectAddress
9084 234 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9085 : {
9086 : Relation attrelation;
9087 : HeapTuple tuple;
9088 : Form_pg_attribute attrtuple;
9089 : AttrNumber attnum;
9090 : ObjectAddress address;
9091 :
9092 234 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9093 :
9094 234 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9095 :
9096 234 : if (!HeapTupleIsValid(tuple))
9097 12 : ereport(ERROR,
9098 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9099 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9100 : colName, RelationGetRelationName(rel))));
9101 222 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9102 :
9103 222 : attnum = attrtuple->attnum;
9104 222 : if (attnum <= 0)
9105 0 : ereport(ERROR,
9106 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9107 : errmsg("cannot alter system column \"%s\"",
9108 : colName)));
9109 :
9110 222 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9111 :
9112 222 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9113 :
9114 222 : InvokeObjectPostAlterHook(RelationRelationId,
9115 : RelationGetRelid(rel),
9116 : attrtuple->attnum);
9117 :
9118 : /*
9119 : * Apply the change to indexes as well (only for simple index columns,
9120 : * matching behavior of index.c ConstructTupleDescriptor()).
9121 : */
9122 222 : SetIndexStorageProperties(rel, attrelation, attnum,
9123 222 : true, attrtuple->attstorage,
9124 : false, 0,
9125 : lockmode);
9126 :
9127 222 : heap_freetuple(tuple);
9128 :
9129 222 : table_close(attrelation, RowExclusiveLock);
9130 :
9131 222 : ObjectAddressSubSet(address, RelationRelationId,
9132 : RelationGetRelid(rel), attnum);
9133 222 : return address;
9134 : }
9135 :
9136 :
9137 : /*
9138 : * ALTER TABLE DROP COLUMN
9139 : *
9140 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9141 : * because we have to decide at runtime whether to recurse or not depending
9142 : * on whether attinhcount goes to zero or not. (We can't check this in a
9143 : * static pre-pass because it won't handle multiple inheritance situations
9144 : * correctly.)
9145 : */
9146 : static void
9147 1618 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9148 : AlterTableCmd *cmd, LOCKMODE lockmode,
9149 : AlterTableUtilityContext *context)
9150 : {
9151 1618 : if (rel->rd_rel->reloftype && !recursing)
9152 6 : ereport(ERROR,
9153 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9154 : errmsg("cannot drop column from typed table")));
9155 :
9156 1612 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9157 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9158 :
9159 1606 : if (recurse)
9160 1336 : cmd->recurse = true;
9161 1606 : }
9162 :
9163 : /*
9164 : * Drops column 'colName' from relation 'rel' and returns the address of the
9165 : * dropped column. The column is also dropped (or marked as no longer
9166 : * inherited from relation) from the relation's inheritance children, if any.
9167 : *
9168 : * In the recursive invocations for inheritance child relations, instead of
9169 : * dropping the column directly (if to be dropped at all), its object address
9170 : * is added to 'addrs', which must be non-NULL in such invocations. All
9171 : * columns are dropped at the same time after all the children have been
9172 : * checked recursively.
9173 : */
9174 : static ObjectAddress
9175 2162 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9176 : DropBehavior behavior,
9177 : bool recurse, bool recursing,
9178 : bool missing_ok, LOCKMODE lockmode,
9179 : ObjectAddresses *addrs)
9180 : {
9181 : HeapTuple tuple;
9182 : Form_pg_attribute targetatt;
9183 : AttrNumber attnum;
9184 : List *children;
9185 : ObjectAddress object;
9186 : bool is_expr;
9187 :
9188 : /* At top level, permission check was done in ATPrepCmd, else do it */
9189 2162 : if (recursing)
9190 556 : ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9191 :
9192 : /* Initialize addrs on the first invocation */
9193 : Assert(!recursing || addrs != NULL);
9194 :
9195 : /* since this function recurses, it could be driven to stack overflow */
9196 2162 : check_stack_depth();
9197 :
9198 2162 : if (!recursing)
9199 1606 : addrs = new_object_addresses();
9200 :
9201 : /*
9202 : * get the number of the attribute
9203 : */
9204 2162 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9205 2162 : if (!HeapTupleIsValid(tuple))
9206 : {
9207 54 : if (!missing_ok)
9208 : {
9209 36 : ereport(ERROR,
9210 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9211 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9212 : colName, RelationGetRelationName(rel))));
9213 : }
9214 : else
9215 : {
9216 18 : ereport(NOTICE,
9217 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9218 : colName, RelationGetRelationName(rel))));
9219 18 : return InvalidObjectAddress;
9220 : }
9221 : }
9222 2108 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9223 :
9224 2108 : attnum = targetatt->attnum;
9225 :
9226 : /* Can't drop a system attribute */
9227 2108 : if (attnum <= 0)
9228 6 : ereport(ERROR,
9229 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9230 : errmsg("cannot drop system column \"%s\"",
9231 : colName)));
9232 :
9233 : /*
9234 : * Don't drop inherited columns, unless recursing (presumably from a drop
9235 : * of the parent column)
9236 : */
9237 2102 : if (targetatt->attinhcount > 0 && !recursing)
9238 48 : ereport(ERROR,
9239 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9240 : errmsg("cannot drop inherited column \"%s\"",
9241 : colName)));
9242 :
9243 : /*
9244 : * Don't drop columns used in the partition key, either. (If we let this
9245 : * go through, the key column's dependencies would cause a cascaded drop
9246 : * of the whole table, which is surely not what the user expected.)
9247 : */
9248 2054 : if (has_partition_attrs(rel,
9249 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9250 : &is_expr))
9251 30 : ereport(ERROR,
9252 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9253 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9254 : colName, RelationGetRelationName(rel))));
9255 :
9256 2024 : ReleaseSysCache(tuple);
9257 :
9258 : /*
9259 : * Propagate to children as appropriate. Unlike most other ALTER
9260 : * routines, we have to do this one level of recursion at a time; we can't
9261 : * use find_all_inheritors to do it in one pass.
9262 : */
9263 : children =
9264 2024 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9265 :
9266 2024 : if (children)
9267 : {
9268 : Relation attr_rel;
9269 : ListCell *child;
9270 :
9271 : /*
9272 : * In case of a partitioned table, the column must be dropped from the
9273 : * partitions as well.
9274 : */
9275 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9276 6 : ereport(ERROR,
9277 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9278 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9279 : errhint("Do not specify the ONLY keyword.")));
9280 :
9281 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9282 882 : foreach(child, children)
9283 : {
9284 592 : Oid childrelid = lfirst_oid(child);
9285 : Relation childrel;
9286 : Form_pg_attribute childatt;
9287 :
9288 : /* find_inheritance_children already got lock */
9289 592 : childrel = table_open(childrelid, NoLock);
9290 592 : CheckTableNotInUse(childrel, "ALTER TABLE");
9291 :
9292 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9293 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9294 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9295 : colName, childrelid);
9296 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9297 :
9298 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9299 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9300 : childrelid, colName);
9301 :
9302 592 : if (recurse)
9303 : {
9304 : /*
9305 : * If the child column has other definition sources, just
9306 : * decrement its inheritance count; if not, recurse to delete
9307 : * it.
9308 : */
9309 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9310 : {
9311 : /* Time to delete this child column, too */
9312 556 : ATExecDropColumn(wqueue, childrel, colName,
9313 : behavior, true, true,
9314 : false, lockmode, addrs);
9315 : }
9316 : else
9317 : {
9318 : /* Child column must survive my deletion */
9319 12 : childatt->attinhcount--;
9320 :
9321 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9322 :
9323 : /* Make update visible */
9324 12 : CommandCounterIncrement();
9325 : }
9326 : }
9327 : else
9328 : {
9329 : /*
9330 : * If we were told to drop ONLY in this table (no recursion),
9331 : * we need to mark the inheritors' attributes as locally
9332 : * defined rather than inherited.
9333 : */
9334 24 : childatt->attinhcount--;
9335 24 : childatt->attislocal = true;
9336 :
9337 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9338 :
9339 : /* Make update visible */
9340 24 : CommandCounterIncrement();
9341 : }
9342 :
9343 586 : heap_freetuple(tuple);
9344 :
9345 586 : table_close(childrel, NoLock);
9346 : }
9347 290 : table_close(attr_rel, RowExclusiveLock);
9348 : }
9349 :
9350 : /* Add object to delete */
9351 2012 : object.classId = RelationRelationId;
9352 2012 : object.objectId = RelationGetRelid(rel);
9353 2012 : object.objectSubId = attnum;
9354 2012 : add_exact_object_address(&object, addrs);
9355 :
9356 2012 : if (!recursing)
9357 : {
9358 : /* Recursion has ended, drop everything that was collected */
9359 1462 : performMultipleDeletions(addrs, behavior, 0);
9360 1414 : free_object_addresses(addrs);
9361 : }
9362 :
9363 1964 : return object;
9364 : }
9365 :
9366 : /*
9367 : * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
9368 : * constraint on its children.
9369 : */
9370 : static void
9371 9482 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9372 : LOCKMODE lockmode, AlterTableUtilityContext *context)
9373 : {
9374 : List *children;
9375 9482 : List *newconstrs = NIL;
9376 : IndexStmt *indexstmt;
9377 :
9378 : /* No work if not creating a primary key */
9379 9482 : if (!IsA(cmd->def, IndexStmt))
9380 0 : return;
9381 9482 : indexstmt = castNode(IndexStmt, cmd->def);
9382 9482 : if (!indexstmt->primary)
9383 4000 : return;
9384 :
9385 : /* No work if no legacy inheritance children are present */
9386 5482 : if (rel->rd_rel->relkind != RELKIND_RELATION ||
9387 5344 : !rel->rd_rel->relhassubclass)
9388 5430 : return;
9389 :
9390 : /*
9391 : * Acquire locks all the way down the hierarchy. The recursion to lower
9392 : * levels occurs at execution time as necessary, so we don't need to do it
9393 : * here, and we don't need the returned list either.
9394 : */
9395 52 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
9396 :
9397 : /*
9398 : * Construct the list of constraints that we need to add to each child
9399 : * relation.
9400 : */
9401 156 : foreach_node(IndexElem, elem, indexstmt->indexParams)
9402 : {
9403 : Constraint *nnconstr;
9404 :
9405 : Assert(elem->expr == NULL);
9406 :
9407 52 : nnconstr = makeNode(Constraint);
9408 52 : nnconstr->contype = CONSTR_NOTNULL;
9409 52 : nnconstr->conname = NULL; /* XXX use PK name? */
9410 52 : nnconstr->inhcount = 1;
9411 52 : nnconstr->deferrable = false;
9412 52 : nnconstr->initdeferred = false;
9413 52 : nnconstr->location = -1;
9414 52 : nnconstr->keys = list_make1(makeString(elem->name));
9415 52 : nnconstr->skip_validation = false;
9416 52 : nnconstr->initially_valid = true;
9417 :
9418 52 : newconstrs = lappend(newconstrs, nnconstr);
9419 : }
9420 :
9421 : /* Finally, add AT subcommands to add each constraint to each child. */
9422 52 : children = find_inheritance_children(RelationGetRelid(rel), NoLock);
9423 196 : foreach_oid(childrelid, children)
9424 : {
9425 92 : Relation childrel = table_open(childrelid, NoLock);
9426 92 : AlterTableCmd *newcmd = makeNode(AlterTableCmd);
9427 : ListCell *lc2;
9428 :
9429 92 : newcmd->subtype = AT_AddConstraint;
9430 92 : newcmd->recurse = true;
9431 :
9432 184 : foreach(lc2, newconstrs)
9433 : {
9434 : /* ATPrepCmd copies newcmd, so we can scribble on it here */
9435 92 : newcmd->def = lfirst(lc2);
9436 :
9437 92 : ATPrepCmd(wqueue, childrel, newcmd,
9438 : true, false, lockmode, context);
9439 : }
9440 :
9441 92 : table_close(childrel, NoLock);
9442 : }
9443 : }
9444 :
9445 : /*
9446 : * ALTER TABLE ADD INDEX
9447 : *
9448 : * There is no such command in the grammar, but parse_utilcmd.c converts
9449 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9450 : * us schedule creation of the index at the appropriate time during ALTER.
9451 : *
9452 : * Return value is the address of the new index.
9453 : */
9454 : static ObjectAddress
9455 1464 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9456 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9457 : {
9458 : bool check_rights;
9459 : bool skip_build;
9460 : bool quiet;
9461 : ObjectAddress address;
9462 :
9463 : Assert(IsA(stmt, IndexStmt));
9464 : Assert(!stmt->concurrent);
9465 :
9466 : /* The IndexStmt has already been through transformIndexStmt */
9467 : Assert(stmt->transformed);
9468 :
9469 : /* suppress schema rights check when rebuilding existing index */
9470 1464 : check_rights = !is_rebuild;
9471 : /* skip index build if phase 3 will do it or we're reusing an old one */
9472 1464 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9473 : /* suppress notices when rebuilding existing index */
9474 1464 : quiet = is_rebuild;
9475 :
9476 1464 : address = DefineIndex(RelationGetRelid(rel),
9477 : stmt,
9478 : InvalidOid, /* no predefined OID */
9479 : InvalidOid, /* no parent index */
9480 : InvalidOid, /* no parent constraint */
9481 : -1, /* total_parts unknown */
9482 : true, /* is_alter_table */
9483 : check_rights,
9484 : false, /* check_not_in_use - we did it already */
9485 : skip_build,
9486 : quiet);
9487 :
9488 : /*
9489 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9490 : * new index instead of building from scratch. Restore associated fields.
9491 : * This may store InvalidSubTransactionId in both fields, in which case
9492 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9493 : * this after the CCI that made catalog rows visible to any rebuild. The
9494 : * DROP of the old edition of this index will have scheduled the storage
9495 : * for deletion at commit, so cancel that pending deletion.
9496 : */
9497 1336 : if (RelFileNumberIsValid(stmt->oldNumber))
9498 : {
9499 72 : Relation irel = index_open(address.objectId, NoLock);
9500 :
9501 72 : irel->rd_createSubid = stmt->oldCreateSubid;
9502 72 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9503 72 : RelationPreserveStorage(irel->rd_locator, true);
9504 72 : index_close(irel, NoLock);
9505 : }
9506 :
9507 1336 : return address;
9508 : }
9509 :
9510 : /*
9511 : * ALTER TABLE ADD STATISTICS
9512 : *
9513 : * This is no such command in the grammar, but we use this internally to add
9514 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9515 : * column type change.
9516 : */
9517 : static ObjectAddress
9518 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9519 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9520 : {
9521 : ObjectAddress address;
9522 :
9523 : Assert(IsA(stmt, CreateStatsStmt));
9524 :
9525 : /* The CreateStatsStmt has already been through transformStatsStmt */
9526 : Assert(stmt->transformed);
9527 :
9528 14 : address = CreateStatistics(stmt);
9529 :
9530 14 : return address;
9531 : }
9532 :
9533 : /*
9534 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9535 : *
9536 : * Returns the address of the new constraint.
9537 : */
9538 : static ObjectAddress
9539 8434 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9540 : IndexStmt *stmt, LOCKMODE lockmode)
9541 : {
9542 8434 : Oid index_oid = stmt->indexOid;
9543 : Relation indexRel;
9544 : char *indexName;
9545 : IndexInfo *indexInfo;
9546 : char *constraintName;
9547 : char constraintType;
9548 : ObjectAddress address;
9549 : bits16 flags;
9550 :
9551 : Assert(IsA(stmt, IndexStmt));
9552 : Assert(OidIsValid(index_oid));
9553 : Assert(stmt->isconstraint);
9554 :
9555 : /*
9556 : * Doing this on partitioned tables is not a simple feature to implement,
9557 : * so let's punt for now.
9558 : */
9559 8434 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9560 6 : ereport(ERROR,
9561 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9562 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9563 :
9564 8428 : indexRel = index_open(index_oid, AccessShareLock);
9565 :
9566 8428 : indexName = pstrdup(RelationGetRelationName(indexRel));
9567 :
9568 8428 : indexInfo = BuildIndexInfo(indexRel);
9569 :
9570 : /* this should have been checked at parse time */
9571 8428 : if (!indexInfo->ii_Unique)
9572 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9573 :
9574 : /*
9575 : * Determine name to assign to constraint. We require a constraint to
9576 : * have the same name as the underlying index; therefore, use the index's
9577 : * existing name as the default constraint name, and if the user
9578 : * explicitly gives some other name for the constraint, rename the index
9579 : * to match.
9580 : */
9581 8428 : constraintName = stmt->idxname;
9582 8428 : if (constraintName == NULL)
9583 8408 : constraintName = indexName;
9584 20 : else if (strcmp(constraintName, indexName) != 0)
9585 : {
9586 14 : ereport(NOTICE,
9587 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9588 : indexName, constraintName)));
9589 14 : RenameRelationInternal(index_oid, constraintName, false, true);
9590 : }
9591 :
9592 : /* Extra checks needed if making primary key */
9593 8428 : if (stmt->primary)
9594 4766 : index_check_primary_key(rel, indexInfo, true, stmt);
9595 :
9596 : /* Note we currently don't support EXCLUSION constraints here */
9597 8422 : if (stmt->primary)
9598 4760 : constraintType = CONSTRAINT_PRIMARY;
9599 : else
9600 3662 : constraintType = CONSTRAINT_UNIQUE;
9601 :
9602 : /* Create the catalog entries for the constraint */
9603 8422 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9604 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9605 16844 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9606 8422 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9607 8422 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9608 :
9609 8422 : address = index_constraint_create(rel,
9610 : index_oid,
9611 : InvalidOid,
9612 : indexInfo,
9613 : constraintName,
9614 : constraintType,
9615 : flags,
9616 : allowSystemTableMods,
9617 : false); /* is_internal */
9618 :
9619 8422 : index_close(indexRel, NoLock);
9620 :
9621 8422 : return address;
9622 : }
9623 :
9624 : /*
9625 : * ALTER TABLE ADD CONSTRAINT
9626 : *
9627 : * Return value is the address of the new constraint; if no constraint was
9628 : * added, InvalidObjectAddress is returned.
9629 : */
9630 : static ObjectAddress
9631 3894 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9632 : Constraint *newConstraint, bool recurse, bool is_readd,
9633 : LOCKMODE lockmode)
9634 : {
9635 3894 : ObjectAddress address = InvalidObjectAddress;
9636 :
9637 : Assert(IsA(newConstraint, Constraint));
9638 :
9639 : /*
9640 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9641 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9642 : * parse_utilcmd.c).
9643 : */
9644 3894 : switch (newConstraint->contype)
9645 : {
9646 1430 : case CONSTR_CHECK:
9647 : case CONSTR_NOTNULL:
9648 : address =
9649 1430 : ATAddCheckNNConstraint(wqueue, tab, rel,
9650 : newConstraint, recurse, false, is_readd,
9651 : lockmode);
9652 1346 : break;
9653 :
9654 2464 : case CONSTR_FOREIGN:
9655 :
9656 : /*
9657 : * Assign or validate constraint name
9658 : */
9659 2464 : if (newConstraint->conname)
9660 : {
9661 1148 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9662 : RelationGetRelid(rel),
9663 1148 : newConstraint->conname))
9664 0 : ereport(ERROR,
9665 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9666 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9667 : newConstraint->conname,
9668 : RelationGetRelationName(rel))));
9669 : }
9670 : else
9671 1316 : newConstraint->conname =
9672 1316 : ChooseConstraintName(RelationGetRelationName(rel),
9673 1316 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9674 : "fkey",
9675 1316 : RelationGetNamespace(rel),
9676 : NIL);
9677 :
9678 2464 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9679 : newConstraint,
9680 : recurse, false,
9681 : lockmode);
9682 1976 : break;
9683 :
9684 0 : default:
9685 0 : elog(ERROR, "unrecognized constraint type: %d",
9686 : (int) newConstraint->contype);
9687 : }
9688 :
9689 3322 : return address;
9690 : }
9691 :
9692 : /*
9693 : * Generate the column-name portion of the constraint name for a new foreign
9694 : * key given the list of column names that reference the referenced
9695 : * table. This will be passed to ChooseConstraintName along with the parent
9696 : * table name and the "fkey" suffix.
9697 : *
9698 : * We know that less than NAMEDATALEN characters will actually be used, so we
9699 : * can truncate the result once we've generated that many.
9700 : *
9701 : * XXX see also ChooseExtendedStatisticNameAddition and
9702 : * ChooseIndexNameAddition.
9703 : */
9704 : static char *
9705 2042 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9706 : {
9707 : char buf[NAMEDATALEN * 2];
9708 2042 : int buflen = 0;
9709 : ListCell *lc;
9710 :
9711 2042 : buf[0] = '\0';
9712 4506 : foreach(lc, colnames)
9713 : {
9714 2464 : const char *name = strVal(lfirst(lc));
9715 :
9716 2464 : if (buflen > 0)
9717 422 : buf[buflen++] = '_'; /* insert _ between names */
9718 :
9719 : /*
9720 : * At this point we have buflen <= NAMEDATALEN. name should be less
9721 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9722 : */
9723 2464 : strlcpy(buf + buflen, name, NAMEDATALEN);
9724 2464 : buflen += strlen(buf + buflen);
9725 2464 : if (buflen >= NAMEDATALEN)
9726 0 : break;
9727 : }
9728 2042 : return pstrdup(buf);
9729 : }
9730 :
9731 : /*
9732 : * Add a check or not-null constraint to a single table and its children.
9733 : * Returns the address of the constraint added to the parent relation,
9734 : * if one gets added, or InvalidObjectAddress otherwise.
9735 : *
9736 : * Subroutine for ATExecAddConstraint.
9737 : *
9738 : * We must recurse to child tables during execution, rather than using
9739 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9740 : * constraints *must* be given the same name, else they won't be seen as
9741 : * related later. If the user didn't explicitly specify a name, then
9742 : * AddRelationNewConstraints would normally assign different names to the
9743 : * child constraints. To fix that, we must capture the name assigned at
9744 : * the parent table and pass that down.
9745 : */
9746 : static ObjectAddress
9747 2092 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9748 : Constraint *constr, bool recurse, bool recursing,
9749 : bool is_readd, LOCKMODE lockmode)
9750 : {
9751 : List *newcons;
9752 : ListCell *lcon;
9753 : List *children;
9754 : ListCell *child;
9755 2092 : ObjectAddress address = InvalidObjectAddress;
9756 :
9757 : /* Guard against stack overflow due to overly deep inheritance tree. */
9758 2092 : check_stack_depth();
9759 :
9760 : /* At top level, permission check was done in ATPrepCmd, else do it */
9761 2092 : if (recursing)
9762 528 : ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9763 :
9764 : /*
9765 : * Call AddRelationNewConstraints to do the work, making sure it works on
9766 : * a copy of the Constraint so transformExpr can't modify the original. It
9767 : * returns a list of cooked constraints.
9768 : *
9769 : * If the constraint ends up getting merged with a pre-existing one, it's
9770 : * omitted from the returned list, which is what we want: we do not need
9771 : * to do any validation work. That can only happen at child tables,
9772 : * though, since we disallow merging at the top level.
9773 : */
9774 2092 : newcons = AddRelationNewConstraints(rel, NIL,
9775 2092 : list_make1(copyObject(constr)),
9776 2092 : recursing || is_readd, /* allow_merge */
9777 2092 : !recursing, /* is_local */
9778 : is_readd, /* is_internal */
9779 2092 : NULL); /* queryString not available
9780 : * here */
9781 :
9782 : /* we don't expect more than one constraint here */
9783 : Assert(list_length(newcons) <= 1);
9784 :
9785 : /* Add each to-be-validated constraint to Phase 3's queue */
9786 3878 : foreach(lcon, newcons)
9787 : {
9788 1864 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9789 :
9790 1864 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9791 : {
9792 : NewConstraint *newcon;
9793 :
9794 796 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9795 796 : newcon->name = ccon->name;
9796 796 : newcon->contype = ccon->contype;
9797 796 : newcon->qual = ccon->expr;
9798 :
9799 796 : tab->constraints = lappend(tab->constraints, newcon);
9800 : }
9801 :
9802 : /* Save the actually assigned name if it was defaulted */
9803 1864 : if (constr->conname == NULL)
9804 566 : constr->conname = ccon->name;
9805 :
9806 : /*
9807 : * If adding a not-null constraint, set the pg_attribute flag and tell
9808 : * phase 3 to verify existing rows, if needed.
9809 : */
9810 1864 : if (constr->contype == CONSTR_NOTNULL)
9811 674 : set_attnotnull(wqueue, rel, ccon->attnum,
9812 674 : !ccon->is_no_inherit, lockmode);
9813 :
9814 1864 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9815 : }
9816 :
9817 : /* At this point we must have a locked-down name to use */
9818 : Assert(newcons == NIL || constr->conname != NULL);
9819 :
9820 : /* Advance command counter in case same table is visited multiple times */
9821 2014 : CommandCounterIncrement();
9822 :
9823 : /*
9824 : * If the constraint got merged with an existing constraint, we're done.
9825 : * We mustn't recurse to child tables in this case, because they've
9826 : * already got the constraint, and visiting them again would lead to an
9827 : * incorrect value for coninhcount.
9828 : */
9829 2014 : if (newcons == NIL)
9830 150 : return address;
9831 :
9832 : /*
9833 : * If adding a NO INHERIT constraint, no need to find our children.
9834 : */
9835 1864 : if (constr->is_no_inherit)
9836 72 : return address;
9837 :
9838 : /*
9839 : * Propagate to children as appropriate. Unlike most other ALTER
9840 : * routines, we have to do this one level of recursion at a time; we can't
9841 : * use find_all_inheritors to do it in one pass.
9842 : */
9843 : children =
9844 1792 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9845 :
9846 : /*
9847 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9848 : * constraint creation only if there are no children currently. Error out
9849 : * otherwise.
9850 : */
9851 1792 : if (!recurse && children != NIL)
9852 6 : ereport(ERROR,
9853 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9854 : errmsg("constraint must be added to child tables too")));
9855 :
9856 : /*
9857 : * The constraint must appear as inherited in children, so create a
9858 : * modified constraint object to use.
9859 : */
9860 1786 : constr = copyObject(constr);
9861 1786 : constr->inhcount = 1;
9862 2308 : foreach(child, children)
9863 : {
9864 528 : Oid childrelid = lfirst_oid(child);
9865 : Relation childrel;
9866 : AlteredTableInfo *childtab;
9867 :
9868 : /* find_inheritance_children already got lock */
9869 528 : childrel = table_open(childrelid, NoLock);
9870 528 : CheckTableNotInUse(childrel, "ALTER TABLE");
9871 :
9872 : /* Find or create work queue entry for this table */
9873 528 : childtab = ATGetQueueEntry(wqueue, childrel);
9874 :
9875 : /*
9876 : * Recurse to child. XXX if we didn't create a constraint on the
9877 : * parent because it already existed, and we do create one on a child,
9878 : * should we return that child's constraint ObjectAddress here?
9879 : */
9880 528 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
9881 : constr, recurse, true, is_readd, lockmode);
9882 :
9883 522 : table_close(childrel, NoLock);
9884 : }
9885 :
9886 1780 : return address;
9887 : }
9888 :
9889 : /*
9890 : * Add a foreign-key constraint to a single table; return the new constraint's
9891 : * address.
9892 : *
9893 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
9894 : * lock on the rel, and have done appropriate validity checks for it.
9895 : * We do permissions checks here, however.
9896 : *
9897 : * When the referenced or referencing tables (or both) are partitioned,
9898 : * multiple pg_constraint rows are required -- one for each partitioned table
9899 : * and each partition on each side (fortunately, not one for every combination
9900 : * thereof). We also need action triggers on each leaf partition on the
9901 : * referenced side, and check triggers on each leaf partition on the
9902 : * referencing side.
9903 : */
9904 : static ObjectAddress
9905 2464 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9906 : Constraint *fkconstraint,
9907 : bool recurse, bool recursing, LOCKMODE lockmode)
9908 : {
9909 : Relation pkrel;
9910 2464 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
9911 2464 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
9912 2464 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
9913 2464 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
9914 2464 : Oid opclasses[INDEX_MAX_KEYS] = {0};
9915 2464 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9916 2464 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9917 2464 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9918 2464 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9919 : bool with_period;
9920 : bool pk_has_without_overlaps;
9921 : int i;
9922 : int numfks,
9923 : numpks,
9924 : numfkdelsetcols;
9925 : Oid indexOid;
9926 : bool old_check_ok;
9927 : ObjectAddress address;
9928 2464 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9929 :
9930 : /*
9931 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9932 : * delete rows out from under us.
9933 : */
9934 2464 : if (OidIsValid(fkconstraint->old_pktable_oid))
9935 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9936 : else
9937 2392 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9938 :
9939 : /*
9940 : * Validity checks (permission checks wait till we have the column
9941 : * numbers)
9942 : */
9943 2464 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9944 : {
9945 330 : if (!recurse)
9946 6 : ereport(ERROR,
9947 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9948 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9949 : RelationGetRelationName(rel),
9950 : RelationGetRelationName(pkrel))));
9951 324 : if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9952 6 : ereport(ERROR,
9953 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9954 : errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9955 : RelationGetRelationName(rel),
9956 : RelationGetRelationName(pkrel)),
9957 : errdetail("This feature is not yet supported on partitioned tables.")));
9958 : }
9959 :
9960 2452 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9961 296 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9962 0 : ereport(ERROR,
9963 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9964 : errmsg("referenced relation \"%s\" is not a table",
9965 : RelationGetRelationName(pkrel))));
9966 :
9967 2452 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
9968 2 : ereport(ERROR,
9969 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9970 : errmsg("permission denied: \"%s\" is a system catalog",
9971 : RelationGetRelationName(pkrel))));
9972 :
9973 : /*
9974 : * References from permanent or unlogged tables to temp tables, and from
9975 : * permanent tables to unlogged tables, are disallowed because the
9976 : * referenced data can vanish out from under us. References from temp
9977 : * tables to any other table type are also disallowed, because other
9978 : * backends might need to run the RI triggers on the perm table, but they
9979 : * can't reliably see tuples in the local buffers of other backends.
9980 : */
9981 2450 : switch (rel->rd_rel->relpersistence)
9982 : {
9983 2140 : case RELPERSISTENCE_PERMANENT:
9984 2140 : if (!RelationIsPermanent(pkrel))
9985 0 : ereport(ERROR,
9986 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9987 : errmsg("constraints on permanent tables may reference only permanent tables")));
9988 2140 : break;
9989 32 : case RELPERSISTENCE_UNLOGGED:
9990 32 : if (!RelationIsPermanent(pkrel)
9991 32 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9992 0 : ereport(ERROR,
9993 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9994 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9995 32 : break;
9996 278 : case RELPERSISTENCE_TEMP:
9997 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9998 0 : ereport(ERROR,
9999 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10000 : errmsg("constraints on temporary tables may reference only temporary tables")));
10001 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10002 0 : ereport(ERROR,
10003 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10004 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10005 278 : break;
10006 : }
10007 :
10008 : /*
10009 : * Look up the referencing attributes to make sure they exist, and record
10010 : * their attnums and type OIDs.
10011 : */
10012 2450 : numfks = transformColumnNameList(RelationGetRelid(rel),
10013 : fkconstraint->fk_attrs,
10014 : fkattnum, fktypoid);
10015 2420 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10016 2420 : if (with_period && !fkconstraint->fk_with_period)
10017 24 : ereport(ERROR,
10018 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10019 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10020 :
10021 2396 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10022 : fkconstraint->fk_del_set_cols,
10023 : fkdelsetcols, NULL);
10024 2390 : validateFkOnDeleteSetColumns(numfks, fkattnum,
10025 : numfkdelsetcols, fkdelsetcols,
10026 : fkconstraint->fk_del_set_cols);
10027 :
10028 : /*
10029 : * If the attribute list for the referenced table was omitted, lookup the
10030 : * definition of the primary key and use it. Otherwise, validate the
10031 : * supplied attribute list. In either case, discover the index OID and
10032 : * index opclasses, and the attnums and type OIDs of the attributes.
10033 : */
10034 2384 : if (fkconstraint->pk_attrs == NIL)
10035 : {
10036 1070 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10037 : &fkconstraint->pk_attrs,
10038 : pkattnum, pktypoid,
10039 : opclasses, &pk_has_without_overlaps);
10040 :
10041 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10042 1070 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10043 24 : ereport(ERROR,
10044 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10045 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10046 : }
10047 : else
10048 : {
10049 1314 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10050 : fkconstraint->pk_attrs,
10051 : pkattnum, pktypoid);
10052 :
10053 : /* Since we got pk_attrs, one should be a period. */
10054 1284 : if (with_period && !fkconstraint->pk_with_period)
10055 24 : ereport(ERROR,
10056 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10057 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10058 :
10059 : /* Look for an index matching the column list */
10060 1260 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10061 : with_period, opclasses, &pk_has_without_overlaps);
10062 : }
10063 :
10064 : /*
10065 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10066 : * must use PERIOD.
10067 : */
10068 2270 : if (pk_has_without_overlaps && !with_period)
10069 12 : ereport(ERROR,
10070 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10071 : errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10072 :
10073 : /*
10074 : * Now we can check permissions.
10075 : */
10076 2258 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10077 :
10078 : /*
10079 : * Check some things for generated columns.
10080 : */
10081 5286 : for (i = 0; i < numfks; i++)
10082 : {
10083 3040 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10084 :
10085 3040 : if (attgenerated)
10086 : {
10087 : /*
10088 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10089 : */
10090 30 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10091 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10092 30 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10093 6 : ereport(ERROR,
10094 : (errcode(ERRCODE_SYNTAX_ERROR),
10095 : errmsg("invalid %s action for foreign key constraint containing generated column",
10096 : "ON UPDATE")));
10097 24 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10098 18 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10099 6 : ereport(ERROR,
10100 : (errcode(ERRCODE_SYNTAX_ERROR),
10101 : errmsg("invalid %s action for foreign key constraint containing generated column",
10102 : "ON DELETE")));
10103 : }
10104 : }
10105 :
10106 : /*
10107 : * Some actions are currently unsupported for foreign keys using PERIOD.
10108 : */
10109 2246 : if (fkconstraint->fk_with_period)
10110 : {
10111 238 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10112 226 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10113 214 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10114 36 : ereport(ERROR,
10115 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10116 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10117 : "ON UPDATE"));
10118 :
10119 202 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10120 202 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10121 202 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10122 0 : ereport(ERROR,
10123 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10124 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10125 : "ON DELETE"));
10126 : }
10127 :
10128 : /*
10129 : * Look up the equality operators to use in the constraint.
10130 : *
10131 : * Note that we have to be careful about the difference between the actual
10132 : * PK column type and the opclass' declared input type, which might be
10133 : * only binary-compatible with it. The declared opcintype is the right
10134 : * thing to probe pg_amop with.
10135 : */
10136 2210 : if (numfks != numpks)
10137 0 : ereport(ERROR,
10138 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10139 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10140 :
10141 : /*
10142 : * On the strength of a previous constraint, we might avoid scanning
10143 : * tables to validate this one. See below.
10144 : */
10145 2210 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10146 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10147 :
10148 4794 : for (i = 0; i < numpks; i++)
10149 : {
10150 2812 : Oid pktype = pktypoid[i];
10151 2812 : Oid fktype = fktypoid[i];
10152 : Oid fktyped;
10153 : HeapTuple cla_ht;
10154 : Form_pg_opclass cla_tup;
10155 : Oid amid;
10156 : Oid opfamily;
10157 : Oid opcintype;
10158 : Oid pfeqop;
10159 : Oid ppeqop;
10160 : Oid ffeqop;
10161 : int16 eqstrategy;
10162 : Oid pfeqop_right;
10163 :
10164 : /* We need several fields out of the pg_opclass entry */
10165 2812 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10166 2812 : if (!HeapTupleIsValid(cla_ht))
10167 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10168 2812 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10169 2812 : amid = cla_tup->opcmethod;
10170 2812 : opfamily = cla_tup->opcfamily;
10171 2812 : opcintype = cla_tup->opcintype;
10172 2812 : ReleaseSysCache(cla_ht);
10173 :
10174 2812 : if (with_period)
10175 : {
10176 : StrategyNumber rtstrategy;
10177 432 : bool for_overlaps = with_period && i == numpks - 1;
10178 :
10179 : /*
10180 : * GiST indexes are required to support temporal foreign keys
10181 : * because they combine equals and overlaps.
10182 : */
10183 432 : if (amid != GIST_AM_OID)
10184 0 : elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
10185 :
10186 432 : rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
10187 :
10188 : /*
10189 : * An opclass can use whatever strategy numbers it wants, so we
10190 : * ask the opclass what number it actually uses instead of our RT*
10191 : * constants.
10192 : */
10193 432 : eqstrategy = GistTranslateStratnum(opclasses[i], rtstrategy);
10194 432 : if (eqstrategy == InvalidStrategy)
10195 : {
10196 : HeapTuple tuple;
10197 :
10198 0 : tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10199 0 : if (!HeapTupleIsValid(tuple))
10200 0 : elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
10201 :
10202 0 : ereport(ERROR,
10203 : errcode(ERRCODE_UNDEFINED_OBJECT),
10204 : for_overlaps
10205 : ? errmsg("could not identify an overlaps operator for foreign key")
10206 : : errmsg("could not identify an equality operator for foreign key"),
10207 : errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
10208 : rtstrategy, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
10209 : }
10210 : }
10211 : else
10212 : {
10213 : /*
10214 : * Check it's a btree; currently this can never fail since no
10215 : * other index AMs support unique indexes. If we ever did have
10216 : * other types of unique indexes, we'd need a way to determine
10217 : * which operator strategy number is equality. (We could use
10218 : * something like GistTranslateStratnum.)
10219 : */
10220 2380 : if (amid != BTREE_AM_OID)
10221 0 : elog(ERROR, "only b-tree indexes are supported for foreign keys");
10222 2380 : eqstrategy = BTEqualStrategyNumber;
10223 : }
10224 :
10225 : /*
10226 : * There had better be a primary equality operator for the index.
10227 : * We'll use it for PK = PK comparisons.
10228 : */
10229 2812 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10230 : eqstrategy);
10231 :
10232 2812 : if (!OidIsValid(ppeqop))
10233 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10234 : eqstrategy, opcintype, opcintype, opfamily);
10235 :
10236 : /*
10237 : * Are there equality operators that take exactly the FK type? Assume
10238 : * we should look through any domain here.
10239 : */
10240 2812 : fktyped = getBaseType(fktype);
10241 :
10242 2812 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10243 : eqstrategy);
10244 2812 : if (OidIsValid(pfeqop))
10245 : {
10246 2130 : pfeqop_right = fktyped;
10247 2130 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10248 : eqstrategy);
10249 : }
10250 : else
10251 : {
10252 : /* keep compiler quiet */
10253 682 : pfeqop_right = InvalidOid;
10254 682 : ffeqop = InvalidOid;
10255 : }
10256 :
10257 2812 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10258 : {
10259 : /*
10260 : * Otherwise, look for an implicit cast from the FK type to the
10261 : * opcintype, and if found, use the primary equality operator.
10262 : * This is a bit tricky because opcintype might be a polymorphic
10263 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10264 : * whether the two actual column types can be concurrently cast to
10265 : * that type. (Otherwise, we'd fail to reject combinations such
10266 : * as int[] and point[].)
10267 : */
10268 : Oid input_typeids[2];
10269 : Oid target_typeids[2];
10270 :
10271 682 : input_typeids[0] = pktype;
10272 682 : input_typeids[1] = fktype;
10273 682 : target_typeids[0] = opcintype;
10274 682 : target_typeids[1] = opcintype;
10275 682 : if (can_coerce_type(2, input_typeids, target_typeids,
10276 : COERCION_IMPLICIT))
10277 : {
10278 454 : pfeqop = ffeqop = ppeqop;
10279 454 : pfeqop_right = opcintype;
10280 : }
10281 : }
10282 :
10283 2812 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10284 228 : ereport(ERROR,
10285 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10286 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10287 : fkconstraint->conname),
10288 : errdetail("Key columns \"%s\" and \"%s\" "
10289 : "are of incompatible types: %s and %s.",
10290 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10291 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10292 : format_type_be(fktype),
10293 : format_type_be(pktype))));
10294 :
10295 2584 : if (old_check_ok)
10296 : {
10297 : /*
10298 : * When a pfeqop changes, revalidate the constraint. We could
10299 : * permit intra-opfamily changes, but that adds subtle complexity
10300 : * without any concrete benefit for core types. We need not
10301 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10302 : */
10303 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10304 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10305 : old_pfeqop_item);
10306 : }
10307 2584 : if (old_check_ok)
10308 : {
10309 : Oid old_fktype;
10310 : Oid new_fktype;
10311 : CoercionPathType old_pathtype;
10312 : CoercionPathType new_pathtype;
10313 : Oid old_castfunc;
10314 : Oid new_castfunc;
10315 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10316 : fkattnum[i] - 1);
10317 :
10318 : /*
10319 : * Identify coercion pathways from each of the old and new FK-side
10320 : * column types to the right (foreign) operand type of the pfeqop.
10321 : * We may assume that pg_constraint.conkey is not changing.
10322 : */
10323 6 : old_fktype = attr->atttypid;
10324 6 : new_fktype = fktype;
10325 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10326 : &old_castfunc);
10327 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10328 : &new_castfunc);
10329 :
10330 : /*
10331 : * Upon a change to the cast from the FK column to its pfeqop
10332 : * operand, revalidate the constraint. For this evaluation, a
10333 : * binary coercion cast is equivalent to no cast at all. While
10334 : * type implementors should design implicit casts with an eye
10335 : * toward consistency of operations like equality, we cannot
10336 : * assume here that they have done so.
10337 : *
10338 : * A function with a polymorphic argument could change behavior
10339 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10340 : * when the cast destination is polymorphic, we only avoid
10341 : * revalidation if the input type has not changed at all. Given
10342 : * just the core data types and operator classes, this requirement
10343 : * prevents no would-be optimizations.
10344 : *
10345 : * If the cast converts from a base type to a domain thereon, then
10346 : * that domain type must be the opcintype of the unique index.
10347 : * Necessarily, the primary key column must then be of the domain
10348 : * type. Since the constraint was previously valid, all values on
10349 : * the foreign side necessarily exist on the primary side and in
10350 : * turn conform to the domain. Consequently, we need not treat
10351 : * domains specially here.
10352 : *
10353 : * Since we require that all collations share the same notion of
10354 : * equality (which they do, because texteq reduces to bitwise
10355 : * equality), we don't compare collation here.
10356 : *
10357 : * We need not directly consider the PK type. It's necessarily
10358 : * binary coercible to the opcintype of the unique index column,
10359 : * and ri_triggers.c will only deal with PK datums in terms of
10360 : * that opcintype. Changing the opcintype also changes pfeqop.
10361 : */
10362 6 : old_check_ok = (new_pathtype == old_pathtype &&
10363 12 : new_castfunc == old_castfunc &&
10364 6 : (!IsPolymorphicType(pfeqop_right) ||
10365 : new_fktype == old_fktype));
10366 : }
10367 :
10368 2584 : pfeqoperators[i] = pfeqop;
10369 2584 : ppeqoperators[i] = ppeqop;
10370 2584 : ffeqoperators[i] = ffeqop;
10371 : }
10372 :
10373 : /*
10374 : * For FKs with PERIOD we need additional operators to check whether the
10375 : * referencing row's range is contained by the aggregated ranges of the
10376 : * referenced row(s). For rangetypes and multirangetypes this is
10377 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10378 : * support for now. FKs will look these up at "runtime", but we should
10379 : * make sure the lookup works here, even if we don't use the values.
10380 : */
10381 1982 : if (with_period)
10382 : {
10383 : Oid periodoperoid;
10384 : Oid aggedperiodoperoid;
10385 :
10386 184 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
10387 : }
10388 :
10389 : /*
10390 : * Create all the constraint and trigger objects, recursing to partitions
10391 : * as necessary. First handle the referenced side.
10392 : */
10393 1976 : address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
10394 : indexOid,
10395 : InvalidOid, /* no parent constraint */
10396 : numfks,
10397 : pkattnum,
10398 : fkattnum,
10399 : pfeqoperators,
10400 : ppeqoperators,
10401 : ffeqoperators,
10402 : numfkdelsetcols,
10403 : fkdelsetcols,
10404 : old_check_ok,
10405 : InvalidOid, InvalidOid,
10406 : with_period);
10407 :
10408 : /* Now handle the referencing side. */
10409 1976 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10410 : indexOid,
10411 : address.objectId,
10412 : numfks,
10413 : pkattnum,
10414 : fkattnum,
10415 : pfeqoperators,
10416 : ppeqoperators,
10417 : ffeqoperators,
10418 : numfkdelsetcols,
10419 : fkdelsetcols,
10420 : old_check_ok,
10421 : lockmode,
10422 : InvalidOid, InvalidOid,
10423 : with_period);
10424 :
10425 : /*
10426 : * Done. Close pk table, but keep lock until we've committed.
10427 : */
10428 1976 : table_close(pkrel, NoLock);
10429 :
10430 1976 : return address;
10431 : }
10432 :
10433 : /*
10434 : * validateFkOnDeleteSetColumns
10435 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10436 : * column lists are valid.
10437 : */
10438 : void
10439 2390 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10440 : int numfksetcols, const int16 *fksetcolsattnums,
10441 : List *fksetcols)
10442 : {
10443 2414 : for (int i = 0; i < numfksetcols; i++)
10444 : {
10445 30 : int16 setcol_attnum = fksetcolsattnums[i];
10446 30 : bool seen = false;
10447 :
10448 54 : for (int j = 0; j < numfks; j++)
10449 : {
10450 48 : if (fkattnums[j] == setcol_attnum)
10451 : {
10452 24 : seen = true;
10453 24 : break;
10454 : }
10455 : }
10456 :
10457 30 : if (!seen)
10458 : {
10459 6 : char *col = strVal(list_nth(fksetcols, i));
10460 :
10461 6 : ereport(ERROR,
10462 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10463 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10464 : }
10465 : }
10466 2384 : }
10467 :
10468 : /*
10469 : * addFkRecurseReferenced
10470 : * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10471 : * side of the constraint
10472 : *
10473 : * Create pg_constraint rows for the referenced side of the constraint,
10474 : * referencing the parent of the referencing side; also create action triggers
10475 : * on leaf partitions. If the table is partitioned, recurse to handle each
10476 : * partition.
10477 : *
10478 : * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10479 : * of an ALTER TABLE sequence.
10480 : * fkconstraint is the constraint being added.
10481 : * rel is the root referencing relation.
10482 : * pkrel is the referenced relation; might be a partition, if recursing.
10483 : * indexOid is the OID of the index (on pkrel) implementing this constraint.
10484 : * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10485 : * top-level constraint.
10486 : * numfks is the number of columns in the foreign key
10487 : * pkattnum is the attnum array of referenced attributes.
10488 : * fkattnum is the attnum array of referencing attributes.
10489 : * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10490 : * (...) clause
10491 : * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10492 : * NULL/DEFAULT clause
10493 : * pf/pp/ffeqoperators are OID array of operators between columns.
10494 : * old_check_ok signals that this constraint replaces an existing one that
10495 : * was already validated (thus this one doesn't need validation).
10496 : * parentDelTrigger and parentUpdTrigger, when being recursively called on
10497 : * a partition, are the OIDs of the parent action triggers for DELETE and
10498 : * UPDATE respectively.
10499 : */
10500 : static ObjectAddress
10501 2696 : addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
10502 : Relation pkrel, Oid indexOid, Oid parentConstr,
10503 : int numfks,
10504 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10505 : Oid *ppeqoperators, Oid *ffeqoperators,
10506 : int numfkdelsetcols, int16 *fkdelsetcols,
10507 : bool old_check_ok,
10508 : Oid parentDelTrigger, Oid parentUpdTrigger,
10509 : bool with_period)
10510 : {
10511 : ObjectAddress address;
10512 : Oid constrOid;
10513 : char *conname;
10514 : bool conislocal;
10515 : int coninhcount;
10516 : bool connoinherit;
10517 : Oid deleteTriggerOid,
10518 : updateTriggerOid;
10519 :
10520 : /*
10521 : * Verify relkind for each referenced partition. At the top level, this
10522 : * is redundant with a previous check, but we need it when recursing.
10523 : */
10524 2696 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10525 378 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10526 0 : ereport(ERROR,
10527 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10528 : errmsg("referenced relation \"%s\" is not a table",
10529 : RelationGetRelationName(pkrel))));
10530 :
10531 : /*
10532 : * Caller supplies us with a constraint name; however, it may be used in
10533 : * this partition, so come up with a different one in that case.
10534 : */
10535 2696 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10536 : RelationGetRelid(rel),
10537 2696 : fkconstraint->conname))
10538 720 : conname = ChooseConstraintName(RelationGetRelationName(rel),
10539 720 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10540 : "fkey",
10541 720 : RelationGetNamespace(rel), NIL);
10542 : else
10543 1976 : conname = fkconstraint->conname;
10544 :
10545 2696 : if (OidIsValid(parentConstr))
10546 : {
10547 720 : conislocal = false;
10548 720 : coninhcount = 1;
10549 720 : connoinherit = false;
10550 : }
10551 : else
10552 : {
10553 1976 : conislocal = true;
10554 1976 : coninhcount = 0;
10555 :
10556 : /*
10557 : * always inherit for partitioned tables, never for legacy inheritance
10558 : */
10559 1976 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10560 : }
10561 :
10562 : /*
10563 : * Record the FK constraint in pg_constraint.
10564 : */
10565 2696 : constrOid = CreateConstraintEntry(conname,
10566 2696 : RelationGetNamespace(rel),
10567 : CONSTRAINT_FOREIGN,
10568 2696 : fkconstraint->deferrable,
10569 2696 : fkconstraint->initdeferred,
10570 2696 : fkconstraint->initially_valid,
10571 : parentConstr,
10572 : RelationGetRelid(rel),
10573 : fkattnum,
10574 : numfks,
10575 : numfks,
10576 : InvalidOid, /* not a domain constraint */
10577 : indexOid,
10578 : RelationGetRelid(pkrel),
10579 : pkattnum,
10580 : pfeqoperators,
10581 : ppeqoperators,
10582 : ffeqoperators,
10583 : numfks,
10584 2696 : fkconstraint->fk_upd_action,
10585 2696 : fkconstraint->fk_del_action,
10586 : fkdelsetcols,
10587 : numfkdelsetcols,
10588 2696 : fkconstraint->fk_matchtype,
10589 : NULL, /* no exclusion constraint */
10590 : NULL, /* no check constraint */
10591 : NULL,
10592 : conislocal, /* islocal */
10593 : coninhcount, /* inhcount */
10594 : connoinherit, /* conNoInherit */
10595 : with_period, /* conPeriod */
10596 : false); /* is_internal */
10597 :
10598 2696 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10599 :
10600 : /*
10601 : * Mark the child constraint as part of the parent constraint; it must not
10602 : * be dropped on its own. (This constraint is deleted when the partition
10603 : * is detached, but a special check needs to occur that the partition
10604 : * contains no referenced values.)
10605 : */
10606 2696 : if (OidIsValid(parentConstr))
10607 : {
10608 : ObjectAddress referenced;
10609 :
10610 720 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10611 720 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10612 : }
10613 :
10614 : /* make new constraint visible, in case we add more */
10615 2696 : CommandCounterIncrement();
10616 :
10617 : /*
10618 : * Create the action triggers that enforce the constraint.
10619 : */
10620 2696 : createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10621 : fkconstraint,
10622 : constrOid, indexOid,
10623 : parentDelTrigger, parentUpdTrigger,
10624 : &deleteTriggerOid, &updateTriggerOid);
10625 :
10626 : /*
10627 : * If the referenced table is partitioned, recurse on ourselves to handle
10628 : * each partition. We need one pg_constraint row created for each
10629 : * partition in addition to the pg_constraint row for the parent table.
10630 : */
10631 2696 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10632 : {
10633 378 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10634 :
10635 948 : for (int i = 0; i < pd->nparts; i++)
10636 : {
10637 : Relation partRel;
10638 : AttrMap *map;
10639 : AttrNumber *mapped_pkattnum;
10640 : Oid partIndexId;
10641 :
10642 570 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10643 :
10644 : /*
10645 : * Map the attribute numbers in the referenced side of the FK
10646 : * definition to match the partition's column layout.
10647 : */
10648 570 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10649 : RelationGetDescr(pkrel),
10650 : false);
10651 570 : if (map)
10652 : {
10653 46 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10654 92 : for (int j = 0; j < numfks; j++)
10655 46 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10656 : }
10657 : else
10658 524 : mapped_pkattnum = pkattnum;
10659 :
10660 : /* do the deed */
10661 570 : partIndexId = index_get_partition(partRel, indexOid);
10662 570 : if (!OidIsValid(partIndexId))
10663 0 : elog(ERROR, "index for %u not found in partition %s",
10664 : indexOid, RelationGetRelationName(partRel));
10665 570 : addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10666 : partIndexId, constrOid, numfks,
10667 : mapped_pkattnum, fkattnum,
10668 : pfeqoperators, ppeqoperators, ffeqoperators,
10669 : numfkdelsetcols, fkdelsetcols,
10670 : old_check_ok,
10671 : deleteTriggerOid, updateTriggerOid,
10672 : with_period);
10673 :
10674 : /* Done -- clean up (but keep the lock) */
10675 570 : table_close(partRel, NoLock);
10676 570 : if (map)
10677 : {
10678 46 : pfree(mapped_pkattnum);
10679 46 : free_attrmap(map);
10680 : }
10681 : }
10682 : }
10683 :
10684 2696 : return address;
10685 : }
10686 :
10687 : /*
10688 : * addFkRecurseReferencing
10689 : * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10690 : *
10691 : * If the referencing relation is a plain relation, create the necessary check
10692 : * triggers that implement the constraint, and set up for Phase 3 constraint
10693 : * verification. If the referencing relation is a partitioned table, then
10694 : * we create a pg_constraint row for it and recurse on this routine for each
10695 : * partition.
10696 : *
10697 : * We assume that the referenced relation is locked against concurrent
10698 : * deletions. If it's a partitioned relation, every partition must be so
10699 : * locked.
10700 : *
10701 : * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10702 : * of an ALTER TABLE sequence.
10703 : * fkconstraint is the constraint being added.
10704 : * rel is the referencing relation; might be a partition, if recursing.
10705 : * pkrel is the root referenced relation.
10706 : * indexOid is the OID of the index (on pkrel) implementing this constraint.
10707 : * parentConstr is the OID of the parent constraint (there is always one).
10708 : * numfks is the number of columns in the foreign key
10709 : * pkattnum is the attnum array of referenced attributes.
10710 : * fkattnum is the attnum array of referencing attributes.
10711 : * pf/pp/ffeqoperators are OID array of operators between columns.
10712 : * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10713 : * (...) clause
10714 : * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10715 : * NULL/DEFAULT clause
10716 : * old_check_ok signals that this constraint replaces an existing one that
10717 : * was already validated (thus this one doesn't need validation).
10718 : * lockmode is the lockmode to acquire on partitions when recursing.
10719 : * parentInsTrigger and parentUpdTrigger, when being recursively called on
10720 : * a partition, are the OIDs of the parent check triggers for INSERT and
10721 : * UPDATE respectively.
10722 : */
10723 : static void
10724 2746 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10725 : Relation pkrel, Oid indexOid, Oid parentConstr,
10726 : int numfks, int16 *pkattnum, int16 *fkattnum,
10727 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10728 : int numfkdelsetcols, int16 *fkdelsetcols,
10729 : bool old_check_ok, LOCKMODE lockmode,
10730 : Oid parentInsTrigger, Oid parentUpdTrigger,
10731 : bool with_period)
10732 : {
10733 : Oid insertTriggerOid,
10734 : updateTriggerOid;
10735 :
10736 : Assert(OidIsValid(parentConstr));
10737 :
10738 2746 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10739 0 : ereport(ERROR,
10740 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10741 : errmsg("foreign key constraints are not supported on foreign tables")));
10742 :
10743 : /*
10744 : * Add the check triggers to it and, if necessary, schedule it to be
10745 : * checked in Phase 3.
10746 : *
10747 : * If the relation is partitioned, drill down to do it to its partitions.
10748 : */
10749 2746 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
10750 : RelationGetRelid(pkrel),
10751 : fkconstraint,
10752 : parentConstr,
10753 : indexOid,
10754 : parentInsTrigger, parentUpdTrigger,
10755 : &insertTriggerOid, &updateTriggerOid);
10756 :
10757 2746 : if (rel->rd_rel->relkind == RELKIND_RELATION)
10758 : {
10759 : /*
10760 : * Tell Phase 3 to check that the constraint is satisfied by existing
10761 : * rows. We can skip this during table creation, when requested
10762 : * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10763 : * and when we're recreating a constraint following a SET DATA TYPE
10764 : * operation that did not impugn its validity.
10765 : */
10766 2334 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10767 : {
10768 : NewConstraint *newcon;
10769 : AlteredTableInfo *tab;
10770 :
10771 792 : tab = ATGetQueueEntry(wqueue, rel);
10772 :
10773 792 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10774 792 : newcon->name = get_constraint_name(parentConstr);
10775 792 : newcon->contype = CONSTR_FOREIGN;
10776 792 : newcon->refrelid = RelationGetRelid(pkrel);
10777 792 : newcon->refindid = indexOid;
10778 792 : newcon->conid = parentConstr;
10779 792 : newcon->conwithperiod = fkconstraint->fk_with_period;
10780 792 : newcon->qual = (Node *) fkconstraint;
10781 :
10782 792 : tab->constraints = lappend(tab->constraints, newcon);
10783 : }
10784 : }
10785 412 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10786 : {
10787 412 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10788 : Relation trigrel;
10789 :
10790 : /*
10791 : * Triggers of the foreign keys will be manipulated a bunch of times
10792 : * in the loop below. To avoid repeatedly opening/closing the trigger
10793 : * catalog relation, we open it here and pass it to the subroutines
10794 : * called below.
10795 : */
10796 412 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10797 :
10798 : /*
10799 : * Recurse to take appropriate action on each partition; either we
10800 : * find an existing constraint to reparent to ours, or we create a new
10801 : * one.
10802 : */
10803 788 : for (int i = 0; i < pd->nparts; i++)
10804 : {
10805 376 : Oid partitionId = pd->oids[i];
10806 376 : Relation partition = table_open(partitionId, lockmode);
10807 : List *partFKs;
10808 : AttrMap *attmap;
10809 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10810 : bool attached;
10811 : char *conname;
10812 : Oid constrOid;
10813 : ObjectAddress address,
10814 : referenced;
10815 : ListCell *cell;
10816 :
10817 376 : CheckTableNotInUse(partition, "ALTER TABLE");
10818 :
10819 376 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
10820 : RelationGetDescr(rel),
10821 : false);
10822 968 : for (int j = 0; j < numfks; j++)
10823 592 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10824 :
10825 : /* Check whether an existing constraint can be repurposed */
10826 376 : partFKs = copyObject(RelationGetFKeyList(partition));
10827 376 : attached = false;
10828 394 : foreach(cell, partFKs)
10829 : {
10830 : ForeignKeyCacheInfo *fk;
10831 :
10832 30 : fk = lfirst_node(ForeignKeyCacheInfo, cell);
10833 30 : if (tryAttachPartitionForeignKey(fk,
10834 : partitionId,
10835 : parentConstr,
10836 : numfks,
10837 : mapped_fkattnum,
10838 : pkattnum,
10839 : pfeqoperators,
10840 : insertTriggerOid,
10841 : updateTriggerOid,
10842 : trigrel))
10843 : {
10844 12 : attached = true;
10845 12 : break;
10846 : }
10847 : }
10848 376 : if (attached)
10849 : {
10850 12 : table_close(partition, NoLock);
10851 12 : continue;
10852 : }
10853 :
10854 : /*
10855 : * No luck finding a good constraint to reuse; create our own.
10856 : */
10857 364 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10858 : RelationGetRelid(partition),
10859 364 : fkconstraint->conname))
10860 0 : conname = ChooseConstraintName(RelationGetRelationName(partition),
10861 0 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10862 : "fkey",
10863 0 : RelationGetNamespace(partition), NIL);
10864 : else
10865 364 : conname = fkconstraint->conname;
10866 : constrOid =
10867 364 : CreateConstraintEntry(conname,
10868 364 : RelationGetNamespace(partition),
10869 : CONSTRAINT_FOREIGN,
10870 364 : fkconstraint->deferrable,
10871 364 : fkconstraint->initdeferred,
10872 364 : fkconstraint->initially_valid,
10873 : parentConstr,
10874 : partitionId,
10875 : mapped_fkattnum,
10876 : numfks,
10877 : numfks,
10878 : InvalidOid,
10879 : indexOid,
10880 : RelationGetRelid(pkrel),
10881 : pkattnum,
10882 : pfeqoperators,
10883 : ppeqoperators,
10884 : ffeqoperators,
10885 : numfks,
10886 364 : fkconstraint->fk_upd_action,
10887 364 : fkconstraint->fk_del_action,
10888 : fkdelsetcols,
10889 : numfkdelsetcols,
10890 364 : fkconstraint->fk_matchtype,
10891 : NULL,
10892 : NULL,
10893 : NULL,
10894 : false,
10895 : 1,
10896 : false,
10897 : with_period, /* conPeriod */
10898 : false);
10899 :
10900 : /*
10901 : * Give this constraint partition-type dependencies on the parent
10902 : * constraint as well as the table.
10903 : */
10904 364 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10905 364 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10906 364 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10907 364 : ObjectAddressSet(referenced, RelationRelationId, partitionId);
10908 364 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10909 :
10910 : /* Make all this visible before recursing */
10911 364 : CommandCounterIncrement();
10912 :
10913 : /* call ourselves to finalize the creation and we're done */
10914 364 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10915 : indexOid,
10916 : constrOid,
10917 : numfks,
10918 : pkattnum,
10919 : mapped_fkattnum,
10920 : pfeqoperators,
10921 : ppeqoperators,
10922 : ffeqoperators,
10923 : numfkdelsetcols,
10924 : fkdelsetcols,
10925 : old_check_ok,
10926 : lockmode,
10927 : insertTriggerOid,
10928 : updateTriggerOid,
10929 : with_period);
10930 :
10931 364 : table_close(partition, NoLock);
10932 : }
10933 :
10934 412 : table_close(trigrel, RowExclusiveLock);
10935 : }
10936 2746 : }
10937 :
10938 : /*
10939 : * CloneForeignKeyConstraints
10940 : * Clone foreign keys from a partitioned table to a newly acquired
10941 : * partition.
10942 : *
10943 : * partitionRel is a partition of parentRel, so we can be certain that it has
10944 : * the same columns with the same datatypes. The columns may be in different
10945 : * order, though.
10946 : *
10947 : * wqueue must be passed to set up phase 3 constraint checking, unless the
10948 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
10949 : * PARTITION OF).
10950 : */
10951 : static void
10952 10318 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10953 : Relation partitionRel)
10954 : {
10955 : /* This only works for declarative partitioning */
10956 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10957 :
10958 : /*
10959 : * Clone constraints for which the parent is on the referenced side.
10960 : */
10961 10318 : CloneFkReferenced(parentRel, partitionRel);
10962 :
10963 : /*
10964 : * Now clone constraints where the parent is on the referencing side.
10965 : */
10966 10318 : CloneFkReferencing(wqueue, parentRel, partitionRel);
10967 10318 : }
10968 :
10969 : /*
10970 : * CloneFkReferenced
10971 : * Subroutine for CloneForeignKeyConstraints
10972 : *
10973 : * Find all the FKs that have the parent relation on the referenced side;
10974 : * clone those constraints to the given partition. This is to be called
10975 : * when the partition is being created or attached.
10976 : *
10977 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10978 : *
10979 : * This recurses to partitions, if the relation being attached is partitioned.
10980 : * Recursion is done by calling addFkRecurseReferenced.
10981 : */
10982 : static void
10983 10318 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
10984 : {
10985 : Relation pg_constraint;
10986 : AttrMap *attmap;
10987 : ListCell *cell;
10988 : SysScanDesc scan;
10989 : ScanKeyData key[2];
10990 : HeapTuple tuple;
10991 10318 : List *clone = NIL;
10992 : Relation trigrel;
10993 :
10994 : /*
10995 : * Search for any constraints where this partition's parent is in the
10996 : * referenced side. However, we must not clone any constraint whose
10997 : * parent constraint is also going to be cloned, to avoid duplicates. So
10998 : * do it in two steps: first construct the list of constraints to clone,
10999 : * then go over that list cloning those whose parents are not in the list.
11000 : * (We must not rely on the parent being seen first, since the catalog
11001 : * scan could return children first.)
11002 : */
11003 10318 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11004 10318 : ScanKeyInit(&key[0],
11005 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11006 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11007 10318 : ScanKeyInit(&key[1],
11008 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11009 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11010 : /* This is a seqscan, as we don't have a usable index ... */
11011 10318 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11012 : NULL, 2, key);
11013 10648 : while ((tuple = systable_getnext(scan)) != NULL)
11014 : {
11015 330 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11016 :
11017 330 : clone = lappend_oid(clone, constrForm->oid);
11018 : }
11019 10318 : systable_endscan(scan);
11020 10318 : table_close(pg_constraint, RowShareLock);
11021 :
11022 : /*
11023 : * Triggers of the foreign keys will be manipulated a bunch of times in
11024 : * the loop below. To avoid repeatedly opening/closing the trigger
11025 : * catalog relation, we open it here and pass it to the subroutines called
11026 : * below.
11027 : */
11028 10318 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11029 :
11030 10318 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11031 : RelationGetDescr(parentRel),
11032 : false);
11033 10648 : foreach(cell, clone)
11034 : {
11035 330 : Oid constrOid = lfirst_oid(cell);
11036 : Form_pg_constraint constrForm;
11037 : Relation fkRel;
11038 : Oid indexOid;
11039 : Oid partIndexId;
11040 : int numfks;
11041 : AttrNumber conkey[INDEX_MAX_KEYS];
11042 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11043 : AttrNumber confkey[INDEX_MAX_KEYS];
11044 : Oid conpfeqop[INDEX_MAX_KEYS];
11045 : Oid conppeqop[INDEX_MAX_KEYS];
11046 : Oid conffeqop[INDEX_MAX_KEYS];
11047 : int numfkdelsetcols;
11048 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11049 : Constraint *fkconstraint;
11050 : Oid deleteTriggerOid,
11051 : updateTriggerOid;
11052 :
11053 330 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11054 330 : if (!HeapTupleIsValid(tuple))
11055 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11056 330 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11057 :
11058 : /*
11059 : * As explained above: don't try to clone a constraint for which we're
11060 : * going to clone the parent.
11061 : */
11062 330 : if (list_member_oid(clone, constrForm->conparentid))
11063 : {
11064 126 : ReleaseSysCache(tuple);
11065 180 : continue;
11066 : }
11067 :
11068 : /*
11069 : * Don't clone self-referencing foreign keys, which can be in the
11070 : * partitioned table or in the partition-to-be.
11071 : */
11072 204 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11073 162 : constrForm->conrelid == RelationGetRelid(partitionRel))
11074 : {
11075 54 : ReleaseSysCache(tuple);
11076 54 : continue;
11077 : }
11078 :
11079 : /*
11080 : * Because we're only expanding the key space at the referenced side,
11081 : * we don't need to prevent any operation in the referencing table, so
11082 : * AccessShareLock suffices (assumes that dropping the constraint
11083 : * acquires AEL).
11084 : */
11085 150 : fkRel = table_open(constrForm->conrelid, AccessShareLock);
11086 :
11087 150 : indexOid = constrForm->conindid;
11088 150 : DeconstructFkConstraintRow(tuple,
11089 : &numfks,
11090 : conkey,
11091 : confkey,
11092 : conpfeqop,
11093 : conppeqop,
11094 : conffeqop,
11095 : &numfkdelsetcols,
11096 : confdelsetcols);
11097 :
11098 300 : for (int i = 0; i < numfks; i++)
11099 150 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11100 :
11101 150 : fkconstraint = makeNode(Constraint);
11102 150 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11103 150 : fkconstraint->conname = NameStr(constrForm->conname);
11104 150 : fkconstraint->deferrable = constrForm->condeferrable;
11105 150 : fkconstraint->initdeferred = constrForm->condeferred;
11106 150 : fkconstraint->location = -1;
11107 150 : fkconstraint->pktable = NULL;
11108 : /* ->fk_attrs determined below */
11109 150 : fkconstraint->pk_attrs = NIL;
11110 150 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11111 150 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11112 150 : fkconstraint->fk_del_action = constrForm->confdeltype;
11113 150 : fkconstraint->fk_del_set_cols = NIL;
11114 150 : fkconstraint->old_conpfeqop = NIL;
11115 150 : fkconstraint->old_pktable_oid = InvalidOid;
11116 150 : fkconstraint->skip_validation = false;
11117 150 : fkconstraint->initially_valid = true;
11118 :
11119 : /* set up colnames that are used to generate the constraint name */
11120 300 : for (int i = 0; i < numfks; i++)
11121 : {
11122 : Form_pg_attribute att;
11123 :
11124 150 : att = TupleDescAttr(RelationGetDescr(fkRel),
11125 : conkey[i] - 1);
11126 150 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11127 150 : makeString(NameStr(att->attname)));
11128 : }
11129 :
11130 : /*
11131 : * Add the new foreign key constraint pointing to the new partition.
11132 : * Because this new partition appears in the referenced side of the
11133 : * constraint, we don't need to set up for Phase 3 check.
11134 : */
11135 150 : partIndexId = index_get_partition(partitionRel, indexOid);
11136 150 : if (!OidIsValid(partIndexId))
11137 0 : elog(ERROR, "index for %u not found in partition %s",
11138 : indexOid, RelationGetRelationName(partitionRel));
11139 :
11140 : /*
11141 : * Get the "action" triggers belonging to the constraint to pass as
11142 : * parent OIDs for similar triggers that will be created on the
11143 : * partition in addFkRecurseReferenced().
11144 : */
11145 150 : GetForeignKeyActionTriggers(trigrel, constrOid,
11146 : constrForm->confrelid, constrForm->conrelid,
11147 : &deleteTriggerOid, &updateTriggerOid);
11148 :
11149 150 : addFkRecurseReferenced(NULL,
11150 : fkconstraint,
11151 : fkRel,
11152 : partitionRel,
11153 : partIndexId,
11154 : constrOid,
11155 : numfks,
11156 : mapped_confkey,
11157 : conkey,
11158 : conpfeqop,
11159 : conppeqop,
11160 : conffeqop,
11161 : numfkdelsetcols,
11162 : confdelsetcols,
11163 : true,
11164 : deleteTriggerOid,
11165 : updateTriggerOid,
11166 150 : constrForm->conperiod);
11167 :
11168 150 : table_close(fkRel, NoLock);
11169 150 : ReleaseSysCache(tuple);
11170 : }
11171 :
11172 10318 : table_close(trigrel, RowExclusiveLock);
11173 10318 : }
11174 :
11175 : /*
11176 : * CloneFkReferencing
11177 : * Subroutine for CloneForeignKeyConstraints
11178 : *
11179 : * For each FK constraint of the parent relation in the given list, find an
11180 : * equivalent constraint in its partition relation that can be reparented;
11181 : * if one cannot be found, create a new constraint in the partition as its
11182 : * child.
11183 : *
11184 : * If wqueue is given, it is used to set up phase-3 verification for each
11185 : * cloned constraint; if omitted, we assume that such verification is not
11186 : * needed (example: the partition is being created anew).
11187 : */
11188 : static void
11189 10318 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11190 : {
11191 : AttrMap *attmap;
11192 : List *partFKs;
11193 10318 : List *clone = NIL;
11194 : ListCell *cell;
11195 : Relation trigrel;
11196 :
11197 : /* obtain a list of constraints that we need to clone */
11198 11214 : foreach(cell, RelationGetFKeyList(parentRel))
11199 : {
11200 896 : ForeignKeyCacheInfo *fk = lfirst(cell);
11201 :
11202 896 : clone = lappend_oid(clone, fk->conoid);
11203 : }
11204 :
11205 : /*
11206 : * Silently do nothing if there's nothing to do. In particular, this
11207 : * avoids throwing a spurious error for foreign tables.
11208 : */
11209 10318 : if (clone == NIL)
11210 9870 : return;
11211 :
11212 448 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11213 0 : ereport(ERROR,
11214 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11215 : errmsg("foreign key constraints are not supported on foreign tables")));
11216 :
11217 : /*
11218 : * Triggers of the foreign keys will be manipulated a bunch of times in
11219 : * the loop below. To avoid repeatedly opening/closing the trigger
11220 : * catalog relation, we open it here and pass it to the subroutines called
11221 : * below.
11222 : */
11223 448 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11224 :
11225 : /*
11226 : * The constraint key may differ, if the columns in the partition are
11227 : * different. This map is used to convert them.
11228 : */
11229 448 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11230 : RelationGetDescr(parentRel),
11231 : false);
11232 :
11233 448 : partFKs = copyObject(RelationGetFKeyList(partRel));
11234 :
11235 1344 : foreach(cell, clone)
11236 : {
11237 896 : Oid parentConstrOid = lfirst_oid(cell);
11238 : Form_pg_constraint constrForm;
11239 : Relation pkrel;
11240 : HeapTuple tuple;
11241 : int numfks;
11242 : AttrNumber conkey[INDEX_MAX_KEYS];
11243 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11244 : AttrNumber confkey[INDEX_MAX_KEYS];
11245 : Oid conpfeqop[INDEX_MAX_KEYS];
11246 : Oid conppeqop[INDEX_MAX_KEYS];
11247 : Oid conffeqop[INDEX_MAX_KEYS];
11248 : int numfkdelsetcols;
11249 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11250 : Constraint *fkconstraint;
11251 : bool attached;
11252 : Oid indexOid;
11253 : Oid constrOid;
11254 : ObjectAddress address,
11255 : referenced;
11256 : ListCell *lc;
11257 : Oid insertTriggerOid,
11258 : updateTriggerOid;
11259 : bool with_period;
11260 :
11261 896 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11262 896 : if (!HeapTupleIsValid(tuple))
11263 0 : elog(ERROR, "cache lookup failed for constraint %u",
11264 : parentConstrOid);
11265 896 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11266 :
11267 : /* Don't clone constraints whose parents are being cloned */
11268 896 : if (list_member_oid(clone, constrForm->conparentid))
11269 : {
11270 424 : ReleaseSysCache(tuple);
11271 490 : continue;
11272 : }
11273 :
11274 : /*
11275 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11276 : * relation, that means to lock all partitions.
11277 : */
11278 472 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11279 472 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11280 184 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11281 : ShareRowExclusiveLock, NULL);
11282 :
11283 472 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11284 : conpfeqop, conppeqop, conffeqop,
11285 : &numfkdelsetcols, confdelsetcols);
11286 1082 : for (int i = 0; i < numfks; i++)
11287 610 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11288 :
11289 : /*
11290 : * Get the "check" triggers belonging to the constraint to pass as
11291 : * parent OIDs for similar triggers that will be created on the
11292 : * partition in addFkRecurseReferencing(). They are also passed to
11293 : * tryAttachPartitionForeignKey() below to simply assign as parents to
11294 : * the partition's existing "check" triggers, that is, if the
11295 : * corresponding constraints is deemed attachable to the parent
11296 : * constraint.
11297 : */
11298 472 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11299 : constrForm->confrelid, constrForm->conrelid,
11300 : &insertTriggerOid, &updateTriggerOid);
11301 :
11302 : /*
11303 : * Before creating a new constraint, see whether any existing FKs are
11304 : * fit for the purpose. If one is, attach the parent constraint to
11305 : * it, and don't clone anything. This way we avoid the expensive
11306 : * verification step and don't end up with a duplicate FK, and we
11307 : * don't need to recurse to partitions for this constraint.
11308 : */
11309 472 : attached = false;
11310 556 : foreach(lc, partFKs)
11311 : {
11312 150 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11313 :
11314 150 : if (tryAttachPartitionForeignKey(fk,
11315 : RelationGetRelid(partRel),
11316 : parentConstrOid,
11317 : numfks,
11318 : mapped_conkey,
11319 : confkey,
11320 : conpfeqop,
11321 : insertTriggerOid,
11322 : updateTriggerOid,
11323 : trigrel))
11324 : {
11325 66 : attached = true;
11326 66 : table_close(pkrel, NoLock);
11327 66 : break;
11328 : }
11329 : }
11330 472 : if (attached)
11331 : {
11332 66 : ReleaseSysCache(tuple);
11333 66 : continue;
11334 : }
11335 :
11336 : /* No dice. Set up to create our own constraint */
11337 406 : fkconstraint = makeNode(Constraint);
11338 406 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11339 : /* ->conname determined below */
11340 406 : fkconstraint->deferrable = constrForm->condeferrable;
11341 406 : fkconstraint->initdeferred = constrForm->condeferred;
11342 406 : fkconstraint->location = -1;
11343 406 : fkconstraint->pktable = NULL;
11344 : /* ->fk_attrs determined below */
11345 406 : fkconstraint->pk_attrs = NIL;
11346 406 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11347 406 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11348 406 : fkconstraint->fk_del_action = constrForm->confdeltype;
11349 406 : fkconstraint->fk_del_set_cols = NIL;
11350 406 : fkconstraint->old_conpfeqop = NIL;
11351 406 : fkconstraint->old_pktable_oid = InvalidOid;
11352 406 : fkconstraint->skip_validation = false;
11353 406 : fkconstraint->initially_valid = true;
11354 902 : for (int i = 0; i < numfks; i++)
11355 : {
11356 : Form_pg_attribute att;
11357 :
11358 496 : att = TupleDescAttr(RelationGetDescr(partRel),
11359 : mapped_conkey[i] - 1);
11360 496 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11361 496 : makeString(NameStr(att->attname)));
11362 : }
11363 406 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
11364 : RelationGetRelid(partRel),
11365 406 : NameStr(constrForm->conname)))
11366 6 : fkconstraint->conname =
11367 6 : ChooseConstraintName(RelationGetRelationName(partRel),
11368 6 : ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
11369 : "fkey",
11370 6 : RelationGetNamespace(partRel), NIL);
11371 : else
11372 400 : fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
11373 :
11374 406 : indexOid = constrForm->conindid;
11375 406 : with_period = constrForm->conperiod;
11376 : constrOid =
11377 406 : CreateConstraintEntry(fkconstraint->conname,
11378 : constrForm->connamespace,
11379 : CONSTRAINT_FOREIGN,
11380 406 : fkconstraint->deferrable,
11381 406 : fkconstraint->initdeferred,
11382 406 : constrForm->convalidated,
11383 : parentConstrOid,
11384 : RelationGetRelid(partRel),
11385 : mapped_conkey,
11386 : numfks,
11387 : numfks,
11388 : InvalidOid, /* not a domain constraint */
11389 : indexOid,
11390 : constrForm->confrelid, /* same foreign rel */
11391 : confkey,
11392 : conpfeqop,
11393 : conppeqop,
11394 : conffeqop,
11395 : numfks,
11396 406 : fkconstraint->fk_upd_action,
11397 406 : fkconstraint->fk_del_action,
11398 : confdelsetcols,
11399 : numfkdelsetcols,
11400 406 : fkconstraint->fk_matchtype,
11401 : NULL,
11402 : NULL,
11403 : NULL,
11404 : false, /* islocal */
11405 : 1, /* inhcount */
11406 : false, /* conNoInherit */
11407 : with_period, /* conPeriod */
11408 : true);
11409 :
11410 : /* Set up partition dependencies for the new constraint */
11411 406 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
11412 406 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
11413 406 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
11414 406 : ObjectAddressSet(referenced, RelationRelationId,
11415 : RelationGetRelid(partRel));
11416 406 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
11417 :
11418 : /* Done with the cloned constraint's tuple */
11419 406 : ReleaseSysCache(tuple);
11420 :
11421 : /* Make all this visible before recursing */
11422 406 : CommandCounterIncrement();
11423 :
11424 406 : addFkRecurseReferencing(wqueue,
11425 : fkconstraint,
11426 : partRel,
11427 : pkrel,
11428 : indexOid,
11429 : constrOid,
11430 : numfks,
11431 : confkey,
11432 : mapped_conkey,
11433 : conpfeqop,
11434 : conppeqop,
11435 : conffeqop,
11436 : numfkdelsetcols,
11437 : confdelsetcols,
11438 : false, /* no old check exists */
11439 : AccessExclusiveLock,
11440 : insertTriggerOid,
11441 : updateTriggerOid,
11442 : with_period);
11443 406 : table_close(pkrel, NoLock);
11444 : }
11445 :
11446 448 : table_close(trigrel, RowExclusiveLock);
11447 : }
11448 :
11449 : /*
11450 : * When the parent of a partition receives [the referencing side of] a foreign
11451 : * key, we must propagate that foreign key to the partition. However, the
11452 : * partition might already have an equivalent foreign key; this routine
11453 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11454 : * by the other parameters. If they are equivalent, create the link between
11455 : * the two constraints and return true.
11456 : *
11457 : * If the given FK does not match the one defined by rest of the params,
11458 : * return false.
11459 : */
11460 : static bool
11461 180 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11462 : Oid partRelid,
11463 : Oid parentConstrOid,
11464 : int numfks,
11465 : AttrNumber *mapped_conkey,
11466 : AttrNumber *confkey,
11467 : Oid *conpfeqop,
11468 : Oid parentInsTrigger,
11469 : Oid parentUpdTrigger,
11470 : Relation trigrel)
11471 : {
11472 : HeapTuple parentConstrTup;
11473 : Form_pg_constraint parentConstr;
11474 : HeapTuple partcontup;
11475 : Form_pg_constraint partConstr;
11476 : ScanKeyData key;
11477 : SysScanDesc scan;
11478 : HeapTuple trigtup;
11479 : Oid insertTriggerOid,
11480 : updateTriggerOid;
11481 :
11482 180 : parentConstrTup = SearchSysCache1(CONSTROID,
11483 : ObjectIdGetDatum(parentConstrOid));
11484 180 : if (!HeapTupleIsValid(parentConstrTup))
11485 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11486 180 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11487 :
11488 : /*
11489 : * Do some quick & easy initial checks. If any of these fail, we cannot
11490 : * use this constraint.
11491 : */
11492 180 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11493 : {
11494 0 : ReleaseSysCache(parentConstrTup);
11495 0 : return false;
11496 : }
11497 510 : for (int i = 0; i < numfks; i++)
11498 : {
11499 330 : if (fk->conkey[i] != mapped_conkey[i] ||
11500 330 : fk->confkey[i] != confkey[i] ||
11501 330 : fk->conpfeqop[i] != conpfeqop[i])
11502 : {
11503 0 : ReleaseSysCache(parentConstrTup);
11504 0 : return false;
11505 : }
11506 : }
11507 :
11508 : /*
11509 : * Looks good so far; do some more extensive checks. Presumably the check
11510 : * for 'convalidated' could be dropped, since we don't really care about
11511 : * that, but let's be careful for now.
11512 : */
11513 180 : partcontup = SearchSysCache1(CONSTROID,
11514 : ObjectIdGetDatum(fk->conoid));
11515 180 : if (!HeapTupleIsValid(partcontup))
11516 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11517 180 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11518 180 : if (OidIsValid(partConstr->conparentid) ||
11519 156 : !partConstr->convalidated ||
11520 156 : partConstr->condeferrable != parentConstr->condeferrable ||
11521 128 : partConstr->condeferred != parentConstr->condeferred ||
11522 128 : partConstr->confupdtype != parentConstr->confupdtype ||
11523 92 : partConstr->confdeltype != parentConstr->confdeltype ||
11524 92 : partConstr->confmatchtype != parentConstr->confmatchtype)
11525 : {
11526 102 : ReleaseSysCache(parentConstrTup);
11527 102 : ReleaseSysCache(partcontup);
11528 102 : return false;
11529 : }
11530 :
11531 78 : ReleaseSysCache(partcontup);
11532 78 : ReleaseSysCache(parentConstrTup);
11533 :
11534 : /*
11535 : * Looks good! Attach this constraint. The action triggers in the new
11536 : * partition become redundant -- the parent table already has equivalent
11537 : * ones, and those will be able to reach the partition. Remove the ones
11538 : * in the partition. We identify them because they have our constraint
11539 : * OID, as well as being on the referenced rel.
11540 : */
11541 78 : ScanKeyInit(&key,
11542 : Anum_pg_trigger_tgconstraint,
11543 : BTEqualStrategyNumber, F_OIDEQ,
11544 : ObjectIdGetDatum(fk->conoid));
11545 78 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11546 : NULL, 1, &key);
11547 390 : while ((trigtup = systable_getnext(scan)) != NULL)
11548 : {
11549 312 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11550 : ObjectAddress trigger;
11551 :
11552 312 : if (trgform->tgconstrrelid != fk->conrelid)
11553 156 : continue;
11554 156 : if (trgform->tgrelid != fk->confrelid)
11555 0 : continue;
11556 :
11557 : /*
11558 : * The constraint is originally set up to contain this trigger as an
11559 : * implementation object, so there's a dependency record that links
11560 : * the two; however, since the trigger is no longer needed, we remove
11561 : * the dependency link in order to be able to drop the trigger while
11562 : * keeping the constraint intact.
11563 : */
11564 156 : deleteDependencyRecordsFor(TriggerRelationId,
11565 : trgform->oid,
11566 : false);
11567 : /* make dependency deletion visible to performDeletion */
11568 156 : CommandCounterIncrement();
11569 156 : ObjectAddressSet(trigger, TriggerRelationId,
11570 : trgform->oid);
11571 156 : performDeletion(&trigger, DROP_RESTRICT, 0);
11572 : /* make trigger drop visible, in case the loop iterates */
11573 156 : CommandCounterIncrement();
11574 : }
11575 :
11576 78 : systable_endscan(scan);
11577 :
11578 78 : ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11579 :
11580 : /*
11581 : * Like the constraint, attach partition's "check" triggers to the
11582 : * corresponding parent triggers.
11583 : */
11584 78 : GetForeignKeyCheckTriggers(trigrel,
11585 : fk->conoid, fk->confrelid, fk->conrelid,
11586 : &insertTriggerOid, &updateTriggerOid);
11587 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11588 78 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11589 : partRelid);
11590 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11591 78 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11592 : partRelid);
11593 :
11594 78 : CommandCounterIncrement();
11595 78 : return true;
11596 : }
11597 :
11598 : /*
11599 : * GetForeignKeyActionTriggers
11600 : * Returns delete and update "action" triggers of the given relation
11601 : * belonging to the given constraint
11602 : */
11603 : static void
11604 150 : GetForeignKeyActionTriggers(Relation trigrel,
11605 : Oid conoid, Oid confrelid, Oid conrelid,
11606 : Oid *deleteTriggerOid,
11607 : Oid *updateTriggerOid)
11608 : {
11609 : ScanKeyData key;
11610 : SysScanDesc scan;
11611 : HeapTuple trigtup;
11612 :
11613 150 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11614 150 : ScanKeyInit(&key,
11615 : Anum_pg_trigger_tgconstraint,
11616 : BTEqualStrategyNumber, F_OIDEQ,
11617 : ObjectIdGetDatum(conoid));
11618 :
11619 150 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11620 : NULL, 1, &key);
11621 312 : while ((trigtup = systable_getnext(scan)) != NULL)
11622 : {
11623 312 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11624 :
11625 312 : if (trgform->tgconstrrelid != conrelid)
11626 12 : continue;
11627 300 : if (trgform->tgrelid != confrelid)
11628 0 : continue;
11629 : /* Only ever look at "action" triggers on the PK side. */
11630 300 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11631 0 : continue;
11632 300 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
11633 : {
11634 : Assert(*deleteTriggerOid == InvalidOid);
11635 150 : *deleteTriggerOid = trgform->oid;
11636 : }
11637 150 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11638 : {
11639 : Assert(*updateTriggerOid == InvalidOid);
11640 150 : *updateTriggerOid = trgform->oid;
11641 : }
11642 : #ifndef USE_ASSERT_CHECKING
11643 : /* In an assert-enabled build, continue looking to find duplicates */
11644 300 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11645 150 : break;
11646 : #endif
11647 : }
11648 :
11649 150 : if (!OidIsValid(*deleteTriggerOid))
11650 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11651 : conoid);
11652 150 : if (!OidIsValid(*updateTriggerOid))
11653 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11654 : conoid);
11655 :
11656 150 : systable_endscan(scan);
11657 150 : }
11658 :
11659 : /*
11660 : * GetForeignKeyCheckTriggers
11661 : * Returns insert and update "check" triggers of the given relation
11662 : * belonging to the given constraint
11663 : */
11664 : static void
11665 616 : GetForeignKeyCheckTriggers(Relation trigrel,
11666 : Oid conoid, Oid confrelid, Oid conrelid,
11667 : Oid *insertTriggerOid,
11668 : Oid *updateTriggerOid)
11669 : {
11670 : ScanKeyData key;
11671 : SysScanDesc scan;
11672 : HeapTuple trigtup;
11673 :
11674 616 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
11675 616 : ScanKeyInit(&key,
11676 : Anum_pg_trigger_tgconstraint,
11677 : BTEqualStrategyNumber, F_OIDEQ,
11678 : ObjectIdGetDatum(conoid));
11679 :
11680 616 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11681 : NULL, 1, &key);
11682 2056 : while ((trigtup = systable_getnext(scan)) != NULL)
11683 : {
11684 2056 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11685 :
11686 2056 : if (trgform->tgconstrrelid != confrelid)
11687 740 : continue;
11688 1316 : if (trgform->tgrelid != conrelid)
11689 0 : continue;
11690 : /* Only ever look at "check" triggers on the FK side. */
11691 1316 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11692 84 : continue;
11693 1232 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
11694 : {
11695 : Assert(*insertTriggerOid == InvalidOid);
11696 616 : *insertTriggerOid = trgform->oid;
11697 : }
11698 616 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11699 : {
11700 : Assert(*updateTriggerOid == InvalidOid);
11701 616 : *updateTriggerOid = trgform->oid;
11702 : }
11703 : #ifndef USE_ASSERT_CHECKING
11704 : /* In an assert-enabled build, continue looking to find duplicates. */
11705 1232 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11706 616 : break;
11707 : #endif
11708 : }
11709 :
11710 616 : if (!OidIsValid(*insertTriggerOid))
11711 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11712 : conoid);
11713 616 : if (!OidIsValid(*updateTriggerOid))
11714 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11715 : conoid);
11716 :
11717 616 : systable_endscan(scan);
11718 616 : }
11719 :
11720 : /*
11721 : * ALTER TABLE ALTER CONSTRAINT
11722 : *
11723 : * Update the attributes of a constraint.
11724 : *
11725 : * Currently only works for Foreign Key constraints.
11726 : *
11727 : * If the constraint is modified, returns its address; otherwise, return
11728 : * InvalidObjectAddress.
11729 : */
11730 : static ObjectAddress
11731 126 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11732 : bool recursing, LOCKMODE lockmode)
11733 : {
11734 : Constraint *cmdcon;
11735 : Relation conrel;
11736 : Relation tgrel;
11737 : SysScanDesc scan;
11738 : ScanKeyData skey[3];
11739 : HeapTuple contuple;
11740 : Form_pg_constraint currcon;
11741 : ObjectAddress address;
11742 126 : List *otherrelids = NIL;
11743 : ListCell *lc;
11744 :
11745 126 : cmdcon = castNode(Constraint, cmd->def);
11746 :
11747 126 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11748 126 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11749 :
11750 : /*
11751 : * Find and check the target constraint
11752 : */
11753 126 : ScanKeyInit(&skey[0],
11754 : Anum_pg_constraint_conrelid,
11755 : BTEqualStrategyNumber, F_OIDEQ,
11756 : ObjectIdGetDatum(RelationGetRelid(rel)));
11757 126 : ScanKeyInit(&skey[1],
11758 : Anum_pg_constraint_contypid,
11759 : BTEqualStrategyNumber, F_OIDEQ,
11760 : ObjectIdGetDatum(InvalidOid));
11761 126 : ScanKeyInit(&skey[2],
11762 : Anum_pg_constraint_conname,
11763 : BTEqualStrategyNumber, F_NAMEEQ,
11764 126 : CStringGetDatum(cmdcon->conname));
11765 126 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11766 : true, NULL, 3, skey);
11767 :
11768 : /* There can be at most one matching row */
11769 126 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11770 0 : ereport(ERROR,
11771 : (errcode(ERRCODE_UNDEFINED_OBJECT),
11772 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11773 : cmdcon->conname, RelationGetRelationName(rel))));
11774 :
11775 126 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11776 126 : if (currcon->contype != CONSTRAINT_FOREIGN)
11777 0 : ereport(ERROR,
11778 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11779 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11780 : cmdcon->conname, RelationGetRelationName(rel))));
11781 :
11782 : /*
11783 : * If it's not the topmost constraint, raise an error.
11784 : *
11785 : * Altering a non-topmost constraint leaves some triggers untouched, since
11786 : * they are not directly connected to this constraint; also, pg_dump would
11787 : * ignore the deferrability status of the individual constraint, since it
11788 : * only dumps topmost constraints. Avoid these problems by refusing this
11789 : * operation and telling the user to alter the parent constraint instead.
11790 : */
11791 126 : if (OidIsValid(currcon->conparentid))
11792 : {
11793 : HeapTuple tp;
11794 12 : Oid parent = currcon->conparentid;
11795 12 : char *ancestorname = NULL;
11796 12 : char *ancestortable = NULL;
11797 :
11798 : /* Loop to find the topmost constraint */
11799 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11800 : {
11801 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11802 :
11803 : /* If no parent, this is the constraint we want */
11804 24 : if (!OidIsValid(contup->conparentid))
11805 : {
11806 12 : ancestorname = pstrdup(NameStr(contup->conname));
11807 12 : ancestortable = get_rel_name(contup->conrelid);
11808 12 : ReleaseSysCache(tp);
11809 12 : break;
11810 : }
11811 :
11812 12 : parent = contup->conparentid;
11813 12 : ReleaseSysCache(tp);
11814 : }
11815 :
11816 12 : ereport(ERROR,
11817 : (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11818 : cmdcon->conname, RelationGetRelationName(rel)),
11819 : ancestorname && ancestortable ?
11820 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11821 : cmdcon->conname, ancestorname, ancestortable) : 0,
11822 : errhint("You may alter the constraint it derives from instead.")));
11823 : }
11824 :
11825 : /*
11826 : * Do the actual catalog work. We can skip changing if already in the
11827 : * desired state, but not if a partitioned table: partitions need to be
11828 : * processed regardless, in case they had the constraint locally changed.
11829 : */
11830 114 : address = InvalidObjectAddress;
11831 114 : if (currcon->condeferrable != cmdcon->deferrable ||
11832 6 : currcon->condeferred != cmdcon->initdeferred ||
11833 0 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11834 : {
11835 114 : if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11836 : &otherrelids, lockmode))
11837 114 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11838 : }
11839 :
11840 : /*
11841 : * ATExecAlterConstrRecurse already invalidated relcache for the relations
11842 : * having the constraint itself; here we also invalidate for relations
11843 : * that have any triggers that are part of the constraint.
11844 : */
11845 258 : foreach(lc, otherrelids)
11846 144 : CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11847 :
11848 114 : systable_endscan(scan);
11849 :
11850 114 : table_close(tgrel, RowExclusiveLock);
11851 114 : table_close(conrel, RowExclusiveLock);
11852 :
11853 114 : return address;
11854 : }
11855 :
11856 : /*
11857 : * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11858 : * constraint is altered.
11859 : *
11860 : * *otherrelids is appended OIDs of relations containing affected triggers.
11861 : *
11862 : * Note that we must recurse even when the values are correct, in case
11863 : * indirect descendants have had their constraints altered locally.
11864 : * (This could be avoided if we forbade altering constraints in partitions
11865 : * but existing releases don't do that.)
11866 : */
11867 : static bool
11868 180 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11869 : Relation rel, HeapTuple contuple, List **otherrelids,
11870 : LOCKMODE lockmode)
11871 : {
11872 : Form_pg_constraint currcon;
11873 : Oid conoid;
11874 : Oid refrelid;
11875 180 : bool changed = false;
11876 :
11877 : /* since this function recurses, it could be driven to stack overflow */
11878 180 : check_stack_depth();
11879 :
11880 180 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11881 180 : conoid = currcon->oid;
11882 180 : refrelid = currcon->confrelid;
11883 :
11884 : /*
11885 : * Update pg_constraint with the flags from cmdcon.
11886 : *
11887 : * If called to modify a constraint that's already in the desired state,
11888 : * silently do nothing.
11889 : */
11890 180 : if (currcon->condeferrable != cmdcon->deferrable ||
11891 6 : currcon->condeferred != cmdcon->initdeferred)
11892 : {
11893 : HeapTuple copyTuple;
11894 : Form_pg_constraint copy_con;
11895 : HeapTuple tgtuple;
11896 : ScanKeyData tgkey;
11897 : SysScanDesc tgscan;
11898 :
11899 180 : copyTuple = heap_copytuple(contuple);
11900 180 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11901 180 : copy_con->condeferrable = cmdcon->deferrable;
11902 180 : copy_con->condeferred = cmdcon->initdeferred;
11903 180 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11904 :
11905 180 : InvokeObjectPostAlterHook(ConstraintRelationId,
11906 : conoid, 0);
11907 :
11908 180 : heap_freetuple(copyTuple);
11909 180 : changed = true;
11910 :
11911 : /* Make new constraint flags visible to others */
11912 180 : CacheInvalidateRelcache(rel);
11913 :
11914 : /*
11915 : * Now we need to update the multiple entries in pg_trigger that
11916 : * implement the constraint.
11917 : */
11918 180 : ScanKeyInit(&tgkey,
11919 : Anum_pg_trigger_tgconstraint,
11920 : BTEqualStrategyNumber, F_OIDEQ,
11921 : ObjectIdGetDatum(conoid));
11922 180 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11923 : NULL, 1, &tgkey);
11924 768 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11925 : {
11926 588 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11927 : Form_pg_trigger copy_tg;
11928 : HeapTuple tgCopyTuple;
11929 :
11930 : /*
11931 : * Remember OIDs of other relation(s) involved in FK constraint.
11932 : * (Note: it's likely that we could skip forcing a relcache inval
11933 : * for other rels that don't have a trigger whose properties
11934 : * change, but let's be conservative.)
11935 : */
11936 588 : if (tgform->tgrelid != RelationGetRelid(rel))
11937 288 : *otherrelids = list_append_unique_oid(*otherrelids,
11938 : tgform->tgrelid);
11939 :
11940 : /*
11941 : * Update deferrability of RI_FKey_noaction_del,
11942 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11943 : * triggers, but not others; see createForeignKeyActionTriggers
11944 : * and CreateFKCheckTrigger.
11945 : */
11946 588 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11947 474 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11948 342 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11949 192 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11950 42 : continue;
11951 :
11952 546 : tgCopyTuple = heap_copytuple(tgtuple);
11953 546 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11954 :
11955 546 : copy_tg->tgdeferrable = cmdcon->deferrable;
11956 546 : copy_tg->tginitdeferred = cmdcon->initdeferred;
11957 546 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11958 :
11959 546 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11960 :
11961 546 : heap_freetuple(tgCopyTuple);
11962 : }
11963 :
11964 180 : systable_endscan(tgscan);
11965 : }
11966 :
11967 : /*
11968 : * If the table at either end of the constraint is partitioned, we need to
11969 : * recurse and handle every constraint that is a child of this one.
11970 : *
11971 : * (This assumes that the recurse flag is forcibly set for partitioned
11972 : * tables, and not set for legacy inheritance, though we don't check for
11973 : * that here.)
11974 : */
11975 336 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11976 156 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11977 : {
11978 : ScanKeyData pkey;
11979 : SysScanDesc pscan;
11980 : HeapTuple childtup;
11981 :
11982 42 : ScanKeyInit(&pkey,
11983 : Anum_pg_constraint_conparentid,
11984 : BTEqualStrategyNumber, F_OIDEQ,
11985 : ObjectIdGetDatum(conoid));
11986 :
11987 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11988 : true, NULL, 1, &pkey);
11989 :
11990 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11991 : {
11992 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11993 : Relation childrel;
11994 :
11995 66 : childrel = table_open(childcon->conrelid, lockmode);
11996 66 : ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11997 : otherrelids, lockmode);
11998 66 : table_close(childrel, NoLock);
11999 : }
12000 :
12001 42 : systable_endscan(pscan);
12002 : }
12003 :
12004 180 : return changed;
12005 : }
12006 :
12007 : /*
12008 : * ALTER TABLE VALIDATE CONSTRAINT
12009 : *
12010 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12011 : * there's no good way to skip recursing when handling foreign keys: there is
12012 : * no need to lock children in that case, yet we wouldn't be able to avoid
12013 : * doing so at that level.
12014 : *
12015 : * Return value is the address of the validated constraint. If the constraint
12016 : * was already validated, InvalidObjectAddress is returned.
12017 : */
12018 : static ObjectAddress
12019 436 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12020 : bool recurse, bool recursing, LOCKMODE lockmode)
12021 : {
12022 : Relation conrel;
12023 : SysScanDesc scan;
12024 : ScanKeyData skey[3];
12025 : HeapTuple tuple;
12026 : Form_pg_constraint con;
12027 : ObjectAddress address;
12028 :
12029 436 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12030 :
12031 : /*
12032 : * Find and check the target constraint
12033 : */
12034 436 : ScanKeyInit(&skey[0],
12035 : Anum_pg_constraint_conrelid,
12036 : BTEqualStrategyNumber, F_OIDEQ,
12037 : ObjectIdGetDatum(RelationGetRelid(rel)));
12038 436 : ScanKeyInit(&skey[1],
12039 : Anum_pg_constraint_contypid,
12040 : BTEqualStrategyNumber, F_OIDEQ,
12041 : ObjectIdGetDatum(InvalidOid));
12042 436 : ScanKeyInit(&skey[2],
12043 : Anum_pg_constraint_conname,
12044 : BTEqualStrategyNumber, F_NAMEEQ,
12045 : CStringGetDatum(constrName));
12046 436 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12047 : true, NULL, 3, skey);
12048 :
12049 : /* There can be at most one matching row */
12050 436 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12051 0 : ereport(ERROR,
12052 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12053 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12054 : constrName, RelationGetRelationName(rel))));
12055 :
12056 436 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12057 436 : if (con->contype != CONSTRAINT_FOREIGN &&
12058 132 : con->contype != CONSTRAINT_CHECK)
12059 0 : ereport(ERROR,
12060 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12061 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12062 : constrName, RelationGetRelationName(rel))));
12063 :
12064 436 : if (!con->convalidated)
12065 : {
12066 : AlteredTableInfo *tab;
12067 : HeapTuple copyTuple;
12068 : Form_pg_constraint copy_con;
12069 :
12070 418 : if (con->contype == CONSTRAINT_FOREIGN)
12071 : {
12072 : NewConstraint *newcon;
12073 : Constraint *fkconstraint;
12074 :
12075 : /* Queue validation for phase 3 */
12076 298 : fkconstraint = makeNode(Constraint);
12077 : /* for now this is all we need */
12078 298 : fkconstraint->conname = constrName;
12079 :
12080 298 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12081 298 : newcon->name = constrName;
12082 298 : newcon->contype = CONSTR_FOREIGN;
12083 298 : newcon->refrelid = con->confrelid;
12084 298 : newcon->refindid = con->conindid;
12085 298 : newcon->conid = con->oid;
12086 298 : newcon->qual = (Node *) fkconstraint;
12087 :
12088 : /* Find or create work queue entry for this table */
12089 298 : tab = ATGetQueueEntry(wqueue, rel);
12090 298 : tab->constraints = lappend(tab->constraints, newcon);
12091 :
12092 : /*
12093 : * We disallow creating invalid foreign keys to or from
12094 : * partitioned tables, so ignoring the recursion bit is okay.
12095 : */
12096 : }
12097 120 : else if (con->contype == CONSTRAINT_CHECK)
12098 : {
12099 120 : List *children = NIL;
12100 : ListCell *child;
12101 : NewConstraint *newcon;
12102 : Datum val;
12103 : char *conbin;
12104 :
12105 : /*
12106 : * If we're recursing, the parent has already done this, so skip
12107 : * it. Also, if the constraint is a NO INHERIT constraint, we
12108 : * shouldn't try to look for it in the children.
12109 : */
12110 120 : if (!recursing && !con->connoinherit)
12111 66 : children = find_all_inheritors(RelationGetRelid(rel),
12112 : lockmode, NULL);
12113 :
12114 : /*
12115 : * For CHECK constraints, we must ensure that we only mark the
12116 : * constraint as validated on the parent if it's already validated
12117 : * on the children.
12118 : *
12119 : * We recurse before validating on the parent, to reduce risk of
12120 : * deadlocks.
12121 : */
12122 234 : foreach(child, children)
12123 : {
12124 114 : Oid childoid = lfirst_oid(child);
12125 : Relation childrel;
12126 :
12127 114 : if (childoid == RelationGetRelid(rel))
12128 66 : continue;
12129 :
12130 : /*
12131 : * If we are told not to recurse, there had better not be any
12132 : * child tables, because we can't mark the constraint on the
12133 : * parent valid unless it is valid for all child tables.
12134 : */
12135 48 : if (!recurse)
12136 0 : ereport(ERROR,
12137 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12138 : errmsg("constraint must be validated on child tables too")));
12139 :
12140 : /* find_all_inheritors already got lock */
12141 48 : childrel = table_open(childoid, NoLock);
12142 :
12143 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
12144 : true, lockmode);
12145 48 : table_close(childrel, NoLock);
12146 : }
12147 :
12148 : /* Queue validation for phase 3 */
12149 120 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12150 120 : newcon->name = constrName;
12151 120 : newcon->contype = CONSTR_CHECK;
12152 120 : newcon->refrelid = InvalidOid;
12153 120 : newcon->refindid = InvalidOid;
12154 120 : newcon->conid = con->oid;
12155 :
12156 120 : val = SysCacheGetAttrNotNull(CONSTROID, tuple,
12157 : Anum_pg_constraint_conbin);
12158 120 : conbin = TextDatumGetCString(val);
12159 120 : newcon->qual = (Node *) stringToNode(conbin);
12160 :
12161 : /* Find or create work queue entry for this table */
12162 120 : tab = ATGetQueueEntry(wqueue, rel);
12163 120 : tab->constraints = lappend(tab->constraints, newcon);
12164 :
12165 : /*
12166 : * Invalidate relcache so that others see the new validated
12167 : * constraint.
12168 : */
12169 120 : CacheInvalidateRelcache(rel);
12170 : }
12171 :
12172 : /*
12173 : * Now update the catalog, while we have the door open.
12174 : */
12175 418 : copyTuple = heap_copytuple(tuple);
12176 418 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12177 418 : copy_con->convalidated = true;
12178 418 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12179 :
12180 418 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12181 :
12182 418 : heap_freetuple(copyTuple);
12183 :
12184 418 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12185 : }
12186 : else
12187 18 : address = InvalidObjectAddress; /* already validated */
12188 :
12189 436 : systable_endscan(scan);
12190 :
12191 436 : table_close(conrel, RowExclusiveLock);
12192 :
12193 436 : return address;
12194 : }
12195 :
12196 :
12197 : /*
12198 : * transformColumnNameList - transform list of column names
12199 : *
12200 : * Lookup each name and return its attnum and, optionally, type OID
12201 : *
12202 : * Note: the name of this function suggests that it's general-purpose,
12203 : * but actually it's only used to look up names appearing in foreign-key
12204 : * clauses. The error messages would need work to use it in other cases,
12205 : * and perhaps the validity checks as well.
12206 : */
12207 : static int
12208 6160 : transformColumnNameList(Oid relId, List *colList,
12209 : int16 *attnums, Oid *atttypids)
12210 : {
12211 : ListCell *l;
12212 : int attnum;
12213 :
12214 6160 : attnum = 0;
12215 11262 : foreach(l, colList)
12216 : {
12217 5168 : char *attname = strVal(lfirst(l));
12218 : HeapTuple atttuple;
12219 : Form_pg_attribute attform;
12220 :
12221 5168 : atttuple = SearchSysCacheAttName(relId, attname);
12222 5168 : if (!HeapTupleIsValid(atttuple))
12223 54 : ereport(ERROR,
12224 : (errcode(ERRCODE_UNDEFINED_COLUMN),
12225 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12226 : attname)));
12227 5114 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12228 5114 : if (attform->attnum < 0)
12229 12 : ereport(ERROR,
12230 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12231 : errmsg("system columns cannot be used in foreign keys")));
12232 5102 : if (attnum >= INDEX_MAX_KEYS)
12233 0 : ereport(ERROR,
12234 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
12235 : errmsg("cannot have more than %d keys in a foreign key",
12236 : INDEX_MAX_KEYS)));
12237 5102 : attnums[attnum] = attform->attnum;
12238 5102 : if (atttypids != NULL)
12239 5072 : atttypids[attnum] = attform->atttypid;
12240 5102 : ReleaseSysCache(atttuple);
12241 5102 : attnum++;
12242 : }
12243 :
12244 6094 : return attnum;
12245 : }
12246 :
12247 : /*
12248 : * transformFkeyGetPrimaryKey -
12249 : *
12250 : * Look up the names, attnums, and types of the primary key attributes
12251 : * for the pkrel. Also return the index OID and index opclasses of the
12252 : * index supporting the primary key. Also return whether the index has
12253 : * WITHOUT OVERLAPS.
12254 : *
12255 : * All parameters except pkrel are output parameters. Also, the function
12256 : * return value is the number of attributes in the primary key.
12257 : *
12258 : * Used when the column list in the REFERENCES specification is omitted.
12259 : */
12260 : static int
12261 1070 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12262 : List **attnamelist,
12263 : int16 *attnums, Oid *atttypids,
12264 : Oid *opclasses, bool *pk_has_without_overlaps)
12265 : {
12266 : List *indexoidlist;
12267 : ListCell *indexoidscan;
12268 1070 : HeapTuple indexTuple = NULL;
12269 1070 : Form_pg_index indexStruct = NULL;
12270 : Datum indclassDatum;
12271 : oidvector *indclass;
12272 : int i;
12273 :
12274 : /*
12275 : * Get the list of index OIDs for the table from the relcache, and look up
12276 : * each one in the pg_index syscache until we find one marked primary key
12277 : * (hopefully there isn't more than one such). Insist it's valid, too.
12278 : */
12279 1070 : *indexOid = InvalidOid;
12280 :
12281 1070 : indexoidlist = RelationGetIndexList(pkrel);
12282 :
12283 1076 : foreach(indexoidscan, indexoidlist)
12284 : {
12285 1076 : Oid indexoid = lfirst_oid(indexoidscan);
12286 :
12287 1076 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12288 1076 : if (!HeapTupleIsValid(indexTuple))
12289 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12290 1076 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12291 1076 : if (indexStruct->indisprimary && indexStruct->indisvalid)
12292 : {
12293 : /*
12294 : * Refuse to use a deferrable primary key. This is per SQL spec,
12295 : * and there would be a lot of interesting semantic problems if we
12296 : * tried to allow it.
12297 : */
12298 1070 : if (!indexStruct->indimmediate)
12299 0 : ereport(ERROR,
12300 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12301 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12302 : RelationGetRelationName(pkrel))));
12303 :
12304 1070 : *indexOid = indexoid;
12305 1070 : break;
12306 : }
12307 6 : ReleaseSysCache(indexTuple);
12308 : }
12309 :
12310 1070 : list_free(indexoidlist);
12311 :
12312 : /*
12313 : * Check that we found it
12314 : */
12315 1070 : if (!OidIsValid(*indexOid))
12316 0 : ereport(ERROR,
12317 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12318 : errmsg("there is no primary key for referenced table \"%s\"",
12319 : RelationGetRelationName(pkrel))));
12320 :
12321 : /* Must get indclass the hard way */
12322 1070 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12323 : Anum_pg_index_indclass);
12324 1070 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12325 :
12326 : /*
12327 : * Now build the list of PK attributes from the indkey definition (we
12328 : * assume a primary key cannot have expressional elements)
12329 : */
12330 1070 : *attnamelist = NIL;
12331 2504 : for (i = 0; i < indexStruct->indnkeyatts; i++)
12332 : {
12333 1434 : int pkattno = indexStruct->indkey.values[i];
12334 :
12335 1434 : attnums[i] = pkattno;
12336 1434 : atttypids[i] = attnumTypeId(pkrel, pkattno);
12337 1434 : opclasses[i] = indclass->values[i];
12338 1434 : *attnamelist = lappend(*attnamelist,
12339 1434 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12340 : }
12341 :
12342 1070 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12343 :
12344 1070 : ReleaseSysCache(indexTuple);
12345 :
12346 1070 : return i;
12347 : }
12348 :
12349 : /*
12350 : * transformFkeyCheckAttrs -
12351 : *
12352 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12353 : * reference as part of a foreign key constraint.
12354 : *
12355 : * Returns the OID of the unique index supporting the constraint and
12356 : * populates the caller-provided 'opclasses' array with the opclasses
12357 : * associated with the index columns. Also sets whether the index
12358 : * uses WITHOUT OVERLAPS.
12359 : *
12360 : * Raises an ERROR on validation failure.
12361 : */
12362 : static Oid
12363 1260 : transformFkeyCheckAttrs(Relation pkrel,
12364 : int numattrs, int16 *attnums,
12365 : bool with_period, Oid *opclasses,
12366 : bool *pk_has_without_overlaps)
12367 : {
12368 1260 : Oid indexoid = InvalidOid;
12369 1260 : bool found = false;
12370 1260 : bool found_deferrable = false;
12371 : List *indexoidlist;
12372 : ListCell *indexoidscan;
12373 : int i,
12374 : j;
12375 :
12376 : /*
12377 : * Reject duplicate appearances of columns in the referenced-columns list.
12378 : * Such a case is forbidden by the SQL standard, and even if we thought it
12379 : * useful to allow it, there would be ambiguity about how to match the
12380 : * list to unique indexes (in particular, it'd be unclear which index
12381 : * opclass goes with which FK column).
12382 : */
12383 2950 : for (i = 0; i < numattrs; i++)
12384 : {
12385 2238 : for (j = i + 1; j < numattrs; j++)
12386 : {
12387 548 : if (attnums[i] == attnums[j])
12388 24 : ereport(ERROR,
12389 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12390 : errmsg("foreign key referenced-columns list must not contain duplicates")));
12391 : }
12392 : }
12393 :
12394 : /*
12395 : * Get the list of index OIDs for the table from the relcache, and look up
12396 : * each one in the pg_index syscache, and match unique indexes to the list
12397 : * of attnums we are given.
12398 : */
12399 1236 : indexoidlist = RelationGetIndexList(pkrel);
12400 :
12401 1416 : foreach(indexoidscan, indexoidlist)
12402 : {
12403 : HeapTuple indexTuple;
12404 : Form_pg_index indexStruct;
12405 :
12406 1404 : indexoid = lfirst_oid(indexoidscan);
12407 1404 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12408 1404 : if (!HeapTupleIsValid(indexTuple))
12409 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
12410 1404 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12411 :
12412 : /*
12413 : * Must have the right number of columns; must be unique (or if
12414 : * temporal then exclusion instead) and not a partial index; forget it
12415 : * if there are any expressions, too. Invalid indexes are out as well.
12416 : */
12417 2700 : if (indexStruct->indnkeyatts == numattrs &&
12418 1296 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12419 2564 : indexStruct->indisvalid &&
12420 2564 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12421 1282 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12422 : {
12423 : Datum indclassDatum;
12424 : oidvector *indclass;
12425 :
12426 : /* Must get indclass the hard way */
12427 1282 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12428 : Anum_pg_index_indclass);
12429 1282 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
12430 :
12431 : /*
12432 : * The given attnum list may match the index columns in any order.
12433 : * Check for a match, and extract the appropriate opclasses while
12434 : * we're at it.
12435 : *
12436 : * We know that attnums[] is duplicate-free per the test at the
12437 : * start of this function, and we checked above that the number of
12438 : * index columns agrees, so if we find a match for each attnums[]
12439 : * entry then we must have a one-to-one match in some order.
12440 : */
12441 2960 : for (i = 0; i < numattrs; i++)
12442 : {
12443 1736 : found = false;
12444 2318 : for (j = 0; j < numattrs; j++)
12445 : {
12446 2260 : if (attnums[i] == indexStruct->indkey.values[j])
12447 : {
12448 1678 : opclasses[i] = indclass->values[j];
12449 1678 : found = true;
12450 1678 : break;
12451 : }
12452 : }
12453 1736 : if (!found)
12454 58 : break;
12455 : }
12456 : /* The last attribute in the index must be the PERIOD FK part */
12457 1282 : if (found && with_period)
12458 : {
12459 130 : int16 periodattnum = attnums[numattrs - 1];
12460 :
12461 130 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12462 : }
12463 :
12464 : /*
12465 : * Refuse to use a deferrable unique/primary key. This is per SQL
12466 : * spec, and there would be a lot of interesting semantic problems
12467 : * if we tried to allow it.
12468 : */
12469 1282 : if (found && !indexStruct->indimmediate)
12470 : {
12471 : /*
12472 : * Remember that we found an otherwise matching index, so that
12473 : * we can generate a more appropriate error message.
12474 : */
12475 0 : found_deferrable = true;
12476 0 : found = false;
12477 : }
12478 :
12479 : /* We need to know whether the index has WITHOUT OVERLAPS */
12480 1282 : if (found)
12481 1224 : *pk_has_without_overlaps = indexStruct->indisexclusion;
12482 : }
12483 1404 : ReleaseSysCache(indexTuple);
12484 1404 : if (found)
12485 1224 : break;
12486 : }
12487 :
12488 1236 : if (!found)
12489 : {
12490 12 : if (found_deferrable)
12491 0 : ereport(ERROR,
12492 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12493 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12494 : RelationGetRelationName(pkrel))));
12495 : else
12496 12 : ereport(ERROR,
12497 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12498 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12499 : RelationGetRelationName(pkrel))));
12500 : }
12501 :
12502 1224 : list_free(indexoidlist);
12503 :
12504 1224 : return indexoid;
12505 : }
12506 :
12507 : /*
12508 : * findFkeyCast -
12509 : *
12510 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12511 : * Caller has equal regard for binary coercibility and for an exact match.
12512 : */
12513 : static CoercionPathType
12514 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12515 : {
12516 : CoercionPathType ret;
12517 :
12518 12 : if (targetTypeId == sourceTypeId)
12519 : {
12520 12 : ret = COERCION_PATH_RELABELTYPE;
12521 12 : *funcid = InvalidOid;
12522 : }
12523 : else
12524 : {
12525 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12526 : COERCION_IMPLICIT, funcid);
12527 0 : if (ret == COERCION_PATH_NONE)
12528 : /* A previously-relied-upon cast is now gone. */
12529 0 : elog(ERROR, "could not find cast from %u to %u",
12530 : sourceTypeId, targetTypeId);
12531 : }
12532 :
12533 12 : return ret;
12534 : }
12535 :
12536 : /*
12537 : * Permissions checks on the referenced table for ADD FOREIGN KEY
12538 : *
12539 : * Note: we have already checked that the user owns the referencing table,
12540 : * else we'd have failed much earlier; no additional checks are needed for it.
12541 : */
12542 : static void
12543 2258 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12544 : {
12545 2258 : Oid roleid = GetUserId();
12546 : AclResult aclresult;
12547 : int i;
12548 :
12549 : /* Okay if we have relation-level REFERENCES permission */
12550 2258 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12551 : ACL_REFERENCES);
12552 2258 : if (aclresult == ACLCHECK_OK)
12553 2258 : return;
12554 : /* Else we must have REFERENCES on each column */
12555 0 : for (i = 0; i < natts; i++)
12556 : {
12557 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12558 : roleid, ACL_REFERENCES);
12559 0 : if (aclresult != ACLCHECK_OK)
12560 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12561 0 : RelationGetRelationName(rel));
12562 : }
12563 : }
12564 :
12565 : /*
12566 : * Scan the existing rows in a table to verify they meet a proposed FK
12567 : * constraint.
12568 : *
12569 : * Caller must have opened and locked both relations appropriately.
12570 : */
12571 : static void
12572 1084 : validateForeignKeyConstraint(char *conname,
12573 : Relation rel,
12574 : Relation pkrel,
12575 : Oid pkindOid,
12576 : Oid constraintOid,
12577 : bool hasperiod)
12578 : {
12579 : TupleTableSlot *slot;
12580 : TableScanDesc scan;
12581 1084 : Trigger trig = {0};
12582 : Snapshot snapshot;
12583 : MemoryContext oldcxt;
12584 : MemoryContext perTupCxt;
12585 :
12586 1084 : ereport(DEBUG1,
12587 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12588 :
12589 : /*
12590 : * Build a trigger call structure; we'll need it either way.
12591 : */
12592 1084 : trig.tgoid = InvalidOid;
12593 1084 : trig.tgname = conname;
12594 1084 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12595 1084 : trig.tgisinternal = true;
12596 1084 : trig.tgconstrrelid = RelationGetRelid(pkrel);
12597 1084 : trig.tgconstrindid = pkindOid;
12598 1084 : trig.tgconstraint = constraintOid;
12599 1084 : trig.tgdeferrable = false;
12600 1084 : trig.tginitdeferred = false;
12601 : /* we needn't fill in remaining fields */
12602 :
12603 : /*
12604 : * See if we can do it with a single LEFT JOIN query. A false result
12605 : * indicates we must proceed with the fire-the-trigger method. We can't do
12606 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12607 : * left joins.
12608 : */
12609 1084 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12610 876 : return;
12611 :
12612 : /*
12613 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12614 : * if that tuple had just been inserted. If any of those fail, it should
12615 : * ereport(ERROR) and that's that.
12616 : */
12617 146 : snapshot = RegisterSnapshot(GetLatestSnapshot());
12618 146 : slot = table_slot_create(rel, NULL);
12619 146 : scan = table_beginscan(rel, snapshot, 0, NULL);
12620 :
12621 146 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12622 : "validateForeignKeyConstraint",
12623 : ALLOCSET_SMALL_SIZES);
12624 146 : oldcxt = MemoryContextSwitchTo(perTupCxt);
12625 :
12626 230 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12627 : {
12628 102 : LOCAL_FCINFO(fcinfo, 0);
12629 102 : TriggerData trigdata = {0};
12630 :
12631 102 : CHECK_FOR_INTERRUPTS();
12632 :
12633 : /*
12634 : * Make a call to the trigger function
12635 : *
12636 : * No parameters are passed, but we do set a context
12637 : */
12638 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12639 :
12640 : /*
12641 : * We assume RI_FKey_check_ins won't look at flinfo...
12642 : */
12643 102 : trigdata.type = T_TriggerData;
12644 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12645 102 : trigdata.tg_relation = rel;
12646 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12647 102 : trigdata.tg_trigslot = slot;
12648 102 : trigdata.tg_trigger = &trig;
12649 :
12650 102 : fcinfo->context = (Node *) &trigdata;
12651 :
12652 102 : RI_FKey_check_ins(fcinfo);
12653 :
12654 84 : MemoryContextReset(perTupCxt);
12655 : }
12656 :
12657 128 : MemoryContextSwitchTo(oldcxt);
12658 128 : MemoryContextDelete(perTupCxt);
12659 128 : table_endscan(scan);
12660 128 : UnregisterSnapshot(snapshot);
12661 128 : ExecDropSingleTupleTableSlot(slot);
12662 : }
12663 :
12664 : /*
12665 : * CreateFKCheckTrigger
12666 : * Creates the insert (on_insert=true) or update "check" trigger that
12667 : * implements a given foreign key
12668 : *
12669 : * Returns the OID of the so created trigger.
12670 : */
12671 : static Oid
12672 5492 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12673 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12674 : bool on_insert)
12675 : {
12676 : ObjectAddress trigAddress;
12677 : CreateTrigStmt *fk_trigger;
12678 :
12679 : /*
12680 : * Note: for a self-referential FK (referencing and referenced tables are
12681 : * the same), it is important that the ON UPDATE action fires before the
12682 : * CHECK action, since both triggers will fire on the same row during an
12683 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12684 : * state of the row. Triggers fire in name order, so we ensure this by
12685 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12686 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12687 : */
12688 5492 : fk_trigger = makeNode(CreateTrigStmt);
12689 5492 : fk_trigger->replace = false;
12690 5492 : fk_trigger->isconstraint = true;
12691 5492 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
12692 5492 : fk_trigger->relation = NULL;
12693 :
12694 : /* Either ON INSERT or ON UPDATE */
12695 5492 : if (on_insert)
12696 : {
12697 2746 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12698 2746 : fk_trigger->events = TRIGGER_TYPE_INSERT;
12699 : }
12700 : else
12701 : {
12702 2746 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12703 2746 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12704 : }
12705 :
12706 5492 : fk_trigger->args = NIL;
12707 5492 : fk_trigger->row = true;
12708 5492 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12709 5492 : fk_trigger->columns = NIL;
12710 5492 : fk_trigger->whenClause = NULL;
12711 5492 : fk_trigger->transitionRels = NIL;
12712 5492 : fk_trigger->deferrable = fkconstraint->deferrable;
12713 5492 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12714 5492 : fk_trigger->constrrel = NULL;
12715 :
12716 5492 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12717 : constraintOid, indexOid, InvalidOid,
12718 : parentTrigOid, NULL, true, false);
12719 :
12720 : /* Make changes-so-far visible */
12721 5492 : CommandCounterIncrement();
12722 :
12723 5492 : return trigAddress.objectId;
12724 : }
12725 :
12726 : /*
12727 : * createForeignKeyActionTriggers
12728 : * Create the referenced-side "action" triggers that implement a foreign
12729 : * key.
12730 : *
12731 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
12732 : * *updateTrigOid.
12733 : */
12734 : static void
12735 2762 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12736 : Oid constraintOid, Oid indexOid,
12737 : Oid parentDelTrigger, Oid parentUpdTrigger,
12738 : Oid *deleteTrigOid, Oid *updateTrigOid)
12739 : {
12740 : CreateTrigStmt *fk_trigger;
12741 : ObjectAddress trigAddress;
12742 :
12743 : /*
12744 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12745 : * DELETE action on the referenced table.
12746 : */
12747 2762 : fk_trigger = makeNode(CreateTrigStmt);
12748 2762 : fk_trigger->replace = false;
12749 2762 : fk_trigger->isconstraint = true;
12750 2762 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12751 2762 : fk_trigger->relation = NULL;
12752 2762 : fk_trigger->args = NIL;
12753 2762 : fk_trigger->row = true;
12754 2762 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12755 2762 : fk_trigger->events = TRIGGER_TYPE_DELETE;
12756 2762 : fk_trigger->columns = NIL;
12757 2762 : fk_trigger->whenClause = NULL;
12758 2762 : fk_trigger->transitionRels = NIL;
12759 2762 : fk_trigger->constrrel = NULL;
12760 :
12761 2762 : switch (fkconstraint->fk_del_action)
12762 : {
12763 2100 : case FKCONSTR_ACTION_NOACTION:
12764 2100 : fk_trigger->deferrable = fkconstraint->deferrable;
12765 2100 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12766 2100 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12767 2100 : break;
12768 64 : case FKCONSTR_ACTION_RESTRICT:
12769 64 : fk_trigger->deferrable = false;
12770 64 : fk_trigger->initdeferred = false;
12771 64 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12772 64 : break;
12773 440 : case FKCONSTR_ACTION_CASCADE:
12774 440 : fk_trigger->deferrable = false;
12775 440 : fk_trigger->initdeferred = false;
12776 440 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12777 440 : break;
12778 98 : case FKCONSTR_ACTION_SETNULL:
12779 98 : fk_trigger->deferrable = false;
12780 98 : fk_trigger->initdeferred = false;
12781 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12782 98 : break;
12783 60 : case FKCONSTR_ACTION_SETDEFAULT:
12784 60 : fk_trigger->deferrable = false;
12785 60 : fk_trigger->initdeferred = false;
12786 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12787 60 : break;
12788 0 : default:
12789 0 : elog(ERROR, "unrecognized FK action type: %d",
12790 : (int) fkconstraint->fk_del_action);
12791 : break;
12792 : }
12793 :
12794 2762 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12795 : RelationGetRelid(rel),
12796 : constraintOid, indexOid, InvalidOid,
12797 : parentDelTrigger, NULL, true, false);
12798 2762 : if (deleteTrigOid)
12799 2696 : *deleteTrigOid = trigAddress.objectId;
12800 :
12801 : /* Make changes-so-far visible */
12802 2762 : CommandCounterIncrement();
12803 :
12804 : /*
12805 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12806 : * UPDATE action on the referenced table.
12807 : */
12808 2762 : fk_trigger = makeNode(CreateTrigStmt);
12809 2762 : fk_trigger->replace = false;
12810 2762 : fk_trigger->isconstraint = true;
12811 2762 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
12812 2762 : fk_trigger->relation = NULL;
12813 2762 : fk_trigger->args = NIL;
12814 2762 : fk_trigger->row = true;
12815 2762 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
12816 2762 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
12817 2762 : fk_trigger->columns = NIL;
12818 2762 : fk_trigger->whenClause = NULL;
12819 2762 : fk_trigger->transitionRels = NIL;
12820 2762 : fk_trigger->constrrel = NULL;
12821 :
12822 2762 : switch (fkconstraint->fk_upd_action)
12823 : {
12824 2328 : case FKCONSTR_ACTION_NOACTION:
12825 2328 : fk_trigger->deferrable = fkconstraint->deferrable;
12826 2328 : fk_trigger->initdeferred = fkconstraint->initdeferred;
12827 2328 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12828 2328 : break;
12829 42 : case FKCONSTR_ACTION_RESTRICT:
12830 42 : fk_trigger->deferrable = false;
12831 42 : fk_trigger->initdeferred = false;
12832 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12833 42 : break;
12834 288 : case FKCONSTR_ACTION_CASCADE:
12835 288 : fk_trigger->deferrable = false;
12836 288 : fk_trigger->initdeferred = false;
12837 288 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12838 288 : break;
12839 62 : case FKCONSTR_ACTION_SETNULL:
12840 62 : fk_trigger->deferrable = false;
12841 62 : fk_trigger->initdeferred = false;
12842 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12843 62 : break;
12844 42 : case FKCONSTR_ACTION_SETDEFAULT:
12845 42 : fk_trigger->deferrable = false;
12846 42 : fk_trigger->initdeferred = false;
12847 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12848 42 : break;
12849 0 : default:
12850 0 : elog(ERROR, "unrecognized FK action type: %d",
12851 : (int) fkconstraint->fk_upd_action);
12852 : break;
12853 : }
12854 :
12855 2762 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12856 : RelationGetRelid(rel),
12857 : constraintOid, indexOid, InvalidOid,
12858 : parentUpdTrigger, NULL, true, false);
12859 2762 : if (updateTrigOid)
12860 2696 : *updateTrigOid = trigAddress.objectId;
12861 2762 : }
12862 :
12863 : /*
12864 : * createForeignKeyCheckTriggers
12865 : * Create the referencing-side "check" triggers that implement a foreign
12866 : * key.
12867 : *
12868 : * Returns the OIDs of the so created triggers in *insertTrigOid and
12869 : * *updateTrigOid.
12870 : */
12871 : static void
12872 2746 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12873 : Constraint *fkconstraint, Oid constraintOid,
12874 : Oid indexOid,
12875 : Oid parentInsTrigger, Oid parentUpdTrigger,
12876 : Oid *insertTrigOid, Oid *updateTrigOid)
12877 : {
12878 2746 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12879 : constraintOid, indexOid,
12880 : parentInsTrigger, true);
12881 2746 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12882 : constraintOid, indexOid,
12883 : parentUpdTrigger, false);
12884 2746 : }
12885 :
12886 : /*
12887 : * ALTER TABLE DROP CONSTRAINT
12888 : *
12889 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12890 : */
12891 : static void
12892 936 : ATExecDropConstraint(Relation rel, const char *constrName,
12893 : DropBehavior behavior, bool recurse,
12894 : bool missing_ok, LOCKMODE lockmode)
12895 : {
12896 : Relation conrel;
12897 : SysScanDesc scan;
12898 : ScanKeyData skey[3];
12899 : HeapTuple tuple;
12900 936 : bool found = false;
12901 :
12902 936 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12903 :
12904 : /*
12905 : * Find and drop the target constraint
12906 : */
12907 936 : ScanKeyInit(&skey[0],
12908 : Anum_pg_constraint_conrelid,
12909 : BTEqualStrategyNumber, F_OIDEQ,
12910 : ObjectIdGetDatum(RelationGetRelid(rel)));
12911 936 : ScanKeyInit(&skey[1],
12912 : Anum_pg_constraint_contypid,
12913 : BTEqualStrategyNumber, F_OIDEQ,
12914 : ObjectIdGetDatum(InvalidOid));
12915 936 : ScanKeyInit(&skey[2],
12916 : Anum_pg_constraint_conname,
12917 : BTEqualStrategyNumber, F_NAMEEQ,
12918 : CStringGetDatum(constrName));
12919 936 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12920 : true, NULL, 3, skey);
12921 :
12922 : /* There can be at most one matching row */
12923 936 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12924 : {
12925 900 : List *readyRels = NIL;
12926 :
12927 900 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
12928 : missing_ok, &readyRels, lockmode);
12929 726 : found = true;
12930 : }
12931 :
12932 762 : systable_endscan(scan);
12933 :
12934 762 : if (!found)
12935 : {
12936 36 : if (!missing_ok)
12937 24 : ereport(ERROR,
12938 : errcode(ERRCODE_UNDEFINED_OBJECT),
12939 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12940 : constrName, RelationGetRelationName(rel)));
12941 : else
12942 12 : ereport(NOTICE,
12943 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12944 : constrName, RelationGetRelationName(rel)));
12945 : }
12946 :
12947 738 : table_close(conrel, RowExclusiveLock);
12948 738 : }
12949 :
12950 : /*
12951 : * Remove a constraint, using its pg_constraint tuple
12952 : *
12953 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12954 : * DROP NOT NULL.
12955 : *
12956 : * Returns the address of the constraint being removed.
12957 : */
12958 : static ObjectAddress
12959 1294 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
12960 : bool recurse, bool recursing, bool missing_ok, List **readyRels,
12961 : LOCKMODE lockmode)
12962 : {
12963 : Relation conrel;
12964 : Form_pg_constraint con;
12965 : ObjectAddress conobj;
12966 : List *children;
12967 1294 : bool is_no_inherit_constraint = false;
12968 : char *constrName;
12969 1294 : List *unconstrained_cols = NIL;
12970 1294 : char *colname = NULL;
12971 1294 : bool dropping_pk = false;
12972 :
12973 1294 : if (list_member_oid(*readyRels, RelationGetRelid(rel)))
12974 0 : return InvalidObjectAddress;
12975 1294 : *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
12976 :
12977 : /* Guard against stack overflow due to overly deep inheritance tree. */
12978 1294 : check_stack_depth();
12979 :
12980 : /* At top level, permission check was done in ATPrepCmd, else do it */
12981 1294 : if (recursing)
12982 234 : ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12983 :
12984 1288 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12985 :
12986 1288 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12987 1288 : constrName = NameStr(con->conname);
12988 :
12989 : /*
12990 : * If we're asked to drop a constraint which is both defined locally and
12991 : * inherited, we can simply mark it as no longer having a local
12992 : * definition, and no further changes are required.
12993 : *
12994 : * XXX We do this for not-null constraints only, not CHECK, because the
12995 : * latter have historically not behaved this way and it might be confusing
12996 : * to change the behavior now.
12997 : */
12998 1288 : if (con->contype == CONSTRAINT_NOTNULL &&
12999 458 : con->conislocal && con->coninhcount > 0)
13000 : {
13001 : HeapTuple copytup;
13002 :
13003 6 : copytup = heap_copytuple(constraintTup);
13004 6 : con = (Form_pg_constraint) GETSTRUCT(copytup);
13005 6 : con->conislocal = false;
13006 6 : CatalogTupleUpdate(conrel, ©tup->t_self, copytup);
13007 6 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13008 :
13009 6 : CommandCounterIncrement();
13010 6 : table_close(conrel, RowExclusiveLock);
13011 6 : return conobj;
13012 : }
13013 :
13014 : /* Don't allow drop of inherited constraints */
13015 1282 : if (con->coninhcount > 0 && !recursing)
13016 138 : ereport(ERROR,
13017 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13018 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13019 : constrName, RelationGetRelationName(rel))));
13020 :
13021 : /*
13022 : * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
13023 : * more checks and actions below, so obtain the list of columns that are
13024 : * constrained by the constraint being dropped.
13025 : */
13026 1144 : if (con->contype == CONSTRAINT_NOTNULL)
13027 : {
13028 : AttrNumber colnum;
13029 :
13030 434 : colnum = extractNotNullColumn(constraintTup);
13031 434 : unconstrained_cols = list_make1_int(colnum);
13032 434 : colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
13033 : colnum - 1)->attname);
13034 : }
13035 710 : else if (con->contype == CONSTRAINT_PRIMARY)
13036 : {
13037 : Datum adatum;
13038 : ArrayType *arr;
13039 : int numkeys;
13040 : bool isNull;
13041 : int16 *attnums;
13042 :
13043 140 : dropping_pk = true;
13044 :
13045 140 : adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
13046 : RelationGetDescr(conrel), &isNull);
13047 140 : if (isNull)
13048 0 : elog(ERROR, "null conkey for constraint %u", con->oid);
13049 140 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
13050 140 : numkeys = ARR_DIMS(arr)[0];
13051 140 : if (ARR_NDIM(arr) != 1 ||
13052 140 : numkeys < 0 ||
13053 140 : ARR_HASNULL(arr) ||
13054 140 : ARR_ELEMTYPE(arr) != INT2OID)
13055 0 : elog(ERROR, "conkey is not a 1-D smallint array");
13056 140 : attnums = (int16 *) ARR_DATA_PTR(arr);
13057 :
13058 318 : for (int i = 0; i < numkeys; i++)
13059 178 : unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
13060 : }
13061 :
13062 1144 : is_no_inherit_constraint = con->connoinherit;
13063 :
13064 : /*
13065 : * If it's a foreign-key constraint, we'd better lock the referenced table
13066 : * and check that that's not in use, just as we've already done for the
13067 : * constrained table (else we might, eg, be dropping a trigger that has
13068 : * unfired events). But we can/must skip that in the self-referential
13069 : * case.
13070 : */
13071 1144 : if (con->contype == CONSTRAINT_FOREIGN &&
13072 192 : con->confrelid != RelationGetRelid(rel))
13073 : {
13074 : Relation frel;
13075 :
13076 : /* Must match lock taken by RemoveTriggerById: */
13077 192 : frel = table_open(con->confrelid, AccessExclusiveLock);
13078 192 : CheckTableNotInUse(frel, "ALTER TABLE");
13079 186 : table_close(frel, NoLock);
13080 : }
13081 :
13082 : /*
13083 : * Perform the actual constraint deletion
13084 : */
13085 1138 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13086 1138 : performDeletion(&conobj, behavior, 0);
13087 :
13088 : /*
13089 : * If this was a NOT NULL or the primary key, verify that we still have
13090 : * constraints to support GENERATED AS IDENTITY or the replica identity.
13091 : */
13092 1102 : if (unconstrained_cols != NIL)
13093 : {
13094 : Relation attrel;
13095 : Bitmapset *pkcols;
13096 : Bitmapset *ircols;
13097 :
13098 : /* Make implicit attnotnull changes visible */
13099 538 : CommandCounterIncrement();
13100 :
13101 538 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
13102 :
13103 : /*
13104 : * We want to test columns for their presence in the primary key, but
13105 : * only if we're not dropping it.
13106 : */
13107 538 : pkcols = dropping_pk ? NULL :
13108 434 : RelationGetIndexAttrBitmap(rel,
13109 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
13110 538 : ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
13111 :
13112 1604 : foreach_int(attnum, unconstrained_cols)
13113 : {
13114 : HeapTuple atttup;
13115 : HeapTuple contup;
13116 : Form_pg_attribute attForm;
13117 : char attidentity;
13118 :
13119 : /*
13120 : * Obtain pg_attribute tuple and verify conditions on it.
13121 : */
13122 564 : atttup = SearchSysCacheAttNum(RelationGetRelid(rel), attnum);
13123 564 : if (!HeapTupleIsValid(atttup))
13124 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13125 : attnum, RelationGetRelid(rel));
13126 564 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13127 564 : attidentity = attForm->attidentity;
13128 564 : ReleaseSysCache(atttup);
13129 :
13130 : /*
13131 : * Since the above deletion has been made visible, we can now
13132 : * search for any remaining constraints on this column (or these
13133 : * columns, in the case we're dropping a multicol primary key.)
13134 : * Then, verify whether any further NOT NULL or primary key
13135 : * exists: if none and this is a generated identity column or the
13136 : * replica identity, abort the whole thing.
13137 : */
13138 564 : contup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
13139 1110 : if (contup ||
13140 546 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
13141 : pkcols))
13142 222 : continue;
13143 :
13144 : /*
13145 : * It's not valid to drop the not-null constraint for a GENERATED
13146 : * AS IDENTITY column.
13147 : */
13148 342 : if (attidentity != '\0')
13149 0 : ereport(ERROR,
13150 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13151 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
13152 : get_attname(RelationGetRelid(rel), attnum,
13153 : false),
13154 : RelationGetRelationName(rel)));
13155 :
13156 : /*
13157 : * It's not valid to drop the not-null constraint for a column in
13158 : * the replica identity index, either. (FULL is not affected.)
13159 : */
13160 342 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, ircols))
13161 18 : ereport(ERROR,
13162 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13163 : errmsg("column \"%s\" is in index used as replica identity",
13164 : get_attname(RelationGetRelid(rel), attnum, false)));
13165 : }
13166 520 : table_close(attrel, RowExclusiveLock);
13167 : }
13168 :
13169 : /*
13170 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13171 : * are dropped via the dependency mechanism, so we're done here.
13172 : */
13173 1084 : if (con->contype != CONSTRAINT_CHECK &&
13174 760 : con->contype != CONSTRAINT_NOTNULL &&
13175 338 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13176 : {
13177 66 : table_close(conrel, RowExclusiveLock);
13178 66 : return conobj;
13179 : }
13180 :
13181 : /*
13182 : * Propagate to children as appropriate. Unlike most other ALTER
13183 : * routines, we have to do this one level of recursion at a time; we can't
13184 : * use find_all_inheritors to do it in one pass.
13185 : */
13186 1018 : if (!is_no_inherit_constraint)
13187 566 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13188 : else
13189 452 : children = NIL;
13190 :
13191 : /*
13192 : * For a partitioned table, if partitions exist and we are told not to
13193 : * recurse, it's a user error. It doesn't make sense to have a constraint
13194 : * be defined only on the parent, especially if it's a partitioned table.
13195 : */
13196 1018 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
13197 62 : children != NIL && !recurse)
13198 6 : ereport(ERROR,
13199 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13200 : errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
13201 : errhint("Do not specify the ONLY keyword.")));
13202 :
13203 2312 : foreach_oid(childrelid, children)
13204 : {
13205 : Relation childrel;
13206 : HeapTuple tuple;
13207 : Form_pg_constraint childcon;
13208 :
13209 300 : if (list_member_oid(*readyRels, childrelid))
13210 6 : continue; /* child already processed */
13211 :
13212 : /* find_inheritance_children already got lock */
13213 294 : childrel = table_open(childrelid, NoLock);
13214 294 : CheckTableNotInUse(childrel, "ALTER TABLE");
13215 :
13216 : /*
13217 : * We search for not-null constraints by column name, and others by
13218 : * constraint name.
13219 : */
13220 294 : if (con->contype == CONSTRAINT_NOTNULL)
13221 : {
13222 94 : tuple = findNotNullConstraint(childrelid, colname);
13223 94 : if (!HeapTupleIsValid(tuple))
13224 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13225 : colname, RelationGetRelid(childrel));
13226 : }
13227 : else
13228 : {
13229 : SysScanDesc scan;
13230 : ScanKeyData skey[3];
13231 :
13232 200 : ScanKeyInit(&skey[0],
13233 : Anum_pg_constraint_conrelid,
13234 : BTEqualStrategyNumber, F_OIDEQ,
13235 : ObjectIdGetDatum(childrelid));
13236 200 : ScanKeyInit(&skey[1],
13237 : Anum_pg_constraint_contypid,
13238 : BTEqualStrategyNumber, F_OIDEQ,
13239 : ObjectIdGetDatum(InvalidOid));
13240 200 : ScanKeyInit(&skey[2],
13241 : Anum_pg_constraint_conname,
13242 : BTEqualStrategyNumber, F_NAMEEQ,
13243 : CStringGetDatum(constrName));
13244 200 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13245 : true, NULL, 3, skey);
13246 : /* There can only be one, so no need to loop */
13247 200 : tuple = systable_getnext(scan);
13248 200 : if (!HeapTupleIsValid(tuple))
13249 0 : ereport(ERROR,
13250 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13251 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13252 : constrName,
13253 : RelationGetRelationName(childrel))));
13254 200 : tuple = heap_copytuple(tuple);
13255 200 : systable_endscan(scan);
13256 : }
13257 :
13258 294 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13259 :
13260 : /* Right now only CHECK and not-null constraints can be inherited */
13261 294 : if (childcon->contype != CONSTRAINT_CHECK &&
13262 94 : childcon->contype != CONSTRAINT_NOTNULL)
13263 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13264 :
13265 294 : if (childcon->coninhcount <= 0) /* shouldn't happen */
13266 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13267 : childrelid, NameStr(childcon->conname));
13268 :
13269 294 : if (recurse)
13270 : {
13271 : /*
13272 : * If the child constraint has other definition sources, just
13273 : * decrement its inheritance count; if not, recurse to delete it.
13274 : */
13275 288 : if (childcon->coninhcount == 1 && !childcon->conislocal)
13276 : {
13277 : /* Time to delete this child constraint, too */
13278 210 : dropconstraint_internal(childrel, tuple, behavior,
13279 : recurse, true, missing_ok, readyRels,
13280 : lockmode);
13281 : }
13282 : else
13283 : {
13284 : /* Child constraint must survive my deletion */
13285 78 : childcon->coninhcount--;
13286 78 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13287 :
13288 : /* Make update visible */
13289 78 : CommandCounterIncrement();
13290 : }
13291 : }
13292 : else
13293 : {
13294 : /*
13295 : * If we were told to drop ONLY in this table (no recursion) and
13296 : * there are no further parents for this constraint, we need to
13297 : * mark the inheritors' constraints as locally defined rather than
13298 : * inherited.
13299 : */
13300 6 : childcon->coninhcount--;
13301 6 : if (childcon->coninhcount == 0)
13302 6 : childcon->conislocal = true;
13303 :
13304 6 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13305 :
13306 : /* Make update visible */
13307 6 : CommandCounterIncrement();
13308 : }
13309 :
13310 288 : heap_freetuple(tuple);
13311 :
13312 288 : table_close(childrel, NoLock);
13313 : }
13314 :
13315 : /*
13316 : * In addition, when dropping a primary key from a legacy-inheritance
13317 : * parent table, we must recurse to children to mark the corresponding NOT
13318 : * NULL constraint as no longer inherited, or drop it if this its last
13319 : * reference.
13320 : */
13321 1006 : if (con->contype == CONSTRAINT_PRIMARY &&
13322 92 : rel->rd_rel->relkind == RELKIND_RELATION &&
13323 92 : rel->rd_rel->relhassubclass)
13324 : {
13325 12 : List *colnames = NIL;
13326 12 : List *pkready = NIL;
13327 :
13328 : /*
13329 : * Because primary keys are always marked as NO INHERIT, we don't have
13330 : * a list of children yet, so obtain one now.
13331 : */
13332 12 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13333 :
13334 : /*
13335 : * Find out the list of column names to process. Fortunately, we
13336 : * already have the list of column numbers.
13337 : */
13338 36 : foreach_int(attnum, unconstrained_cols)
13339 : {
13340 12 : colnames = lappend(colnames, get_attname(RelationGetRelid(rel),
13341 : attnum, false));
13342 : }
13343 :
13344 48 : foreach_oid(childrelid, children)
13345 : {
13346 : Relation childrel;
13347 :
13348 24 : if (list_member_oid(pkready, childrelid))
13349 0 : continue; /* child already processed */
13350 :
13351 : /* find_inheritance_children already got lock */
13352 24 : childrel = table_open(childrelid, NoLock);
13353 24 : CheckTableNotInUse(childrel, "ALTER TABLE");
13354 :
13355 72 : foreach_ptr(char, colName, colnames)
13356 : {
13357 : HeapTuple contup;
13358 :
13359 24 : contup = findNotNullConstraint(childrelid, colName);
13360 24 : if (contup == NULL)
13361 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
13362 : colName, RelationGetRelationName(childrel));
13363 :
13364 24 : dropconstraint_internal(childrel, contup,
13365 : DROP_RESTRICT, true, true,
13366 : false, &pkready,
13367 : lockmode);
13368 24 : pkready = NIL;
13369 : }
13370 :
13371 24 : table_close(childrel, NoLock);
13372 :
13373 24 : pkready = lappend_oid(pkready, childrelid);
13374 : }
13375 : }
13376 :
13377 1006 : table_close(conrel, RowExclusiveLock);
13378 :
13379 1006 : return conobj;
13380 : }
13381 :
13382 : /*
13383 : * ALTER COLUMN TYPE
13384 : *
13385 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13386 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13387 : * transformed (and must be, because we rely on some transformed fields).
13388 : *
13389 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13390 : * table will be done "in parallel" during phase 3, so all the USING
13391 : * expressions should be parsed assuming the original column types. Also,
13392 : * this allows a USING expression to refer to a field that will be dropped.
13393 : *
13394 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13395 : * the first two execution steps in phase 2; they must not see the effects
13396 : * of any other subcommand types, since the USING expressions are parsed
13397 : * against the unmodified table's state.
13398 : */
13399 : static void
13400 1140 : ATPrepAlterColumnType(List **wqueue,
13401 : AlteredTableInfo *tab, Relation rel,
13402 : bool recurse, bool recursing,
13403 : AlterTableCmd *cmd, LOCKMODE lockmode,
13404 : AlterTableUtilityContext *context)
13405 : {
13406 1140 : char *colName = cmd->name;
13407 1140 : ColumnDef *def = (ColumnDef *) cmd->def;
13408 1140 : TypeName *typeName = def->typeName;
13409 1140 : Node *transform = def->cooked_default;
13410 : HeapTuple tuple;
13411 : Form_pg_attribute attTup;
13412 : AttrNumber attnum;
13413 : Oid targettype;
13414 : int32 targettypmod;
13415 : Oid targetcollid;
13416 : NewColumnValue *newval;
13417 1140 : ParseState *pstate = make_parsestate(NULL);
13418 : AclResult aclresult;
13419 : bool is_expr;
13420 :
13421 1140 : if (rel->rd_rel->reloftype && !recursing)
13422 6 : ereport(ERROR,
13423 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13424 : errmsg("cannot alter column type of typed table")));
13425 :
13426 : /* lookup the attribute so we can check inheritance status */
13427 1134 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13428 1134 : if (!HeapTupleIsValid(tuple))
13429 0 : ereport(ERROR,
13430 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13431 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13432 : colName, RelationGetRelationName(rel))));
13433 1134 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13434 1134 : attnum = attTup->attnum;
13435 :
13436 : /* Can't alter a system attribute */
13437 1134 : if (attnum <= 0)
13438 0 : ereport(ERROR,
13439 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13440 : errmsg("cannot alter system column \"%s\"",
13441 : colName)));
13442 :
13443 : /*
13444 : * Don't alter inherited columns. At outer level, there had better not be
13445 : * any inherited definition; when recursing, we assume this was checked at
13446 : * the parent level (see below).
13447 : */
13448 1134 : if (attTup->attinhcount > 0 && !recursing)
13449 6 : ereport(ERROR,
13450 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13451 : errmsg("cannot alter inherited column \"%s\"",
13452 : colName)));
13453 :
13454 : /* Don't alter columns used in the partition key */
13455 1128 : if (has_partition_attrs(rel,
13456 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13457 : &is_expr))
13458 18 : ereport(ERROR,
13459 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13460 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13461 : colName, RelationGetRelationName(rel))));
13462 :
13463 : /* Look up the target type */
13464 1110 : typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
13465 :
13466 1110 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13467 1110 : if (aclresult != ACLCHECK_OK)
13468 12 : aclcheck_error_type(aclresult, targettype);
13469 :
13470 : /* And the collation */
13471 1098 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13472 :
13473 : /* make sure datatype is legal for a column */
13474 1098 : CheckAttributeType(colName, targettype, targetcollid,
13475 1098 : list_make1_oid(rel->rd_rel->reltype),
13476 : 0);
13477 :
13478 1092 : if (tab->relkind == RELKIND_RELATION ||
13479 196 : tab->relkind == RELKIND_PARTITIONED_TABLE)
13480 : {
13481 : /*
13482 : * Set up an expression to transform the old data value to the new
13483 : * type. If a USING option was given, use the expression as
13484 : * transformed by transformAlterTableStmt, else just take the old
13485 : * value and try to coerce it. We do this first so that type
13486 : * incompatibility can be detected before we waste effort, and because
13487 : * we need the expression to be parsed against the original table row
13488 : * type.
13489 : */
13490 956 : if (!transform)
13491 : {
13492 734 : transform = (Node *) makeVar(1, attnum,
13493 : attTup->atttypid, attTup->atttypmod,
13494 : attTup->attcollation,
13495 : 0);
13496 : }
13497 :
13498 956 : transform = coerce_to_target_type(pstate,
13499 : transform, exprType(transform),
13500 : targettype, targettypmod,
13501 : COERCION_ASSIGNMENT,
13502 : COERCE_IMPLICIT_CAST,
13503 : -1);
13504 956 : if (transform == NULL)
13505 : {
13506 : /* error text depends on whether USING was specified or not */
13507 24 : if (def->cooked_default != NULL)
13508 6 : ereport(ERROR,
13509 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13510 : errmsg("result of USING clause for column \"%s\""
13511 : " cannot be cast automatically to type %s",
13512 : colName, format_type_be(targettype)),
13513 : errhint("You might need to add an explicit cast.")));
13514 : else
13515 18 : ereport(ERROR,
13516 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13517 : errmsg("column \"%s\" cannot be cast automatically to type %s",
13518 : colName, format_type_be(targettype)),
13519 : /* translator: USING is SQL, don't translate it */
13520 : errhint("You might need to specify \"USING %s::%s\".",
13521 : quote_identifier(colName),
13522 : format_type_with_typemod(targettype,
13523 : targettypmod))));
13524 : }
13525 :
13526 : /* Fix collations after all else */
13527 932 : assign_expr_collations(pstate, transform);
13528 :
13529 : /* Plan the expr now so we can accurately assess the need to rewrite. */
13530 932 : transform = (Node *) expression_planner((Expr *) transform);
13531 :
13532 : /*
13533 : * Add a work queue item to make ATRewriteTable update the column
13534 : * contents.
13535 : */
13536 932 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13537 932 : newval->attnum = attnum;
13538 932 : newval->expr = (Expr *) transform;
13539 932 : newval->is_generated = false;
13540 :
13541 932 : tab->newvals = lappend(tab->newvals, newval);
13542 932 : if (ATColumnChangeRequiresRewrite(transform, attnum))
13543 750 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13544 : }
13545 136 : else if (transform)
13546 12 : ereport(ERROR,
13547 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13548 : errmsg("\"%s\" is not a table",
13549 : RelationGetRelationName(rel))));
13550 :
13551 1056 : if (!RELKIND_HAS_STORAGE(tab->relkind))
13552 : {
13553 : /*
13554 : * For relations without storage, do this check now. Regular tables
13555 : * will check it later when the table is being rewritten.
13556 : */
13557 184 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13558 : }
13559 :
13560 1026 : ReleaseSysCache(tuple);
13561 :
13562 : /*
13563 : * Recurse manually by queueing a new command for each child, if
13564 : * necessary. We cannot apply ATSimpleRecursion here because we need to
13565 : * remap attribute numbers in the USING expression, if any.
13566 : *
13567 : * If we are told not to recurse, there had better not be any child
13568 : * tables; else the alter would put them out of step.
13569 : */
13570 1026 : if (recurse)
13571 : {
13572 774 : Oid relid = RelationGetRelid(rel);
13573 : List *child_oids,
13574 : *child_numparents;
13575 : ListCell *lo,
13576 : *li;
13577 :
13578 774 : child_oids = find_all_inheritors(relid, lockmode,
13579 : &child_numparents);
13580 :
13581 : /*
13582 : * find_all_inheritors does the recursive search of the inheritance
13583 : * hierarchy, so all we have to do is process all of the relids in the
13584 : * list that it returns.
13585 : */
13586 1750 : forboth(lo, child_oids, li, child_numparents)
13587 : {
13588 1000 : Oid childrelid = lfirst_oid(lo);
13589 1000 : int numparents = lfirst_int(li);
13590 : Relation childrel;
13591 : HeapTuple childtuple;
13592 : Form_pg_attribute childattTup;
13593 :
13594 1000 : if (childrelid == relid)
13595 774 : continue;
13596 :
13597 : /* find_all_inheritors already got lock */
13598 226 : childrel = relation_open(childrelid, NoLock);
13599 226 : CheckTableNotInUse(childrel, "ALTER TABLE");
13600 :
13601 : /*
13602 : * Verify that the child doesn't have any inherited definitions of
13603 : * this column that came from outside this inheritance hierarchy.
13604 : * (renameatt makes a similar test, though in a different way
13605 : * because of its different recursion mechanism.)
13606 : */
13607 226 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13608 : colName);
13609 226 : if (!HeapTupleIsValid(childtuple))
13610 0 : ereport(ERROR,
13611 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13612 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13613 : colName, RelationGetRelationName(childrel))));
13614 226 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13615 :
13616 226 : if (childattTup->attinhcount > numparents)
13617 6 : ereport(ERROR,
13618 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13619 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13620 : colName, RelationGetRelationName(childrel))));
13621 :
13622 220 : ReleaseSysCache(childtuple);
13623 :
13624 : /*
13625 : * Remap the attribute numbers. If no USING expression was
13626 : * specified, there is no need for this step.
13627 : */
13628 220 : if (def->cooked_default)
13629 : {
13630 : AttrMap *attmap;
13631 : bool found_whole_row;
13632 :
13633 : /* create a copy to scribble on */
13634 72 : cmd = copyObject(cmd);
13635 :
13636 72 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13637 : RelationGetDescr(rel),
13638 : false);
13639 144 : ((ColumnDef *) cmd->def)->cooked_default =
13640 72 : map_variable_attnos(def->cooked_default,
13641 : 1, 0,
13642 : attmap,
13643 : InvalidOid, &found_whole_row);
13644 72 : if (found_whole_row)
13645 6 : ereport(ERROR,
13646 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13647 : errmsg("cannot convert whole-row table reference"),
13648 : errdetail("USING expression contains a whole-row table reference.")));
13649 66 : pfree(attmap);
13650 : }
13651 214 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13652 202 : relation_close(childrel, NoLock);
13653 : }
13654 : }
13655 302 : else if (!recursing &&
13656 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13657 0 : ereport(ERROR,
13658 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13659 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
13660 : colName)));
13661 :
13662 1002 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13663 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13664 996 : }
13665 :
13666 : /*
13667 : * When the data type of a column is changed, a rewrite might not be required
13668 : * if the new type is sufficiently identical to the old one, and the USING
13669 : * clause isn't trying to insert some other value. It's safe to skip the
13670 : * rewrite in these cases:
13671 : *
13672 : * - the old type is binary coercible to the new type
13673 : * - the new type is an unconstrained domain over the old type
13674 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13675 : *
13676 : * In the case of a constrained domain, we could get by with scanning the
13677 : * table and checking the constraint rather than actually rewriting it, but we
13678 : * don't currently try to do that.
13679 : */
13680 : static bool
13681 1036 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13682 : {
13683 : Assert(expr != NULL);
13684 :
13685 : for (;;)
13686 : {
13687 : /* only one varno, so no need to check that */
13688 1036 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13689 182 : return false;
13690 854 : else if (IsA(expr, RelabelType))
13691 92 : expr = (Node *) ((RelabelType *) expr)->arg;
13692 762 : else if (IsA(expr, CoerceToDomain))
13693 : {
13694 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
13695 :
13696 0 : if (DomainHasConstraints(d->resulttype))
13697 0 : return true;
13698 0 : expr = (Node *) d->arg;
13699 : }
13700 762 : else if (IsA(expr, FuncExpr))
13701 : {
13702 562 : FuncExpr *f = (FuncExpr *) expr;
13703 :
13704 562 : switch (f->funcid)
13705 : {
13706 18 : case F_TIMESTAMPTZ_TIMESTAMP:
13707 : case F_TIMESTAMP_TIMESTAMPTZ:
13708 18 : if (TimestampTimestampTzRequiresRewrite())
13709 6 : return true;
13710 : else
13711 12 : expr = linitial(f->args);
13712 12 : break;
13713 544 : default:
13714 544 : return true;
13715 : }
13716 : }
13717 : else
13718 200 : return true;
13719 : }
13720 : }
13721 :
13722 : /*
13723 : * ALTER COLUMN .. SET DATA TYPE
13724 : *
13725 : * Return the address of the modified column.
13726 : */
13727 : static ObjectAddress
13728 966 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13729 : AlterTableCmd *cmd, LOCKMODE lockmode)
13730 : {
13731 966 : char *colName = cmd->name;
13732 966 : ColumnDef *def = (ColumnDef *) cmd->def;
13733 966 : TypeName *typeName = def->typeName;
13734 : HeapTuple heapTup;
13735 : Form_pg_attribute attTup,
13736 : attOldTup;
13737 : AttrNumber attnum;
13738 : HeapTuple typeTuple;
13739 : Form_pg_type tform;
13740 : Oid targettype;
13741 : int32 targettypmod;
13742 : Oid targetcollid;
13743 : Node *defaultexpr;
13744 : Relation attrelation;
13745 : Relation depRel;
13746 : ScanKeyData key[3];
13747 : SysScanDesc scan;
13748 : HeapTuple depTup;
13749 : ObjectAddress address;
13750 :
13751 : /*
13752 : * Clear all the missing values if we're rewriting the table, since this
13753 : * renders them pointless.
13754 : */
13755 966 : if (tab->rewrite)
13756 : {
13757 : Relation newrel;
13758 :
13759 696 : newrel = table_open(RelationGetRelid(rel), NoLock);
13760 696 : RelationClearMissing(newrel);
13761 696 : relation_close(newrel, NoLock);
13762 : /* make sure we don't conflict with later attribute modifications */
13763 696 : CommandCounterIncrement();
13764 : }
13765 :
13766 966 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13767 :
13768 : /* Look up the target column */
13769 966 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13770 966 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13771 0 : ereport(ERROR,
13772 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13773 : errmsg("column \"%s\" of relation \"%s\" does not exist",
13774 : colName, RelationGetRelationName(rel))));
13775 966 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13776 966 : attnum = attTup->attnum;
13777 966 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13778 :
13779 : /* Check for multiple ALTER TYPE on same column --- can't cope */
13780 966 : if (attTup->atttypid != attOldTup->atttypid ||
13781 966 : attTup->atttypmod != attOldTup->atttypmod)
13782 0 : ereport(ERROR,
13783 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13784 : errmsg("cannot alter type of column \"%s\" twice",
13785 : colName)));
13786 :
13787 : /* Look up the target type (should not fail, since prep found it) */
13788 966 : typeTuple = typenameType(NULL, typeName, &targettypmod);
13789 966 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
13790 966 : targettype = tform->oid;
13791 : /* And the collation */
13792 966 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
13793 :
13794 : /*
13795 : * If there is a default expression for the column, get it and ensure we
13796 : * can coerce it to the new datatype. (We must do this before changing
13797 : * the column type, because build_column_default itself will try to
13798 : * coerce, and will not issue the error message we want if it fails.)
13799 : *
13800 : * We remove any implicit coercion steps at the top level of the old
13801 : * default expression; this has been agreed to satisfy the principle of
13802 : * least surprise. (The conversion to the new column type should act like
13803 : * it started from what the user sees as the stored expression, and the
13804 : * implicit coercions aren't going to be shown.)
13805 : */
13806 966 : if (attTup->atthasdef)
13807 : {
13808 62 : defaultexpr = build_column_default(rel, attnum);
13809 : Assert(defaultexpr);
13810 62 : defaultexpr = strip_implicit_coercions(defaultexpr);
13811 62 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13812 : defaultexpr, exprType(defaultexpr),
13813 : targettype, targettypmod,
13814 : COERCION_ASSIGNMENT,
13815 : COERCE_IMPLICIT_CAST,
13816 : -1);
13817 62 : if (defaultexpr == NULL)
13818 : {
13819 12 : if (attTup->attgenerated)
13820 6 : ereport(ERROR,
13821 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13822 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13823 : colName, format_type_be(targettype))));
13824 : else
13825 6 : ereport(ERROR,
13826 : (errcode(ERRCODE_DATATYPE_MISMATCH),
13827 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13828 : colName, format_type_be(targettype))));
13829 : }
13830 : }
13831 : else
13832 904 : defaultexpr = NULL;
13833 :
13834 : /*
13835 : * Find everything that depends on the column (constraints, indexes, etc),
13836 : * and record enough information to let us recreate the objects.
13837 : *
13838 : * The actual recreation does not happen here, but only after we have
13839 : * performed all the individual ALTER TYPE operations. We have to save
13840 : * the info before executing ALTER TYPE, though, else the deparser will
13841 : * get confused.
13842 : */
13843 954 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13844 :
13845 : /*
13846 : * Now scan for dependencies of this column on other things. The only
13847 : * things we should find are the dependency on the column datatype and
13848 : * possibly a collation dependency. Those can be removed.
13849 : */
13850 930 : depRel = table_open(DependRelationId, RowExclusiveLock);
13851 :
13852 930 : ScanKeyInit(&key[0],
13853 : Anum_pg_depend_classid,
13854 : BTEqualStrategyNumber, F_OIDEQ,
13855 : ObjectIdGetDatum(RelationRelationId));
13856 930 : ScanKeyInit(&key[1],
13857 : Anum_pg_depend_objid,
13858 : BTEqualStrategyNumber, F_OIDEQ,
13859 : ObjectIdGetDatum(RelationGetRelid(rel)));
13860 930 : ScanKeyInit(&key[2],
13861 : Anum_pg_depend_objsubid,
13862 : BTEqualStrategyNumber, F_INT4EQ,
13863 : Int32GetDatum((int32) attnum));
13864 :
13865 930 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
13866 : NULL, 3, key);
13867 :
13868 934 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13869 : {
13870 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13871 : ObjectAddress foundObject;
13872 :
13873 4 : foundObject.classId = foundDep->refclassid;
13874 4 : foundObject.objectId = foundDep->refobjid;
13875 4 : foundObject.objectSubId = foundDep->refobjsubid;
13876 :
13877 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
13878 0 : elog(ERROR, "found unexpected dependency type '%c'",
13879 : foundDep->deptype);
13880 4 : if (!(foundDep->refclassid == TypeRelationId &&
13881 4 : foundDep->refobjid == attTup->atttypid) &&
13882 0 : !(foundDep->refclassid == CollationRelationId &&
13883 0 : foundDep->refobjid == attTup->attcollation))
13884 0 : elog(ERROR, "found unexpected dependency for column: %s",
13885 : getObjectDescription(&foundObject, false));
13886 :
13887 4 : CatalogTupleDelete(depRel, &depTup->t_self);
13888 : }
13889 :
13890 930 : systable_endscan(scan);
13891 :
13892 930 : table_close(depRel, RowExclusiveLock);
13893 :
13894 : /*
13895 : * Here we go --- change the recorded column type and collation. (Note
13896 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13897 : * fix up the missing value if any.
13898 : */
13899 930 : if (attTup->atthasmissing)
13900 : {
13901 : Datum missingval;
13902 : bool missingNull;
13903 :
13904 : /* if rewrite is true the missing value should already be cleared */
13905 : Assert(tab->rewrite == 0);
13906 :
13907 : /* Get the missing value datum */
13908 6 : missingval = heap_getattr(heapTup,
13909 : Anum_pg_attribute_attmissingval,
13910 : attrelation->rd_att,
13911 : &missingNull);
13912 :
13913 : /* if it's a null array there is nothing to do */
13914 :
13915 6 : if (!missingNull)
13916 : {
13917 : /*
13918 : * Get the datum out of the array and repack it in a new array
13919 : * built with the new type data. We assume that since the table
13920 : * doesn't need rewriting, the actual Datum doesn't need to be
13921 : * changed, only the array metadata.
13922 : */
13923 :
13924 6 : int one = 1;
13925 : bool isNull;
13926 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
13927 6 : bool nullsAtt[Natts_pg_attribute] = {0};
13928 6 : bool replacesAtt[Natts_pg_attribute] = {0};
13929 : HeapTuple newTup;
13930 :
13931 12 : missingval = array_get_element(missingval,
13932 : 1,
13933 : &one,
13934 : 0,
13935 6 : attTup->attlen,
13936 6 : attTup->attbyval,
13937 6 : attTup->attalign,
13938 : &isNull);
13939 6 : missingval = PointerGetDatum(construct_array(&missingval,
13940 : 1,
13941 : targettype,
13942 6 : tform->typlen,
13943 6 : tform->typbyval,
13944 6 : tform->typalign));
13945 :
13946 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13947 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13948 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13949 :
13950 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13951 : valuesAtt, nullsAtt, replacesAtt);
13952 6 : heap_freetuple(heapTup);
13953 6 : heapTup = newTup;
13954 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13955 : }
13956 : }
13957 :
13958 930 : attTup->atttypid = targettype;
13959 930 : attTup->atttypmod = targettypmod;
13960 930 : attTup->attcollation = targetcollid;
13961 930 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13962 0 : ereport(ERROR,
13963 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13964 : errmsg("too many array dimensions"));
13965 930 : attTup->attndims = list_length(typeName->arrayBounds);
13966 930 : attTup->attlen = tform->typlen;
13967 930 : attTup->attbyval = tform->typbyval;
13968 930 : attTup->attalign = tform->typalign;
13969 930 : attTup->attstorage = tform->typstorage;
13970 930 : attTup->attcompression = InvalidCompressionMethod;
13971 :
13972 930 : ReleaseSysCache(typeTuple);
13973 :
13974 930 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13975 :
13976 930 : table_close(attrelation, RowExclusiveLock);
13977 :
13978 : /* Install dependencies on new datatype and collation */
13979 930 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13980 930 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13981 :
13982 : /*
13983 : * Drop any pg_statistic entry for the column, since it's now wrong type
13984 : */
13985 930 : RemoveStatistics(RelationGetRelid(rel), attnum);
13986 :
13987 930 : InvokeObjectPostAlterHook(RelationRelationId,
13988 : RelationGetRelid(rel), attnum);
13989 :
13990 : /*
13991 : * Update the default, if present, by brute force --- remove and re-add
13992 : * the default. Probably unsafe to take shortcuts, since the new version
13993 : * may well have additional dependencies. (It's okay to do this now,
13994 : * rather than after other ALTER TYPE commands, since the default won't
13995 : * depend on other column types.)
13996 : */
13997 930 : if (defaultexpr)
13998 : {
13999 : /*
14000 : * If it's a GENERATED default, drop its dependency records, in
14001 : * particular its INTERNAL dependency on the column, which would
14002 : * otherwise cause dependency.c to refuse to perform the deletion.
14003 : */
14004 50 : if (attTup->attgenerated)
14005 : {
14006 6 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14007 :
14008 6 : if (!OidIsValid(attrdefoid))
14009 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14010 : RelationGetRelid(rel), attnum);
14011 6 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14012 : }
14013 :
14014 : /*
14015 : * Make updates-so-far visible, particularly the new pg_attribute row
14016 : * which will be updated again.
14017 : */
14018 50 : CommandCounterIncrement();
14019 :
14020 : /*
14021 : * We use RESTRICT here for safety, but at present we do not expect
14022 : * anything to depend on the default.
14023 : */
14024 50 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14025 : true);
14026 :
14027 50 : StoreAttrDefault(rel, attnum, defaultexpr, true, false);
14028 : }
14029 :
14030 930 : ObjectAddressSubSet(address, RelationRelationId,
14031 : RelationGetRelid(rel), attnum);
14032 :
14033 : /* Cleanup */
14034 930 : heap_freetuple(heapTup);
14035 :
14036 930 : return address;
14037 : }
14038 :
14039 : /*
14040 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14041 : * that depends on the column (constraints, indexes, etc), and record enough
14042 : * information to let us recreate the objects.
14043 : */
14044 : static void
14045 1032 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
14046 : Relation rel, AttrNumber attnum, const char *colName)
14047 : {
14048 : Relation depRel;
14049 : ScanKeyData key[3];
14050 : SysScanDesc scan;
14051 : HeapTuple depTup;
14052 :
14053 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14054 :
14055 1032 : depRel = table_open(DependRelationId, RowExclusiveLock);
14056 :
14057 1032 : ScanKeyInit(&key[0],
14058 : Anum_pg_depend_refclassid,
14059 : BTEqualStrategyNumber, F_OIDEQ,
14060 : ObjectIdGetDatum(RelationRelationId));
14061 1032 : ScanKeyInit(&key[1],
14062 : Anum_pg_depend_refobjid,
14063 : BTEqualStrategyNumber, F_OIDEQ,
14064 : ObjectIdGetDatum(RelationGetRelid(rel)));
14065 1032 : ScanKeyInit(&key[2],
14066 : Anum_pg_depend_refobjsubid,
14067 : BTEqualStrategyNumber, F_INT4EQ,
14068 : Int32GetDatum((int32) attnum));
14069 :
14070 1032 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14071 : NULL, 3, key);
14072 :
14073 1986 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14074 : {
14075 978 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14076 : ObjectAddress foundObject;
14077 :
14078 978 : foundObject.classId = foundDep->classid;
14079 978 : foundObject.objectId = foundDep->objid;
14080 978 : foundObject.objectSubId = foundDep->objsubid;
14081 :
14082 978 : switch (foundObject.classId)
14083 : {
14084 270 : case RelationRelationId:
14085 : {
14086 270 : char relKind = get_rel_relkind(foundObject.objectId);
14087 :
14088 270 : if (relKind == RELKIND_INDEX ||
14089 : relKind == RELKIND_PARTITIONED_INDEX)
14090 : {
14091 : Assert(foundObject.objectSubId == 0);
14092 232 : RememberIndexForRebuilding(foundObject.objectId, tab);
14093 : }
14094 38 : else if (relKind == RELKIND_SEQUENCE)
14095 : {
14096 : /*
14097 : * This must be a SERIAL column's sequence. We need
14098 : * not do anything to it.
14099 : */
14100 : Assert(foundObject.objectSubId == 0);
14101 : }
14102 : else
14103 : {
14104 : /* Not expecting any other direct dependencies... */
14105 0 : elog(ERROR, "unexpected object depending on column: %s",
14106 : getObjectDescription(&foundObject, false));
14107 : }
14108 270 : break;
14109 : }
14110 :
14111 542 : case ConstraintRelationId:
14112 : Assert(foundObject.objectSubId == 0);
14113 542 : RememberConstraintForRebuilding(foundObject.objectId, tab);
14114 542 : break;
14115 :
14116 0 : case ProcedureRelationId:
14117 :
14118 : /*
14119 : * A new-style SQL function can depend on a column, if that
14120 : * column is referenced in the parsed function body. Ideally
14121 : * we'd automatically update the function by deparsing and
14122 : * reparsing it, but that's risky and might well fail anyhow.
14123 : * FIXME someday.
14124 : *
14125 : * This is only a problem for AT_AlterColumnType, not
14126 : * AT_SetExpression.
14127 : */
14128 0 : if (subtype == AT_AlterColumnType)
14129 0 : ereport(ERROR,
14130 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14131 : errmsg("cannot alter type of a column used by a function or procedure"),
14132 : errdetail("%s depends on column \"%s\"",
14133 : getObjectDescription(&foundObject, false),
14134 : colName)));
14135 0 : break;
14136 :
14137 12 : case RewriteRelationId:
14138 :
14139 : /*
14140 : * View/rule bodies have pretty much the same issues as
14141 : * function bodies. FIXME someday.
14142 : */
14143 12 : if (subtype == AT_AlterColumnType)
14144 12 : ereport(ERROR,
14145 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14146 : errmsg("cannot alter type of a column used by a view or rule"),
14147 : errdetail("%s depends on column \"%s\"",
14148 : getObjectDescription(&foundObject, false),
14149 : colName)));
14150 0 : break;
14151 :
14152 0 : case TriggerRelationId:
14153 :
14154 : /*
14155 : * A trigger can depend on a column because the column is
14156 : * specified as an update target, or because the column is
14157 : * used in the trigger's WHEN condition. The first case would
14158 : * not require any extra work, but the second case would
14159 : * require updating the WHEN expression, which has the same
14160 : * issues as above. Since we can't easily tell which case
14161 : * applies, we punt for both. FIXME someday.
14162 : */
14163 0 : if (subtype == AT_AlterColumnType)
14164 0 : ereport(ERROR,
14165 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14166 : errmsg("cannot alter type of a column used in a trigger definition"),
14167 : errdetail("%s depends on column \"%s\"",
14168 : getObjectDescription(&foundObject, false),
14169 : colName)));
14170 0 : break;
14171 :
14172 0 : case PolicyRelationId:
14173 :
14174 : /*
14175 : * A policy can depend on a column because the column is
14176 : * specified in the policy's USING or WITH CHECK qual
14177 : * expressions. It might be possible to rewrite and recheck
14178 : * the policy expression, but punt for now. It's certainly
14179 : * easy enough to remove and recreate the policy; still, FIXME
14180 : * someday.
14181 : */
14182 0 : if (subtype == AT_AlterColumnType)
14183 0 : ereport(ERROR,
14184 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14185 : errmsg("cannot alter type of a column used in a policy definition"),
14186 : errdetail("%s depends on column \"%s\"",
14187 : getObjectDescription(&foundObject, false),
14188 : colName)));
14189 0 : break;
14190 :
14191 140 : case AttrDefaultRelationId:
14192 : {
14193 140 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14194 :
14195 140 : if (col.objectId == RelationGetRelid(rel) &&
14196 140 : col.objectSubId == attnum)
14197 : {
14198 : /*
14199 : * Ignore the column's own default expression. The
14200 : * caller deals with it.
14201 : */
14202 : }
14203 : else
14204 : {
14205 : /*
14206 : * This must be a reference from the expression of a
14207 : * generated column elsewhere in the same table.
14208 : * Changing the type/generated expression of a column
14209 : * that is used by a generated column is not allowed
14210 : * by SQL standard, so just punt for now. It might be
14211 : * doable with some thinking and effort.
14212 : */
14213 12 : if (subtype == AT_AlterColumnType)
14214 12 : ereport(ERROR,
14215 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14216 : errmsg("cannot alter type of a column used by a generated column"),
14217 : errdetail("Column \"%s\" is used by generated column \"%s\".",
14218 : colName,
14219 : get_attname(col.objectId,
14220 : col.objectSubId,
14221 : false))));
14222 : }
14223 128 : break;
14224 : }
14225 :
14226 14 : case StatisticExtRelationId:
14227 :
14228 : /*
14229 : * Give the extended-stats machinery a chance to fix anything
14230 : * that this column type change would break.
14231 : */
14232 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
14233 14 : break;
14234 :
14235 0 : case PublicationRelRelationId:
14236 :
14237 : /*
14238 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14239 : * clause. Same issues as above. FIXME someday.
14240 : */
14241 0 : if (subtype == AT_AlterColumnType)
14242 0 : ereport(ERROR,
14243 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14244 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
14245 : errdetail("%s depends on column \"%s\"",
14246 : getObjectDescription(&foundObject, false),
14247 : colName)));
14248 0 : break;
14249 :
14250 0 : default:
14251 :
14252 : /*
14253 : * We don't expect any other sorts of objects to depend on a
14254 : * column.
14255 : */
14256 0 : elog(ERROR, "unexpected object depending on column: %s",
14257 : getObjectDescription(&foundObject, false));
14258 : break;
14259 : }
14260 : }
14261 :
14262 1008 : systable_endscan(scan);
14263 1008 : table_close(depRel, NoLock);
14264 1008 : }
14265 :
14266 : /*
14267 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
14268 : * needs to be reset.
14269 : */
14270 : static void
14271 434 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14272 : {
14273 434 : if (!get_index_isreplident(indoid))
14274 416 : return;
14275 :
14276 18 : if (tab->replicaIdentityIndex)
14277 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14278 :
14279 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
14280 : }
14281 :
14282 : /*
14283 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
14284 : */
14285 : static void
14286 434 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14287 : {
14288 434 : if (!get_index_isclustered(indoid))
14289 416 : return;
14290 :
14291 18 : if (tab->clusterOnIndex)
14292 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14293 :
14294 18 : tab->clusterOnIndex = get_rel_name(indoid);
14295 : }
14296 :
14297 : /*
14298 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14299 : * to be rebuilt (which we might already know).
14300 : */
14301 : static void
14302 554 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14303 : {
14304 : /*
14305 : * This de-duplication check is critical for two independent reasons: we
14306 : * mustn't try to recreate the same constraint twice, and if a constraint
14307 : * depends on more than one column whose type is to be altered, we must
14308 : * capture its definition string before applying any of the column type
14309 : * changes. ruleutils.c will get confused if we ask again later.
14310 : */
14311 554 : if (!list_member_oid(tab->changedConstraintOids, conoid))
14312 : {
14313 : /* OK, capture the constraint's existing definition string */
14314 464 : char *defstring = pg_get_constraintdef_command(conoid);
14315 : Oid indoid;
14316 :
14317 464 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14318 : conoid);
14319 464 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14320 : defstring);
14321 :
14322 : /*
14323 : * For the index of a constraint, if any, remember if it is used for
14324 : * the table's replica identity or if it is a clustered index, so that
14325 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14326 : * those properties.
14327 : */
14328 464 : indoid = get_constraint_index(conoid);
14329 464 : if (OidIsValid(indoid))
14330 : {
14331 222 : RememberReplicaIdentityForRebuilding(indoid, tab);
14332 222 : RememberClusterOnForRebuilding(indoid, tab);
14333 : }
14334 : }
14335 554 : }
14336 :
14337 : /*
14338 : * Subroutine for ATExecAlterColumnType: remember that an index needs
14339 : * to be rebuilt (which we might already know).
14340 : */
14341 : static void
14342 232 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14343 : {
14344 : /*
14345 : * This de-duplication check is critical for two independent reasons: we
14346 : * mustn't try to recreate the same index twice, and if an index depends
14347 : * on more than one column whose type is to be altered, we must capture
14348 : * its definition string before applying any of the column type changes.
14349 : * ruleutils.c will get confused if we ask again later.
14350 : */
14351 232 : if (!list_member_oid(tab->changedIndexOids, indoid))
14352 : {
14353 : /*
14354 : * Before adding it as an index-to-rebuild, we'd better see if it
14355 : * belongs to a constraint, and if so rebuild the constraint instead.
14356 : * Typically this check fails, because constraint indexes normally
14357 : * have only dependencies on their constraint. But it's possible for
14358 : * such an index to also have direct dependencies on table columns,
14359 : * for example with a partial exclusion constraint.
14360 : */
14361 224 : Oid conoid = get_index_constraint(indoid);
14362 :
14363 224 : if (OidIsValid(conoid))
14364 : {
14365 12 : RememberConstraintForRebuilding(conoid, tab);
14366 : }
14367 : else
14368 : {
14369 : /* OK, capture the index's existing definition string */
14370 212 : char *defstring = pg_get_indexdef_string(indoid);
14371 :
14372 212 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14373 : indoid);
14374 212 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14375 : defstring);
14376 :
14377 : /*
14378 : * Remember if this index is used for the table's replica identity
14379 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14380 : * can queue up commands necessary to restore those properties.
14381 : */
14382 212 : RememberReplicaIdentityForRebuilding(indoid, tab);
14383 212 : RememberClusterOnForRebuilding(indoid, tab);
14384 : }
14385 : }
14386 232 : }
14387 :
14388 : /*
14389 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
14390 : * needs to be rebuilt (which we might already know).
14391 : */
14392 : static void
14393 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14394 : {
14395 : /*
14396 : * This de-duplication check is critical for two independent reasons: we
14397 : * mustn't try to recreate the same statistics object twice, and if the
14398 : * statistics object depends on more than one column whose type is to be
14399 : * altered, we must capture its definition string before applying any of
14400 : * the type changes. ruleutils.c will get confused if we ask again later.
14401 : */
14402 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14403 : {
14404 : /* OK, capture the statistics object's existing definition string */
14405 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
14406 :
14407 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14408 : stxoid);
14409 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14410 : defstring);
14411 : }
14412 14 : }
14413 :
14414 : /*
14415 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14416 : * operations for a particular relation. We have to drop and recreate all the
14417 : * indexes and constraints that depend on the altered columns. We do the
14418 : * actual dropping here, but re-creation is managed by adding work queue
14419 : * entries to do those steps later.
14420 : */
14421 : static void
14422 978 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14423 : {
14424 : ObjectAddress obj;
14425 : ObjectAddresses *objects;
14426 : ListCell *def_item;
14427 : ListCell *oid_item;
14428 :
14429 : /*
14430 : * Collect all the constraints and indexes to drop so we can process them
14431 : * in a single call. That way we don't have to worry about dependencies
14432 : * among them.
14433 : */
14434 978 : objects = new_object_addresses();
14435 :
14436 : /*
14437 : * Re-parse the index and constraint definitions, and attach them to the
14438 : * appropriate work queue entries. We do this before dropping because in
14439 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14440 : * lock on the table the constraint is attached to, and we need to get
14441 : * that before reparsing/dropping.
14442 : *
14443 : * We can't rely on the output of deparsing to tell us which relation to
14444 : * operate on, because concurrent activity might have made the name
14445 : * resolve differently. Instead, we've got to use the OID of the
14446 : * constraint or index we're processing to figure out which relation to
14447 : * operate on.
14448 : */
14449 1442 : forboth(oid_item, tab->changedConstraintOids,
14450 : def_item, tab->changedConstraintDefs)
14451 : {
14452 464 : Oid oldId = lfirst_oid(oid_item);
14453 : HeapTuple tup;
14454 : Form_pg_constraint con;
14455 : Oid relid;
14456 : Oid confrelid;
14457 : char contype;
14458 : bool conislocal;
14459 :
14460 464 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14461 464 : if (!HeapTupleIsValid(tup)) /* should not happen */
14462 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14463 464 : con = (Form_pg_constraint) GETSTRUCT(tup);
14464 464 : if (OidIsValid(con->conrelid))
14465 450 : relid = con->conrelid;
14466 : else
14467 : {
14468 : /* must be a domain constraint */
14469 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
14470 14 : if (!OidIsValid(relid))
14471 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14472 : }
14473 464 : confrelid = con->confrelid;
14474 464 : contype = con->contype;
14475 464 : conislocal = con->conislocal;
14476 464 : ReleaseSysCache(tup);
14477 :
14478 464 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
14479 464 : add_exact_object_address(&obj, objects);
14480 :
14481 : /*
14482 : * If the constraint is inherited (only), we don't want to inject a
14483 : * new definition here; it'll get recreated when
14484 : * ATAddCheckNNConstraint recurses from adding the parent table's
14485 : * constraint. But we had to carry the info this far so that we can
14486 : * drop the constraint below.
14487 : */
14488 464 : if (!conislocal)
14489 28 : continue;
14490 :
14491 : /*
14492 : * When rebuilding an FK constraint that references the table we're
14493 : * modifying, we might not yet have any lock on the FK's table, so get
14494 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14495 : * step, so there's no value in asking for anything weaker.
14496 : */
14497 436 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14498 36 : LockRelationOid(relid, AccessExclusiveLock);
14499 :
14500 436 : ATPostAlterTypeParse(oldId, relid, confrelid,
14501 436 : (char *) lfirst(def_item),
14502 436 : wqueue, lockmode, tab->rewrite);
14503 : }
14504 1190 : forboth(oid_item, tab->changedIndexOids,
14505 : def_item, tab->changedIndexDefs)
14506 : {
14507 212 : Oid oldId = lfirst_oid(oid_item);
14508 : Oid relid;
14509 :
14510 212 : relid = IndexGetRelation(oldId, false);
14511 212 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14512 212 : (char *) lfirst(def_item),
14513 212 : wqueue, lockmode, tab->rewrite);
14514 :
14515 212 : ObjectAddressSet(obj, RelationRelationId, oldId);
14516 212 : add_exact_object_address(&obj, objects);
14517 : }
14518 :
14519 : /* add dependencies for new statistics */
14520 992 : forboth(oid_item, tab->changedStatisticsOids,
14521 : def_item, tab->changedStatisticsDefs)
14522 : {
14523 14 : Oid oldId = lfirst_oid(oid_item);
14524 : Oid relid;
14525 :
14526 14 : relid = StatisticsGetRelation(oldId, false);
14527 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
14528 14 : (char *) lfirst(def_item),
14529 14 : wqueue, lockmode, tab->rewrite);
14530 :
14531 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14532 14 : add_exact_object_address(&obj, objects);
14533 : }
14534 :
14535 : /*
14536 : * Queue up command to restore replica identity index marking
14537 : */
14538 978 : if (tab->replicaIdentityIndex)
14539 : {
14540 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14541 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14542 :
14543 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14544 18 : subcmd->name = tab->replicaIdentityIndex;
14545 18 : cmd->subtype = AT_ReplicaIdentity;
14546 18 : cmd->def = (Node *) subcmd;
14547 :
14548 : /* do it after indexes and constraints */
14549 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14550 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14551 : }
14552 :
14553 : /*
14554 : * Queue up command to restore marking of index used for cluster.
14555 : */
14556 978 : if (tab->clusterOnIndex)
14557 : {
14558 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14559 :
14560 18 : cmd->subtype = AT_ClusterOn;
14561 18 : cmd->name = tab->clusterOnIndex;
14562 :
14563 : /* do it after indexes and constraints */
14564 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14565 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14566 : }
14567 :
14568 : /*
14569 : * It should be okay to use DROP_RESTRICT here, since nothing else should
14570 : * be depending on these objects.
14571 : */
14572 978 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14573 :
14574 978 : free_object_addresses(objects);
14575 :
14576 : /*
14577 : * The objects will get recreated during subsequent passes over the work
14578 : * queue.
14579 : */
14580 978 : }
14581 :
14582 : /*
14583 : * Parse the previously-saved definition string for a constraint, index or
14584 : * statistics object against the newly-established column data type(s), and
14585 : * queue up the resulting command parsetrees for execution.
14586 : *
14587 : * This might fail if, for example, you have a WHERE clause that uses an
14588 : * operator that's not available for the new column type.
14589 : */
14590 : static void
14591 662 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14592 : List **wqueue, LOCKMODE lockmode, bool rewrite)
14593 : {
14594 : List *raw_parsetree_list;
14595 : List *querytree_list;
14596 : ListCell *list_item;
14597 : Relation rel;
14598 :
14599 : /*
14600 : * We expect that we will get only ALTER TABLE and CREATE INDEX
14601 : * statements. Hence, there is no need to pass them through
14602 : * parse_analyze_*() or the rewriter, but instead we need to pass them
14603 : * through parse_utilcmd.c to make them ready for execution.
14604 : */
14605 662 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14606 662 : querytree_list = NIL;
14607 1324 : foreach(list_item, raw_parsetree_list)
14608 : {
14609 662 : RawStmt *rs = lfirst_node(RawStmt, list_item);
14610 662 : Node *stmt = rs->stmt;
14611 :
14612 662 : if (IsA(stmt, IndexStmt))
14613 212 : querytree_list = lappend(querytree_list,
14614 212 : transformIndexStmt(oldRelId,
14615 : (IndexStmt *) stmt,
14616 : cmd));
14617 450 : else if (IsA(stmt, AlterTableStmt))
14618 : {
14619 : List *beforeStmts;
14620 : List *afterStmts;
14621 :
14622 422 : stmt = (Node *) transformAlterTableStmt(oldRelId,
14623 : (AlterTableStmt *) stmt,
14624 : cmd,
14625 : &beforeStmts,
14626 : &afterStmts);
14627 422 : querytree_list = list_concat(querytree_list, beforeStmts);
14628 422 : querytree_list = lappend(querytree_list, stmt);
14629 422 : querytree_list = list_concat(querytree_list, afterStmts);
14630 : }
14631 28 : else if (IsA(stmt, CreateStatsStmt))
14632 14 : querytree_list = lappend(querytree_list,
14633 14 : transformStatsStmt(oldRelId,
14634 : (CreateStatsStmt *) stmt,
14635 : cmd));
14636 : else
14637 14 : querytree_list = lappend(querytree_list, stmt);
14638 : }
14639 :
14640 : /* Caller should already have acquired whatever lock we need. */
14641 662 : rel = relation_open(oldRelId, NoLock);
14642 :
14643 : /*
14644 : * Attach each generated command to the proper place in the work queue.
14645 : * Note this could result in creation of entirely new work-queue entries.
14646 : *
14647 : * Also note that we have to tweak the command subtypes, because it turns
14648 : * out that re-creation of indexes and constraints has to act a bit
14649 : * differently from initial creation.
14650 : */
14651 1324 : foreach(list_item, querytree_list)
14652 : {
14653 662 : Node *stm = (Node *) lfirst(list_item);
14654 : AlteredTableInfo *tab;
14655 :
14656 662 : tab = ATGetQueueEntry(wqueue, rel);
14657 :
14658 662 : if (IsA(stm, IndexStmt))
14659 : {
14660 212 : IndexStmt *stmt = (IndexStmt *) stm;
14661 : AlterTableCmd *newcmd;
14662 :
14663 212 : if (!rewrite)
14664 54 : TryReuseIndex(oldId, stmt);
14665 212 : stmt->reset_default_tblspc = true;
14666 : /* keep the index's comment */
14667 212 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14668 :
14669 212 : newcmd = makeNode(AlterTableCmd);
14670 212 : newcmd->subtype = AT_ReAddIndex;
14671 212 : newcmd->def = (Node *) stmt;
14672 212 : tab->subcmds[AT_PASS_OLD_INDEX] =
14673 212 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14674 : }
14675 450 : else if (IsA(stm, AlterTableStmt))
14676 : {
14677 422 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
14678 : ListCell *lcmd;
14679 :
14680 1000 : foreach(lcmd, stmt->cmds)
14681 : {
14682 578 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14683 :
14684 578 : if (cmd->subtype == AT_AddIndex)
14685 : {
14686 : IndexStmt *indstmt;
14687 : Oid indoid;
14688 :
14689 222 : indstmt = castNode(IndexStmt, cmd->def);
14690 222 : indoid = get_constraint_index(oldId);
14691 :
14692 222 : if (!rewrite)
14693 48 : TryReuseIndex(indoid, indstmt);
14694 : /* keep any comment on the index */
14695 222 : indstmt->idxcomment = GetComment(indoid,
14696 : RelationRelationId, 0);
14697 222 : indstmt->reset_default_tblspc = true;
14698 :
14699 222 : cmd->subtype = AT_ReAddIndex;
14700 222 : tab->subcmds[AT_PASS_OLD_INDEX] =
14701 222 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14702 :
14703 : /* recreate any comment on the constraint */
14704 222 : RebuildConstraintComment(tab,
14705 : AT_PASS_OLD_INDEX,
14706 : oldId,
14707 : rel,
14708 : NIL,
14709 222 : indstmt->idxname);
14710 : }
14711 356 : else if (cmd->subtype == AT_AddConstraint)
14712 : {
14713 200 : Constraint *con = castNode(Constraint, cmd->def);
14714 :
14715 200 : con->old_pktable_oid = refRelId;
14716 : /* rewriting neither side of a FK */
14717 200 : if (con->contype == CONSTR_FOREIGN &&
14718 72 : !rewrite && tab->rewrite == 0)
14719 6 : TryReuseForeignKey(oldId, con);
14720 200 : con->reset_default_tblspc = true;
14721 200 : cmd->subtype = AT_ReAddConstraint;
14722 200 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14723 200 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14724 :
14725 : /* recreate any comment on the constraint */
14726 200 : RebuildConstraintComment(tab,
14727 : AT_PASS_OLD_CONSTR,
14728 : oldId,
14729 : rel,
14730 : NIL,
14731 200 : con->conname);
14732 : }
14733 156 : else if (cmd->subtype == AT_SetAttNotNull)
14734 : {
14735 : /*
14736 : * We see this subtype when a primary key is created
14737 : * internally, for example when it is replaced with a new
14738 : * constraint (say because one of the columns changes
14739 : * type); in this case we need to reinstate attnotnull,
14740 : * because it was removed because of the drop of the old
14741 : * PK. Schedule this subcommand to an upcoming AT pass.
14742 : */
14743 156 : cmd->subtype = AT_SetAttNotNull;
14744 156 : tab->subcmds[AT_PASS_OLD_COL_ATTRS] =
14745 156 : lappend(tab->subcmds[AT_PASS_OLD_COL_ATTRS], cmd);
14746 : }
14747 : else
14748 0 : elog(ERROR, "unexpected statement subtype: %d",
14749 : (int) cmd->subtype);
14750 : }
14751 : }
14752 28 : else if (IsA(stm, AlterDomainStmt))
14753 : {
14754 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14755 :
14756 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14757 : {
14758 14 : Constraint *con = castNode(Constraint, stmt->def);
14759 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
14760 :
14761 14 : cmd->subtype = AT_ReAddDomainConstraint;
14762 14 : cmd->def = (Node *) stmt;
14763 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
14764 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14765 :
14766 : /* recreate any comment on the constraint */
14767 14 : RebuildConstraintComment(tab,
14768 : AT_PASS_OLD_CONSTR,
14769 : oldId,
14770 : NULL,
14771 : stmt->typeName,
14772 14 : con->conname);
14773 : }
14774 : else
14775 0 : elog(ERROR, "unexpected statement subtype: %d",
14776 : (int) stmt->subtype);
14777 : }
14778 14 : else if (IsA(stm, CreateStatsStmt))
14779 : {
14780 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14781 : AlterTableCmd *newcmd;
14782 :
14783 : /* keep the statistics object's comment */
14784 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14785 :
14786 14 : newcmd = makeNode(AlterTableCmd);
14787 14 : newcmd->subtype = AT_ReAddStatistics;
14788 14 : newcmd->def = (Node *) stmt;
14789 14 : tab->subcmds[AT_PASS_MISC] =
14790 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14791 : }
14792 : else
14793 0 : elog(ERROR, "unexpected statement type: %d",
14794 : (int) nodeTag(stm));
14795 : }
14796 :
14797 662 : relation_close(rel, NoLock);
14798 662 : }
14799 :
14800 : /*
14801 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14802 : * for a table or domain constraint that is being rebuilt.
14803 : *
14804 : * objid is the OID of the constraint.
14805 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14806 : * as a string list) for a domain constraint.
14807 : * (We could dig that info, as well as the conname, out of the pg_constraint
14808 : * entry; but callers already have them so might as well pass them.)
14809 : */
14810 : static void
14811 436 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14812 : Relation rel, List *domname,
14813 : const char *conname)
14814 : {
14815 : CommentStmt *cmd;
14816 : char *comment_str;
14817 : AlterTableCmd *newcmd;
14818 :
14819 : /* Look for comment for object wanted, and leave if none */
14820 436 : comment_str = GetComment(objid, ConstraintRelationId, 0);
14821 436 : if (comment_str == NULL)
14822 370 : return;
14823 :
14824 : /* Build CommentStmt node, copying all input data for safety */
14825 66 : cmd = makeNode(CommentStmt);
14826 66 : if (rel)
14827 : {
14828 54 : cmd->objtype = OBJECT_TABCONSTRAINT;
14829 54 : cmd->object = (Node *)
14830 54 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14831 : makeString(pstrdup(RelationGetRelationName(rel))),
14832 : makeString(pstrdup(conname)));
14833 : }
14834 : else
14835 : {
14836 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
14837 12 : cmd->object = (Node *)
14838 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
14839 : makeString(pstrdup(conname)));
14840 : }
14841 66 : cmd->comment = comment_str;
14842 :
14843 : /* Append it to list of commands */
14844 66 : newcmd = makeNode(AlterTableCmd);
14845 66 : newcmd->subtype = AT_ReAddComment;
14846 66 : newcmd->def = (Node *) cmd;
14847 66 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14848 : }
14849 :
14850 : /*
14851 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14852 : * for the real analysis, then mutates the IndexStmt based on that verdict.
14853 : */
14854 : static void
14855 102 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
14856 : {
14857 102 : if (CheckIndexCompatible(oldId,
14858 102 : stmt->accessMethod,
14859 102 : stmt->indexParams,
14860 102 : stmt->excludeOpNames,
14861 102 : stmt->iswithoutoverlaps))
14862 : {
14863 102 : Relation irel = index_open(oldId, NoLock);
14864 :
14865 : /* If it's a partitioned index, there is no storage to share. */
14866 102 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14867 : {
14868 72 : stmt->oldNumber = irel->rd_locator.relNumber;
14869 72 : stmt->oldCreateSubid = irel->rd_createSubid;
14870 72 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14871 : }
14872 102 : index_close(irel, NoLock);
14873 : }
14874 102 : }
14875 :
14876 : /*
14877 : * Subroutine for ATPostAlterTypeParse().
14878 : *
14879 : * Stash the old P-F equality operator into the Constraint node, for possible
14880 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14881 : * this constraint can be skipped.
14882 : */
14883 : static void
14884 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
14885 : {
14886 : HeapTuple tup;
14887 : Datum adatum;
14888 : ArrayType *arr;
14889 : Oid *rawarr;
14890 : int numkeys;
14891 : int i;
14892 :
14893 : Assert(con->contype == CONSTR_FOREIGN);
14894 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14895 :
14896 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14897 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
14898 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
14899 :
14900 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14901 : Anum_pg_constraint_conpfeqop);
14902 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14903 6 : numkeys = ARR_DIMS(arr)[0];
14904 : /* test follows the one in ri_FetchConstraintInfo() */
14905 6 : if (ARR_NDIM(arr) != 1 ||
14906 6 : ARR_HASNULL(arr) ||
14907 6 : ARR_ELEMTYPE(arr) != OIDOID)
14908 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
14909 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
14910 :
14911 : /* stash a List of the operator Oids in our Constraint node */
14912 12 : for (i = 0; i < numkeys; i++)
14913 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14914 :
14915 6 : ReleaseSysCache(tup);
14916 6 : }
14917 :
14918 : /*
14919 : * ALTER COLUMN .. OPTIONS ( ... )
14920 : *
14921 : * Returns the address of the modified column
14922 : */
14923 : static ObjectAddress
14924 164 : ATExecAlterColumnGenericOptions(Relation rel,
14925 : const char *colName,
14926 : List *options,
14927 : LOCKMODE lockmode)
14928 : {
14929 : Relation ftrel;
14930 : Relation attrel;
14931 : ForeignServer *server;
14932 : ForeignDataWrapper *fdw;
14933 : HeapTuple tuple;
14934 : HeapTuple newtuple;
14935 : bool isnull;
14936 : Datum repl_val[Natts_pg_attribute];
14937 : bool repl_null[Natts_pg_attribute];
14938 : bool repl_repl[Natts_pg_attribute];
14939 : Datum datum;
14940 : Form_pg_foreign_table fttableform;
14941 : Form_pg_attribute atttableform;
14942 : AttrNumber attnum;
14943 : ObjectAddress address;
14944 :
14945 164 : if (options == NIL)
14946 0 : return InvalidObjectAddress;
14947 :
14948 : /* First, determine FDW validator associated to the foreign table. */
14949 164 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14950 164 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14951 164 : if (!HeapTupleIsValid(tuple))
14952 0 : ereport(ERROR,
14953 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14954 : errmsg("foreign table \"%s\" does not exist",
14955 : RelationGetRelationName(rel))));
14956 164 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14957 164 : server = GetForeignServer(fttableform->ftserver);
14958 164 : fdw = GetForeignDataWrapper(server->fdwid);
14959 :
14960 164 : table_close(ftrel, AccessShareLock);
14961 164 : ReleaseSysCache(tuple);
14962 :
14963 164 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
14964 164 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14965 164 : if (!HeapTupleIsValid(tuple))
14966 0 : ereport(ERROR,
14967 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14968 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14969 : colName, RelationGetRelationName(rel))));
14970 :
14971 : /* Prevent them from altering a system attribute */
14972 164 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14973 164 : attnum = atttableform->attnum;
14974 164 : if (attnum <= 0)
14975 6 : ereport(ERROR,
14976 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14977 : errmsg("cannot alter system column \"%s\"", colName)));
14978 :
14979 :
14980 : /* Initialize buffers for new tuple values */
14981 158 : memset(repl_val, 0, sizeof(repl_val));
14982 158 : memset(repl_null, false, sizeof(repl_null));
14983 158 : memset(repl_repl, false, sizeof(repl_repl));
14984 :
14985 : /* Extract the current options */
14986 158 : datum = SysCacheGetAttr(ATTNAME,
14987 : tuple,
14988 : Anum_pg_attribute_attfdwoptions,
14989 : &isnull);
14990 158 : if (isnull)
14991 148 : datum = PointerGetDatum(NULL);
14992 :
14993 : /* Transform the options */
14994 158 : datum = transformGenericOptions(AttributeRelationId,
14995 : datum,
14996 : options,
14997 : fdw->fdwvalidator);
14998 :
14999 158 : if (PointerIsValid(DatumGetPointer(datum)))
15000 158 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15001 : else
15002 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15003 :
15004 158 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15005 :
15006 : /* Everything looks good - update the tuple */
15007 :
15008 158 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15009 : repl_val, repl_null, repl_repl);
15010 :
15011 158 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15012 :
15013 158 : InvokeObjectPostAlterHook(RelationRelationId,
15014 : RelationGetRelid(rel),
15015 : atttableform->attnum);
15016 158 : ObjectAddressSubSet(address, RelationRelationId,
15017 : RelationGetRelid(rel), attnum);
15018 :
15019 158 : ReleaseSysCache(tuple);
15020 :
15021 158 : table_close(attrel, RowExclusiveLock);
15022 :
15023 158 : heap_freetuple(newtuple);
15024 :
15025 158 : return address;
15026 : }
15027 :
15028 : /*
15029 : * ALTER TABLE OWNER
15030 : *
15031 : * recursing is true if we are recursing from a table to its indexes,
15032 : * sequences, or toast table. We don't allow the ownership of those things to
15033 : * be changed separately from the parent table. Also, we can skip permission
15034 : * checks (this is necessary not just an optimization, else we'd fail to
15035 : * handle toast tables properly).
15036 : *
15037 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15038 : * free-standing composite type.
15039 : */
15040 : void
15041 1988 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
15042 : {
15043 : Relation target_rel;
15044 : Relation class_rel;
15045 : HeapTuple tuple;
15046 : Form_pg_class tuple_class;
15047 :
15048 : /*
15049 : * Get exclusive lock till end of transaction on the target table. Use
15050 : * relation_open so that we can work on indexes and sequences.
15051 : */
15052 1988 : target_rel = relation_open(relationOid, lockmode);
15053 :
15054 : /* Get its pg_class tuple, too */
15055 1988 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
15056 :
15057 1988 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
15058 1988 : if (!HeapTupleIsValid(tuple))
15059 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
15060 1988 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15061 :
15062 : /* Can we change the ownership of this tuple? */
15063 1988 : switch (tuple_class->relkind)
15064 : {
15065 1736 : case RELKIND_RELATION:
15066 : case RELKIND_VIEW:
15067 : case RELKIND_MATVIEW:
15068 : case RELKIND_FOREIGN_TABLE:
15069 : case RELKIND_PARTITIONED_TABLE:
15070 : /* ok to change owner */
15071 1736 : break;
15072 96 : case RELKIND_INDEX:
15073 96 : if (!recursing)
15074 : {
15075 : /*
15076 : * Because ALTER INDEX OWNER used to be allowed, and in fact
15077 : * is generated by old versions of pg_dump, we give a warning
15078 : * and do nothing rather than erroring out. Also, to avoid
15079 : * unnecessary chatter while restoring those old dumps, say
15080 : * nothing at all if the command would be a no-op anyway.
15081 : */
15082 0 : if (tuple_class->relowner != newOwnerId)
15083 0 : ereport(WARNING,
15084 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15085 : errmsg("cannot change owner of index \"%s\"",
15086 : NameStr(tuple_class->relname)),
15087 : errhint("Change the ownership of the index's table instead.")));
15088 : /* quick hack to exit via the no-op path */
15089 0 : newOwnerId = tuple_class->relowner;
15090 : }
15091 96 : break;
15092 20 : case RELKIND_PARTITIONED_INDEX:
15093 20 : if (recursing)
15094 20 : break;
15095 0 : ereport(ERROR,
15096 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15097 : errmsg("cannot change owner of index \"%s\"",
15098 : NameStr(tuple_class->relname)),
15099 : errhint("Change the ownership of the index's table instead.")));
15100 : break;
15101 88 : case RELKIND_SEQUENCE:
15102 88 : if (!recursing &&
15103 52 : tuple_class->relowner != newOwnerId)
15104 : {
15105 : /* if it's an owned sequence, disallow changing it by itself */
15106 : Oid tableId;
15107 : int32 colId;
15108 :
15109 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15110 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15111 0 : ereport(ERROR,
15112 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15113 : errmsg("cannot change owner of sequence \"%s\"",
15114 : NameStr(tuple_class->relname)),
15115 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
15116 : NameStr(tuple_class->relname),
15117 : get_rel_name(tableId))));
15118 : }
15119 88 : break;
15120 6 : case RELKIND_COMPOSITE_TYPE:
15121 6 : if (recursing)
15122 6 : break;
15123 0 : ereport(ERROR,
15124 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15125 : errmsg("\"%s\" is a composite type",
15126 : NameStr(tuple_class->relname)),
15127 : /* translator: %s is an SQL ALTER command */
15128 : errhint("Use %s instead.",
15129 : "ALTER TYPE")));
15130 : break;
15131 42 : case RELKIND_TOASTVALUE:
15132 42 : if (recursing)
15133 42 : break;
15134 : /* FALL THRU */
15135 : default:
15136 0 : ereport(ERROR,
15137 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15138 : errmsg("cannot change owner of relation \"%s\"",
15139 : NameStr(tuple_class->relname)),
15140 : errdetail_relkind_not_supported(tuple_class->relkind)));
15141 : }
15142 :
15143 : /*
15144 : * If the new owner is the same as the existing owner, consider the
15145 : * command to have succeeded. This is for dump restoration purposes.
15146 : */
15147 1988 : if (tuple_class->relowner != newOwnerId)
15148 : {
15149 : Datum repl_val[Natts_pg_class];
15150 : bool repl_null[Natts_pg_class];
15151 : bool repl_repl[Natts_pg_class];
15152 : Acl *newAcl;
15153 : Datum aclDatum;
15154 : bool isNull;
15155 : HeapTuple newtuple;
15156 :
15157 : /* skip permission checks when recursing to index or toast table */
15158 480 : if (!recursing)
15159 : {
15160 : /* Superusers can always do it */
15161 280 : if (!superuser())
15162 : {
15163 42 : Oid namespaceOid = tuple_class->relnamespace;
15164 : AclResult aclresult;
15165 :
15166 : /* Otherwise, must be owner of the existing object */
15167 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15168 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15169 0 : RelationGetRelationName(target_rel));
15170 :
15171 : /* Must be able to become new owner */
15172 42 : check_can_set_role(GetUserId(), newOwnerId);
15173 :
15174 : /* New owner must have CREATE privilege on namespace */
15175 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15176 : ACL_CREATE);
15177 30 : if (aclresult != ACLCHECK_OK)
15178 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
15179 0 : get_namespace_name(namespaceOid));
15180 : }
15181 : }
15182 :
15183 468 : memset(repl_null, false, sizeof(repl_null));
15184 468 : memset(repl_repl, false, sizeof(repl_repl));
15185 :
15186 468 : repl_repl[Anum_pg_class_relowner - 1] = true;
15187 468 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15188 :
15189 : /*
15190 : * Determine the modified ACL for the new owner. This is only
15191 : * necessary when the ACL is non-null.
15192 : */
15193 468 : aclDatum = SysCacheGetAttr(RELOID, tuple,
15194 : Anum_pg_class_relacl,
15195 : &isNull);
15196 468 : if (!isNull)
15197 : {
15198 32 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15199 : tuple_class->relowner, newOwnerId);
15200 32 : repl_repl[Anum_pg_class_relacl - 1] = true;
15201 32 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15202 : }
15203 :
15204 468 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15205 :
15206 468 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15207 :
15208 468 : heap_freetuple(newtuple);
15209 :
15210 : /*
15211 : * We must similarly update any per-column ACLs to reflect the new
15212 : * owner; for neatness reasons that's split out as a subroutine.
15213 : */
15214 468 : change_owner_fix_column_acls(relationOid,
15215 : tuple_class->relowner,
15216 : newOwnerId);
15217 :
15218 : /*
15219 : * Update owner dependency reference, if any. A composite type has
15220 : * none, because it's tracked for the pg_type entry instead of here;
15221 : * indexes and TOAST tables don't have their own entries either.
15222 : */
15223 468 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15224 462 : tuple_class->relkind != RELKIND_INDEX &&
15225 366 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15226 346 : tuple_class->relkind != RELKIND_TOASTVALUE)
15227 304 : changeDependencyOnOwner(RelationRelationId, relationOid,
15228 : newOwnerId);
15229 :
15230 : /*
15231 : * Also change the ownership of the table's row type, if it has one
15232 : */
15233 468 : if (OidIsValid(tuple_class->reltype))
15234 286 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15235 :
15236 : /*
15237 : * If we are operating on a table or materialized view, also change
15238 : * the ownership of any indexes and sequences that belong to the
15239 : * relation, as well as its toast table (if it has one).
15240 : */
15241 468 : if (tuple_class->relkind == RELKIND_RELATION ||
15242 250 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15243 212 : tuple_class->relkind == RELKIND_MATVIEW ||
15244 212 : tuple_class->relkind == RELKIND_TOASTVALUE)
15245 : {
15246 : List *index_oid_list;
15247 : ListCell *i;
15248 :
15249 : /* Find all the indexes belonging to this relation */
15250 298 : index_oid_list = RelationGetIndexList(target_rel);
15251 :
15252 : /* For each index, recursively change its ownership */
15253 414 : foreach(i, index_oid_list)
15254 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15255 :
15256 298 : list_free(index_oid_list);
15257 : }
15258 :
15259 : /* If it has a toast table, recurse to change its ownership */
15260 468 : if (tuple_class->reltoastrelid != InvalidOid)
15261 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15262 : true, lockmode);
15263 :
15264 : /* If it has dependent sequences, recurse to change them too */
15265 468 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15266 : }
15267 :
15268 1976 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15269 :
15270 1976 : ReleaseSysCache(tuple);
15271 1976 : table_close(class_rel, RowExclusiveLock);
15272 1976 : relation_close(target_rel, NoLock);
15273 1976 : }
15274 :
15275 : /*
15276 : * change_owner_fix_column_acls
15277 : *
15278 : * Helper function for ATExecChangeOwner. Scan the columns of the table
15279 : * and fix any non-null column ACLs to reflect the new owner.
15280 : */
15281 : static void
15282 468 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15283 : {
15284 : Relation attRelation;
15285 : SysScanDesc scan;
15286 : ScanKeyData key[1];
15287 : HeapTuple attributeTuple;
15288 :
15289 468 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15290 468 : ScanKeyInit(&key[0],
15291 : Anum_pg_attribute_attrelid,
15292 : BTEqualStrategyNumber, F_OIDEQ,
15293 : ObjectIdGetDatum(relationOid));
15294 468 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15295 : true, NULL, 1, key);
15296 3216 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15297 : {
15298 2748 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15299 : Datum repl_val[Natts_pg_attribute];
15300 : bool repl_null[Natts_pg_attribute];
15301 : bool repl_repl[Natts_pg_attribute];
15302 : Acl *newAcl;
15303 : Datum aclDatum;
15304 : bool isNull;
15305 : HeapTuple newtuple;
15306 :
15307 : /* Ignore dropped columns */
15308 2748 : if (att->attisdropped)
15309 2748 : continue;
15310 :
15311 2748 : aclDatum = heap_getattr(attributeTuple,
15312 : Anum_pg_attribute_attacl,
15313 : RelationGetDescr(attRelation),
15314 : &isNull);
15315 : /* Null ACLs do not require changes */
15316 2748 : if (isNull)
15317 2748 : continue;
15318 :
15319 0 : memset(repl_null, false, sizeof(repl_null));
15320 0 : memset(repl_repl, false, sizeof(repl_repl));
15321 :
15322 0 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
15323 : oldOwnerId, newOwnerId);
15324 0 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
15325 0 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15326 :
15327 0 : newtuple = heap_modify_tuple(attributeTuple,
15328 : RelationGetDescr(attRelation),
15329 : repl_val, repl_null, repl_repl);
15330 :
15331 0 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15332 :
15333 0 : heap_freetuple(newtuple);
15334 : }
15335 468 : systable_endscan(scan);
15336 468 : table_close(attRelation, RowExclusiveLock);
15337 468 : }
15338 :
15339 : /*
15340 : * change_owner_recurse_to_sequences
15341 : *
15342 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
15343 : * for sequences that are dependent on serial columns, and changes their
15344 : * ownership.
15345 : */
15346 : static void
15347 468 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15348 : {
15349 : Relation depRel;
15350 : SysScanDesc scan;
15351 : ScanKeyData key[2];
15352 : HeapTuple tup;
15353 :
15354 : /*
15355 : * SERIAL sequences are those having an auto dependency on one of the
15356 : * table's columns (we don't care *which* column, exactly).
15357 : */
15358 468 : depRel = table_open(DependRelationId, AccessShareLock);
15359 :
15360 468 : ScanKeyInit(&key[0],
15361 : Anum_pg_depend_refclassid,
15362 : BTEqualStrategyNumber, F_OIDEQ,
15363 : ObjectIdGetDatum(RelationRelationId));
15364 468 : ScanKeyInit(&key[1],
15365 : Anum_pg_depend_refobjid,
15366 : BTEqualStrategyNumber, F_OIDEQ,
15367 : ObjectIdGetDatum(relationOid));
15368 : /* we leave refobjsubid unspecified */
15369 :
15370 468 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15371 : NULL, 2, key);
15372 :
15373 1318 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
15374 : {
15375 850 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15376 : Relation seqRel;
15377 :
15378 : /* skip dependencies other than auto dependencies on columns */
15379 850 : if (depForm->refobjsubid == 0 ||
15380 322 : depForm->classid != RelationRelationId ||
15381 140 : depForm->objsubid != 0 ||
15382 140 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15383 710 : continue;
15384 :
15385 : /* Use relation_open just in case it's an index */
15386 140 : seqRel = relation_open(depForm->objid, lockmode);
15387 :
15388 : /* skip non-sequence relations */
15389 140 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15390 : {
15391 : /* No need to keep the lock */
15392 116 : relation_close(seqRel, lockmode);
15393 116 : continue;
15394 : }
15395 :
15396 : /* We don't need to close the sequence while we alter it. */
15397 24 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15398 :
15399 : /* Now we can close it. Keep the lock till end of transaction. */
15400 24 : relation_close(seqRel, NoLock);
15401 : }
15402 :
15403 468 : systable_endscan(scan);
15404 :
15405 468 : relation_close(depRel, AccessShareLock);
15406 468 : }
15407 :
15408 : /*
15409 : * ALTER TABLE CLUSTER ON
15410 : *
15411 : * The only thing we have to do is to change the indisclustered bits.
15412 : *
15413 : * Return the address of the new clustering index.
15414 : */
15415 : static ObjectAddress
15416 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15417 : {
15418 : Oid indexOid;
15419 : ObjectAddress address;
15420 :
15421 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15422 :
15423 64 : if (!OidIsValid(indexOid))
15424 0 : ereport(ERROR,
15425 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15426 : errmsg("index \"%s\" for table \"%s\" does not exist",
15427 : indexName, RelationGetRelationName(rel))));
15428 :
15429 : /* Check index is valid to cluster on */
15430 64 : check_index_is_clusterable(rel, indexOid, lockmode);
15431 :
15432 : /* And do the work */
15433 64 : mark_index_clustered(rel, indexOid, false);
15434 :
15435 58 : ObjectAddressSet(address,
15436 : RelationRelationId, indexOid);
15437 :
15438 58 : return address;
15439 : }
15440 :
15441 : /*
15442 : * ALTER TABLE SET WITHOUT CLUSTER
15443 : *
15444 : * We have to find any indexes on the table that have indisclustered bit
15445 : * set and turn it off.
15446 : */
15447 : static void
15448 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15449 : {
15450 18 : mark_index_clustered(rel, InvalidOid, false);
15451 12 : }
15452 :
15453 : /*
15454 : * Preparation phase for SET ACCESS METHOD
15455 : *
15456 : * Check that the access method exists and determine whether a change is
15457 : * actually needed.
15458 : */
15459 : static void
15460 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15461 : {
15462 : Oid amoid;
15463 :
15464 : /*
15465 : * Look up the access method name and check that it differs from the
15466 : * table's current AM. If DEFAULT was specified for a partitioned table
15467 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15468 : */
15469 110 : if (amname != NULL)
15470 74 : amoid = get_table_am_oid(amname, false);
15471 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15472 18 : amoid = InvalidOid;
15473 : else
15474 18 : amoid = get_table_am_oid(default_table_access_method, false);
15475 :
15476 : /* if it's a match, phase 3 doesn't need to do anything */
15477 110 : if (rel->rd_rel->relam == amoid)
15478 12 : return;
15479 :
15480 : /* Save info for Phase 3 to do the real work */
15481 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15482 98 : tab->newAccessMethod = amoid;
15483 98 : tab->chgAccessMethod = true;
15484 : }
15485 :
15486 : /*
15487 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15488 : * storage that have an interest in preserving AM.
15489 : *
15490 : * Since these have no storage, setting the access method is a catalog only
15491 : * operation.
15492 : */
15493 : static void
15494 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15495 : {
15496 : Relation pg_class;
15497 : Oid oldAccessMethodId;
15498 : HeapTuple tuple;
15499 : Form_pg_class rd_rel;
15500 44 : Oid reloid = RelationGetRelid(rel);
15501 :
15502 : /*
15503 : * Shouldn't be called on relations having storage; these are processed in
15504 : * phase 3.
15505 : */
15506 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15507 :
15508 : /* Get a modifiable copy of the relation's pg_class row. */
15509 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
15510 :
15511 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15512 44 : if (!HeapTupleIsValid(tuple))
15513 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
15514 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15515 :
15516 : /* Update the pg_class row. */
15517 44 : oldAccessMethodId = rd_rel->relam;
15518 44 : rd_rel->relam = newAccessMethodId;
15519 :
15520 : /* Leave if no update required */
15521 44 : if (rd_rel->relam == oldAccessMethodId)
15522 : {
15523 0 : heap_freetuple(tuple);
15524 0 : table_close(pg_class, RowExclusiveLock);
15525 0 : return;
15526 : }
15527 :
15528 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15529 :
15530 : /*
15531 : * Update the dependency on the new access method. No dependency is added
15532 : * if the new access method is InvalidOid (default case). Be very careful
15533 : * that this has to compare the previous value stored in pg_class with the
15534 : * new one.
15535 : */
15536 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15537 20 : {
15538 : ObjectAddress relobj,
15539 : referenced;
15540 :
15541 : /*
15542 : * New access method is defined and there was no dependency
15543 : * previously, so record a new one.
15544 : */
15545 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
15546 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15547 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15548 : }
15549 24 : else if (OidIsValid(oldAccessMethodId) &&
15550 24 : !OidIsValid(rd_rel->relam))
15551 : {
15552 : /*
15553 : * There was an access method defined, and no new one, so just remove
15554 : * the existing dependency.
15555 : */
15556 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
15557 : AccessMethodRelationId,
15558 : DEPENDENCY_NORMAL);
15559 : }
15560 : else
15561 : {
15562 : Assert(OidIsValid(oldAccessMethodId) &&
15563 : OidIsValid(rd_rel->relam));
15564 :
15565 : /* Both are valid, so update the dependency */
15566 12 : changeDependencyFor(RelationRelationId, reloid,
15567 : AccessMethodRelationId,
15568 : oldAccessMethodId, rd_rel->relam);
15569 : }
15570 :
15571 : /* make the relam and dependency changes visible */
15572 44 : CommandCounterIncrement();
15573 :
15574 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15575 :
15576 44 : heap_freetuple(tuple);
15577 44 : table_close(pg_class, RowExclusiveLock);
15578 : }
15579 :
15580 : /*
15581 : * ALTER TABLE SET TABLESPACE
15582 : */
15583 : static void
15584 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15585 : {
15586 : Oid tablespaceId;
15587 :
15588 : /* Check that the tablespace exists */
15589 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
15590 :
15591 : /* Check permissions except when moving to database's default */
15592 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15593 : {
15594 : AclResult aclresult;
15595 :
15596 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15597 66 : if (aclresult != ACLCHECK_OK)
15598 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15599 : }
15600 :
15601 : /* Save info for Phase 3 to do the real work */
15602 158 : if (OidIsValid(tab->newTableSpace))
15603 0 : ereport(ERROR,
15604 : (errcode(ERRCODE_SYNTAX_ERROR),
15605 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
15606 :
15607 158 : tab->newTableSpace = tablespaceId;
15608 158 : }
15609 :
15610 : /*
15611 : * Set, reset, or replace reloptions.
15612 : */
15613 : static void
15614 934 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15615 : LOCKMODE lockmode)
15616 : {
15617 : Oid relid;
15618 : Relation pgclass;
15619 : HeapTuple tuple;
15620 : HeapTuple newtuple;
15621 : Datum datum;
15622 : bool isnull;
15623 : Datum newOptions;
15624 : Datum repl_val[Natts_pg_class];
15625 : bool repl_null[Natts_pg_class];
15626 : bool repl_repl[Natts_pg_class];
15627 : static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
15628 :
15629 934 : if (defList == NIL && operation != AT_ReplaceRelOptions)
15630 0 : return; /* nothing to do */
15631 :
15632 934 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
15633 :
15634 : /* Fetch heap tuple */
15635 934 : relid = RelationGetRelid(rel);
15636 934 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
15637 934 : if (!HeapTupleIsValid(tuple))
15638 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
15639 :
15640 934 : if (operation == AT_ReplaceRelOptions)
15641 : {
15642 : /*
15643 : * If we're supposed to replace the reloptions list, we just pretend
15644 : * there were none before.
15645 : */
15646 194 : datum = (Datum) 0;
15647 194 : isnull = true;
15648 : }
15649 : else
15650 : {
15651 : /* Get the old reloptions */
15652 740 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15653 : &isnull);
15654 : }
15655 :
15656 : /* Generate new proposed reloptions (text array) */
15657 934 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15658 : defList, NULL, validnsps, false,
15659 : operation == AT_ResetRelOptions);
15660 :
15661 : /* Validate */
15662 928 : switch (rel->rd_rel->relkind)
15663 : {
15664 512 : case RELKIND_RELATION:
15665 : case RELKIND_TOASTVALUE:
15666 : case RELKIND_MATVIEW:
15667 512 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15668 512 : break;
15669 6 : case RELKIND_PARTITIONED_TABLE:
15670 6 : (void) partitioned_table_reloptions(newOptions, true);
15671 0 : break;
15672 296 : case RELKIND_VIEW:
15673 296 : (void) view_reloptions(newOptions, true);
15674 278 : break;
15675 114 : case RELKIND_INDEX:
15676 : case RELKIND_PARTITIONED_INDEX:
15677 114 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15678 92 : break;
15679 0 : default:
15680 0 : ereport(ERROR,
15681 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15682 : errmsg("cannot set options for relation \"%s\"",
15683 : RelationGetRelationName(rel)),
15684 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15685 : break;
15686 : }
15687 :
15688 : /* Special-case validation of view options */
15689 882 : if (rel->rd_rel->relkind == RELKIND_VIEW)
15690 : {
15691 278 : Query *view_query = get_view_query(rel);
15692 278 : List *view_options = untransformRelOptions(newOptions);
15693 : ListCell *cell;
15694 278 : bool check_option = false;
15695 :
15696 380 : foreach(cell, view_options)
15697 : {
15698 102 : DefElem *defel = (DefElem *) lfirst(cell);
15699 :
15700 102 : if (strcmp(defel->defname, "check_option") == 0)
15701 24 : check_option = true;
15702 : }
15703 :
15704 : /*
15705 : * If the check option is specified, look to see if the view is
15706 : * actually auto-updatable or not.
15707 : */
15708 278 : if (check_option)
15709 : {
15710 : const char *view_updatable_error =
15711 24 : view_query_is_auto_updatable(view_query, true);
15712 :
15713 24 : if (view_updatable_error)
15714 0 : ereport(ERROR,
15715 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15716 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15717 : errhint("%s", _(view_updatable_error))));
15718 : }
15719 : }
15720 :
15721 : /*
15722 : * All we need do here is update the pg_class row; the new options will be
15723 : * propagated into relcaches during post-commit cache inval.
15724 : */
15725 882 : memset(repl_val, 0, sizeof(repl_val));
15726 882 : memset(repl_null, false, sizeof(repl_null));
15727 882 : memset(repl_repl, false, sizeof(repl_repl));
15728 :
15729 882 : if (newOptions != (Datum) 0)
15730 594 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15731 : else
15732 288 : repl_null[Anum_pg_class_reloptions - 1] = true;
15733 :
15734 882 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15735 :
15736 882 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15737 : repl_val, repl_null, repl_repl);
15738 :
15739 882 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15740 :
15741 882 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15742 :
15743 882 : heap_freetuple(newtuple);
15744 :
15745 882 : ReleaseSysCache(tuple);
15746 :
15747 : /* repeat the whole exercise for the toast table, if there's one */
15748 882 : if (OidIsValid(rel->rd_rel->reltoastrelid))
15749 : {
15750 : Relation toastrel;
15751 256 : Oid toastid = rel->rd_rel->reltoastrelid;
15752 :
15753 256 : toastrel = table_open(toastid, lockmode);
15754 :
15755 : /* Fetch heap tuple */
15756 256 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15757 256 : if (!HeapTupleIsValid(tuple))
15758 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
15759 :
15760 256 : if (operation == AT_ReplaceRelOptions)
15761 : {
15762 : /*
15763 : * If we're supposed to replace the reloptions list, we just
15764 : * pretend there were none before.
15765 : */
15766 0 : datum = (Datum) 0;
15767 0 : isnull = true;
15768 : }
15769 : else
15770 : {
15771 : /* Get the old reloptions */
15772 256 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15773 : &isnull);
15774 : }
15775 :
15776 256 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15777 : defList, "toast", validnsps, false,
15778 : operation == AT_ResetRelOptions);
15779 :
15780 256 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15781 :
15782 256 : memset(repl_val, 0, sizeof(repl_val));
15783 256 : memset(repl_null, false, sizeof(repl_null));
15784 256 : memset(repl_repl, false, sizeof(repl_repl));
15785 :
15786 256 : if (newOptions != (Datum) 0)
15787 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15788 : else
15789 214 : repl_null[Anum_pg_class_reloptions - 1] = true;
15790 :
15791 256 : repl_repl[Anum_pg_class_reloptions - 1] = true;
15792 :
15793 256 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15794 : repl_val, repl_null, repl_repl);
15795 :
15796 256 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15797 :
15798 256 : InvokeObjectPostAlterHookArg(RelationRelationId,
15799 : RelationGetRelid(toastrel), 0,
15800 : InvalidOid, true);
15801 :
15802 256 : heap_freetuple(newtuple);
15803 :
15804 256 : ReleaseSysCache(tuple);
15805 :
15806 256 : table_close(toastrel, NoLock);
15807 : }
15808 :
15809 882 : table_close(pgclass, RowExclusiveLock);
15810 : }
15811 :
15812 : /*
15813 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15814 : * rewriting to be done, so we just want to copy the data as fast as possible.
15815 : */
15816 : static void
15817 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15818 : {
15819 : Relation rel;
15820 : Oid reltoastrelid;
15821 : RelFileNumber newrelfilenumber;
15822 : RelFileLocator newrlocator;
15823 162 : List *reltoastidxids = NIL;
15824 : ListCell *lc;
15825 :
15826 : /*
15827 : * Need lock here in case we are recursing to toast table or index
15828 : */
15829 162 : rel = relation_open(tableOid, lockmode);
15830 :
15831 : /* Check first if relation can be moved to new tablespace */
15832 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15833 : {
15834 2 : InvokeObjectPostAlterHook(RelationRelationId,
15835 : RelationGetRelid(rel), 0);
15836 2 : relation_close(rel, NoLock);
15837 2 : return;
15838 : }
15839 :
15840 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
15841 : /* Fetch the list of indexes on toast relation if necessary */
15842 160 : if (OidIsValid(reltoastrelid))
15843 : {
15844 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
15845 :
15846 20 : reltoastidxids = RelationGetIndexList(toastRel);
15847 20 : relation_close(toastRel, lockmode);
15848 : }
15849 :
15850 : /*
15851 : * Relfilenumbers are not unique in databases across tablespaces, so we
15852 : * need to allocate a new one in the new tablespace.
15853 : */
15854 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15855 160 : rel->rd_rel->relpersistence);
15856 :
15857 : /* Open old and new relation */
15858 160 : newrlocator = rel->rd_locator;
15859 160 : newrlocator.relNumber = newrelfilenumber;
15860 160 : newrlocator.spcOid = newTableSpace;
15861 :
15862 : /* hand off to AM to actually create new rel storage and copy the data */
15863 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
15864 : {
15865 62 : index_copy_data(rel, newrlocator);
15866 : }
15867 : else
15868 : {
15869 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15870 98 : table_relation_copy_data(rel, &newrlocator);
15871 : }
15872 :
15873 : /*
15874 : * Update the pg_class row.
15875 : *
15876 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15877 : * executed on pg_class or its indexes (the above copy wouldn't contain
15878 : * the updated pg_class entry), but that's forbidden with
15879 : * CheckRelationTableSpaceMove().
15880 : */
15881 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15882 :
15883 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15884 :
15885 160 : RelationAssumeNewRelfilelocator(rel);
15886 :
15887 160 : relation_close(rel, NoLock);
15888 :
15889 : /* Make sure the reltablespace change is visible */
15890 160 : CommandCounterIncrement();
15891 :
15892 : /* Move associated toast relation and/or indexes, too */
15893 160 : if (OidIsValid(reltoastrelid))
15894 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15895 180 : foreach(lc, reltoastidxids)
15896 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15897 :
15898 : /* Clean up */
15899 160 : list_free(reltoastidxids);
15900 : }
15901 :
15902 : /*
15903 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15904 : * storage that have an interest in preserving tablespace.
15905 : *
15906 : * Since these have no storage the tablespace can be updated with a simple
15907 : * metadata only operation to update the tablespace.
15908 : */
15909 : static void
15910 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15911 : {
15912 : /*
15913 : * Shouldn't be called on relations having storage; these are processed in
15914 : * phase 3.
15915 : */
15916 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15917 :
15918 : /* check if relation can be moved to its new tablespace */
15919 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15920 : {
15921 0 : InvokeObjectPostAlterHook(RelationRelationId,
15922 : RelationGetRelid(rel),
15923 : 0);
15924 0 : return;
15925 : }
15926 :
15927 : /* Update can be done, so change reltablespace */
15928 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15929 :
15930 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15931 :
15932 : /* Make sure the reltablespace change is visible */
15933 30 : CommandCounterIncrement();
15934 : }
15935 :
15936 : /*
15937 : * Alter Table ALL ... SET TABLESPACE
15938 : *
15939 : * Allows a user to move all objects of some type in a given tablespace in the
15940 : * current database to another tablespace. Objects can be chosen based on the
15941 : * owner of the object also, to allow users to move only their objects.
15942 : * The user must have CREATE rights on the new tablespace, as usual. The main
15943 : * permissions handling is done by the lower-level table move function.
15944 : *
15945 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
15946 : * lock can't be acquired then we ereport(ERROR).
15947 : */
15948 : Oid
15949 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15950 : {
15951 30 : List *relations = NIL;
15952 : ListCell *l;
15953 : ScanKeyData key[1];
15954 : Relation rel;
15955 : TableScanDesc scan;
15956 : HeapTuple tuple;
15957 : Oid orig_tablespaceoid;
15958 : Oid new_tablespaceoid;
15959 30 : List *role_oids = roleSpecsToIds(stmt->roles);
15960 :
15961 : /* Ensure we were not asked to move something we can't */
15962 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15963 12 : stmt->objtype != OBJECT_MATVIEW)
15964 0 : ereport(ERROR,
15965 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15966 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15967 :
15968 : /* Get the orig and new tablespace OIDs */
15969 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15970 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15971 :
15972 : /* Can't move shared relations in to or out of pg_global */
15973 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15974 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15975 : new_tablespaceoid == GLOBALTABLESPACE_OID)
15976 0 : ereport(ERROR,
15977 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15978 : errmsg("cannot move relations in to or out of pg_global tablespace")));
15979 :
15980 : /*
15981 : * Must have CREATE rights on the new tablespace, unless it is the
15982 : * database default tablespace (which all users implicitly have CREATE
15983 : * rights on).
15984 : */
15985 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15986 : {
15987 : AclResult aclresult;
15988 :
15989 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15990 : ACL_CREATE);
15991 0 : if (aclresult != ACLCHECK_OK)
15992 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
15993 0 : get_tablespace_name(new_tablespaceoid));
15994 : }
15995 :
15996 : /*
15997 : * Now that the checks are done, check if we should set either to
15998 : * InvalidOid because it is our database's default tablespace.
15999 : */
16000 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
16001 0 : orig_tablespaceoid = InvalidOid;
16002 :
16003 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
16004 30 : new_tablespaceoid = InvalidOid;
16005 :
16006 : /* no-op */
16007 30 : if (orig_tablespaceoid == new_tablespaceoid)
16008 0 : return new_tablespaceoid;
16009 :
16010 : /*
16011 : * Walk the list of objects in the tablespace and move them. This will
16012 : * only find objects in our database, of course.
16013 : */
16014 30 : ScanKeyInit(&key[0],
16015 : Anum_pg_class_reltablespace,
16016 : BTEqualStrategyNumber, F_OIDEQ,
16017 : ObjectIdGetDatum(orig_tablespaceoid));
16018 :
16019 30 : rel = table_open(RelationRelationId, AccessShareLock);
16020 30 : scan = table_beginscan_catalog(rel, 1, key);
16021 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16022 : {
16023 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16024 102 : Oid relOid = relForm->oid;
16025 :
16026 : /*
16027 : * Do not move objects in pg_catalog as part of this, if an admin
16028 : * really wishes to do so, they can issue the individual ALTER
16029 : * commands directly.
16030 : *
16031 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
16032 : * (TOAST will be moved with the main table).
16033 : */
16034 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
16035 204 : relForm->relisshared ||
16036 204 : isAnyTempNamespace(relForm->relnamespace) ||
16037 102 : IsToastNamespace(relForm->relnamespace))
16038 0 : continue;
16039 :
16040 : /* Only move the object type requested */
16041 102 : if ((stmt->objtype == OBJECT_TABLE &&
16042 60 : relForm->relkind != RELKIND_RELATION &&
16043 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16044 66 : (stmt->objtype == OBJECT_INDEX &&
16045 36 : relForm->relkind != RELKIND_INDEX &&
16046 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16047 60 : (stmt->objtype == OBJECT_MATVIEW &&
16048 6 : relForm->relkind != RELKIND_MATVIEW))
16049 42 : continue;
16050 :
16051 : /* Check if we are only moving objects owned by certain roles */
16052 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16053 0 : continue;
16054 :
16055 : /*
16056 : * Handle permissions-checking here since we are locking the tables
16057 : * and also to avoid doing a bunch of work only to fail part-way. Note
16058 : * that permissions will also be checked by AlterTableInternal().
16059 : *
16060 : * Caller must be considered an owner on the table to move it.
16061 : */
16062 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16063 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
16064 0 : NameStr(relForm->relname));
16065 :
16066 60 : if (stmt->nowait &&
16067 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
16068 0 : ereport(ERROR,
16069 : (errcode(ERRCODE_OBJECT_IN_USE),
16070 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
16071 : get_namespace_name(relForm->relnamespace),
16072 : NameStr(relForm->relname))));
16073 : else
16074 60 : LockRelationOid(relOid, AccessExclusiveLock);
16075 :
16076 : /* Add to our list of objects to move */
16077 60 : relations = lappend_oid(relations, relOid);
16078 : }
16079 :
16080 30 : table_endscan(scan);
16081 30 : table_close(rel, AccessShareLock);
16082 :
16083 30 : if (relations == NIL)
16084 12 : ereport(NOTICE,
16085 : (errcode(ERRCODE_NO_DATA_FOUND),
16086 : errmsg("no matching relations in tablespace \"%s\" found",
16087 : orig_tablespaceoid == InvalidOid ? "(database default)" :
16088 : get_tablespace_name(orig_tablespaceoid))));
16089 :
16090 : /* Everything is locked, loop through and move all of the relations. */
16091 90 : foreach(l, relations)
16092 : {
16093 60 : List *cmds = NIL;
16094 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
16095 :
16096 60 : cmd->subtype = AT_SetTableSpace;
16097 60 : cmd->name = stmt->new_tablespacename;
16098 :
16099 60 : cmds = lappend(cmds, cmd);
16100 :
16101 60 : EventTriggerAlterTableStart((Node *) stmt);
16102 : /* OID is set by AlterTableInternal */
16103 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
16104 60 : EventTriggerAlterTableEnd();
16105 : }
16106 :
16107 30 : return new_tablespaceoid;
16108 : }
16109 :
16110 : static void
16111 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
16112 : {
16113 : SMgrRelation dstrel;
16114 :
16115 : /*
16116 : * Since we copy the file directly without looking at the shared buffers,
16117 : * we'd better first flush out any pages of the source relation that are
16118 : * in shared buffers. We assume no new changes will be made while we are
16119 : * holding exclusive lock on the rel.
16120 : */
16121 62 : FlushRelationBuffers(rel);
16122 :
16123 : /*
16124 : * Create and copy all forks of the relation, and schedule unlinking of
16125 : * old physical files.
16126 : *
16127 : * NOTE: any conflict in relfilenumber value will be caught in
16128 : * RelationCreateStorage().
16129 : */
16130 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16131 :
16132 : /* copy main fork */
16133 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
16134 62 : rel->rd_rel->relpersistence);
16135 :
16136 : /* copy those extra forks that exist */
16137 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16138 186 : forkNum <= MAX_FORKNUM; forkNum++)
16139 : {
16140 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
16141 : {
16142 0 : smgrcreate(dstrel, forkNum, false);
16143 :
16144 : /*
16145 : * WAL log creation if the relation is persistent, or this is the
16146 : * init fork of an unlogged relation.
16147 : */
16148 0 : if (RelationIsPermanent(rel) ||
16149 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16150 : forkNum == INIT_FORKNUM))
16151 0 : log_smgrcreate(&newrlocator, forkNum);
16152 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16153 0 : rel->rd_rel->relpersistence);
16154 : }
16155 : }
16156 :
16157 : /* drop old relation, and close new one */
16158 62 : RelationDropStorage(rel);
16159 62 : smgrclose(dstrel);
16160 62 : }
16161 :
16162 : /*
16163 : * ALTER TABLE ENABLE/DISABLE TRIGGER
16164 : *
16165 : * We just pass this off to trigger.c.
16166 : */
16167 : static void
16168 340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16169 : char fires_when, bool skip_system, bool recurse,
16170 : LOCKMODE lockmode)
16171 : {
16172 340 : EnableDisableTrigger(rel, trigname, InvalidOid,
16173 : fires_when, skip_system, recurse,
16174 : lockmode);
16175 :
16176 340 : InvokeObjectPostAlterHook(RelationRelationId,
16177 : RelationGetRelid(rel), 0);
16178 340 : }
16179 :
16180 : /*
16181 : * ALTER TABLE ENABLE/DISABLE RULE
16182 : *
16183 : * We just pass this off to rewriteDefine.c.
16184 : */
16185 : static void
16186 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
16187 : char fires_when, LOCKMODE lockmode)
16188 : {
16189 46 : EnableDisableRule(rel, rulename, fires_when);
16190 :
16191 46 : InvokeObjectPostAlterHook(RelationRelationId,
16192 : RelationGetRelid(rel), 0);
16193 46 : }
16194 :
16195 : /*
16196 : * ALTER TABLE INHERIT
16197 : *
16198 : * Add a parent to the child's parents. This verifies that all the columns and
16199 : * check constraints of the parent appear in the child and that they have the
16200 : * same data types and expressions.
16201 : */
16202 : static void
16203 342 : ATPrepAddInherit(Relation child_rel)
16204 : {
16205 342 : if (child_rel->rd_rel->reloftype)
16206 6 : ereport(ERROR,
16207 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16208 : errmsg("cannot change inheritance of typed table")));
16209 :
16210 336 : if (child_rel->rd_rel->relispartition)
16211 6 : ereport(ERROR,
16212 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16213 : errmsg("cannot change inheritance of a partition")));
16214 :
16215 330 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16216 6 : ereport(ERROR,
16217 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16218 : errmsg("cannot change inheritance of partitioned table")));
16219 324 : }
16220 :
16221 : /*
16222 : * Return the address of the new parent relation.
16223 : */
16224 : static ObjectAddress
16225 324 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16226 : {
16227 : Relation parent_rel;
16228 : List *children;
16229 : ObjectAddress address;
16230 : const char *trigger_name;
16231 :
16232 : /*
16233 : * A self-exclusive lock is needed here. See the similar case in
16234 : * MergeAttributes() for a full explanation.
16235 : */
16236 324 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16237 :
16238 : /*
16239 : * Must be owner of both parent and child -- child was checked by
16240 : * ATSimplePermissions call in ATPrepCmd
16241 : */
16242 324 : ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
16243 :
16244 : /* Permanent rels cannot inherit from temporary ones */
16245 324 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16246 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16247 0 : ereport(ERROR,
16248 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16249 : errmsg("cannot inherit from temporary relation \"%s\"",
16250 : RelationGetRelationName(parent_rel))));
16251 :
16252 : /* If parent rel is temp, it must belong to this session */
16253 324 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16254 6 : !parent_rel->rd_islocaltemp)
16255 0 : ereport(ERROR,
16256 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16257 : errmsg("cannot inherit from temporary relation of another session")));
16258 :
16259 : /* Ditto for the child */
16260 324 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16261 6 : !child_rel->rd_islocaltemp)
16262 0 : ereport(ERROR,
16263 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16264 : errmsg("cannot inherit to temporary relation of another session")));
16265 :
16266 : /* Prevent partitioned tables from becoming inheritance parents */
16267 324 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16268 6 : ereport(ERROR,
16269 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16270 : errmsg("cannot inherit from partitioned table \"%s\"",
16271 : parent->relname)));
16272 :
16273 : /* Likewise for partitions */
16274 318 : if (parent_rel->rd_rel->relispartition)
16275 6 : ereport(ERROR,
16276 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16277 : errmsg("cannot inherit from a partition")));
16278 :
16279 : /*
16280 : * Prevent circularity by seeing if proposed parent inherits from child.
16281 : * (In particular, this disallows making a rel inherit from itself.)
16282 : *
16283 : * This is not completely bulletproof because of race conditions: in
16284 : * multi-level inheritance trees, someone else could concurrently be
16285 : * making another inheritance link that closes the loop but does not join
16286 : * either of the rels we have locked. Preventing that seems to require
16287 : * exclusive locks on the entire inheritance tree, which is a cure worse
16288 : * than the disease. find_all_inheritors() will cope with circularity
16289 : * anyway, so don't sweat it too much.
16290 : *
16291 : * We use weakest lock we can on child's children, namely AccessShareLock.
16292 : */
16293 312 : children = find_all_inheritors(RelationGetRelid(child_rel),
16294 : AccessShareLock, NULL);
16295 :
16296 312 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
16297 12 : ereport(ERROR,
16298 : (errcode(ERRCODE_DUPLICATE_TABLE),
16299 : errmsg("circular inheritance not allowed"),
16300 : errdetail("\"%s\" is already a child of \"%s\".",
16301 : parent->relname,
16302 : RelationGetRelationName(child_rel))));
16303 :
16304 : /*
16305 : * If child_rel has row-level triggers with transition tables, we
16306 : * currently don't allow it to become an inheritance child. See also
16307 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
16308 : */
16309 300 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16310 300 : if (trigger_name != NULL)
16311 6 : ereport(ERROR,
16312 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16313 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16314 : trigger_name, RelationGetRelationName(child_rel)),
16315 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16316 :
16317 : /* OK to create inheritance */
16318 294 : CreateInheritance(child_rel, parent_rel, false);
16319 :
16320 : /*
16321 : * If parent_rel has a primary key, then child_rel has not-null
16322 : * constraints that make these columns as non nullable. Make those
16323 : * constraints as inherited.
16324 : */
16325 240 : ATInheritAdjustNotNulls(parent_rel, child_rel, 1);
16326 :
16327 234 : ObjectAddressSet(address, RelationRelationId,
16328 : RelationGetRelid(parent_rel));
16329 :
16330 : /* keep our lock on the parent relation until commit */
16331 234 : table_close(parent_rel, NoLock);
16332 :
16333 234 : return address;
16334 : }
16335 :
16336 : /*
16337 : * CreateInheritance
16338 : * Catalog manipulation portion of creating inheritance between a child
16339 : * table and a parent table.
16340 : *
16341 : * Common to ATExecAddInherit() and ATExecAttachPartition().
16342 : */
16343 : static void
16344 2812 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16345 : {
16346 : Relation catalogRelation;
16347 : SysScanDesc scan;
16348 : ScanKeyData key;
16349 : HeapTuple inheritsTuple;
16350 : int32 inhseqno;
16351 :
16352 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16353 2812 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16354 :
16355 : /*
16356 : * Check for duplicates in the list of parents, and determine the highest
16357 : * inhseqno already present; we'll use the next one for the new parent.
16358 : * Also, if proposed child is a partition, it cannot already be
16359 : * inheriting.
16360 : *
16361 : * Note: we do not reject the case where the child already inherits from
16362 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16363 : */
16364 2812 : ScanKeyInit(&key,
16365 : Anum_pg_inherits_inhrelid,
16366 : BTEqualStrategyNumber, F_OIDEQ,
16367 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16368 2812 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16369 : true, NULL, 1, &key);
16370 :
16371 : /* inhseqno sequences start at 1 */
16372 2812 : inhseqno = 0;
16373 2860 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16374 : {
16375 54 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16376 :
16377 54 : if (inh->inhparent == RelationGetRelid(parent_rel))
16378 6 : ereport(ERROR,
16379 : (errcode(ERRCODE_DUPLICATE_TABLE),
16380 : errmsg("relation \"%s\" would be inherited from more than once",
16381 : RelationGetRelationName(parent_rel))));
16382 :
16383 48 : if (inh->inhseqno > inhseqno)
16384 48 : inhseqno = inh->inhseqno;
16385 : }
16386 2806 : systable_endscan(scan);
16387 :
16388 : /* Match up the columns and bump attinhcount as needed */
16389 2806 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16390 :
16391 : /* Match up the constraints and bump coninhcount as needed */
16392 2728 : MergeConstraintsIntoExisting(child_rel, parent_rel);
16393 :
16394 : /*
16395 : * OK, it looks valid. Make the catalog entries that show inheritance.
16396 : */
16397 2686 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
16398 : RelationGetRelid(parent_rel),
16399 : inhseqno + 1,
16400 : catalogRelation,
16401 2686 : parent_rel->rd_rel->relkind ==
16402 : RELKIND_PARTITIONED_TABLE);
16403 :
16404 : /* Now we're done with pg_inherits */
16405 2686 : table_close(catalogRelation, RowExclusiveLock);
16406 2686 : }
16407 :
16408 : /*
16409 : * Obtain the source-text form of the constraint expression for a check
16410 : * constraint, given its pg_constraint tuple
16411 : */
16412 : static char *
16413 168 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16414 : {
16415 : Form_pg_constraint con;
16416 : bool isnull;
16417 : Datum attr;
16418 : Datum expr;
16419 :
16420 168 : con = (Form_pg_constraint) GETSTRUCT(contup);
16421 168 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16422 168 : if (isnull)
16423 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
16424 :
16425 168 : expr = DirectFunctionCall2(pg_get_expr, attr,
16426 : ObjectIdGetDatum(con->conrelid));
16427 168 : return TextDatumGetCString(expr);
16428 : }
16429 :
16430 : /*
16431 : * Determine whether two check constraints are functionally equivalent
16432 : *
16433 : * The test we apply is to see whether they reverse-compile to the same
16434 : * source string. This insulates us from issues like whether attributes
16435 : * have the same physical column numbers in parent and child relations.
16436 : */
16437 : static bool
16438 84 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16439 : {
16440 84 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16441 84 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16442 :
16443 84 : if (acon->condeferrable != bcon->condeferrable ||
16444 84 : acon->condeferred != bcon->condeferred ||
16445 84 : strcmp(decompile_conbin(a, tupleDesc),
16446 84 : decompile_conbin(b, tupleDesc)) != 0)
16447 6 : return false;
16448 : else
16449 78 : return true;
16450 : }
16451 :
16452 : /*
16453 : * Check columns in child table match up with columns in parent, and increment
16454 : * their attinhcount.
16455 : *
16456 : * Called by CreateInheritance
16457 : *
16458 : * Currently all parent columns must be found in child. Missing columns are an
16459 : * error. One day we might consider creating new columns like CREATE TABLE
16460 : * does. However, that is widely unpopular --- in the common use case of
16461 : * partitioned tables it's a foot-gun.
16462 : *
16463 : * The data type must match exactly. If the parent column is NOT NULL then
16464 : * the child must be as well. Defaults are not compared, however.
16465 : */
16466 : static void
16467 2806 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16468 : {
16469 : Relation attrrel;
16470 : TupleDesc parent_desc;
16471 :
16472 2806 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16473 2806 : parent_desc = RelationGetDescr(parent_rel);
16474 :
16475 9558 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16476 : {
16477 6830 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16478 6830 : char *parent_attname = NameStr(parent_att->attname);
16479 : HeapTuple tuple;
16480 :
16481 : /* Ignore dropped columns in the parent. */
16482 6830 : if (parent_att->attisdropped)
16483 296 : continue;
16484 :
16485 : /* Find same column in child (matching on column name). */
16486 6534 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16487 6534 : if (HeapTupleIsValid(tuple))
16488 : {
16489 6522 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16490 :
16491 6522 : if (parent_att->atttypid != child_att->atttypid ||
16492 6516 : parent_att->atttypmod != child_att->atttypmod)
16493 12 : ereport(ERROR,
16494 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16495 : errmsg("child table \"%s\" has different type for column \"%s\"",
16496 : RelationGetRelationName(child_rel), parent_attname)));
16497 :
16498 6510 : if (parent_att->attcollation != child_att->attcollation)
16499 6 : ereport(ERROR,
16500 : (errcode(ERRCODE_COLLATION_MISMATCH),
16501 : errmsg("child table \"%s\" has different collation for column \"%s\"",
16502 : RelationGetRelationName(child_rel), parent_attname)));
16503 :
16504 : /*
16505 : * If the parent has a not-null constraint that's not NO INHERIT,
16506 : * make sure the child has one too.
16507 : *
16508 : * Other constraints are checked elsewhere.
16509 : */
16510 6504 : if (parent_att->attnotnull && !child_att->attnotnull)
16511 : {
16512 : HeapTuple contup;
16513 :
16514 32 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16515 32 : parent_att->attnum);
16516 32 : if (HeapTupleIsValid(contup) &&
16517 20 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16518 18 : ereport(ERROR,
16519 : errcode(ERRCODE_DATATYPE_MISMATCH),
16520 : errmsg("column \"%s\" in child table must be marked NOT NULL",
16521 : parent_attname));
16522 : }
16523 :
16524 : /*
16525 : * Child column must be generated if and only if parent column is.
16526 : */
16527 6486 : if (parent_att->attgenerated && !child_att->attgenerated)
16528 18 : ereport(ERROR,
16529 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16530 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16531 6468 : if (child_att->attgenerated && !parent_att->attgenerated)
16532 12 : ereport(ERROR,
16533 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16534 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16535 :
16536 : /*
16537 : * Regular inheritance children are independent enough not to
16538 : * inherit identity columns. But partitions are integral part of
16539 : * a partitioned table and inherit identity column.
16540 : */
16541 6456 : if (ispartition)
16542 5916 : child_att->attidentity = parent_att->attidentity;
16543 :
16544 : /*
16545 : * OK, bump the child column's inheritance count. (If we fail
16546 : * later on, this change will just roll back.)
16547 : */
16548 6456 : child_att->attinhcount++;
16549 6456 : if (child_att->attinhcount < 0)
16550 0 : ereport(ERROR,
16551 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16552 : errmsg("too many inheritance parents"));
16553 :
16554 : /*
16555 : * In case of partitions, we must enforce that value of attislocal
16556 : * is same in all partitions. (Note: there are only inherited
16557 : * attributes in partitions)
16558 : */
16559 6456 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16560 : {
16561 : Assert(child_att->attinhcount == 1);
16562 5916 : child_att->attislocal = false;
16563 : }
16564 :
16565 6456 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16566 6456 : heap_freetuple(tuple);
16567 : }
16568 : else
16569 : {
16570 12 : ereport(ERROR,
16571 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16572 : errmsg("child table is missing column \"%s\"", parent_attname)));
16573 : }
16574 : }
16575 :
16576 2728 : table_close(attrrel, RowExclusiveLock);
16577 2728 : }
16578 :
16579 : /*
16580 : * Check constraints in child table match up with constraints in parent,
16581 : * and increment their coninhcount.
16582 : *
16583 : * Constraints that are marked ONLY in the parent are ignored.
16584 : *
16585 : * Called by CreateInheritance
16586 : *
16587 : * Currently all constraints in parent must be present in the child. One day we
16588 : * may consider adding new constraints like CREATE TABLE does.
16589 : *
16590 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
16591 : * constraints. As long as tables have more like 10 constraints it shouldn't be
16592 : * a problem though. Even 100 constraints ought not be the end of the world.
16593 : *
16594 : * XXX See MergeWithExistingConstraint too if you change this code.
16595 : */
16596 : static void
16597 2728 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16598 : {
16599 : Relation constraintrel;
16600 : SysScanDesc parent_scan;
16601 : ScanKeyData parent_key;
16602 : HeapTuple parent_tuple;
16603 2728 : Oid parent_relid = RelationGetRelid(parent_rel);
16604 :
16605 2728 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16606 :
16607 : /* Outer loop scans through the parent's constraint definitions */
16608 2728 : ScanKeyInit(&parent_key,
16609 : Anum_pg_constraint_conrelid,
16610 : BTEqualStrategyNumber, F_OIDEQ,
16611 : ObjectIdGetDatum(parent_relid));
16612 2728 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16613 : true, NULL, 1, &parent_key);
16614 :
16615 4050 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16616 : {
16617 1364 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16618 : SysScanDesc child_scan;
16619 : ScanKeyData child_key;
16620 : HeapTuple child_tuple;
16621 1364 : bool found = false;
16622 :
16623 1364 : if (parent_con->contype != CONSTRAINT_CHECK &&
16624 1236 : parent_con->contype != CONSTRAINT_NOTNULL)
16625 780 : continue;
16626 :
16627 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16628 630 : if (parent_con->connoinherit)
16629 46 : continue;
16630 :
16631 : /* Search for a child constraint matching this one */
16632 584 : ScanKeyInit(&child_key,
16633 : Anum_pg_constraint_conrelid,
16634 : BTEqualStrategyNumber, F_OIDEQ,
16635 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16636 584 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16637 : true, NULL, 1, &child_key);
16638 :
16639 996 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16640 : {
16641 966 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16642 : HeapTuple child_copy;
16643 :
16644 966 : if (child_con->contype != parent_con->contype)
16645 216 : continue;
16646 :
16647 : /*
16648 : * CHECK constraint are matched by name, NOT NULL ones by
16649 : * attribute number
16650 : */
16651 750 : if (child_con->contype == CONSTRAINT_CHECK)
16652 : {
16653 114 : if (strcmp(NameStr(parent_con->conname),
16654 114 : NameStr(child_con->conname)) != 0)
16655 30 : continue;
16656 : }
16657 636 : else if (child_con->contype == CONSTRAINT_NOTNULL)
16658 : {
16659 636 : AttrNumber parent_attno = extractNotNullColumn(parent_tuple);
16660 636 : AttrNumber child_attno = extractNotNullColumn(child_tuple);
16661 :
16662 636 : if (strcmp(get_attname(parent_relid, parent_attno, false),
16663 636 : get_attname(RelationGetRelid(child_rel), child_attno,
16664 : false)) != 0)
16665 166 : continue;
16666 : }
16667 :
16668 554 : if (child_con->contype == CONSTRAINT_CHECK &&
16669 84 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16670 6 : ereport(ERROR,
16671 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16672 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16673 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16674 :
16675 : /*
16676 : * If the CHECK child constraint is "no inherit" then cannot
16677 : * merge.
16678 : *
16679 : * This is not desirable for not-null constraints, mostly because
16680 : * it breaks our pg_upgrade strategy, but it also makes sense on
16681 : * its own: if a child has its own not-null constraint and then
16682 : * acquires a parent with the same constraint, then we start to
16683 : * enforce that constraint for all the descendants of that child
16684 : * too, if any.
16685 : */
16686 548 : if (child_con->contype == CONSTRAINT_CHECK &&
16687 78 : child_con->connoinherit)
16688 0 : ereport(ERROR,
16689 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16690 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16691 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16692 :
16693 : /*
16694 : * If the child constraint is "not valid" then cannot merge with a
16695 : * valid parent constraint
16696 : */
16697 548 : if (parent_con->convalidated && !child_con->convalidated)
16698 0 : ereport(ERROR,
16699 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16700 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16701 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16702 :
16703 : /*
16704 : * OK, bump the child constraint's inheritance count. (If we fail
16705 : * later on, this change will just roll back.)
16706 : */
16707 548 : child_copy = heap_copytuple(child_tuple);
16708 548 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16709 548 : child_con->coninhcount++;
16710 548 : if (child_con->coninhcount < 0)
16711 0 : ereport(ERROR,
16712 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16713 : errmsg("too many inheritance parents"));
16714 548 : if (child_con->contype == CONSTRAINT_NOTNULL &&
16715 470 : child_con->connoinherit)
16716 : {
16717 : /*
16718 : * If the child has children, it's not possible to turn a NO
16719 : * INHERIT constraint into an inheritable one: we would need
16720 : * to recurse to create constraints in those children, but
16721 : * this is not a good place to do that.
16722 : */
16723 6 : if (child_rel->rd_rel->relhassubclass)
16724 6 : ereport(ERROR,
16725 : errmsg("cannot add NOT NULL constraint to column \"%s\" of relation \"%s\" with inheritance children",
16726 : get_attname(RelationGetRelid(child_rel),
16727 : extractNotNullColumn(child_tuple),
16728 : false),
16729 : RelationGetRelationName(child_rel)),
16730 : errdetail("Existing constraint \"%s\" is marked NO INHERIT.",
16731 : NameStr(child_con->conname)));
16732 :
16733 0 : child_con->connoinherit = false;
16734 : }
16735 :
16736 : /*
16737 : * In case of partitions, an inherited constraint must be
16738 : * inherited only once since it cannot have multiple parents and
16739 : * it is never considered local.
16740 : */
16741 542 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16742 : {
16743 : Assert(child_con->coninhcount == 1);
16744 468 : child_con->conislocal = false;
16745 : }
16746 :
16747 542 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16748 542 : heap_freetuple(child_copy);
16749 :
16750 542 : found = true;
16751 542 : break;
16752 : }
16753 :
16754 572 : systable_endscan(child_scan);
16755 :
16756 572 : if (!found)
16757 : {
16758 30 : if (parent_con->contype == CONSTRAINT_NOTNULL)
16759 6 : ereport(ERROR,
16760 : errcode(ERRCODE_DATATYPE_MISMATCH),
16761 : errmsg("column \"%s\" in child table must be marked NOT NULL",
16762 : get_attname(parent_relid,
16763 : extractNotNullColumn(parent_tuple),
16764 : false)));
16765 :
16766 24 : ereport(ERROR,
16767 : (errcode(ERRCODE_DATATYPE_MISMATCH),
16768 : errmsg("child table is missing constraint \"%s\"",
16769 : NameStr(parent_con->conname))));
16770 : }
16771 : }
16772 :
16773 2686 : systable_endscan(parent_scan);
16774 2686 : table_close(constraintrel, RowExclusiveLock);
16775 2686 : }
16776 :
16777 : /*
16778 : * ALTER TABLE NO INHERIT
16779 : *
16780 : * Return value is the address of the relation that is no longer parent.
16781 : */
16782 : static ObjectAddress
16783 44 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16784 : {
16785 : ObjectAddress address;
16786 : Relation parent_rel;
16787 :
16788 44 : if (rel->rd_rel->relispartition)
16789 0 : ereport(ERROR,
16790 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16791 : errmsg("cannot change inheritance of a partition")));
16792 :
16793 : /*
16794 : * AccessShareLock on the parent is probably enough, seeing that DROP
16795 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
16796 : * be inspecting the parent's schema.
16797 : */
16798 44 : parent_rel = table_openrv(parent, AccessShareLock);
16799 :
16800 : /*
16801 : * We don't bother to check ownership of the parent table --- ownership of
16802 : * the child is presumed enough rights.
16803 : */
16804 :
16805 : /* Off to RemoveInheritance() where most of the work happens */
16806 44 : RemoveInheritance(rel, parent_rel, false);
16807 :
16808 : /*
16809 : * If parent_rel has a primary key, then child_rel has not-null
16810 : * constraints that make these columns as non nullable. Mark those
16811 : * constraints as no longer inherited by this parent.
16812 : */
16813 38 : ATInheritAdjustNotNulls(parent_rel, rel, -1);
16814 :
16815 : /*
16816 : * If the parent has a primary key, then we decrement counts for all NOT
16817 : * NULL constraints
16818 : */
16819 :
16820 38 : ObjectAddressSet(address, RelationRelationId,
16821 : RelationGetRelid(parent_rel));
16822 :
16823 : /* keep our lock on the parent relation until commit */
16824 38 : table_close(parent_rel, NoLock);
16825 :
16826 38 : return address;
16827 : }
16828 :
16829 : /*
16830 : * MarkInheritDetached
16831 : *
16832 : * Set inhdetachpending for a partition, for ATExecDetachPartition
16833 : * in concurrent mode. While at it, verify that no other partition is
16834 : * already pending detach.
16835 : */
16836 : static void
16837 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
16838 : {
16839 : Relation catalogRelation;
16840 : SysScanDesc scan;
16841 : ScanKeyData key;
16842 : HeapTuple inheritsTuple;
16843 146 : bool found = false;
16844 :
16845 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16846 :
16847 : /*
16848 : * Find pg_inherits entries by inhparent. (We need to scan them all in
16849 : * order to verify that no other partition is pending detach.)
16850 : */
16851 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16852 146 : ScanKeyInit(&key,
16853 : Anum_pg_inherits_inhparent,
16854 : BTEqualStrategyNumber, F_OIDEQ,
16855 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16856 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16857 : true, NULL, 1, &key);
16858 :
16859 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16860 : {
16861 : Form_pg_inherits inhForm;
16862 :
16863 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16864 286 : if (inhForm->inhdetachpending)
16865 2 : ereport(ERROR,
16866 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16867 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16868 : get_rel_name(inhForm->inhrelid),
16869 : get_namespace_name(parent_rel->rd_rel->relnamespace),
16870 : RelationGetRelationName(parent_rel)),
16871 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16872 :
16873 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
16874 : {
16875 : HeapTuple newtup;
16876 :
16877 144 : newtup = heap_copytuple(inheritsTuple);
16878 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16879 :
16880 144 : CatalogTupleUpdate(catalogRelation,
16881 : &inheritsTuple->t_self,
16882 : newtup);
16883 144 : found = true;
16884 144 : heap_freetuple(newtup);
16885 : /* keep looking, to ensure we catch others pending detach */
16886 : }
16887 : }
16888 :
16889 : /* Done */
16890 144 : systable_endscan(scan);
16891 144 : table_close(catalogRelation, RowExclusiveLock);
16892 :
16893 144 : if (!found)
16894 0 : ereport(ERROR,
16895 : (errcode(ERRCODE_UNDEFINED_TABLE),
16896 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16897 : RelationGetRelationName(child_rel),
16898 : RelationGetRelationName(parent_rel))));
16899 144 : }
16900 :
16901 : /*
16902 : * RemoveInheritance
16903 : *
16904 : * Drop a parent from the child's parents. This just adjusts the attinhcount
16905 : * and attislocal of the columns and removes the pg_inherit and pg_depend
16906 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16907 : *
16908 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16909 : * up attislocal stays true, which means if a child is ever removed from a
16910 : * parent then its columns will never be automatically dropped which may
16911 : * surprise. But at least we'll never surprise by dropping columns someone
16912 : * isn't expecting to be dropped which would actually mean data loss.
16913 : *
16914 : * coninhcount and conislocal for inherited constraints are adjusted in
16915 : * exactly the same way.
16916 : *
16917 : * Common to ATExecDropInherit() and ATExecDetachPartition().
16918 : */
16919 : static void
16920 872 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16921 : {
16922 : Relation catalogRelation;
16923 : SysScanDesc scan;
16924 : ScanKeyData key[3];
16925 : HeapTuple attributeTuple,
16926 : constraintTuple;
16927 : List *connames;
16928 : List *nncolumns;
16929 : bool found;
16930 : bool is_partitioning;
16931 :
16932 872 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16933 :
16934 872 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16935 : RelationGetRelid(parent_rel),
16936 : expect_detached,
16937 872 : RelationGetRelationName(child_rel));
16938 872 : if (!found)
16939 : {
16940 24 : if (is_partitioning)
16941 18 : ereport(ERROR,
16942 : (errcode(ERRCODE_UNDEFINED_TABLE),
16943 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16944 : RelationGetRelationName(child_rel),
16945 : RelationGetRelationName(parent_rel))));
16946 : else
16947 6 : ereport(ERROR,
16948 : (errcode(ERRCODE_UNDEFINED_TABLE),
16949 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16950 : RelationGetRelationName(parent_rel),
16951 : RelationGetRelationName(child_rel))));
16952 : }
16953 :
16954 : /*
16955 : * Search through child columns looking for ones matching parent rel
16956 : */
16957 848 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16958 848 : ScanKeyInit(&key[0],
16959 : Anum_pg_attribute_attrelid,
16960 : BTEqualStrategyNumber, F_OIDEQ,
16961 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
16962 848 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16963 : true, NULL, 1, key);
16964 7916 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16965 : {
16966 7068 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16967 :
16968 : /* Ignore if dropped or not inherited */
16969 7068 : if (att->attisdropped)
16970 36 : continue;
16971 7032 : if (att->attinhcount <= 0)
16972 5106 : continue;
16973 :
16974 1926 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16975 1926 : NameStr(att->attname)))
16976 : {
16977 : /* Decrement inhcount and possibly set islocal to true */
16978 1914 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
16979 1914 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16980 :
16981 1914 : copy_att->attinhcount--;
16982 1914 : if (copy_att->attinhcount == 0)
16983 1914 : copy_att->attislocal = true;
16984 :
16985 1914 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
16986 1914 : heap_freetuple(copyTuple);
16987 : }
16988 : }
16989 848 : systable_endscan(scan);
16990 848 : table_close(catalogRelation, RowExclusiveLock);
16991 :
16992 : /*
16993 : * Likewise, find inherited check constraints and disinherit them. To do
16994 : * this, we first need a list of the names of the parent's check
16995 : * constraints. (We cheat a bit by only checking for name matches,
16996 : * assuming that the expressions will match.)
16997 : *
16998 : * For NOT NULL columns, we store column numbers to match.
16999 : */
17000 848 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17001 848 : ScanKeyInit(&key[0],
17002 : Anum_pg_constraint_conrelid,
17003 : BTEqualStrategyNumber, F_OIDEQ,
17004 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17005 848 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17006 : true, NULL, 1, key);
17007 :
17008 848 : connames = NIL;
17009 848 : nncolumns = NIL;
17010 :
17011 1176 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17012 : {
17013 328 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17014 :
17015 328 : if (con->contype == CONSTRAINT_CHECK)
17016 18 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
17017 328 : if (con->contype == CONSTRAINT_NOTNULL)
17018 80 : nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
17019 : }
17020 :
17021 848 : systable_endscan(scan);
17022 :
17023 : /* Now scan the child's constraints */
17024 848 : ScanKeyInit(&key[0],
17025 : Anum_pg_constraint_conrelid,
17026 : BTEqualStrategyNumber, F_OIDEQ,
17027 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17028 848 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17029 : true, NULL, 1, key);
17030 :
17031 1372 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17032 : {
17033 524 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17034 524 : bool match = false;
17035 : ListCell *lc;
17036 :
17037 : /*
17038 : * Match CHECK constraints by name, not-null constraints by column
17039 : * number, and ignore all others.
17040 : */
17041 524 : if (con->contype == CONSTRAINT_CHECK)
17042 : {
17043 190 : foreach(lc, connames)
17044 : {
17045 30 : if (con->contype == CONSTRAINT_CHECK &&
17046 30 : strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
17047 : {
17048 18 : match = true;
17049 18 : break;
17050 : }
17051 : }
17052 : }
17053 346 : else if (con->contype == CONSTRAINT_NOTNULL)
17054 : {
17055 98 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
17056 :
17057 128 : foreach(lc, nncolumns)
17058 : {
17059 110 : if (lfirst_int(lc) == child_attno)
17060 : {
17061 80 : match = true;
17062 80 : break;
17063 : }
17064 : }
17065 : }
17066 : else
17067 248 : continue;
17068 :
17069 276 : if (match)
17070 : {
17071 : /* Decrement inhcount and possibly set islocal to true */
17072 98 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
17073 98 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17074 :
17075 98 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
17076 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17077 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
17078 :
17079 98 : copy_con->coninhcount--;
17080 98 : if (copy_con->coninhcount == 0)
17081 98 : copy_con->conislocal = true;
17082 :
17083 98 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17084 98 : heap_freetuple(copyTuple);
17085 : }
17086 : }
17087 :
17088 848 : systable_endscan(scan);
17089 848 : table_close(catalogRelation, RowExclusiveLock);
17090 :
17091 848 : drop_parent_dependency(RelationGetRelid(child_rel),
17092 : RelationRelationId,
17093 : RelationGetRelid(parent_rel),
17094 : child_dependency_type(is_partitioning));
17095 :
17096 : /*
17097 : * Post alter hook of this inherits. Since object_access_hook doesn't take
17098 : * multiple object identifiers, we relay oid of parent relation using
17099 : * auxiliary_id argument.
17100 : */
17101 848 : InvokeObjectPostAlterHookArg(InheritsRelationId,
17102 : RelationGetRelid(child_rel), 0,
17103 : RelationGetRelid(parent_rel), false);
17104 848 : }
17105 :
17106 : /*
17107 : * Adjust coninhcount of not-null constraints upwards or downwards when a
17108 : * table is marked as inheriting or no longer doing so a table with a primary
17109 : * key.
17110 : *
17111 : * Note: these constraints are not dropped, even if their inhcount goes to zero
17112 : * and conislocal is false. Instead we mark the constraints as locally defined.
17113 : * This is seen as more useful behavior, with no downsides. The user can always
17114 : * drop them afterwards.
17115 : */
17116 : static void
17117 278 : ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel, int inhcount)
17118 : {
17119 : Bitmapset *pkattnos;
17120 :
17121 : /* Quick exit when parent has no PK */
17122 278 : if (!parent_rel->rd_rel->relhasindex)
17123 236 : return;
17124 :
17125 42 : pkattnos = RelationGetIndexAttrBitmap(parent_rel,
17126 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
17127 42 : if (pkattnos != NULL)
17128 : {
17129 36 : Bitmapset *childattnums = NULL;
17130 : AttrMap *attmap;
17131 : int i;
17132 :
17133 36 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17134 : RelationGetDescr(child_rel), true);
17135 :
17136 36 : i = -1;
17137 72 : while ((i = bms_next_member(pkattnos, i)) >= 0)
17138 : {
17139 36 : childattnums = bms_add_member(childattnums,
17140 36 : attmap->attnums[i + FirstLowInvalidHeapAttributeNumber - 1]);
17141 : }
17142 :
17143 : /*
17144 : * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
17145 : * parent: the relevant not-null constraint in the child already had
17146 : * its inhcount modified earlier.
17147 : */
17148 36 : CommandCounterIncrement();
17149 36 : AdjustNotNullInheritance(RelationGetRelid(child_rel), childattnums,
17150 : inhcount);
17151 : }
17152 : }
17153 :
17154 : /*
17155 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17156 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17157 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17158 : * be TypeRelationId). There's no convenient way to do this, so go trawling
17159 : * through pg_depend.
17160 : */
17161 : static void
17162 860 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
17163 : DependencyType deptype)
17164 : {
17165 : Relation catalogRelation;
17166 : SysScanDesc scan;
17167 : ScanKeyData key[3];
17168 : HeapTuple depTuple;
17169 :
17170 860 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17171 :
17172 860 : ScanKeyInit(&key[0],
17173 : Anum_pg_depend_classid,
17174 : BTEqualStrategyNumber, F_OIDEQ,
17175 : ObjectIdGetDatum(RelationRelationId));
17176 860 : ScanKeyInit(&key[1],
17177 : Anum_pg_depend_objid,
17178 : BTEqualStrategyNumber, F_OIDEQ,
17179 : ObjectIdGetDatum(relid));
17180 860 : ScanKeyInit(&key[2],
17181 : Anum_pg_depend_objsubid,
17182 : BTEqualStrategyNumber, F_INT4EQ,
17183 : Int32GetDatum(0));
17184 :
17185 860 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17186 : NULL, 3, key);
17187 :
17188 2628 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17189 : {
17190 1768 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17191 :
17192 1768 : if (dep->refclassid == refclassid &&
17193 872 : dep->refobjid == refobjid &&
17194 860 : dep->refobjsubid == 0 &&
17195 860 : dep->deptype == deptype)
17196 860 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17197 : }
17198 :
17199 860 : systable_endscan(scan);
17200 860 : table_close(catalogRelation, RowExclusiveLock);
17201 860 : }
17202 :
17203 : /*
17204 : * ALTER TABLE OF
17205 : *
17206 : * Attach a table to a composite type, as though it had been created with CREATE
17207 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17208 : * subject table must not have inheritance parents. These restrictions ensure
17209 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17210 : *
17211 : * The address of the type is returned.
17212 : */
17213 : static ObjectAddress
17214 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17215 : {
17216 66 : Oid relid = RelationGetRelid(rel);
17217 : Type typetuple;
17218 : Form_pg_type typeform;
17219 : Oid typeid;
17220 : Relation inheritsRelation,
17221 : relationRelation;
17222 : SysScanDesc scan;
17223 : ScanKeyData key;
17224 : AttrNumber table_attno,
17225 : type_attno;
17226 : TupleDesc typeTupleDesc,
17227 : tableTupleDesc;
17228 : ObjectAddress tableobj,
17229 : typeobj;
17230 : HeapTuple classtuple;
17231 :
17232 : /* Validate the type. */
17233 66 : typetuple = typenameType(NULL, ofTypename, NULL);
17234 66 : check_of_type(typetuple);
17235 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
17236 66 : typeid = typeform->oid;
17237 :
17238 : /* Fail if the table has any inheritance parents. */
17239 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17240 66 : ScanKeyInit(&key,
17241 : Anum_pg_inherits_inhrelid,
17242 : BTEqualStrategyNumber, F_OIDEQ,
17243 : ObjectIdGetDatum(relid));
17244 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17245 : true, NULL, 1, &key);
17246 66 : if (HeapTupleIsValid(systable_getnext(scan)))
17247 6 : ereport(ERROR,
17248 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17249 : errmsg("typed tables cannot inherit")));
17250 60 : systable_endscan(scan);
17251 60 : table_close(inheritsRelation, AccessShareLock);
17252 :
17253 : /*
17254 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
17255 : * require that the order also match. However, attnotnull need not match.
17256 : */
17257 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17258 60 : tableTupleDesc = RelationGetDescr(rel);
17259 60 : table_attno = 1;
17260 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17261 : {
17262 : Form_pg_attribute type_attr,
17263 : table_attr;
17264 : const char *type_attname,
17265 : *table_attname;
17266 :
17267 : /* Get the next non-dropped type attribute. */
17268 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17269 154 : if (type_attr->attisdropped)
17270 44 : continue;
17271 110 : type_attname = NameStr(type_attr->attname);
17272 :
17273 : /* Get the next non-dropped table attribute. */
17274 : do
17275 : {
17276 122 : if (table_attno > tableTupleDesc->natts)
17277 6 : ereport(ERROR,
17278 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17279 : errmsg("table is missing column \"%s\"",
17280 : type_attname)));
17281 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17282 116 : table_attno++;
17283 116 : } while (table_attr->attisdropped);
17284 104 : table_attname = NameStr(table_attr->attname);
17285 :
17286 : /* Compare name. */
17287 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17288 6 : ereport(ERROR,
17289 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17290 : errmsg("table has column \"%s\" where type requires \"%s\"",
17291 : table_attname, type_attname)));
17292 :
17293 : /* Compare type. */
17294 98 : if (table_attr->atttypid != type_attr->atttypid ||
17295 92 : table_attr->atttypmod != type_attr->atttypmod ||
17296 86 : table_attr->attcollation != type_attr->attcollation)
17297 12 : ereport(ERROR,
17298 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17299 : errmsg("table \"%s\" has different type for column \"%s\"",
17300 : RelationGetRelationName(rel), type_attname)));
17301 : }
17302 36 : ReleaseTupleDesc(typeTupleDesc);
17303 :
17304 : /* Any remaining columns at the end of the table had better be dropped. */
17305 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
17306 : {
17307 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17308 : table_attno - 1);
17309 :
17310 6 : if (!table_attr->attisdropped)
17311 6 : ereport(ERROR,
17312 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17313 : errmsg("table has extra column \"%s\"",
17314 : NameStr(table_attr->attname))));
17315 : }
17316 :
17317 : /* If the table was already typed, drop the existing dependency. */
17318 30 : if (rel->rd_rel->reloftype)
17319 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17320 : DEPENDENCY_NORMAL);
17321 :
17322 : /* Record a dependency on the new type. */
17323 30 : tableobj.classId = RelationRelationId;
17324 30 : tableobj.objectId = relid;
17325 30 : tableobj.objectSubId = 0;
17326 30 : typeobj.classId = TypeRelationId;
17327 30 : typeobj.objectId = typeid;
17328 30 : typeobj.objectSubId = 0;
17329 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17330 :
17331 : /* Update pg_class.reloftype */
17332 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17333 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17334 30 : if (!HeapTupleIsValid(classtuple))
17335 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17336 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17337 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17338 :
17339 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17340 :
17341 30 : heap_freetuple(classtuple);
17342 30 : table_close(relationRelation, RowExclusiveLock);
17343 :
17344 30 : ReleaseSysCache(typetuple);
17345 :
17346 30 : return typeobj;
17347 : }
17348 :
17349 : /*
17350 : * ALTER TABLE NOT OF
17351 : *
17352 : * Detach a typed table from its originating type. Just clear reloftype and
17353 : * remove the dependency.
17354 : */
17355 : static void
17356 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
17357 : {
17358 6 : Oid relid = RelationGetRelid(rel);
17359 : Relation relationRelation;
17360 : HeapTuple tuple;
17361 :
17362 6 : if (!OidIsValid(rel->rd_rel->reloftype))
17363 0 : ereport(ERROR,
17364 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17365 : errmsg("\"%s\" is not a typed table",
17366 : RelationGetRelationName(rel))));
17367 :
17368 : /*
17369 : * We don't bother to check ownership of the type --- ownership of the
17370 : * table is presumed enough rights. No lock required on the type, either.
17371 : */
17372 :
17373 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17374 : DEPENDENCY_NORMAL);
17375 :
17376 : /* Clear pg_class.reloftype */
17377 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17378 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17379 6 : if (!HeapTupleIsValid(tuple))
17380 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17381 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17382 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17383 :
17384 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17385 :
17386 6 : heap_freetuple(tuple);
17387 6 : table_close(relationRelation, RowExclusiveLock);
17388 6 : }
17389 :
17390 : /*
17391 : * relation_mark_replica_identity: Update a table's replica identity
17392 : *
17393 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17394 : * index. Otherwise, it must be InvalidOid.
17395 : *
17396 : * Caller had better hold an exclusive lock on the relation, as the results
17397 : * of running two of these concurrently wouldn't be pretty.
17398 : */
17399 : static void
17400 420 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17401 : bool is_internal)
17402 : {
17403 : Relation pg_index;
17404 : Relation pg_class;
17405 : HeapTuple pg_class_tuple;
17406 : HeapTuple pg_index_tuple;
17407 : Form_pg_class pg_class_form;
17408 : Form_pg_index pg_index_form;
17409 : ListCell *index;
17410 :
17411 : /*
17412 : * Check whether relreplident has changed, and update it if so.
17413 : */
17414 420 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17415 420 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
17416 : ObjectIdGetDatum(RelationGetRelid(rel)));
17417 420 : if (!HeapTupleIsValid(pg_class_tuple))
17418 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
17419 : RelationGetRelationName(rel));
17420 420 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17421 420 : if (pg_class_form->relreplident != ri_type)
17422 : {
17423 370 : pg_class_form->relreplident = ri_type;
17424 370 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17425 : }
17426 420 : table_close(pg_class, RowExclusiveLock);
17427 420 : heap_freetuple(pg_class_tuple);
17428 :
17429 : /*
17430 : * Update the per-index indisreplident flags correctly.
17431 : */
17432 420 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
17433 1130 : foreach(index, RelationGetIndexList(rel))
17434 : {
17435 710 : Oid thisIndexOid = lfirst_oid(index);
17436 710 : bool dirty = false;
17437 :
17438 710 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17439 : ObjectIdGetDatum(thisIndexOid));
17440 710 : if (!HeapTupleIsValid(pg_index_tuple))
17441 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17442 710 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17443 :
17444 710 : if (thisIndexOid == indexOid)
17445 : {
17446 : /* Set the bit if not already set. */
17447 234 : if (!pg_index_form->indisreplident)
17448 : {
17449 216 : dirty = true;
17450 216 : pg_index_form->indisreplident = true;
17451 : }
17452 : }
17453 : else
17454 : {
17455 : /* Unset the bit if set. */
17456 476 : if (pg_index_form->indisreplident)
17457 : {
17458 52 : dirty = true;
17459 52 : pg_index_form->indisreplident = false;
17460 : }
17461 : }
17462 :
17463 710 : if (dirty)
17464 : {
17465 268 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17466 268 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17467 : InvalidOid, is_internal);
17468 :
17469 : /*
17470 : * Invalidate the relcache for the table, so that after we commit
17471 : * all sessions will refresh the table's replica identity index
17472 : * before attempting any UPDATE or DELETE on the table. (If we
17473 : * changed the table's pg_class row above, then a relcache inval
17474 : * is already queued due to that; but we might not have.)
17475 : */
17476 268 : CacheInvalidateRelcache(rel);
17477 : }
17478 710 : heap_freetuple(pg_index_tuple);
17479 : }
17480 :
17481 420 : table_close(pg_index, RowExclusiveLock);
17482 420 : }
17483 :
17484 : /*
17485 : * ALTER TABLE <name> REPLICA IDENTITY ...
17486 : */
17487 : static void
17488 474 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17489 : {
17490 : Oid indexOid;
17491 : Relation indexRel;
17492 : int key;
17493 :
17494 474 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17495 : {
17496 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17497 6 : return;
17498 : }
17499 468 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17500 : {
17501 144 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17502 144 : return;
17503 : }
17504 324 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17505 : {
17506 36 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17507 36 : return;
17508 : }
17509 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17510 : {
17511 : /* fallthrough */ ;
17512 : }
17513 : else
17514 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17515 :
17516 : /* Check that the index exists */
17517 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17518 288 : if (!OidIsValid(indexOid))
17519 0 : ereport(ERROR,
17520 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17521 : errmsg("index \"%s\" for table \"%s\" does not exist",
17522 : stmt->name, RelationGetRelationName(rel))));
17523 :
17524 288 : indexRel = index_open(indexOid, ShareLock);
17525 :
17526 : /* Check that the index is on the relation we're altering. */
17527 288 : if (indexRel->rd_index == NULL ||
17528 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
17529 6 : ereport(ERROR,
17530 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17531 : errmsg("\"%s\" is not an index for table \"%s\"",
17532 : RelationGetRelationName(indexRel),
17533 : RelationGetRelationName(rel))));
17534 : /* The AM must support uniqueness, and the index must in fact be unique. */
17535 282 : if (!indexRel->rd_indam->amcanunique ||
17536 270 : !indexRel->rd_index->indisunique)
17537 18 : ereport(ERROR,
17538 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17539 : errmsg("cannot use non-unique index \"%s\" as replica identity",
17540 : RelationGetRelationName(indexRel))));
17541 : /* Deferred indexes are not guaranteed to be always unique. */
17542 264 : if (!indexRel->rd_index->indimmediate)
17543 12 : ereport(ERROR,
17544 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17545 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
17546 : RelationGetRelationName(indexRel))));
17547 : /* Expression indexes aren't supported. */
17548 252 : if (RelationGetIndexExpressions(indexRel) != NIL)
17549 6 : ereport(ERROR,
17550 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17551 : errmsg("cannot use expression index \"%s\" as replica identity",
17552 : RelationGetRelationName(indexRel))));
17553 : /* Predicate indexes aren't supported. */
17554 246 : if (RelationGetIndexPredicate(indexRel) != NIL)
17555 6 : ereport(ERROR,
17556 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17557 : errmsg("cannot use partial index \"%s\" as replica identity",
17558 : RelationGetRelationName(indexRel))));
17559 :
17560 : /* Check index for nullable columns. */
17561 526 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17562 : {
17563 292 : int16 attno = indexRel->rd_index->indkey.values[key];
17564 : Form_pg_attribute attr;
17565 :
17566 : /*
17567 : * Reject any other system columns. (Going forward, we'll disallow
17568 : * indexes containing such columns in the first place, but they might
17569 : * exist in older branches.)
17570 : */
17571 292 : if (attno <= 0)
17572 0 : ereport(ERROR,
17573 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17574 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17575 : RelationGetRelationName(indexRel), attno)));
17576 :
17577 292 : attr = TupleDescAttr(rel->rd_att, attno - 1);
17578 292 : if (!attr->attnotnull)
17579 6 : ereport(ERROR,
17580 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17581 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17582 : RelationGetRelationName(indexRel),
17583 : NameStr(attr->attname))));
17584 : }
17585 :
17586 : /* This index is suitable for use as a replica identity. Mark it. */
17587 234 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17588 :
17589 234 : index_close(indexRel, NoLock);
17590 : }
17591 :
17592 : /*
17593 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17594 : */
17595 : static void
17596 294 : ATExecSetRowSecurity(Relation rel, bool rls)
17597 : {
17598 : Relation pg_class;
17599 : Oid relid;
17600 : HeapTuple tuple;
17601 :
17602 294 : relid = RelationGetRelid(rel);
17603 :
17604 : /* Pull the record for this relation and update it */
17605 294 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17606 :
17607 294 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17608 :
17609 294 : if (!HeapTupleIsValid(tuple))
17610 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17611 :
17612 294 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17613 294 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17614 :
17615 294 : InvokeObjectPostAlterHook(RelationRelationId,
17616 : RelationGetRelid(rel), 0);
17617 :
17618 294 : table_close(pg_class, RowExclusiveLock);
17619 294 : heap_freetuple(tuple);
17620 294 : }
17621 :
17622 : /*
17623 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17624 : */
17625 : static void
17626 120 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17627 : {
17628 : Relation pg_class;
17629 : Oid relid;
17630 : HeapTuple tuple;
17631 :
17632 120 : relid = RelationGetRelid(rel);
17633 :
17634 120 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
17635 :
17636 120 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17637 :
17638 120 : if (!HeapTupleIsValid(tuple))
17639 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
17640 :
17641 120 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17642 120 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17643 :
17644 120 : InvokeObjectPostAlterHook(RelationRelationId,
17645 : RelationGetRelid(rel), 0);
17646 :
17647 120 : table_close(pg_class, RowExclusiveLock);
17648 120 : heap_freetuple(tuple);
17649 120 : }
17650 :
17651 : /*
17652 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
17653 : */
17654 : static void
17655 50 : ATExecGenericOptions(Relation rel, List *options)
17656 : {
17657 : Relation ftrel;
17658 : ForeignServer *server;
17659 : ForeignDataWrapper *fdw;
17660 : HeapTuple tuple;
17661 : bool isnull;
17662 : Datum repl_val[Natts_pg_foreign_table];
17663 : bool repl_null[Natts_pg_foreign_table];
17664 : bool repl_repl[Natts_pg_foreign_table];
17665 : Datum datum;
17666 : Form_pg_foreign_table tableform;
17667 :
17668 50 : if (options == NIL)
17669 0 : return;
17670 :
17671 50 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17672 :
17673 50 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17674 : ObjectIdGetDatum(rel->rd_id));
17675 50 : if (!HeapTupleIsValid(tuple))
17676 0 : ereport(ERROR,
17677 : (errcode(ERRCODE_UNDEFINED_OBJECT),
17678 : errmsg("foreign table \"%s\" does not exist",
17679 : RelationGetRelationName(rel))));
17680 50 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17681 50 : server = GetForeignServer(tableform->ftserver);
17682 50 : fdw = GetForeignDataWrapper(server->fdwid);
17683 :
17684 50 : memset(repl_val, 0, sizeof(repl_val));
17685 50 : memset(repl_null, false, sizeof(repl_null));
17686 50 : memset(repl_repl, false, sizeof(repl_repl));
17687 :
17688 : /* Extract the current options */
17689 50 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
17690 : tuple,
17691 : Anum_pg_foreign_table_ftoptions,
17692 : &isnull);
17693 50 : if (isnull)
17694 4 : datum = PointerGetDatum(NULL);
17695 :
17696 : /* Transform the options */
17697 50 : datum = transformGenericOptions(ForeignTableRelationId,
17698 : datum,
17699 : options,
17700 : fdw->fdwvalidator);
17701 :
17702 48 : if (PointerIsValid(DatumGetPointer(datum)))
17703 48 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17704 : else
17705 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17706 :
17707 48 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17708 :
17709 : /* Everything looks good - update the tuple */
17710 :
17711 48 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17712 : repl_val, repl_null, repl_repl);
17713 :
17714 48 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17715 :
17716 : /*
17717 : * Invalidate relcache so that all sessions will refresh any cached plans
17718 : * that might depend on the old options.
17719 : */
17720 48 : CacheInvalidateRelcache(rel);
17721 :
17722 48 : InvokeObjectPostAlterHook(ForeignTableRelationId,
17723 : RelationGetRelid(rel), 0);
17724 :
17725 48 : table_close(ftrel, RowExclusiveLock);
17726 :
17727 48 : heap_freetuple(tuple);
17728 : }
17729 :
17730 : /*
17731 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
17732 : *
17733 : * Return value is the address of the modified column
17734 : */
17735 : static ObjectAddress
17736 68 : ATExecSetCompression(Relation rel,
17737 : const char *column,
17738 : Node *newValue,
17739 : LOCKMODE lockmode)
17740 : {
17741 : Relation attrel;
17742 : HeapTuple tuple;
17743 : Form_pg_attribute atttableform;
17744 : AttrNumber attnum;
17745 : char *compression;
17746 : char cmethod;
17747 : ObjectAddress address;
17748 :
17749 68 : compression = strVal(newValue);
17750 :
17751 68 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
17752 :
17753 : /* copy the cache entry so we can scribble on it below */
17754 68 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17755 68 : if (!HeapTupleIsValid(tuple))
17756 0 : ereport(ERROR,
17757 : (errcode(ERRCODE_UNDEFINED_COLUMN),
17758 : errmsg("column \"%s\" of relation \"%s\" does not exist",
17759 : column, RelationGetRelationName(rel))));
17760 :
17761 : /* prevent them from altering a system attribute */
17762 68 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17763 68 : attnum = atttableform->attnum;
17764 68 : if (attnum <= 0)
17765 0 : ereport(ERROR,
17766 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17767 : errmsg("cannot alter system column \"%s\"", column)));
17768 :
17769 : /*
17770 : * Check that column type is compressible, then get the attribute
17771 : * compression method code
17772 : */
17773 68 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17774 :
17775 : /* update pg_attribute entry */
17776 62 : atttableform->attcompression = cmethod;
17777 62 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17778 :
17779 62 : InvokeObjectPostAlterHook(RelationRelationId,
17780 : RelationGetRelid(rel),
17781 : attnum);
17782 :
17783 : /*
17784 : * Apply the change to indexes as well (only for simple index columns,
17785 : * matching behavior of index.c ConstructTupleDescriptor()).
17786 : */
17787 62 : SetIndexStorageProperties(rel, attrel, attnum,
17788 : false, 0,
17789 : true, cmethod,
17790 : lockmode);
17791 :
17792 62 : heap_freetuple(tuple);
17793 :
17794 62 : table_close(attrel, RowExclusiveLock);
17795 :
17796 : /* make changes visible */
17797 62 : CommandCounterIncrement();
17798 :
17799 62 : ObjectAddressSubSet(address, RelationRelationId,
17800 : RelationGetRelid(rel), attnum);
17801 62 : return address;
17802 : }
17803 :
17804 :
17805 : /*
17806 : * Preparation phase for SET LOGGED/UNLOGGED
17807 : *
17808 : * This verifies that we're not trying to change a temp table. Also,
17809 : * existing foreign key constraints are checked to avoid ending up with
17810 : * permanent tables referencing unlogged tables.
17811 : *
17812 : * Return value is false if the operation is a no-op (in which case the
17813 : * checks are skipped), otherwise true.
17814 : */
17815 : static bool
17816 88 : ATPrepChangePersistence(Relation rel, bool toLogged)
17817 : {
17818 : Relation pg_constraint;
17819 : HeapTuple tuple;
17820 : SysScanDesc scan;
17821 : ScanKeyData skey[1];
17822 :
17823 : /*
17824 : * Disallow changing status for a temp table. Also verify whether we can
17825 : * get away with doing nothing; in such cases we don't need to run the
17826 : * checks below, either.
17827 : */
17828 88 : switch (rel->rd_rel->relpersistence)
17829 : {
17830 0 : case RELPERSISTENCE_TEMP:
17831 0 : ereport(ERROR,
17832 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17833 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
17834 : RelationGetRelationName(rel)),
17835 : errtable(rel)));
17836 : break;
17837 50 : case RELPERSISTENCE_PERMANENT:
17838 50 : if (toLogged)
17839 : /* nothing to do */
17840 6 : return false;
17841 44 : break;
17842 38 : case RELPERSISTENCE_UNLOGGED:
17843 38 : if (!toLogged)
17844 : /* nothing to do */
17845 6 : return false;
17846 32 : break;
17847 : }
17848 :
17849 : /*
17850 : * Check that the table is not part of any publication when changing to
17851 : * UNLOGGED, as UNLOGGED tables can't be published.
17852 : */
17853 120 : if (!toLogged &&
17854 44 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
17855 0 : ereport(ERROR,
17856 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17857 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17858 : RelationGetRelationName(rel)),
17859 : errdetail("Unlogged relations cannot be replicated.")));
17860 :
17861 : /*
17862 : * Check existing foreign key constraints to preserve the invariant that
17863 : * permanent tables cannot reference unlogged ones. Self-referencing
17864 : * foreign keys can safely be ignored.
17865 : */
17866 76 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17867 :
17868 : /*
17869 : * Scan conrelid if changing to permanent, else confrelid. This also
17870 : * determines whether a useful index exists.
17871 : */
17872 76 : ScanKeyInit(&skey[0],
17873 : toLogged ? Anum_pg_constraint_conrelid :
17874 : Anum_pg_constraint_confrelid,
17875 : BTEqualStrategyNumber, F_OIDEQ,
17876 : ObjectIdGetDatum(RelationGetRelid(rel)));
17877 76 : scan = systable_beginscan(pg_constraint,
17878 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17879 : true, NULL, 1, skey);
17880 :
17881 130 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17882 : {
17883 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17884 :
17885 66 : if (con->contype == CONSTRAINT_FOREIGN)
17886 : {
17887 : Oid foreignrelid;
17888 : Relation foreignrel;
17889 :
17890 : /* the opposite end of what we used as scankey */
17891 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
17892 :
17893 : /* ignore if self-referencing */
17894 30 : if (RelationGetRelid(rel) == foreignrelid)
17895 12 : continue;
17896 :
17897 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
17898 :
17899 18 : if (toLogged)
17900 : {
17901 6 : if (!RelationIsPermanent(foreignrel))
17902 6 : ereport(ERROR,
17903 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17904 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17905 : RelationGetRelationName(rel),
17906 : RelationGetRelationName(foreignrel)),
17907 : errtableconstraint(rel, NameStr(con->conname))));
17908 : }
17909 : else
17910 : {
17911 12 : if (RelationIsPermanent(foreignrel))
17912 6 : ereport(ERROR,
17913 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17914 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17915 : RelationGetRelationName(rel),
17916 : RelationGetRelationName(foreignrel)),
17917 : errtableconstraint(rel, NameStr(con->conname))));
17918 : }
17919 :
17920 6 : relation_close(foreignrel, AccessShareLock);
17921 : }
17922 : }
17923 :
17924 64 : systable_endscan(scan);
17925 :
17926 64 : table_close(pg_constraint, AccessShareLock);
17927 :
17928 64 : return true;
17929 : }
17930 :
17931 : /*
17932 : * Execute ALTER TABLE SET SCHEMA
17933 : */
17934 : ObjectAddress
17935 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17936 : {
17937 : Relation rel;
17938 : Oid relid;
17939 : Oid oldNspOid;
17940 : Oid nspOid;
17941 : RangeVar *newrv;
17942 : ObjectAddresses *objsMoved;
17943 : ObjectAddress myself;
17944 :
17945 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17946 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
17947 : RangeVarCallbackForAlterRelation,
17948 : (void *) stmt);
17949 :
17950 102 : if (!OidIsValid(relid))
17951 : {
17952 12 : ereport(NOTICE,
17953 : (errmsg("relation \"%s\" does not exist, skipping",
17954 : stmt->relation->relname)));
17955 12 : return InvalidObjectAddress;
17956 : }
17957 :
17958 90 : rel = relation_open(relid, NoLock);
17959 :
17960 90 : oldNspOid = RelationGetNamespace(rel);
17961 :
17962 : /* If it's an owned sequence, disallow moving it by itself. */
17963 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17964 : {
17965 : Oid tableId;
17966 : int32 colId;
17967 :
17968 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17969 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17970 6 : ereport(ERROR,
17971 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17972 : errmsg("cannot move an owned sequence into another schema"),
17973 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
17974 : RelationGetRelationName(rel),
17975 : get_rel_name(tableId))));
17976 : }
17977 :
17978 : /* Get and lock schema OID and check its permissions. */
17979 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17980 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17981 :
17982 : /* common checks on switching namespaces */
17983 84 : CheckSetNamespace(oldNspOid, nspOid);
17984 :
17985 84 : objsMoved = new_object_addresses();
17986 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17987 84 : free_object_addresses(objsMoved);
17988 :
17989 84 : ObjectAddressSet(myself, RelationRelationId, relid);
17990 :
17991 84 : if (oldschema)
17992 84 : *oldschema = oldNspOid;
17993 :
17994 : /* close rel, but keep lock until commit */
17995 84 : relation_close(rel, NoLock);
17996 :
17997 84 : return myself;
17998 : }
17999 :
18000 : /*
18001 : * The guts of relocating a table or materialized view to another namespace:
18002 : * besides moving the relation itself, its dependent objects are relocated to
18003 : * the new schema.
18004 : */
18005 : void
18006 84 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
18007 : ObjectAddresses *objsMoved)
18008 : {
18009 : Relation classRel;
18010 :
18011 : Assert(objsMoved != NULL);
18012 :
18013 : /* OK, modify the pg_class row and pg_depend entry */
18014 84 : classRel = table_open(RelationRelationId, RowExclusiveLock);
18015 :
18016 84 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18017 : nspOid, true, objsMoved);
18018 :
18019 : /* Fix the table's row type too, if it has one */
18020 84 : if (OidIsValid(rel->rd_rel->reltype))
18021 82 : AlterTypeNamespaceInternal(rel->rd_rel->reltype,
18022 : nspOid, false, false, objsMoved);
18023 :
18024 : /* Fix other dependent stuff */
18025 84 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18026 84 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18027 : objsMoved, AccessExclusiveLock);
18028 84 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18029 : false, objsMoved);
18030 :
18031 84 : table_close(classRel, RowExclusiveLock);
18032 84 : }
18033 :
18034 : /*
18035 : * The guts of relocating a relation to another namespace: fix the pg_class
18036 : * entry, and the pg_depend entry if any. Caller must already have
18037 : * opened and write-locked pg_class.
18038 : */
18039 : void
18040 182 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
18041 : Oid oldNspOid, Oid newNspOid,
18042 : bool hasDependEntry,
18043 : ObjectAddresses *objsMoved)
18044 : {
18045 : HeapTuple classTup;
18046 : Form_pg_class classForm;
18047 : ObjectAddress thisobj;
18048 182 : bool already_done = false;
18049 :
18050 182 : classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
18051 182 : if (!HeapTupleIsValid(classTup))
18052 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
18053 182 : classForm = (Form_pg_class) GETSTRUCT(classTup);
18054 :
18055 : Assert(classForm->relnamespace == oldNspOid);
18056 :
18057 182 : thisobj.classId = RelationRelationId;
18058 182 : thisobj.objectId = relOid;
18059 182 : thisobj.objectSubId = 0;
18060 :
18061 : /*
18062 : * If the object has already been moved, don't move it again. If it's
18063 : * already in the right place, don't move it, but still fire the object
18064 : * access hook.
18065 : */
18066 182 : already_done = object_address_present(&thisobj, objsMoved);
18067 182 : if (!already_done && oldNspOid != newNspOid)
18068 : {
18069 : /* check for duplicate name (more friendly than unique-index failure) */
18070 140 : if (get_relname_relid(NameStr(classForm->relname),
18071 : newNspOid) != InvalidOid)
18072 0 : ereport(ERROR,
18073 : (errcode(ERRCODE_DUPLICATE_TABLE),
18074 : errmsg("relation \"%s\" already exists in schema \"%s\"",
18075 : NameStr(classForm->relname),
18076 : get_namespace_name(newNspOid))));
18077 :
18078 : /* classTup is a copy, so OK to scribble on */
18079 140 : classForm->relnamespace = newNspOid;
18080 :
18081 140 : CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
18082 :
18083 : /* Update dependency on schema if caller said so */
18084 242 : if (hasDependEntry &&
18085 102 : changeDependencyFor(RelationRelationId,
18086 : relOid,
18087 : NamespaceRelationId,
18088 : oldNspOid,
18089 : newNspOid) != 1)
18090 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
18091 : NameStr(classForm->relname));
18092 : }
18093 182 : if (!already_done)
18094 : {
18095 182 : add_exact_object_address(&thisobj, objsMoved);
18096 :
18097 182 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18098 : }
18099 :
18100 182 : heap_freetuple(classTup);
18101 182 : }
18102 :
18103 : /*
18104 : * Move all indexes for the specified relation to another namespace.
18105 : *
18106 : * Note: we assume adequate permission checking was done by the caller,
18107 : * and that the caller has a suitable lock on the owning relation.
18108 : */
18109 : static void
18110 84 : AlterIndexNamespaces(Relation classRel, Relation rel,
18111 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
18112 : {
18113 : List *indexList;
18114 : ListCell *l;
18115 :
18116 84 : indexList = RelationGetIndexList(rel);
18117 :
18118 128 : foreach(l, indexList)
18119 : {
18120 44 : Oid indexOid = lfirst_oid(l);
18121 : ObjectAddress thisobj;
18122 :
18123 44 : thisobj.classId = RelationRelationId;
18124 44 : thisobj.objectId = indexOid;
18125 44 : thisobj.objectSubId = 0;
18126 :
18127 : /*
18128 : * Note: currently, the index will not have its own dependency on the
18129 : * namespace, so we don't need to do changeDependencyFor(). There's no
18130 : * row type in pg_type, either.
18131 : *
18132 : * XXX this objsMoved test may be pointless -- surely we have a single
18133 : * dependency link from a relation to each index?
18134 : */
18135 44 : if (!object_address_present(&thisobj, objsMoved))
18136 : {
18137 44 : AlterRelationNamespaceInternal(classRel, indexOid,
18138 : oldNspOid, newNspOid,
18139 : false, objsMoved);
18140 44 : add_exact_object_address(&thisobj, objsMoved);
18141 : }
18142 : }
18143 :
18144 84 : list_free(indexList);
18145 84 : }
18146 :
18147 : /*
18148 : * Move all identity and SERIAL-column sequences of the specified relation to another
18149 : * namespace.
18150 : *
18151 : * Note: we assume adequate permission checking was done by the caller,
18152 : * and that the caller has a suitable lock on the owning relation.
18153 : */
18154 : static void
18155 84 : AlterSeqNamespaces(Relation classRel, Relation rel,
18156 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
18157 : LOCKMODE lockmode)
18158 : {
18159 : Relation depRel;
18160 : SysScanDesc scan;
18161 : ScanKeyData key[2];
18162 : HeapTuple tup;
18163 :
18164 : /*
18165 : * SERIAL sequences are those having an auto dependency on one of the
18166 : * table's columns (we don't care *which* column, exactly).
18167 : */
18168 84 : depRel = table_open(DependRelationId, AccessShareLock);
18169 :
18170 84 : ScanKeyInit(&key[0],
18171 : Anum_pg_depend_refclassid,
18172 : BTEqualStrategyNumber, F_OIDEQ,
18173 : ObjectIdGetDatum(RelationRelationId));
18174 84 : ScanKeyInit(&key[1],
18175 : Anum_pg_depend_refobjid,
18176 : BTEqualStrategyNumber, F_OIDEQ,
18177 : ObjectIdGetDatum(RelationGetRelid(rel)));
18178 : /* we leave refobjsubid unspecified */
18179 :
18180 84 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18181 : NULL, 2, key);
18182 :
18183 588 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
18184 : {
18185 504 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18186 : Relation seqRel;
18187 :
18188 : /* skip dependencies other than auto dependencies on columns */
18189 504 : if (depForm->refobjsubid == 0 ||
18190 360 : depForm->classid != RelationRelationId ||
18191 42 : depForm->objsubid != 0 ||
18192 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18193 462 : continue;
18194 :
18195 : /* Use relation_open just in case it's an index */
18196 42 : seqRel = relation_open(depForm->objid, lockmode);
18197 :
18198 : /* skip non-sequence relations */
18199 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18200 : {
18201 : /* No need to keep the lock */
18202 0 : relation_close(seqRel, lockmode);
18203 0 : continue;
18204 : }
18205 :
18206 : /* Fix the pg_class and pg_depend entries */
18207 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
18208 : oldNspOid, newNspOid,
18209 : true, objsMoved);
18210 :
18211 : /*
18212 : * Sequences used to have entries in pg_type, but no longer do. If we
18213 : * ever re-instate that, we'll need to move the pg_type entry to the
18214 : * new namespace, too (using AlterTypeNamespaceInternal).
18215 : */
18216 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18217 :
18218 : /* Now we can close it. Keep the lock till end of transaction. */
18219 42 : relation_close(seqRel, NoLock);
18220 : }
18221 :
18222 84 : systable_endscan(scan);
18223 :
18224 84 : relation_close(depRel, AccessShareLock);
18225 84 : }
18226 :
18227 :
18228 : /*
18229 : * This code supports
18230 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18231 : *
18232 : * Because we only support this for TEMP tables, it's sufficient to remember
18233 : * the state in a backend-local data structure.
18234 : */
18235 :
18236 : /*
18237 : * Register a newly-created relation's ON COMMIT action.
18238 : */
18239 : void
18240 166 : register_on_commit_action(Oid relid, OnCommitAction action)
18241 : {
18242 : OnCommitItem *oc;
18243 : MemoryContext oldcxt;
18244 :
18245 : /*
18246 : * We needn't bother registering the relation unless there is an ON COMMIT
18247 : * action we need to take.
18248 : */
18249 166 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18250 24 : return;
18251 :
18252 142 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18253 :
18254 142 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18255 142 : oc->relid = relid;
18256 142 : oc->oncommit = action;
18257 142 : oc->creating_subid = GetCurrentSubTransactionId();
18258 142 : oc->deleting_subid = InvalidSubTransactionId;
18259 :
18260 : /*
18261 : * We use lcons() here so that ON COMMIT actions are processed in reverse
18262 : * order of registration. That might not be essential but it seems
18263 : * reasonable.
18264 : */
18265 142 : on_commits = lcons(oc, on_commits);
18266 :
18267 142 : MemoryContextSwitchTo(oldcxt);
18268 : }
18269 :
18270 : /*
18271 : * Unregister any ON COMMIT action when a relation is deleted.
18272 : *
18273 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18274 : */
18275 : void
18276 42556 : remove_on_commit_action(Oid relid)
18277 : {
18278 : ListCell *l;
18279 :
18280 42690 : foreach(l, on_commits)
18281 : {
18282 264 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18283 :
18284 264 : if (oc->relid == relid)
18285 : {
18286 130 : oc->deleting_subid = GetCurrentSubTransactionId();
18287 130 : break;
18288 : }
18289 : }
18290 42556 : }
18291 :
18292 : /*
18293 : * Perform ON COMMIT actions.
18294 : *
18295 : * This is invoked just before actually committing, since it's possible
18296 : * to encounter errors.
18297 : */
18298 : void
18299 520696 : PreCommit_on_commit_actions(void)
18300 : {
18301 : ListCell *l;
18302 520696 : List *oids_to_truncate = NIL;
18303 520696 : List *oids_to_drop = NIL;
18304 :
18305 521412 : foreach(l, on_commits)
18306 : {
18307 716 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18308 :
18309 : /* Ignore entry if already dropped in this xact */
18310 716 : if (oc->deleting_subid != InvalidSubTransactionId)
18311 68 : continue;
18312 :
18313 648 : switch (oc->oncommit)
18314 : {
18315 0 : case ONCOMMIT_NOOP:
18316 : case ONCOMMIT_PRESERVE_ROWS:
18317 : /* Do nothing (there shouldn't be such entries, actually) */
18318 0 : break;
18319 598 : case ONCOMMIT_DELETE_ROWS:
18320 :
18321 : /*
18322 : * If this transaction hasn't accessed any temporary
18323 : * relations, we can skip truncating ON COMMIT DELETE ROWS
18324 : * tables, as they must still be empty.
18325 : */
18326 598 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18327 400 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18328 598 : break;
18329 50 : case ONCOMMIT_DROP:
18330 50 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18331 50 : break;
18332 : }
18333 716 : }
18334 :
18335 : /*
18336 : * Truncate relations before dropping so that all dependencies between
18337 : * relations are removed after they are worked on. Doing it like this
18338 : * might be a waste as it is possible that a relation being truncated will
18339 : * be dropped anyway due to its parent being dropped, but this makes the
18340 : * code more robust because of not having to re-check that the relation
18341 : * exists at truncation time.
18342 : */
18343 520696 : if (oids_to_truncate != NIL)
18344 334 : heap_truncate(oids_to_truncate);
18345 :
18346 520690 : if (oids_to_drop != NIL)
18347 : {
18348 44 : ObjectAddresses *targetObjects = new_object_addresses();
18349 :
18350 94 : foreach(l, oids_to_drop)
18351 : {
18352 : ObjectAddress object;
18353 :
18354 50 : object.classId = RelationRelationId;
18355 50 : object.objectId = lfirst_oid(l);
18356 50 : object.objectSubId = 0;
18357 :
18358 : Assert(!object_address_present(&object, targetObjects));
18359 :
18360 50 : add_exact_object_address(&object, targetObjects);
18361 : }
18362 :
18363 : /*
18364 : * Object deletion might involve toast table access (to clean up
18365 : * toasted catalog entries), so ensure we have a valid snapshot.
18366 : */
18367 44 : PushActiveSnapshot(GetTransactionSnapshot());
18368 :
18369 : /*
18370 : * Since this is an automatic drop, rather than one directly initiated
18371 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18372 : */
18373 44 : performMultipleDeletions(targetObjects, DROP_CASCADE,
18374 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18375 :
18376 44 : PopActiveSnapshot();
18377 :
18378 : #ifdef USE_ASSERT_CHECKING
18379 :
18380 : /*
18381 : * Note that table deletion will call remove_on_commit_action, so the
18382 : * entry should get marked as deleted.
18383 : */
18384 : foreach(l, on_commits)
18385 : {
18386 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18387 :
18388 : if (oc->oncommit != ONCOMMIT_DROP)
18389 : continue;
18390 :
18391 : Assert(oc->deleting_subid != InvalidSubTransactionId);
18392 : }
18393 : #endif
18394 : }
18395 520690 : }
18396 :
18397 : /*
18398 : * Post-commit or post-abort cleanup for ON COMMIT management.
18399 : *
18400 : * All we do here is remove no-longer-needed OnCommitItem entries.
18401 : *
18402 : * During commit, remove entries that were deleted during this transaction;
18403 : * during abort, remove those created during this transaction.
18404 : */
18405 : void
18406 565496 : AtEOXact_on_commit_actions(bool isCommit)
18407 : {
18408 : ListCell *cur_item;
18409 :
18410 566242 : foreach(cur_item, on_commits)
18411 : {
18412 746 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18413 :
18414 848 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18415 102 : oc->creating_subid != InvalidSubTransactionId)
18416 : {
18417 : /* cur_item must be removed */
18418 142 : on_commits = foreach_delete_current(on_commits, cur_item);
18419 142 : pfree(oc);
18420 : }
18421 : else
18422 : {
18423 : /* cur_item must be preserved */
18424 604 : oc->creating_subid = InvalidSubTransactionId;
18425 604 : oc->deleting_subid = InvalidSubTransactionId;
18426 : }
18427 : }
18428 565496 : }
18429 :
18430 : /*
18431 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18432 : *
18433 : * During subabort, we can immediately remove entries created during this
18434 : * subtransaction. During subcommit, just relabel entries marked during
18435 : * this subtransaction as being the parent's responsibility.
18436 : */
18437 : void
18438 18036 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18439 : SubTransactionId parentSubid)
18440 : {
18441 : ListCell *cur_item;
18442 :
18443 18036 : foreach(cur_item, on_commits)
18444 : {
18445 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18446 :
18447 0 : if (!isCommit && oc->creating_subid == mySubid)
18448 : {
18449 : /* cur_item must be removed */
18450 0 : on_commits = foreach_delete_current(on_commits, cur_item);
18451 0 : pfree(oc);
18452 : }
18453 : else
18454 : {
18455 : /* cur_item must be preserved */
18456 0 : if (oc->creating_subid == mySubid)
18457 0 : oc->creating_subid = parentSubid;
18458 0 : if (oc->deleting_subid == mySubid)
18459 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18460 : }
18461 : }
18462 18036 : }
18463 :
18464 : /*
18465 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18466 : * the relation to be locked only if (1) it's a plain or partitioned table,
18467 : * materialized view, or TOAST table and (2) the current user is the owner (or
18468 : * the superuser) or has been granted MAINTAIN. This meets the
18469 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18470 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18471 : */
18472 : void
18473 980 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
18474 : Oid relId, Oid oldRelId, void *arg)
18475 : {
18476 : char relkind;
18477 : AclResult aclresult;
18478 :
18479 : /* Nothing to do if the relation was not found. */
18480 980 : if (!OidIsValid(relId))
18481 6 : return;
18482 :
18483 : /*
18484 : * If the relation does exist, check whether it's an index. But note that
18485 : * the relation might have been dropped between the time we did the name
18486 : * lookup and now. In that case, there's nothing to do.
18487 : */
18488 974 : relkind = get_rel_relkind(relId);
18489 974 : if (!relkind)
18490 0 : return;
18491 974 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18492 136 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18493 28 : ereport(ERROR,
18494 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18495 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18496 :
18497 : /* Check permissions */
18498 946 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18499 946 : if (aclresult != ACLCHECK_OK)
18500 30 : aclcheck_error(aclresult,
18501 30 : get_relkind_objtype(get_rel_relkind(relId)),
18502 30 : relation->relname);
18503 : }
18504 :
18505 : /*
18506 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18507 : */
18508 : static void
18509 1790 : RangeVarCallbackForTruncate(const RangeVar *relation,
18510 : Oid relId, Oid oldRelId, void *arg)
18511 : {
18512 : HeapTuple tuple;
18513 :
18514 : /* Nothing to do if the relation was not found. */
18515 1790 : if (!OidIsValid(relId))
18516 0 : return;
18517 :
18518 1790 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18519 1790 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18520 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18521 :
18522 1790 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18523 1786 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18524 :
18525 1754 : ReleaseSysCache(tuple);
18526 : }
18527 :
18528 : /*
18529 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18530 : * the owner of the relation, or superuser.
18531 : */
18532 : void
18533 14762 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
18534 : Oid relId, Oid oldRelId, void *arg)
18535 : {
18536 : HeapTuple tuple;
18537 :
18538 : /* Nothing to do if the relation was not found. */
18539 14762 : if (!OidIsValid(relId))
18540 12 : return;
18541 :
18542 14750 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18543 14750 : if (!HeapTupleIsValid(tuple)) /* should not happen */
18544 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
18545 :
18546 14750 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18547 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18548 6 : relation->relname);
18549 :
18550 29368 : if (!allowSystemTableMods &&
18551 14624 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18552 2 : ereport(ERROR,
18553 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18554 : errmsg("permission denied: \"%s\" is a system catalog",
18555 : relation->relname)));
18556 :
18557 14742 : ReleaseSysCache(tuple);
18558 : }
18559 :
18560 : /*
18561 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
18562 : * processing.
18563 : */
18564 : static void
18565 36000 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18566 : void *arg)
18567 : {
18568 36000 : Node *stmt = (Node *) arg;
18569 : ObjectType reltype;
18570 : HeapTuple tuple;
18571 : Form_pg_class classform;
18572 : AclResult aclresult;
18573 : char relkind;
18574 :
18575 36000 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18576 36000 : if (!HeapTupleIsValid(tuple))
18577 222 : return; /* concurrently dropped */
18578 35778 : classform = (Form_pg_class) GETSTRUCT(tuple);
18579 35778 : relkind = classform->relkind;
18580 :
18581 : /* Must own relation. */
18582 35778 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18583 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18584 :
18585 : /* No system table modifications unless explicitly allowed. */
18586 35718 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
18587 28 : ereport(ERROR,
18588 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18589 : errmsg("permission denied: \"%s\" is a system catalog",
18590 : rv->relname)));
18591 :
18592 : /*
18593 : * Extract the specified relation type from the statement parse tree.
18594 : *
18595 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
18596 : * have CREATE rights on the containing namespace.
18597 : */
18598 35690 : if (IsA(stmt, RenameStmt))
18599 : {
18600 482 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18601 : GetUserId(), ACL_CREATE);
18602 482 : if (aclresult != ACLCHECK_OK)
18603 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
18604 0 : get_namespace_name(classform->relnamespace));
18605 482 : reltype = ((RenameStmt *) stmt)->renameType;
18606 : }
18607 35208 : else if (IsA(stmt, AlterObjectSchemaStmt))
18608 90 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18609 :
18610 35118 : else if (IsA(stmt, AlterTableStmt))
18611 35118 : reltype = ((AlterTableStmt *) stmt)->objtype;
18612 : else
18613 : {
18614 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18615 : reltype = OBJECT_TABLE; /* placate compiler */
18616 : }
18617 :
18618 : /*
18619 : * For compatibility with prior releases, we allow ALTER TABLE to be used
18620 : * with most other types of relations (but not composite types). We allow
18621 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
18622 : * otherwise. Otherwise, the user must select the correct form of the
18623 : * command for the relation at issue.
18624 : */
18625 35690 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18626 0 : ereport(ERROR,
18627 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18628 : errmsg("\"%s\" is not a sequence", rv->relname)));
18629 :
18630 35690 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18631 0 : ereport(ERROR,
18632 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18633 : errmsg("\"%s\" is not a view", rv->relname)));
18634 :
18635 35690 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18636 0 : ereport(ERROR,
18637 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18638 : errmsg("\"%s\" is not a materialized view", rv->relname)));
18639 :
18640 35690 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18641 0 : ereport(ERROR,
18642 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18643 : errmsg("\"%s\" is not a foreign table", rv->relname)));
18644 :
18645 35690 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18646 0 : ereport(ERROR,
18647 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18648 : errmsg("\"%s\" is not a composite type", rv->relname)));
18649 :
18650 35690 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18651 : relkind != RELKIND_PARTITIONED_INDEX
18652 32 : && !IsA(stmt, RenameStmt))
18653 6 : ereport(ERROR,
18654 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18655 : errmsg("\"%s\" is not an index", rv->relname)));
18656 :
18657 : /*
18658 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18659 : * TYPE for that.
18660 : */
18661 35684 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18662 0 : ereport(ERROR,
18663 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18664 : errmsg("\"%s\" is a composite type", rv->relname),
18665 : /* translator: %s is an SQL ALTER command */
18666 : errhint("Use %s instead.",
18667 : "ALTER TYPE")));
18668 :
18669 : /*
18670 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18671 : * to a different schema, such as indexes and TOAST tables.
18672 : */
18673 35684 : if (IsA(stmt, AlterObjectSchemaStmt))
18674 : {
18675 90 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18676 0 : ereport(ERROR,
18677 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18678 : errmsg("cannot change schema of index \"%s\"",
18679 : rv->relname),
18680 : errhint("Change the schema of the table instead.")));
18681 90 : else if (relkind == RELKIND_COMPOSITE_TYPE)
18682 0 : ereport(ERROR,
18683 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18684 : errmsg("cannot change schema of composite type \"%s\"",
18685 : rv->relname),
18686 : /* translator: %s is an SQL ALTER command */
18687 : errhint("Use %s instead.",
18688 : "ALTER TYPE")));
18689 90 : else if (relkind == RELKIND_TOASTVALUE)
18690 0 : ereport(ERROR,
18691 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18692 : errmsg("cannot change schema of TOAST table \"%s\"",
18693 : rv->relname),
18694 : errhint("Change the schema of the table instead.")));
18695 : }
18696 :
18697 35684 : ReleaseSysCache(tuple);
18698 : }
18699 :
18700 : /*
18701 : * Transform any expressions present in the partition key
18702 : *
18703 : * Returns a transformed PartitionSpec.
18704 : */
18705 : static PartitionSpec *
18706 4900 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18707 : {
18708 : PartitionSpec *newspec;
18709 : ParseState *pstate;
18710 : ParseNamespaceItem *nsitem;
18711 : ListCell *l;
18712 :
18713 4900 : newspec = makeNode(PartitionSpec);
18714 :
18715 4900 : newspec->strategy = partspec->strategy;
18716 4900 : newspec->partParams = NIL;
18717 4900 : newspec->location = partspec->location;
18718 :
18719 : /* Check valid number of columns for strategy */
18720 7236 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18721 2336 : list_length(partspec->partParams) != 1)
18722 6 : ereport(ERROR,
18723 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18724 : errmsg("cannot use \"list\" partition strategy with more than one column")));
18725 :
18726 : /*
18727 : * Create a dummy ParseState and insert the target relation as its sole
18728 : * rangetable entry. We need a ParseState for transformExpr.
18729 : */
18730 4894 : pstate = make_parsestate(NULL);
18731 4894 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18732 : NULL, false, true);
18733 4894 : addNSItemToQuery(pstate, nsitem, true, true, true);
18734 :
18735 : /* take care of any partition expressions */
18736 10220 : foreach(l, partspec->partParams)
18737 : {
18738 5350 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
18739 :
18740 5350 : if (pelem->expr)
18741 : {
18742 : /* Copy, to avoid scribbling on the input */
18743 298 : pelem = copyObject(pelem);
18744 :
18745 : /* Now do parse transformation of the expression */
18746 298 : pelem->expr = transformExpr(pstate, pelem->expr,
18747 : EXPR_KIND_PARTITION_EXPRESSION);
18748 :
18749 : /* we have to fix its collations too */
18750 274 : assign_expr_collations(pstate, pelem->expr);
18751 : }
18752 :
18753 5326 : newspec->partParams = lappend(newspec->partParams, pelem);
18754 : }
18755 :
18756 4870 : return newspec;
18757 : }
18758 :
18759 : /*
18760 : * Compute per-partition-column information from a list of PartitionElems.
18761 : * Expressions in the PartitionElems must be parse-analyzed already.
18762 : */
18763 : static void
18764 4870 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18765 : List **partexprs, Oid *partopclass, Oid *partcollation,
18766 : PartitionStrategy strategy)
18767 : {
18768 : int attn;
18769 : ListCell *lc;
18770 : Oid am_oid;
18771 :
18772 4870 : attn = 0;
18773 10112 : foreach(lc, partParams)
18774 : {
18775 5326 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18776 : Oid atttype;
18777 : Oid attcollation;
18778 :
18779 5326 : if (pelem->name != NULL)
18780 : {
18781 : /* Simple attribute reference */
18782 : HeapTuple atttuple;
18783 : Form_pg_attribute attform;
18784 :
18785 5052 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18786 5052 : pelem->name);
18787 5052 : if (!HeapTupleIsValid(atttuple))
18788 12 : ereport(ERROR,
18789 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18790 : errmsg("column \"%s\" named in partition key does not exist",
18791 : pelem->name),
18792 : parser_errposition(pstate, pelem->location)));
18793 5040 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18794 :
18795 5040 : if (attform->attnum <= 0)
18796 6 : ereport(ERROR,
18797 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18798 : errmsg("cannot use system column \"%s\" in partition key",
18799 : pelem->name),
18800 : parser_errposition(pstate, pelem->location)));
18801 :
18802 : /*
18803 : * Generated columns cannot work: They are computed after BEFORE
18804 : * triggers, but partition routing is done before all triggers.
18805 : */
18806 5034 : if (attform->attgenerated)
18807 6 : ereport(ERROR,
18808 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18809 : errmsg("cannot use generated column in partition key"),
18810 : errdetail("Column \"%s\" is a generated column.",
18811 : pelem->name),
18812 : parser_errposition(pstate, pelem->location)));
18813 :
18814 5028 : partattrs[attn] = attform->attnum;
18815 5028 : atttype = attform->atttypid;
18816 5028 : attcollation = attform->attcollation;
18817 5028 : ReleaseSysCache(atttuple);
18818 : }
18819 : else
18820 : {
18821 : /* Expression */
18822 274 : Node *expr = pelem->expr;
18823 : char partattname[16];
18824 :
18825 : Assert(expr != NULL);
18826 274 : atttype = exprType(expr);
18827 274 : attcollation = exprCollation(expr);
18828 :
18829 : /*
18830 : * The expression must be of a storable type (e.g., not RECORD).
18831 : * The test is the same as for whether a table column is of a safe
18832 : * type (which is why we needn't check for the non-expression
18833 : * case).
18834 : */
18835 274 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18836 274 : CheckAttributeType(partattname,
18837 : atttype, attcollation,
18838 : NIL, CHKATYPE_IS_PARTKEY);
18839 :
18840 : /*
18841 : * Strip any top-level COLLATE clause. This ensures that we treat
18842 : * "x COLLATE y" and "(x COLLATE y)" alike.
18843 : */
18844 262 : while (IsA(expr, CollateExpr))
18845 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
18846 :
18847 262 : if (IsA(expr, Var) &&
18848 12 : ((Var *) expr)->varattno > 0)
18849 : {
18850 : /*
18851 : * User wrote "(column)" or "(column COLLATE something)".
18852 : * Treat it like simple attribute anyway.
18853 : */
18854 6 : partattrs[attn] = ((Var *) expr)->varattno;
18855 : }
18856 : else
18857 : {
18858 256 : Bitmapset *expr_attrs = NULL;
18859 : int i;
18860 :
18861 256 : partattrs[attn] = 0; /* marks the column as expression */
18862 256 : *partexprs = lappend(*partexprs, expr);
18863 :
18864 : /*
18865 : * transformPartitionSpec() should have already rejected
18866 : * subqueries, aggregates, window functions, and SRFs, based
18867 : * on the EXPR_KIND_ for partition expressions.
18868 : */
18869 :
18870 : /*
18871 : * Cannot allow system column references, since that would
18872 : * make partition routing impossible: their values won't be
18873 : * known yet when we need to do that.
18874 : */
18875 256 : pull_varattnos(expr, 1, &expr_attrs);
18876 2048 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18877 : {
18878 1792 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18879 : expr_attrs))
18880 0 : ereport(ERROR,
18881 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18882 : errmsg("partition key expressions cannot contain system column references")));
18883 : }
18884 :
18885 : /*
18886 : * Generated columns cannot work: They are computed after
18887 : * BEFORE triggers, but partition routing is done before all
18888 : * triggers.
18889 : */
18890 256 : i = -1;
18891 564 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
18892 : {
18893 314 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18894 :
18895 314 : if (attno > 0 &&
18896 308 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18897 6 : ereport(ERROR,
18898 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18899 : errmsg("cannot use generated column in partition key"),
18900 : errdetail("Column \"%s\" is a generated column.",
18901 : get_attname(RelationGetRelid(rel), attno, false)),
18902 : parser_errposition(pstate, pelem->location)));
18903 : }
18904 :
18905 : /*
18906 : * Preprocess the expression before checking for mutability.
18907 : * This is essential for the reasons described in
18908 : * contain_mutable_functions_after_planning. However, we call
18909 : * expression_planner for ourselves rather than using that
18910 : * function, because if constant-folding reduces the
18911 : * expression to a constant, we'd like to know that so we can
18912 : * complain below.
18913 : *
18914 : * Like contain_mutable_functions_after_planning, assume that
18915 : * expression_planner won't scribble on its input, so this
18916 : * won't affect the partexprs entry we saved above.
18917 : */
18918 250 : expr = (Node *) expression_planner((Expr *) expr);
18919 :
18920 : /*
18921 : * Partition expressions cannot contain mutable functions,
18922 : * because a given row must always map to the same partition
18923 : * as long as there is no change in the partition boundary
18924 : * structure.
18925 : */
18926 250 : if (contain_mutable_functions(expr))
18927 6 : ereport(ERROR,
18928 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18929 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
18930 :
18931 : /*
18932 : * While it is not exactly *wrong* for a partition expression
18933 : * to be a constant, it seems better to reject such keys.
18934 : */
18935 244 : if (IsA(expr, Const))
18936 12 : ereport(ERROR,
18937 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18938 : errmsg("cannot use constant expression as partition key")));
18939 : }
18940 : }
18941 :
18942 : /*
18943 : * Apply collation override if any
18944 : */
18945 5266 : if (pelem->collation)
18946 30 : attcollation = get_collation_oid(pelem->collation, false);
18947 :
18948 : /*
18949 : * Check we have a collation iff it's a collatable type. The only
18950 : * expected failures here are (1) COLLATE applied to a noncollatable
18951 : * type, or (2) partition expression had an unresolved collation. But
18952 : * we might as well code this to be a complete consistency check.
18953 : */
18954 5266 : if (type_is_collatable(atttype))
18955 : {
18956 626 : if (!OidIsValid(attcollation))
18957 0 : ereport(ERROR,
18958 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
18959 : errmsg("could not determine which collation to use for partition expression"),
18960 : errhint("Use the COLLATE clause to set the collation explicitly.")));
18961 : }
18962 : else
18963 : {
18964 4640 : if (OidIsValid(attcollation))
18965 0 : ereport(ERROR,
18966 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18967 : errmsg("collations are not supported by type %s",
18968 : format_type_be(atttype))));
18969 : }
18970 :
18971 5266 : partcollation[attn] = attcollation;
18972 :
18973 : /*
18974 : * Identify the appropriate operator class. For list and range
18975 : * partitioning, we use a btree operator class; hash partitioning uses
18976 : * a hash operator class.
18977 : */
18978 5266 : if (strategy == PARTITION_STRATEGY_HASH)
18979 276 : am_oid = HASH_AM_OID;
18980 : else
18981 4990 : am_oid = BTREE_AM_OID;
18982 :
18983 5266 : if (!pelem->opclass)
18984 : {
18985 5134 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18986 :
18987 5134 : if (!OidIsValid(partopclass[attn]))
18988 : {
18989 12 : if (strategy == PARTITION_STRATEGY_HASH)
18990 0 : ereport(ERROR,
18991 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18992 : errmsg("data type %s has no default operator class for access method \"%s\"",
18993 : format_type_be(atttype), "hash"),
18994 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18995 : else
18996 12 : ereport(ERROR,
18997 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18998 : errmsg("data type %s has no default operator class for access method \"%s\"",
18999 : format_type_be(atttype), "btree"),
19000 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19001 : }
19002 : }
19003 : else
19004 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
19005 : atttype,
19006 : am_oid == HASH_AM_OID ? "hash" : "btree",
19007 : am_oid);
19008 :
19009 5242 : attn++;
19010 : }
19011 4786 : }
19012 :
19013 : /*
19014 : * PartConstraintImpliedByRelConstraint
19015 : * Do scanrel's existing constraints imply the partition constraint?
19016 : *
19017 : * "Existing constraints" include its check constraints and column-level
19018 : * not-null constraints. partConstraint describes the partition constraint,
19019 : * in implicit-AND form.
19020 : */
19021 : bool
19022 3046 : PartConstraintImpliedByRelConstraint(Relation scanrel,
19023 : List *partConstraint)
19024 : {
19025 3046 : List *existConstraint = NIL;
19026 3046 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19027 : int i;
19028 :
19029 3046 : if (constr && constr->has_not_null)
19030 : {
19031 716 : int natts = scanrel->rd_att->natts;
19032 :
19033 2314 : for (i = 1; i <= natts; i++)
19034 : {
19035 1598 : Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
19036 :
19037 1598 : if (att->attnotnull && !att->attisdropped)
19038 : {
19039 934 : NullTest *ntest = makeNode(NullTest);
19040 :
19041 934 : ntest->arg = (Expr *) makeVar(1,
19042 : i,
19043 : att->atttypid,
19044 : att->atttypmod,
19045 : att->attcollation,
19046 : 0);
19047 934 : ntest->nulltesttype = IS_NOT_NULL;
19048 :
19049 : /*
19050 : * argisrow=false is correct even for a composite column,
19051 : * because attnotnull does not represent a SQL-spec IS NOT
19052 : * NULL test in such a case, just IS DISTINCT FROM NULL.
19053 : */
19054 934 : ntest->argisrow = false;
19055 934 : ntest->location = -1;
19056 934 : existConstraint = lappend(existConstraint, ntest);
19057 : }
19058 : }
19059 : }
19060 :
19061 3046 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19062 : }
19063 :
19064 : /*
19065 : * ConstraintImpliedByRelConstraint
19066 : * Do scanrel's existing constraints imply the given constraint?
19067 : *
19068 : * testConstraint is the constraint to validate. provenConstraint is a
19069 : * caller-provided list of conditions which this function may assume
19070 : * to be true. Both provenConstraint and testConstraint must be in
19071 : * implicit-AND form, must only contain immutable clauses, and must
19072 : * contain only Vars with varno = 1.
19073 : */
19074 : bool
19075 4698 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
19076 : {
19077 4698 : List *existConstraint = list_copy(provenConstraint);
19078 4698 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19079 : int num_check,
19080 : i;
19081 :
19082 4698 : num_check = (constr != NULL) ? constr->num_check : 0;
19083 5284 : for (i = 0; i < num_check; i++)
19084 : {
19085 : Node *cexpr;
19086 :
19087 : /*
19088 : * If this constraint hasn't been fully validated yet, we must ignore
19089 : * it here.
19090 : */
19091 586 : if (!constr->check[i].ccvalid)
19092 6 : continue;
19093 :
19094 580 : cexpr = stringToNode(constr->check[i].ccbin);
19095 :
19096 : /*
19097 : * Run each expression through const-simplification and
19098 : * canonicalization. It is necessary, because we will be comparing it
19099 : * to similarly-processed partition constraint expressions, and may
19100 : * fail to detect valid matches without this.
19101 : */
19102 580 : cexpr = eval_const_expressions(NULL, cexpr);
19103 580 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19104 :
19105 580 : existConstraint = list_concat(existConstraint,
19106 580 : make_ands_implicit((Expr *) cexpr));
19107 : }
19108 :
19109 : /*
19110 : * Try to make the proof. Since we are comparing CHECK constraints, we
19111 : * need to use weak implication, i.e., we assume existConstraint is
19112 : * not-false and try to prove the same for testConstraint.
19113 : *
19114 : * Note that predicate_implied_by assumes its first argument is known
19115 : * immutable. That should always be true for both NOT NULL and partition
19116 : * constraints, so we don't test it here.
19117 : */
19118 4698 : return predicate_implied_by(testConstraint, existConstraint, true);
19119 : }
19120 :
19121 : /*
19122 : * QueuePartitionConstraintValidation
19123 : *
19124 : * Add an entry to wqueue to have the given partition constraint validated by
19125 : * Phase 3, for the given relation, and all its children.
19126 : *
19127 : * We first verify whether the given constraint is implied by pre-existing
19128 : * relation constraints; if it is, there's no need to scan the table to
19129 : * validate, so don't queue in that case.
19130 : */
19131 : static void
19132 2416 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
19133 : List *partConstraint,
19134 : bool validate_default)
19135 : {
19136 : /*
19137 : * Based on the table's existing constraints, determine whether or not we
19138 : * may skip scanning the table.
19139 : */
19140 2416 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19141 : {
19142 90 : if (!validate_default)
19143 68 : ereport(DEBUG1,
19144 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19145 : RelationGetRelationName(scanrel))));
19146 : else
19147 22 : ereport(DEBUG1,
19148 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19149 : RelationGetRelationName(scanrel))));
19150 90 : return;
19151 : }
19152 :
19153 : /*
19154 : * Constraints proved insufficient. For plain relations, queue a
19155 : * validation item now; for partitioned tables, recurse to process each
19156 : * partition.
19157 : */
19158 2326 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19159 : {
19160 : AlteredTableInfo *tab;
19161 :
19162 : /* Grab a work queue entry. */
19163 1932 : tab = ATGetQueueEntry(wqueue, scanrel);
19164 : Assert(tab->partition_constraint == NULL);
19165 1932 : tab->partition_constraint = (Expr *) linitial(partConstraint);
19166 1932 : tab->validate_default = validate_default;
19167 : }
19168 394 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19169 : {
19170 346 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19171 : int i;
19172 :
19173 740 : for (i = 0; i < partdesc->nparts; i++)
19174 : {
19175 : Relation part_rel;
19176 : List *thisPartConstraint;
19177 :
19178 : /*
19179 : * This is the minimum lock we need to prevent deadlocks.
19180 : */
19181 394 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19182 :
19183 : /*
19184 : * Adjust the constraint for scanrel so that it matches this
19185 : * partition's attribute numbers.
19186 : */
19187 : thisPartConstraint =
19188 394 : map_partition_varattnos(partConstraint, 1,
19189 : part_rel, scanrel);
19190 :
19191 394 : QueuePartitionConstraintValidation(wqueue, part_rel,
19192 : thisPartConstraint,
19193 : validate_default);
19194 394 : table_close(part_rel, NoLock); /* keep lock till commit */
19195 : }
19196 : }
19197 : }
19198 :
19199 : /*
19200 : * attachPartitionTable: attach a new partition to the partitioned table
19201 : *
19202 : * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
19203 : * of an ALTER TABLE sequence.
19204 : * rel: partitioned relation;
19205 : * attachrel: relation of attached partition;
19206 : * bound: bounds of attached relation.
19207 : */
19208 : static void
19209 2518 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
19210 : {
19211 : /* OK to create inheritance. Rest of the checks performed there */
19212 2518 : CreateInheritance(attachrel, rel, true);
19213 :
19214 : /* Update the pg_class entry. */
19215 2446 : StorePartitionBound(attachrel, rel, bound);
19216 :
19217 : /* Ensure there exists a correct set of indexes in the partition. */
19218 2446 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19219 :
19220 : /* and triggers */
19221 2416 : CloneRowTriggersToPartition(rel, attachrel);
19222 :
19223 : /*
19224 : * Clone foreign key constraints. Callee is responsible for setting up
19225 : * for phase 3 constraint verification.
19226 : */
19227 2410 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
19228 2410 : }
19229 :
19230 : /*
19231 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19232 : *
19233 : * Return the address of the newly attached partition.
19234 : */
19235 : static ObjectAddress
19236 2182 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19237 : AlterTableUtilityContext *context)
19238 : {
19239 : Relation attachrel,
19240 : catalog;
19241 : List *attachrel_children;
19242 : List *partConstraint;
19243 : SysScanDesc scan;
19244 : ScanKeyData skey;
19245 : AttrNumber attno;
19246 : int natts;
19247 : TupleDesc tupleDesc;
19248 : ObjectAddress address;
19249 : const char *trigger_name;
19250 : Oid defaultPartOid;
19251 : List *partBoundConstraint;
19252 2182 : ParseState *pstate = make_parsestate(NULL);
19253 :
19254 2182 : pstate->p_sourcetext = context->queryString;
19255 :
19256 : /*
19257 : * We must lock the default partition if one exists, because attaching a
19258 : * new partition will change its partition constraint.
19259 : */
19260 : defaultPartOid =
19261 2182 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19262 2182 : if (OidIsValid(defaultPartOid))
19263 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19264 :
19265 2182 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19266 :
19267 : /*
19268 : * XXX I think it'd be a good idea to grab locks on all tables referenced
19269 : * by FKs at this point also.
19270 : */
19271 :
19272 : /*
19273 : * Must be owner of both parent and source table -- parent was checked by
19274 : * ATSimplePermissions call in ATPrepCmd
19275 : */
19276 2176 : ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
19277 :
19278 : /* A partition can only have one parent */
19279 2170 : if (attachrel->rd_rel->relispartition)
19280 6 : ereport(ERROR,
19281 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19282 : errmsg("\"%s\" is already a partition",
19283 : RelationGetRelationName(attachrel))));
19284 :
19285 2164 : if (OidIsValid(attachrel->rd_rel->reloftype))
19286 6 : ereport(ERROR,
19287 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19288 : errmsg("cannot attach a typed table as partition")));
19289 :
19290 : /*
19291 : * Table being attached should not already be part of inheritance; either
19292 : * as a child table...
19293 : */
19294 2158 : catalog = table_open(InheritsRelationId, AccessShareLock);
19295 2158 : ScanKeyInit(&skey,
19296 : Anum_pg_inherits_inhrelid,
19297 : BTEqualStrategyNumber, F_OIDEQ,
19298 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19299 2158 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19300 : NULL, 1, &skey);
19301 2158 : if (HeapTupleIsValid(systable_getnext(scan)))
19302 6 : ereport(ERROR,
19303 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19304 : errmsg("cannot attach inheritance child as partition")));
19305 2152 : systable_endscan(scan);
19306 :
19307 : /* ...or as a parent table (except the case when it is partitioned) */
19308 2152 : ScanKeyInit(&skey,
19309 : Anum_pg_inherits_inhparent,
19310 : BTEqualStrategyNumber, F_OIDEQ,
19311 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
19312 2152 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19313 : 1, &skey);
19314 2152 : if (HeapTupleIsValid(systable_getnext(scan)) &&
19315 248 : attachrel->rd_rel->relkind == RELKIND_RELATION)
19316 6 : ereport(ERROR,
19317 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19318 : errmsg("cannot attach inheritance parent as partition")));
19319 2146 : systable_endscan(scan);
19320 2146 : table_close(catalog, AccessShareLock);
19321 :
19322 : /*
19323 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
19324 : * particular, this disallows making a rel a partition of itself.)
19325 : *
19326 : * We do that by checking if rel is a member of the list of attachrel's
19327 : * partitions provided the latter is partitioned at all. We want to avoid
19328 : * having to construct this list again, so we request the strongest lock
19329 : * on all partitions. We need the strongest lock, because we may decide
19330 : * to scan them if we find out that the table being attached (or its leaf
19331 : * partitions) may contain rows that violate the partition constraint. If
19332 : * the table has a constraint that would prevent such rows, which by
19333 : * definition is present in all the partitions, we need not scan the
19334 : * table, nor its partitions. But we cannot risk a deadlock by taking a
19335 : * weaker lock now and the stronger one only when needed.
19336 : */
19337 2146 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19338 : AccessExclusiveLock, NULL);
19339 2146 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19340 12 : ereport(ERROR,
19341 : (errcode(ERRCODE_DUPLICATE_TABLE),
19342 : errmsg("circular inheritance not allowed"),
19343 : errdetail("\"%s\" is already a child of \"%s\".",
19344 : RelationGetRelationName(rel),
19345 : RelationGetRelationName(attachrel))));
19346 :
19347 : /* If the parent is permanent, so must be all of its partitions. */
19348 2134 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19349 2110 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19350 6 : ereport(ERROR,
19351 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19352 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19353 : RelationGetRelationName(rel))));
19354 :
19355 : /* Temp parent cannot have a partition that is itself not a temp */
19356 2128 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19357 24 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19358 18 : ereport(ERROR,
19359 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19360 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19361 : RelationGetRelationName(rel))));
19362 :
19363 : /* If the parent is temp, it must belong to this session */
19364 2110 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19365 6 : !rel->rd_islocaltemp)
19366 0 : ereport(ERROR,
19367 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19368 : errmsg("cannot attach as partition of temporary relation of another session")));
19369 :
19370 : /* Ditto for the partition */
19371 2110 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19372 6 : !attachrel->rd_islocaltemp)
19373 0 : ereport(ERROR,
19374 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19375 : errmsg("cannot attach temporary relation of another session as partition")));
19376 :
19377 : /*
19378 : * Check if attachrel has any identity columns or any columns that aren't
19379 : * in the parent.
19380 : */
19381 2110 : tupleDesc = RelationGetDescr(attachrel);
19382 2110 : natts = tupleDesc->natts;
19383 7256 : for (attno = 1; attno <= natts; attno++)
19384 : {
19385 5182 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19386 5182 : char *attributeName = NameStr(attribute->attname);
19387 :
19388 : /* Ignore dropped */
19389 5182 : if (attribute->attisdropped)
19390 592 : continue;
19391 :
19392 4590 : if (attribute->attidentity)
19393 18 : ereport(ERROR,
19394 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19395 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19396 : RelationGetRelationName(attachrel), attributeName),
19397 : errdetail("The new partition may not contain an identity column."));
19398 :
19399 : /* Try to find the column in parent (matching on column name) */
19400 4572 : if (!SearchSysCacheExists2(ATTNAME,
19401 : ObjectIdGetDatum(RelationGetRelid(rel)),
19402 : CStringGetDatum(attributeName)))
19403 18 : ereport(ERROR,
19404 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19405 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19406 : RelationGetRelationName(attachrel), attributeName,
19407 : RelationGetRelationName(rel)),
19408 : errdetail("The new partition may contain only the columns present in parent.")));
19409 : }
19410 :
19411 : /*
19412 : * If child_rel has row-level triggers with transition tables, we
19413 : * currently don't allow it to become a partition. See also prohibitions
19414 : * in ATExecAddInherit() and CreateTrigger().
19415 : */
19416 2074 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19417 2074 : if (trigger_name != NULL)
19418 6 : ereport(ERROR,
19419 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19420 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19421 : trigger_name, RelationGetRelationName(attachrel)),
19422 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
19423 :
19424 : /*
19425 : * Check that the new partition's bound is valid and does not overlap any
19426 : * of existing partitions of the parent - note that it does not return on
19427 : * error.
19428 : */
19429 2068 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19430 : cmd->bound, pstate);
19431 :
19432 : /* Attach a new partition to the partitioned table. */
19433 2032 : attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
19434 :
19435 : /*
19436 : * Generate partition constraint from the partition bound specification.
19437 : * If the parent itself is a partition, make sure to include its
19438 : * constraint as well.
19439 : */
19440 1924 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19441 1924 : partConstraint = list_concat(partBoundConstraint,
19442 1924 : RelationGetPartitionQual(rel));
19443 :
19444 : /* Skip validation if there are no constraints to validate. */
19445 1924 : if (partConstraint)
19446 : {
19447 : /*
19448 : * Run the partition quals through const-simplification similar to
19449 : * check constraints. We skip canonicalize_qual, though, because
19450 : * partition quals should be in canonical form already.
19451 : */
19452 : partConstraint =
19453 1876 : (List *) eval_const_expressions(NULL,
19454 : (Node *) partConstraint);
19455 :
19456 : /* XXX this sure looks wrong */
19457 1876 : partConstraint = list_make1(make_ands_explicit(partConstraint));
19458 :
19459 : /*
19460 : * Adjust the generated constraint to match this partition's attribute
19461 : * numbers.
19462 : */
19463 1876 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19464 : rel);
19465 :
19466 : /* Validate partition constraints against the table being attached. */
19467 1876 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19468 : false);
19469 : }
19470 :
19471 : /*
19472 : * If we're attaching a partition other than the default partition and a
19473 : * default one exists, then that partition's partition constraint changes,
19474 : * so add an entry to the work queue to validate it, too. (We must not do
19475 : * this when the partition being attached is the default one; we already
19476 : * did it above!)
19477 : */
19478 1924 : if (OidIsValid(defaultPartOid))
19479 : {
19480 : Relation defaultrel;
19481 : List *defPartConstraint;
19482 :
19483 : Assert(!cmd->bound->is_default);
19484 :
19485 : /* we already hold a lock on the default partition */
19486 146 : defaultrel = table_open(defaultPartOid, NoLock);
19487 : defPartConstraint =
19488 146 : get_proposed_default_constraint(partBoundConstraint);
19489 :
19490 : /*
19491 : * Map the Vars in the constraint expression from rel's attnos to
19492 : * defaultrel's.
19493 : */
19494 : defPartConstraint =
19495 146 : map_partition_varattnos(defPartConstraint,
19496 : 1, defaultrel, rel);
19497 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
19498 : defPartConstraint, true);
19499 :
19500 : /* keep our lock until commit. */
19501 146 : table_close(defaultrel, NoLock);
19502 : }
19503 :
19504 1924 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19505 :
19506 : /*
19507 : * If the partition we just attached is partitioned itself, invalidate
19508 : * relcache for all descendent partitions too to ensure that their
19509 : * rd_partcheck expression trees are rebuilt; partitions already locked at
19510 : * the beginning of this function.
19511 : */
19512 1924 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19513 : {
19514 : ListCell *l;
19515 :
19516 996 : foreach(l, attachrel_children)
19517 : {
19518 674 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19519 : }
19520 : }
19521 :
19522 : /* keep our lock until commit */
19523 1924 : table_close(attachrel, NoLock);
19524 :
19525 1924 : return address;
19526 : }
19527 :
19528 : /*
19529 : * AttachPartitionEnsureIndexes
19530 : * subroutine for ATExecAttachPartition to create/match indexes
19531 : *
19532 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19533 : * PARTITION: every partition must have an index attached to each index on the
19534 : * partitioned table.
19535 : */
19536 : static void
19537 2446 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19538 : {
19539 : List *idxes;
19540 : List *attachRelIdxs;
19541 : Relation *attachrelIdxRels;
19542 : IndexInfo **attachInfos;
19543 : ListCell *cell;
19544 : MemoryContext cxt;
19545 : MemoryContext oldcxt;
19546 :
19547 2446 : cxt = AllocSetContextCreate(CurrentMemoryContext,
19548 : "AttachPartitionEnsureIndexes",
19549 : ALLOCSET_DEFAULT_SIZES);
19550 2446 : oldcxt = MemoryContextSwitchTo(cxt);
19551 :
19552 2446 : idxes = RelationGetIndexList(rel);
19553 2446 : attachRelIdxs = RelationGetIndexList(attachrel);
19554 2446 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19555 2446 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19556 :
19557 : /* Build arrays of all existing indexes and their IndexInfos */
19558 2780 : foreach(cell, attachRelIdxs)
19559 : {
19560 334 : Oid cldIdxId = lfirst_oid(cell);
19561 334 : int i = foreach_current_index(cell);
19562 :
19563 334 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19564 334 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19565 : }
19566 :
19567 : /*
19568 : * If we're attaching a foreign table, we must fail if any of the indexes
19569 : * is a constraint index; otherwise, there's nothing to do here. Do this
19570 : * before starting work, to avoid wasting the effort of building a few
19571 : * non-unique indexes before coming across a unique one.
19572 : */
19573 2446 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19574 : {
19575 86 : foreach(cell, idxes)
19576 : {
19577 36 : Oid idx = lfirst_oid(cell);
19578 36 : Relation idxRel = index_open(idx, AccessShareLock);
19579 :
19580 36 : if (idxRel->rd_index->indisunique ||
19581 24 : idxRel->rd_index->indisprimary)
19582 12 : ereport(ERROR,
19583 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19584 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19585 : RelationGetRelationName(attachrel),
19586 : RelationGetRelationName(rel)),
19587 : errdetail("Partitioned table \"%s\" contains unique indexes.",
19588 : RelationGetRelationName(rel))));
19589 24 : index_close(idxRel, AccessShareLock);
19590 : }
19591 :
19592 50 : goto out;
19593 : }
19594 :
19595 : /*
19596 : * For each index on the partitioned table, find a matching one in the
19597 : * partition-to-be; if one is not found, create one.
19598 : */
19599 3004 : foreach(cell, idxes)
19600 : {
19601 638 : Oid idx = lfirst_oid(cell);
19602 638 : Relation idxRel = index_open(idx, AccessShareLock);
19603 : IndexInfo *info;
19604 : AttrMap *attmap;
19605 638 : bool found = false;
19606 : Oid constraintOid;
19607 :
19608 : /*
19609 : * Ignore indexes in the partitioned table other than partitioned
19610 : * indexes.
19611 : */
19612 638 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19613 : {
19614 0 : index_close(idxRel, AccessShareLock);
19615 0 : continue;
19616 : }
19617 :
19618 : /* construct an indexinfo to compare existing indexes against */
19619 638 : info = BuildIndexInfo(idxRel);
19620 638 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19621 : RelationGetDescr(rel),
19622 : false);
19623 638 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19624 :
19625 : /*
19626 : * Scan the list of existing indexes in the partition-to-be, and mark
19627 : * the first matching, valid, unattached one we find, if any, as
19628 : * partition of the parent index. If we find one, we're done.
19629 : */
19630 704 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19631 : {
19632 256 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19633 256 : Oid cldConstrOid = InvalidOid;
19634 :
19635 : /* does this index have a parent? if so, can't use it */
19636 256 : if (attachrelIdxRels[i]->rd_rel->relispartition)
19637 12 : continue;
19638 :
19639 : /* If this index is invalid, can't use it */
19640 244 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
19641 6 : continue;
19642 :
19643 238 : if (CompareIndexInfo(attachInfos[i], info,
19644 238 : attachrelIdxRels[i]->rd_indcollation,
19645 238 : idxRel->rd_indcollation,
19646 238 : attachrelIdxRels[i]->rd_opfamily,
19647 238 : idxRel->rd_opfamily,
19648 : attmap))
19649 : {
19650 : /*
19651 : * If this index is being created in the parent because of a
19652 : * constraint, then the child needs to have a constraint also,
19653 : * so look for one. If there is no such constraint, this
19654 : * index is no good, so keep looking.
19655 : */
19656 202 : if (OidIsValid(constraintOid))
19657 : {
19658 : cldConstrOid =
19659 116 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19660 : cldIdxId);
19661 : /* no dice */
19662 116 : if (!OidIsValid(cldConstrOid))
19663 6 : continue;
19664 :
19665 : /* Ensure they're both the same type of constraint */
19666 220 : if (get_constraint_type(constraintOid) !=
19667 110 : get_constraint_type(cldConstrOid))
19668 6 : continue;
19669 : }
19670 :
19671 : /* bingo. */
19672 190 : IndexSetParentIndex(attachrelIdxRels[i], idx);
19673 190 : if (OidIsValid(constraintOid))
19674 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19675 : RelationGetRelid(attachrel));
19676 190 : found = true;
19677 :
19678 190 : CommandCounterIncrement();
19679 190 : break;
19680 : }
19681 : }
19682 :
19683 : /*
19684 : * If no suitable index was found in the partition-to-be, create one
19685 : * now.
19686 : */
19687 638 : if (!found)
19688 : {
19689 : IndexStmt *stmt;
19690 : Oid conOid;
19691 :
19692 448 : stmt = generateClonedIndexStmt(NULL,
19693 : idxRel, attmap,
19694 : &conOid);
19695 :
19696 : /*
19697 : * If the index is a primary key, mark all columns as NOT NULL if
19698 : * they aren't already.
19699 : */
19700 448 : if (stmt->primary)
19701 : {
19702 226 : MemoryContextSwitchTo(oldcxt);
19703 464 : for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
19704 : {
19705 : AttrNumber childattno;
19706 :
19707 238 : childattno = get_attnum(RelationGetRelid(attachrel),
19708 238 : get_attname(RelationGetRelid(rel),
19709 238 : info->ii_IndexAttrNumbers[j],
19710 : false));
19711 238 : set_attnotnull(wqueue, attachrel, childattno,
19712 : true, AccessExclusiveLock);
19713 : }
19714 226 : MemoryContextSwitchTo(cxt);
19715 : }
19716 :
19717 448 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19718 : RelationGetRelid(idxRel),
19719 : conOid,
19720 : -1,
19721 : true, false, false, false, false);
19722 : }
19723 :
19724 620 : index_close(idxRel, AccessShareLock);
19725 : }
19726 :
19727 2416 : out:
19728 : /* Clean up. */
19729 2738 : for (int i = 0; i < list_length(attachRelIdxs); i++)
19730 322 : index_close(attachrelIdxRels[i], AccessShareLock);
19731 2416 : MemoryContextSwitchTo(oldcxt);
19732 2416 : MemoryContextDelete(cxt);
19733 2416 : }
19734 :
19735 : /*
19736 : * CloneRowTriggersToPartition
19737 : * subroutine for ATExecAttachPartition/DefineRelation to create row
19738 : * triggers on partitions
19739 : */
19740 : static void
19741 2842 : CloneRowTriggersToPartition(Relation parent, Relation partition)
19742 : {
19743 : Relation pg_trigger;
19744 : ScanKeyData key;
19745 : SysScanDesc scan;
19746 : HeapTuple tuple;
19747 : MemoryContext perTupCxt;
19748 :
19749 2842 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19750 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19751 2842 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19752 2842 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19753 : true, NULL, 1, &key);
19754 :
19755 2842 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19756 : "clone trig", ALLOCSET_SMALL_SIZES);
19757 :
19758 4422 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19759 : {
19760 1586 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19761 : CreateTrigStmt *trigStmt;
19762 1586 : Node *qual = NULL;
19763 : Datum value;
19764 : bool isnull;
19765 1586 : List *cols = NIL;
19766 1586 : List *trigargs = NIL;
19767 : MemoryContext oldcxt;
19768 :
19769 : /*
19770 : * Ignore statement-level triggers; those are not cloned.
19771 : */
19772 1586 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19773 1406 : continue;
19774 :
19775 : /*
19776 : * Don't clone internal triggers, because the constraint cloning code
19777 : * will.
19778 : */
19779 1562 : if (trigForm->tgisinternal)
19780 1382 : continue;
19781 :
19782 : /*
19783 : * Complain if we find an unexpected trigger type.
19784 : */
19785 180 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19786 162 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
19787 0 : elog(ERROR, "unexpected trigger \"%s\" found",
19788 : NameStr(trigForm->tgname));
19789 :
19790 : /* Use short-lived context for CREATE TRIGGER */
19791 180 : oldcxt = MemoryContextSwitchTo(perTupCxt);
19792 :
19793 : /*
19794 : * If there is a WHEN clause, generate a 'cooked' version of it that's
19795 : * appropriate for the partition.
19796 : */
19797 180 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19798 : RelationGetDescr(pg_trigger), &isnull);
19799 180 : if (!isnull)
19800 : {
19801 6 : qual = stringToNode(TextDatumGetCString(value));
19802 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19803 : partition, parent);
19804 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19805 : partition, parent);
19806 : }
19807 :
19808 : /*
19809 : * If there is a column list, transform it to a list of column names.
19810 : * Note we don't need to map this list in any way ...
19811 : */
19812 180 : if (trigForm->tgattr.dim1 > 0)
19813 : {
19814 : int i;
19815 :
19816 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
19817 : {
19818 : Form_pg_attribute col;
19819 :
19820 6 : col = TupleDescAttr(parent->rd_att,
19821 : trigForm->tgattr.values[i] - 1);
19822 6 : cols = lappend(cols,
19823 6 : makeString(pstrdup(NameStr(col->attname))));
19824 : }
19825 : }
19826 :
19827 : /* Reconstruct trigger arguments list. */
19828 180 : if (trigForm->tgnargs > 0)
19829 : {
19830 : char *p;
19831 :
19832 36 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19833 : RelationGetDescr(pg_trigger), &isnull);
19834 36 : if (isnull)
19835 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19836 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
19837 :
19838 36 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19839 :
19840 84 : for (int i = 0; i < trigForm->tgnargs; i++)
19841 : {
19842 48 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
19843 48 : p += strlen(p) + 1;
19844 : }
19845 : }
19846 :
19847 180 : trigStmt = makeNode(CreateTrigStmt);
19848 180 : trigStmt->replace = false;
19849 180 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19850 180 : trigStmt->trigname = NameStr(trigForm->tgname);
19851 180 : trigStmt->relation = NULL;
19852 180 : trigStmt->funcname = NULL; /* passed separately */
19853 180 : trigStmt->args = trigargs;
19854 180 : trigStmt->row = true;
19855 180 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19856 180 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19857 180 : trigStmt->columns = cols;
19858 180 : trigStmt->whenClause = NULL; /* passed separately */
19859 180 : trigStmt->transitionRels = NIL; /* not supported at present */
19860 180 : trigStmt->deferrable = trigForm->tgdeferrable;
19861 180 : trigStmt->initdeferred = trigForm->tginitdeferred;
19862 180 : trigStmt->constrrel = NULL; /* passed separately */
19863 :
19864 180 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19865 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19866 : trigForm->tgfoid, trigForm->oid, qual,
19867 180 : false, true, trigForm->tgenabled);
19868 :
19869 174 : MemoryContextSwitchTo(oldcxt);
19870 174 : MemoryContextReset(perTupCxt);
19871 : }
19872 :
19873 2836 : MemoryContextDelete(perTupCxt);
19874 :
19875 2836 : systable_endscan(scan);
19876 2836 : table_close(pg_trigger, RowExclusiveLock);
19877 2836 : }
19878 :
19879 : /*
19880 : * ALTER TABLE DETACH PARTITION
19881 : *
19882 : * Return the address of the relation that is no longer a partition of rel.
19883 : *
19884 : * If concurrent mode is requested, we run in two transactions. A side-
19885 : * effect is that this command cannot run in a multi-part ALTER TABLE.
19886 : * Currently, that's enforced by the grammar.
19887 : *
19888 : * The strategy for concurrency is to first modify the partition's
19889 : * pg_inherit catalog row to make it visible to everyone that the
19890 : * partition is detached, lock the partition against writes, and commit
19891 : * the transaction; anyone who requests the partition descriptor from
19892 : * that point onwards has to ignore such a partition. In a second
19893 : * transaction, we wait until all transactions that could have seen the
19894 : * partition as attached are gone, then we remove the rest of partition
19895 : * metadata (pg_inherits and pg_class.relpartbounds).
19896 : */
19897 : static ObjectAddress
19898 516 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19899 : RangeVar *name, bool concurrent)
19900 : {
19901 : Relation partRel;
19902 : ObjectAddress address;
19903 : Oid defaultPartOid;
19904 :
19905 : /*
19906 : * We must lock the default partition, because detaching this partition
19907 : * will change its partition constraint.
19908 : */
19909 : defaultPartOid =
19910 516 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19911 516 : if (OidIsValid(defaultPartOid))
19912 : {
19913 : /*
19914 : * Concurrent detaching when a default partition exists is not
19915 : * supported. The main problem is that the default partition
19916 : * constraint would change. And there's a definitional problem: what
19917 : * should happen to the tuples that are being inserted that belong to
19918 : * the partition being detached? Putting them on the partition being
19919 : * detached would be wrong, since they'd become "lost" after the
19920 : * detaching completes but we cannot put them in the default partition
19921 : * either until we alter its partition constraint.
19922 : *
19923 : * I think we could solve this problem if we effected the constraint
19924 : * change before committing the first transaction. But the lock would
19925 : * have to remain AEL and it would cause concurrent query planning to
19926 : * be blocked, so changing it that way would be even worse.
19927 : */
19928 112 : if (concurrent)
19929 12 : ereport(ERROR,
19930 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19931 : errmsg("cannot detach partitions concurrently when a default partition exists")));
19932 100 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
19933 : }
19934 :
19935 : /*
19936 : * In concurrent mode, the partition is locked with share-update-exclusive
19937 : * in the first transaction. This allows concurrent transactions to be
19938 : * doing DML to the partition.
19939 : */
19940 504 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19941 : AccessExclusiveLock);
19942 :
19943 : /*
19944 : * Check inheritance conditions and either delete the pg_inherits row (in
19945 : * non-concurrent mode) or just set the inhdetachpending flag.
19946 : */
19947 492 : if (!concurrent)
19948 346 : RemoveInheritance(partRel, rel, false);
19949 : else
19950 146 : MarkInheritDetached(partRel, rel);
19951 :
19952 : /*
19953 : * Ensure that foreign keys still hold after this detach. This keeps
19954 : * locks on the referencing tables, which prevents concurrent transactions
19955 : * from adding rows that we wouldn't see. For this to work in concurrent
19956 : * mode, it is critical that the partition appears as no longer attached
19957 : * for the RI queries as soon as the first transaction commits.
19958 : */
19959 472 : ATDetachCheckNoForeignKeyRefs(partRel);
19960 :
19961 : /*
19962 : * Concurrent mode has to work harder; first we add a new constraint to
19963 : * the partition that matches the partition constraint. Then we close our
19964 : * existing transaction, and in a new one wait for all processes to catch
19965 : * up on the catalog updates we've done so far; at that point we can
19966 : * complete the operation.
19967 : */
19968 438 : if (concurrent)
19969 : {
19970 : Oid partrelid,
19971 : parentrelid;
19972 : LOCKTAG tag;
19973 : char *parentrelname;
19974 : char *partrelname;
19975 :
19976 : /*
19977 : * Add a new constraint to the partition being detached, which
19978 : * supplants the partition constraint (unless there is one already).
19979 : */
19980 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
19981 :
19982 : /*
19983 : * We're almost done now; the only traces that remain are the
19984 : * pg_inherits tuple and the partition's relpartbounds. Before we can
19985 : * remove those, we need to wait until all transactions that know that
19986 : * this is a partition are gone.
19987 : */
19988 :
19989 : /*
19990 : * Remember relation OIDs to re-acquire them later; and relation names
19991 : * too, for error messages if something is dropped in between.
19992 : */
19993 140 : partrelid = RelationGetRelid(partRel);
19994 140 : parentrelid = RelationGetRelid(rel);
19995 140 : parentrelname = MemoryContextStrdup(PortalContext,
19996 140 : RelationGetRelationName(rel));
19997 140 : partrelname = MemoryContextStrdup(PortalContext,
19998 140 : RelationGetRelationName(partRel));
19999 :
20000 : /* Invalidate relcache entries for the parent -- must be before close */
20001 140 : CacheInvalidateRelcache(rel);
20002 :
20003 140 : table_close(partRel, NoLock);
20004 140 : table_close(rel, NoLock);
20005 140 : tab->rel = NULL;
20006 :
20007 : /* Make updated catalog entry visible */
20008 140 : PopActiveSnapshot();
20009 140 : CommitTransactionCommand();
20010 :
20011 140 : StartTransactionCommand();
20012 :
20013 : /*
20014 : * Now wait. This ensures that all queries that were planned
20015 : * including the partition are finished before we remove the rest of
20016 : * catalog entries. We don't need or indeed want to acquire this
20017 : * lock, though -- that would block later queries.
20018 : *
20019 : * We don't need to concern ourselves with waiting for a lock on the
20020 : * partition itself, since we will acquire AccessExclusiveLock below.
20021 : */
20022 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20023 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
20024 :
20025 : /*
20026 : * Now acquire locks in both relations again. Note they may have been
20027 : * removed in the meantime, so care is required.
20028 : */
20029 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20030 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
20031 :
20032 : /* If the relations aren't there, something bad happened; bail out */
20033 90 : if (rel == NULL)
20034 : {
20035 0 : if (partRel != NULL) /* shouldn't happen */
20036 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20037 : partrelname);
20038 0 : ereport(ERROR,
20039 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20040 : errmsg("partitioned table \"%s\" was removed concurrently",
20041 : parentrelname)));
20042 : }
20043 90 : if (partRel == NULL)
20044 0 : ereport(ERROR,
20045 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20046 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
20047 :
20048 90 : tab->rel = rel;
20049 : }
20050 :
20051 : /* Do the final part of detaching */
20052 388 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20053 :
20054 386 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20055 :
20056 : /* keep our lock until commit */
20057 386 : table_close(partRel, NoLock);
20058 :
20059 386 : return address;
20060 : }
20061 :
20062 : /*
20063 : * Second part of ALTER TABLE .. DETACH.
20064 : *
20065 : * This is separate so that it can be run independently when the second
20066 : * transaction of the concurrent algorithm fails (crash or abort).
20067 : */
20068 : static void
20069 780 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20070 : Oid defaultPartOid)
20071 : {
20072 : Relation classRel;
20073 : List *fks;
20074 : ListCell *cell;
20075 : List *indexes;
20076 : Datum new_val[Natts_pg_class];
20077 : bool new_null[Natts_pg_class],
20078 : new_repl[Natts_pg_class];
20079 : HeapTuple tuple,
20080 : newtuple;
20081 780 : Relation trigrel = NULL;
20082 :
20083 780 : if (concurrent)
20084 : {
20085 : /*
20086 : * We can remove the pg_inherits row now. (In the non-concurrent case,
20087 : * this was already done).
20088 : */
20089 104 : RemoveInheritance(partRel, rel, true);
20090 : }
20091 :
20092 : /* Drop any triggers that were cloned on creation/attach. */
20093 780 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
20094 :
20095 : /*
20096 : * Detach any foreign keys that are inherited. This includes creating
20097 : * additional action triggers.
20098 : */
20099 780 : fks = copyObject(RelationGetFKeyList(partRel));
20100 780 : if (fks != NIL)
20101 54 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20102 864 : foreach(cell, fks)
20103 : {
20104 84 : ForeignKeyCacheInfo *fk = lfirst(cell);
20105 : HeapTuple contup;
20106 : Form_pg_constraint conform;
20107 : Constraint *fkconstraint;
20108 : Oid insertTriggerOid,
20109 : updateTriggerOid;
20110 :
20111 84 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20112 84 : if (!HeapTupleIsValid(contup))
20113 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20114 84 : conform = (Form_pg_constraint) GETSTRUCT(contup);
20115 :
20116 : /* consider only the inherited foreign keys */
20117 84 : if (conform->contype != CONSTRAINT_FOREIGN ||
20118 84 : !OidIsValid(conform->conparentid))
20119 : {
20120 18 : ReleaseSysCache(contup);
20121 18 : continue;
20122 : }
20123 :
20124 : /* unset conparentid and adjust conislocal, coninhcount, etc. */
20125 66 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
20126 :
20127 : /*
20128 : * Also, look up the partition's "check" triggers corresponding to the
20129 : * constraint being detached and detach them from the parent triggers.
20130 : */
20131 66 : GetForeignKeyCheckTriggers(trigrel,
20132 : fk->conoid, fk->confrelid, fk->conrelid,
20133 : &insertTriggerOid, &updateTriggerOid);
20134 : Assert(OidIsValid(insertTriggerOid));
20135 66 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20136 : RelationGetRelid(partRel));
20137 : Assert(OidIsValid(updateTriggerOid));
20138 66 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20139 : RelationGetRelid(partRel));
20140 :
20141 : /*
20142 : * Make the action triggers on the referenced relation. When this was
20143 : * a partition the action triggers pointed to the parent rel (they
20144 : * still do), but now we need separate ones of our own.
20145 : */
20146 66 : fkconstraint = makeNode(Constraint);
20147 66 : fkconstraint->contype = CONSTRAINT_FOREIGN;
20148 66 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
20149 66 : fkconstraint->deferrable = conform->condeferrable;
20150 66 : fkconstraint->initdeferred = conform->condeferred;
20151 66 : fkconstraint->location = -1;
20152 66 : fkconstraint->pktable = NULL;
20153 66 : fkconstraint->fk_attrs = NIL;
20154 66 : fkconstraint->pk_attrs = NIL;
20155 66 : fkconstraint->fk_matchtype = conform->confmatchtype;
20156 66 : fkconstraint->fk_upd_action = conform->confupdtype;
20157 66 : fkconstraint->fk_del_action = conform->confdeltype;
20158 66 : fkconstraint->fk_del_set_cols = NIL;
20159 66 : fkconstraint->old_conpfeqop = NIL;
20160 66 : fkconstraint->old_pktable_oid = InvalidOid;
20161 66 : fkconstraint->skip_validation = false;
20162 66 : fkconstraint->initially_valid = true;
20163 :
20164 66 : createForeignKeyActionTriggers(partRel, conform->confrelid,
20165 : fkconstraint, fk->conoid,
20166 : conform->conindid,
20167 : InvalidOid, InvalidOid,
20168 : NULL, NULL);
20169 :
20170 66 : ReleaseSysCache(contup);
20171 : }
20172 780 : list_free_deep(fks);
20173 780 : if (trigrel)
20174 54 : table_close(trigrel, RowExclusiveLock);
20175 :
20176 : /*
20177 : * Any sub-constraints that are in the referenced-side of a larger
20178 : * constraint have to be removed. This partition is no longer part of the
20179 : * key space of the constraint.
20180 : */
20181 822 : foreach(cell, GetParentedForeignKeyRefs(partRel))
20182 : {
20183 44 : Oid constrOid = lfirst_oid(cell);
20184 : ObjectAddress constraint;
20185 :
20186 44 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20187 44 : deleteDependencyRecordsForClass(ConstraintRelationId,
20188 : constrOid,
20189 : ConstraintRelationId,
20190 : DEPENDENCY_INTERNAL);
20191 44 : CommandCounterIncrement();
20192 :
20193 44 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20194 44 : performDeletion(&constraint, DROP_RESTRICT, 0);
20195 : }
20196 :
20197 : /* Now we can detach indexes */
20198 778 : indexes = RelationGetIndexList(partRel);
20199 1116 : foreach(cell, indexes)
20200 : {
20201 338 : Oid idxid = lfirst_oid(cell);
20202 : Relation idx;
20203 : Oid constrOid;
20204 :
20205 338 : if (!has_superclass(idxid))
20206 12 : continue;
20207 :
20208 : Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
20209 : RelationGetRelid(rel)));
20210 :
20211 326 : idx = index_open(idxid, AccessExclusiveLock);
20212 326 : IndexSetParentIndex(idx, InvalidOid);
20213 :
20214 : /* If there's a constraint associated with the index, detach it too */
20215 326 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20216 : idxid);
20217 326 : if (OidIsValid(constrOid))
20218 132 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20219 :
20220 326 : index_close(idx, NoLock);
20221 : }
20222 :
20223 : /* Update pg_class tuple */
20224 778 : classRel = table_open(RelationRelationId, RowExclusiveLock);
20225 778 : tuple = SearchSysCacheCopy1(RELOID,
20226 : ObjectIdGetDatum(RelationGetRelid(partRel)));
20227 778 : if (!HeapTupleIsValid(tuple))
20228 0 : elog(ERROR, "cache lookup failed for relation %u",
20229 : RelationGetRelid(partRel));
20230 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20231 :
20232 : /* Clear relpartbound and reset relispartition */
20233 778 : memset(new_val, 0, sizeof(new_val));
20234 778 : memset(new_null, false, sizeof(new_null));
20235 778 : memset(new_repl, false, sizeof(new_repl));
20236 778 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20237 778 : new_null[Anum_pg_class_relpartbound - 1] = true;
20238 778 : new_repl[Anum_pg_class_relpartbound - 1] = true;
20239 778 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20240 : new_val, new_null, new_repl);
20241 :
20242 778 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20243 778 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20244 778 : heap_freetuple(newtuple);
20245 778 : table_close(classRel, RowExclusiveLock);
20246 :
20247 : /*
20248 : * Drop identity property from all identity columns of partition.
20249 : */
20250 2650 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20251 : {
20252 1872 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20253 :
20254 1872 : if (!attr->attisdropped && attr->attidentity)
20255 30 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20256 : AccessExclusiveLock, true, true);
20257 : }
20258 :
20259 778 : if (OidIsValid(defaultPartOid))
20260 : {
20261 : /*
20262 : * If the relation being detached is the default partition itself,
20263 : * remove it from the parent's pg_partitioned_table entry.
20264 : *
20265 : * If not, we must invalidate default partition's relcache entry, as
20266 : * in StorePartitionBound: its partition constraint depends on every
20267 : * other partition's partition constraint.
20268 : */
20269 232 : if (RelationGetRelid(partRel) == defaultPartOid)
20270 38 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20271 : else
20272 194 : CacheInvalidateRelcacheByRelid(defaultPartOid);
20273 : }
20274 :
20275 : /*
20276 : * Invalidate the parent's relcache so that the partition is no longer
20277 : * included in its partition descriptor.
20278 : */
20279 778 : CacheInvalidateRelcache(rel);
20280 :
20281 : /*
20282 : * If the partition we just detached is partitioned itself, invalidate
20283 : * relcache for all descendent partitions too to ensure that their
20284 : * rd_partcheck expression trees are rebuilt; must lock partitions before
20285 : * doing so, using the same lockmode as what partRel has been locked with
20286 : * by the caller.
20287 : */
20288 778 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20289 : {
20290 : List *children;
20291 :
20292 50 : children = find_all_inheritors(RelationGetRelid(partRel),
20293 : AccessExclusiveLock, NULL);
20294 162 : foreach(cell, children)
20295 : {
20296 112 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20297 : }
20298 : }
20299 778 : }
20300 :
20301 : /*
20302 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20303 : *
20304 : * To use when a DETACH PARTITION command previously did not run to
20305 : * completion; this completes the detaching process.
20306 : */
20307 : static ObjectAddress
20308 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20309 : {
20310 : Relation partRel;
20311 : ObjectAddress address;
20312 14 : Snapshot snap = GetActiveSnapshot();
20313 :
20314 14 : partRel = table_openrv(name, AccessExclusiveLock);
20315 :
20316 : /*
20317 : * Wait until existing snapshots are gone. This is important if the
20318 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20319 : * user could immediately run DETACH FINALIZE without actually waiting for
20320 : * existing transactions. We must not complete the detach action until
20321 : * all such queries are complete (otherwise we would present them with an
20322 : * inconsistent view of catalogs).
20323 : */
20324 14 : WaitForOlderSnapshots(snap->xmin, false);
20325 :
20326 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20327 :
20328 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20329 :
20330 14 : table_close(partRel, NoLock);
20331 :
20332 14 : return address;
20333 : }
20334 :
20335 : /*
20336 : * DetachAddConstraintIfNeeded
20337 : * Subroutine for ATExecDetachPartition. Create a constraint that
20338 : * takes the place of the partition constraint, but avoid creating
20339 : * a dupe if a constraint already exists which implies the needed
20340 : * constraint.
20341 : */
20342 : static void
20343 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20344 : {
20345 : List *constraintExpr;
20346 :
20347 140 : constraintExpr = RelationGetPartitionQual(partRel);
20348 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20349 :
20350 : /*
20351 : * Avoid adding a new constraint if the needed constraint is implied by an
20352 : * existing constraint
20353 : */
20354 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20355 : {
20356 : AlteredTableInfo *tab;
20357 : Constraint *n;
20358 :
20359 134 : tab = ATGetQueueEntry(wqueue, partRel);
20360 :
20361 : /* Add constraint on partition, equivalent to the partition constraint */
20362 134 : n = makeNode(Constraint);
20363 134 : n->contype = CONSTR_CHECK;
20364 134 : n->conname = NULL;
20365 134 : n->location = -1;
20366 134 : n->is_no_inherit = false;
20367 134 : n->raw_expr = NULL;
20368 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20369 134 : n->initially_valid = true;
20370 134 : n->skip_validation = true;
20371 : /* It's a re-add, since it nominally already exists */
20372 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20373 : true, false, true, ShareUpdateExclusiveLock);
20374 : }
20375 140 : }
20376 :
20377 : /*
20378 : * DropClonedTriggersFromPartition
20379 : * subroutine for ATExecDetachPartition to remove any triggers that were
20380 : * cloned to the partition when it was created-as-partition or attached.
20381 : * This undoes what CloneRowTriggersToPartition did.
20382 : */
20383 : static void
20384 780 : DropClonedTriggersFromPartition(Oid partitionId)
20385 : {
20386 : ScanKeyData skey;
20387 : SysScanDesc scan;
20388 : HeapTuple trigtup;
20389 : Relation tgrel;
20390 : ObjectAddresses *objects;
20391 :
20392 780 : objects = new_object_addresses();
20393 :
20394 : /*
20395 : * Scan pg_trigger to search for all triggers on this rel.
20396 : */
20397 780 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20398 : F_OIDEQ, ObjectIdGetDatum(partitionId));
20399 780 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20400 780 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20401 : true, NULL, 1, &skey);
20402 1078 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20403 : {
20404 298 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20405 : ObjectAddress trig;
20406 :
20407 : /* Ignore triggers that weren't cloned */
20408 298 : if (!OidIsValid(pg_trigger->tgparentid))
20409 256 : continue;
20410 :
20411 : /*
20412 : * Ignore internal triggers that are implementation objects of foreign
20413 : * keys, because these will be detached when the foreign keys
20414 : * themselves are.
20415 : */
20416 262 : if (OidIsValid(pg_trigger->tgconstrrelid))
20417 220 : continue;
20418 :
20419 : /*
20420 : * This is ugly, but necessary: remove the dependency markings on the
20421 : * trigger so that it can be removed.
20422 : */
20423 42 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20424 : TriggerRelationId,
20425 : DEPENDENCY_PARTITION_PRI);
20426 42 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20427 : RelationRelationId,
20428 : DEPENDENCY_PARTITION_SEC);
20429 :
20430 : /* remember this trigger to remove it below */
20431 42 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20432 42 : add_exact_object_address(&trig, objects);
20433 : }
20434 :
20435 : /* make the dependency removal visible to the deletion below */
20436 780 : CommandCounterIncrement();
20437 780 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20438 :
20439 : /* done */
20440 780 : free_object_addresses(objects);
20441 780 : systable_endscan(scan);
20442 780 : table_close(tgrel, RowExclusiveLock);
20443 780 : }
20444 :
20445 : /*
20446 : * Before acquiring lock on an index, acquire the same lock on the owning
20447 : * table.
20448 : */
20449 : struct AttachIndexCallbackState
20450 : {
20451 : Oid partitionOid;
20452 : Oid parentTblOid;
20453 : bool lockedParentTbl;
20454 : };
20455 :
20456 : static void
20457 394 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20458 : void *arg)
20459 : {
20460 : struct AttachIndexCallbackState *state;
20461 : Form_pg_class classform;
20462 : HeapTuple tuple;
20463 :
20464 394 : state = (struct AttachIndexCallbackState *) arg;
20465 :
20466 394 : if (!state->lockedParentTbl)
20467 : {
20468 384 : LockRelationOid(state->parentTblOid, AccessShareLock);
20469 384 : state->lockedParentTbl = true;
20470 : }
20471 :
20472 : /*
20473 : * If we previously locked some other heap, and the name we're looking up
20474 : * no longer refers to an index on that relation, release the now-useless
20475 : * lock. XXX maybe we should do *after* we verify whether the index does
20476 : * not actually belong to the same relation ...
20477 : */
20478 394 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20479 : {
20480 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
20481 0 : state->partitionOid = InvalidOid;
20482 : }
20483 :
20484 : /* Didn't find a relation, so no need for locking or permission checks. */
20485 394 : if (!OidIsValid(relOid))
20486 6 : return;
20487 :
20488 388 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20489 388 : if (!HeapTupleIsValid(tuple))
20490 0 : return; /* concurrently dropped, so nothing to do */
20491 388 : classform = (Form_pg_class) GETSTRUCT(tuple);
20492 388 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20493 296 : classform->relkind != RELKIND_INDEX)
20494 6 : ereport(ERROR,
20495 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20496 : errmsg("\"%s\" is not an index", rv->relname)));
20497 382 : ReleaseSysCache(tuple);
20498 :
20499 : /*
20500 : * Since we need only examine the heap's tupledesc, an access share lock
20501 : * on it (preventing any DDL) is sufficient.
20502 : */
20503 382 : state->partitionOid = IndexGetRelation(relOid, false);
20504 382 : LockRelationOid(state->partitionOid, AccessShareLock);
20505 : }
20506 :
20507 : /*
20508 : * ALTER INDEX i1 ATTACH PARTITION i2
20509 : */
20510 : static ObjectAddress
20511 384 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20512 : {
20513 : Relation partIdx;
20514 : Relation partTbl;
20515 : Relation parentTbl;
20516 : ObjectAddress address;
20517 : Oid partIdxId;
20518 : Oid currParent;
20519 : struct AttachIndexCallbackState state;
20520 :
20521 : /*
20522 : * We need to obtain lock on the index 'name' to modify it, but we also
20523 : * need to read its owning table's tuple descriptor -- so we need to lock
20524 : * both. To avoid deadlocks, obtain lock on the table before doing so on
20525 : * the index. Furthermore, we need to examine the parent table of the
20526 : * partition, so lock that one too.
20527 : */
20528 384 : state.partitionOid = InvalidOid;
20529 384 : state.parentTblOid = parentIdx->rd_index->indrelid;
20530 384 : state.lockedParentTbl = false;
20531 : partIdxId =
20532 384 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20533 : RangeVarCallbackForAttachIndex,
20534 : (void *) &state);
20535 : /* Not there? */
20536 372 : if (!OidIsValid(partIdxId))
20537 0 : ereport(ERROR,
20538 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20539 : errmsg("index \"%s\" does not exist", name->relname)));
20540 :
20541 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20542 372 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
20543 :
20544 : /* we already hold locks on both tables, so this is safe: */
20545 372 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20546 372 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20547 :
20548 372 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20549 :
20550 : /* Silently do nothing if already in the right state */
20551 744 : currParent = partIdx->rd_rel->relispartition ?
20552 372 : get_partition_parent(partIdxId, false) : InvalidOid;
20553 372 : if (currParent != RelationGetRelid(parentIdx))
20554 : {
20555 : IndexInfo *childInfo;
20556 : IndexInfo *parentInfo;
20557 : AttrMap *attmap;
20558 : bool found;
20559 : int i;
20560 : PartitionDesc partDesc;
20561 : Oid constraintOid,
20562 360 : cldConstrId = InvalidOid;
20563 :
20564 : /*
20565 : * If this partition already has an index attached, refuse the
20566 : * operation.
20567 : */
20568 360 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20569 :
20570 354 : if (OidIsValid(currParent))
20571 0 : ereport(ERROR,
20572 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20573 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20574 : RelationGetRelationName(partIdx),
20575 : RelationGetRelationName(parentIdx)),
20576 : errdetail("Index \"%s\" is already attached to another index.",
20577 : RelationGetRelationName(partIdx))));
20578 :
20579 : /* Make sure it indexes a partition of the other index's table */
20580 354 : partDesc = RelationGetPartitionDesc(parentTbl, true);
20581 354 : found = false;
20582 556 : for (i = 0; i < partDesc->nparts; i++)
20583 : {
20584 550 : if (partDesc->oids[i] == state.partitionOid)
20585 : {
20586 348 : found = true;
20587 348 : break;
20588 : }
20589 : }
20590 354 : if (!found)
20591 6 : ereport(ERROR,
20592 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20593 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20594 : RelationGetRelationName(partIdx),
20595 : RelationGetRelationName(parentIdx)),
20596 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20597 : RelationGetRelationName(partIdx),
20598 : RelationGetRelationName(parentTbl))));
20599 :
20600 : /* Ensure the indexes are compatible */
20601 348 : childInfo = BuildIndexInfo(partIdx);
20602 348 : parentInfo = BuildIndexInfo(parentIdx);
20603 348 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20604 : RelationGetDescr(parentTbl),
20605 : false);
20606 348 : if (!CompareIndexInfo(childInfo, parentInfo,
20607 348 : partIdx->rd_indcollation,
20608 348 : parentIdx->rd_indcollation,
20609 348 : partIdx->rd_opfamily,
20610 348 : parentIdx->rd_opfamily,
20611 : attmap))
20612 42 : ereport(ERROR,
20613 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20614 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20615 : RelationGetRelationName(partIdx),
20616 : RelationGetRelationName(parentIdx)),
20617 : errdetail("The index definitions do not match.")));
20618 :
20619 : /*
20620 : * If there is a constraint in the parent, make sure there is one in
20621 : * the child too.
20622 : */
20623 306 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20624 : RelationGetRelid(parentIdx));
20625 :
20626 306 : if (OidIsValid(constraintOid))
20627 : {
20628 128 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20629 : partIdxId);
20630 128 : if (!OidIsValid(cldConstrId))
20631 6 : ereport(ERROR,
20632 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20633 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20634 : RelationGetRelationName(partIdx),
20635 : RelationGetRelationName(parentIdx)),
20636 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20637 : RelationGetRelationName(parentIdx),
20638 : RelationGetRelationName(parentTbl),
20639 : RelationGetRelationName(partIdx))));
20640 : }
20641 :
20642 : /*
20643 : * If it's a primary key, make sure the columns in the partition are
20644 : * NOT NULL.
20645 : */
20646 300 : if (parentIdx->rd_index->indisprimary)
20647 98 : verifyPartitionIndexNotNull(childInfo, partTbl);
20648 :
20649 : /* All good -- do it */
20650 294 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20651 294 : if (OidIsValid(constraintOid))
20652 116 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
20653 : RelationGetRelid(partTbl));
20654 :
20655 294 : free_attrmap(attmap);
20656 :
20657 294 : validatePartitionedIndex(parentIdx, parentTbl);
20658 : }
20659 :
20660 306 : relation_close(parentTbl, AccessShareLock);
20661 : /* keep these locks till commit */
20662 306 : relation_close(partTbl, NoLock);
20663 306 : relation_close(partIdx, NoLock);
20664 :
20665 306 : return address;
20666 : }
20667 :
20668 : /*
20669 : * Verify whether the given partition already contains an index attached
20670 : * to the given partitioned index. If so, raise an error.
20671 : */
20672 : static void
20673 360 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
20674 : {
20675 : Oid existingIdx;
20676 :
20677 360 : existingIdx = index_get_partition(partitionTbl,
20678 : RelationGetRelid(parentIdx));
20679 360 : if (OidIsValid(existingIdx))
20680 6 : ereport(ERROR,
20681 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20682 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20683 : RelationGetRelationName(partIdx),
20684 : RelationGetRelationName(parentIdx)),
20685 : errdetail("Another index is already attached for partition \"%s\".",
20686 : RelationGetRelationName(partitionTbl))));
20687 354 : }
20688 :
20689 : /*
20690 : * Verify whether the set of attached partition indexes to a parent index on
20691 : * a partitioned table is complete. If it is, mark the parent index valid.
20692 : *
20693 : * This should be called each time a partition index is attached.
20694 : */
20695 : static void
20696 336 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
20697 : {
20698 : Relation inheritsRel;
20699 : SysScanDesc scan;
20700 : ScanKeyData key;
20701 336 : int tuples = 0;
20702 : HeapTuple inhTup;
20703 336 : bool updated = false;
20704 :
20705 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20706 :
20707 : /*
20708 : * Scan pg_inherits for this parent index. Count each valid index we find
20709 : * (verifying the pg_index entry for each), and if we reach the total
20710 : * amount we expect, we can mark this parent index as valid.
20711 : */
20712 336 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20713 336 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20714 : BTEqualStrategyNumber, F_OIDEQ,
20715 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20716 336 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20717 : NULL, 1, &key);
20718 876 : while ((inhTup = systable_getnext(scan)) != NULL)
20719 : {
20720 540 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20721 : HeapTuple indTup;
20722 : Form_pg_index indexForm;
20723 :
20724 540 : indTup = SearchSysCache1(INDEXRELID,
20725 : ObjectIdGetDatum(inhForm->inhrelid));
20726 540 : if (!HeapTupleIsValid(indTup))
20727 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20728 540 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20729 540 : if (indexForm->indisvalid)
20730 482 : tuples += 1;
20731 540 : ReleaseSysCache(indTup);
20732 : }
20733 :
20734 : /* Done with pg_inherits */
20735 336 : systable_endscan(scan);
20736 336 : table_close(inheritsRel, AccessShareLock);
20737 :
20738 : /*
20739 : * If we found as many inherited indexes as the partitioned table has
20740 : * partitions, we're good; update pg_index to set indisvalid.
20741 : */
20742 336 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20743 : {
20744 : Relation idxRel;
20745 : HeapTuple indTup;
20746 : Form_pg_index indexForm;
20747 :
20748 166 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
20749 166 : indTup = SearchSysCacheCopy1(INDEXRELID,
20750 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20751 166 : if (!HeapTupleIsValid(indTup))
20752 0 : elog(ERROR, "cache lookup failed for index %u",
20753 : RelationGetRelid(partedIdx));
20754 166 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
20755 :
20756 166 : indexForm->indisvalid = true;
20757 166 : updated = true;
20758 :
20759 166 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20760 :
20761 166 : table_close(idxRel, RowExclusiveLock);
20762 166 : heap_freetuple(indTup);
20763 : }
20764 :
20765 : /*
20766 : * If this index is in turn a partition of a larger index, validating it
20767 : * might cause the parent to become valid also. Try that.
20768 : */
20769 336 : if (updated && partedIdx->rd_rel->relispartition)
20770 : {
20771 : Oid parentIdxId,
20772 : parentTblId;
20773 : Relation parentIdx,
20774 : parentTbl;
20775 :
20776 : /* make sure we see the validation we just did */
20777 42 : CommandCounterIncrement();
20778 :
20779 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20780 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20781 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20782 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20783 : Assert(!parentIdx->rd_index->indisvalid);
20784 :
20785 42 : validatePartitionedIndex(parentIdx, parentTbl);
20786 :
20787 42 : relation_close(parentIdx, AccessExclusiveLock);
20788 42 : relation_close(parentTbl, AccessExclusiveLock);
20789 : }
20790 336 : }
20791 :
20792 : /*
20793 : * When attaching an index as a partition of a partitioned index which is a
20794 : * primary key, verify that all the columns in the partition are marked NOT
20795 : * NULL.
20796 : */
20797 : static void
20798 98 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20799 : {
20800 192 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20801 : {
20802 100 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20803 : iinfo->ii_IndexAttrNumbers[i] - 1);
20804 :
20805 100 : if (!att->attnotnull)
20806 6 : ereport(ERROR,
20807 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20808 : errmsg("invalid primary key definition"),
20809 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20810 : NameStr(att->attname),
20811 : RelationGetRelationName(partition)));
20812 : }
20813 92 : }
20814 :
20815 : /*
20816 : * Return an OID list of constraints that reference the given relation
20817 : * that are marked as having a parent constraints.
20818 : */
20819 : static List *
20820 1252 : GetParentedForeignKeyRefs(Relation partition)
20821 : {
20822 : Relation pg_constraint;
20823 : HeapTuple tuple;
20824 : SysScanDesc scan;
20825 : ScanKeyData key[2];
20826 1252 : List *constraints = NIL;
20827 :
20828 : /*
20829 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
20830 : * scan.
20831 : */
20832 1746 : if (RelationGetIndexList(partition) == NIL ||
20833 494 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
20834 : INDEX_ATTR_BITMAP_KEY)))
20835 1004 : return NIL;
20836 :
20837 : /* Search for constraints referencing this table */
20838 248 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20839 248 : ScanKeyInit(&key[0],
20840 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20841 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20842 248 : ScanKeyInit(&key[1],
20843 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
20844 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20845 :
20846 : /* XXX This is a seqscan, as we don't have a usable index */
20847 248 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20848 378 : while ((tuple = systable_getnext(scan)) != NULL)
20849 : {
20850 130 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20851 :
20852 : /*
20853 : * We only need to process constraints that are part of larger ones.
20854 : */
20855 130 : if (!OidIsValid(constrForm->conparentid))
20856 0 : continue;
20857 :
20858 130 : constraints = lappend_oid(constraints, constrForm->oid);
20859 : }
20860 :
20861 248 : systable_endscan(scan);
20862 248 : table_close(pg_constraint, AccessShareLock);
20863 :
20864 248 : return constraints;
20865 : }
20866 :
20867 : /*
20868 : * During DETACH PARTITION, verify that any foreign keys pointing to the
20869 : * partitioned table would not become invalid. An error is raised if any
20870 : * referenced values exist.
20871 : */
20872 : static void
20873 472 : ATDetachCheckNoForeignKeyRefs(Relation partition)
20874 : {
20875 : List *constraints;
20876 : ListCell *cell;
20877 :
20878 472 : constraints = GetParentedForeignKeyRefs(partition);
20879 :
20880 524 : foreach(cell, constraints)
20881 : {
20882 86 : Oid constrOid = lfirst_oid(cell);
20883 : HeapTuple tuple;
20884 : Form_pg_constraint constrForm;
20885 : Relation rel;
20886 86 : Trigger trig = {0};
20887 :
20888 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20889 86 : if (!HeapTupleIsValid(tuple))
20890 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20891 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20892 :
20893 : Assert(OidIsValid(constrForm->conparentid));
20894 : Assert(constrForm->confrelid == RelationGetRelid(partition));
20895 :
20896 : /* prevent data changes into the referencing table until commit */
20897 86 : rel = table_open(constrForm->conrelid, ShareLock);
20898 :
20899 86 : trig.tgoid = InvalidOid;
20900 86 : trig.tgname = NameStr(constrForm->conname);
20901 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
20902 86 : trig.tgisinternal = true;
20903 86 : trig.tgconstrrelid = RelationGetRelid(partition);
20904 86 : trig.tgconstrindid = constrForm->conindid;
20905 86 : trig.tgconstraint = constrForm->oid;
20906 86 : trig.tgdeferrable = false;
20907 86 : trig.tginitdeferred = false;
20908 : /* we needn't fill in remaining fields */
20909 :
20910 86 : RI_PartitionRemove_Check(&trig, rel, partition);
20911 :
20912 52 : ReleaseSysCache(tuple);
20913 :
20914 52 : table_close(rel, NoLock);
20915 : }
20916 438 : }
20917 :
20918 : /*
20919 : * resolve column compression specification to compression method.
20920 : */
20921 : static char
20922 217698 : GetAttributeCompression(Oid atttypid, const char *compression)
20923 : {
20924 : char cmethod;
20925 :
20926 217698 : if (compression == NULL || strcmp(compression, "default") == 0)
20927 217548 : return InvalidCompressionMethod;
20928 :
20929 : /*
20930 : * To specify a nondefault method, the column data type must be toastable.
20931 : * Note this says nothing about whether the column's attstorage setting
20932 : * permits compression; we intentionally allow attstorage and
20933 : * attcompression to be independent. But with a non-toastable type,
20934 : * attstorage could not be set to a value that would permit compression.
20935 : *
20936 : * We don't actually need to enforce this, since nothing bad would happen
20937 : * if attcompression were non-default; it would never be consulted. But
20938 : * it seems more user-friendly to complain about a certainly-useless
20939 : * attempt to set the property.
20940 : */
20941 150 : if (!TypeIsToastable(atttypid))
20942 6 : ereport(ERROR,
20943 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20944 : errmsg("column data type %s does not support compression",
20945 : format_type_be(atttypid))));
20946 :
20947 144 : cmethod = CompressionNameToMethod(compression);
20948 144 : if (!CompressionMethodIsValid(cmethod))
20949 12 : ereport(ERROR,
20950 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20951 : errmsg("invalid compression method \"%s\"", compression)));
20952 :
20953 132 : return cmethod;
20954 : }
20955 :
20956 : /*
20957 : * resolve column storage specification
20958 : */
20959 : static char
20960 242 : GetAttributeStorage(Oid atttypid, const char *storagemode)
20961 : {
20962 242 : char cstorage = 0;
20963 :
20964 242 : if (pg_strcasecmp(storagemode, "plain") == 0)
20965 50 : cstorage = TYPSTORAGE_PLAIN;
20966 192 : else if (pg_strcasecmp(storagemode, "external") == 0)
20967 156 : cstorage = TYPSTORAGE_EXTERNAL;
20968 36 : else if (pg_strcasecmp(storagemode, "extended") == 0)
20969 16 : cstorage = TYPSTORAGE_EXTENDED;
20970 20 : else if (pg_strcasecmp(storagemode, "main") == 0)
20971 14 : cstorage = TYPSTORAGE_MAIN;
20972 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
20973 6 : cstorage = get_typstorage(atttypid);
20974 : else
20975 0 : ereport(ERROR,
20976 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20977 : errmsg("invalid storage type \"%s\"",
20978 : storagemode)));
20979 :
20980 : /*
20981 : * safety check: do not allow toasted storage modes unless column datatype
20982 : * is TOAST-aware.
20983 : */
20984 242 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20985 6 : ereport(ERROR,
20986 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20987 : errmsg("column data type %s can only have storage PLAIN",
20988 : format_type_be(atttypid))));
20989 :
20990 236 : return cstorage;
20991 : }
20992 :
20993 : /*
20994 : * Struct with context of new partition for inserting rows from split partition
20995 : */
20996 : typedef struct SplitPartitionContext
20997 : {
20998 : ExprState *partqualstate; /* expression for checking slot for partition
20999 : * (NULL for DEFAULT partition) */
21000 : BulkInsertState bistate; /* state of bulk inserts for partition */
21001 : TupleTableSlot *dstslot; /* slot for inserting row into partition */
21002 : Relation partRel; /* relation for partition */
21003 : } SplitPartitionContext;
21004 :
21005 :
21006 : /*
21007 : * createSplitPartitionContext: create context for partition and fill it
21008 : */
21009 : static SplitPartitionContext *
21010 444 : createSplitPartitionContext(Relation partRel)
21011 : {
21012 : SplitPartitionContext *pc;
21013 :
21014 444 : pc = (SplitPartitionContext *) palloc0(sizeof(SplitPartitionContext));
21015 444 : pc->partRel = partRel;
21016 :
21017 : /*
21018 : * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
21019 : * don't bother using it.
21020 : */
21021 444 : pc->bistate = GetBulkInsertState();
21022 :
21023 : /* Create tuple slot for new partition. */
21024 444 : pc->dstslot = MakeSingleTupleTableSlot(RelationGetDescr(pc->partRel),
21025 : table_slot_callbacks(pc->partRel));
21026 444 : ExecStoreAllNullTuple(pc->dstslot);
21027 :
21028 444 : return pc;
21029 : }
21030 :
21031 : /*
21032 : * deleteSplitPartitionContext: delete context for partition
21033 : */
21034 : static void
21035 444 : deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
21036 : {
21037 444 : ExecDropSingleTupleTableSlot(pc->dstslot);
21038 444 : FreeBulkInsertState(pc->bistate);
21039 :
21040 444 : table_finish_bulk_insert(pc->partRel, ti_options);
21041 :
21042 444 : pfree(pc);
21043 444 : }
21044 :
21045 : /*
21046 : * moveSplitTableRows: scan split partition (splitRel) of partitioned table
21047 : * (rel) and move rows into new partitions.
21048 : *
21049 : * New partitions description:
21050 : * partlist: list of pointers to SinglePartitionSpec structures.
21051 : * newPartRels: list of Relations.
21052 : * defaultPartOid: oid of DEFAULT partition, for table rel.
21053 : */
21054 : static void
21055 132 : moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
21056 : {
21057 : /* The FSM is empty, so don't bother using it. */
21058 132 : int ti_options = TABLE_INSERT_SKIP_FSM;
21059 : CommandId mycid;
21060 : EState *estate;
21061 : ListCell *listptr,
21062 : *listptr2;
21063 : TupleTableSlot *srcslot;
21064 : ExprContext *econtext;
21065 : TableScanDesc scan;
21066 : Snapshot snapshot;
21067 : MemoryContext oldCxt;
21068 132 : List *partContexts = NIL;
21069 : TupleConversionMap *tuple_map;
21070 132 : SplitPartitionContext *defaultPartCtx = NULL,
21071 : *pc;
21072 132 : bool isOldDefaultPart = false;
21073 :
21074 132 : mycid = GetCurrentCommandId(true);
21075 :
21076 132 : estate = CreateExecutorState();
21077 :
21078 528 : forboth(listptr, partlist, listptr2, newPartRels)
21079 : {
21080 396 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21081 :
21082 396 : pc = createSplitPartitionContext((Relation) lfirst(listptr2));
21083 :
21084 396 : if (sps->bound->is_default)
21085 : {
21086 : /* We should not create constraint for detached DEFAULT partition. */
21087 30 : defaultPartCtx = pc;
21088 : }
21089 : else
21090 : {
21091 : List *partConstraint;
21092 :
21093 : /* Build expression execution states for partition check quals. */
21094 366 : partConstraint = get_qual_from_partbound(rel, sps->bound);
21095 : partConstraint =
21096 366 : (List *) eval_const_expressions(NULL,
21097 : (Node *) partConstraint);
21098 : /* Make boolean expression for ExecCheck(). */
21099 366 : partConstraint = list_make1(make_ands_explicit(partConstraint));
21100 :
21101 : /*
21102 : * Map the vars in the constraint expression from rel's attnos to
21103 : * splitRel's.
21104 : */
21105 366 : partConstraint = map_partition_varattnos(partConstraint,
21106 : 1, splitRel, rel);
21107 :
21108 366 : pc->partqualstate =
21109 366 : ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
21110 : Assert(pc->partqualstate != NULL);
21111 : }
21112 :
21113 : /* Store partition context into list. */
21114 396 : partContexts = lappend(partContexts, pc);
21115 : }
21116 :
21117 : /*
21118 : * Create partition context for DEFAULT partition. We can insert values
21119 : * into this partition in case spaces with values between new partitions.
21120 : */
21121 132 : if (!defaultPartCtx && OidIsValid(defaultPartOid))
21122 : {
21123 : /* Indicate that we allocate context for old DEFAULT partition */
21124 48 : isOldDefaultPart = true;
21125 48 : defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
21126 : }
21127 :
21128 132 : econtext = GetPerTupleExprContext(estate);
21129 :
21130 : /* Create necessary tuple slot. */
21131 132 : srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
21132 : table_slot_callbacks(splitRel));
21133 :
21134 : /*
21135 : * Map computing for moving attributes of split partition to new partition
21136 : * (for first new partition, but other new partitions can use the same
21137 : * map).
21138 : */
21139 132 : pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
21140 132 : tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
21141 132 : RelationGetDescr(pc->partRel));
21142 :
21143 : /* Scan through the rows. */
21144 132 : snapshot = RegisterSnapshot(GetLatestSnapshot());
21145 132 : scan = table_beginscan(splitRel, snapshot, 0, NULL);
21146 :
21147 : /*
21148 : * Switch to per-tuple memory context and reset it for each tuple
21149 : * produced, so we don't leak memory.
21150 : */
21151 132 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
21152 :
21153 672 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21154 : {
21155 540 : bool found = false;
21156 : TupleTableSlot *insertslot;
21157 :
21158 : /* Extract data from old tuple. */
21159 540 : slot_getallattrs(srcslot);
21160 :
21161 540 : econtext->ecxt_scantuple = srcslot;
21162 :
21163 : /* Search partition for current slot srcslot. */
21164 1488 : foreach(listptr, partContexts)
21165 : {
21166 1374 : pc = (SplitPartitionContext *) lfirst(listptr);
21167 :
21168 2640 : if (pc->partqualstate /* skip DEFAULT partition */ &&
21169 1266 : ExecCheck(pc->partqualstate, econtext))
21170 : {
21171 426 : found = true;
21172 426 : break;
21173 : }
21174 948 : ResetExprContext(econtext);
21175 : }
21176 540 : if (!found)
21177 : {
21178 : /* Use DEFAULT partition if it exists. */
21179 114 : if (defaultPartCtx)
21180 114 : pc = defaultPartCtx;
21181 : else
21182 0 : ereport(ERROR,
21183 : (errcode(ERRCODE_CHECK_VIOLATION),
21184 : errmsg("can not find partition for split partition row"),
21185 : errtable(splitRel)));
21186 : }
21187 :
21188 540 : if (tuple_map)
21189 : {
21190 : /* Need to use map to copy attributes. */
21191 24 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
21192 : }
21193 : else
21194 : {
21195 : /* Copy attributes directly. */
21196 516 : insertslot = pc->dstslot;
21197 :
21198 516 : ExecClearTuple(insertslot);
21199 :
21200 516 : memcpy(insertslot->tts_values, srcslot->tts_values,
21201 516 : sizeof(Datum) * srcslot->tts_nvalid);
21202 516 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21203 516 : sizeof(bool) * srcslot->tts_nvalid);
21204 :
21205 516 : ExecStoreVirtualTuple(insertslot);
21206 : }
21207 :
21208 : /* Write the tuple out to the new relation. */
21209 540 : table_tuple_insert(pc->partRel, insertslot, mycid,
21210 : ti_options, pc->bistate);
21211 :
21212 540 : ResetExprContext(econtext);
21213 :
21214 540 : CHECK_FOR_INTERRUPTS();
21215 : }
21216 :
21217 132 : MemoryContextSwitchTo(oldCxt);
21218 :
21219 132 : table_endscan(scan);
21220 132 : UnregisterSnapshot(snapshot);
21221 :
21222 132 : if (tuple_map)
21223 6 : free_conversion_map(tuple_map);
21224 :
21225 132 : ExecDropSingleTupleTableSlot(srcslot);
21226 :
21227 132 : FreeExecutorState(estate);
21228 :
21229 528 : foreach(listptr, partContexts)
21230 396 : deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
21231 :
21232 : /* Need to close table and free buffers for DEFAULT partition. */
21233 132 : if (isOldDefaultPart)
21234 : {
21235 48 : Relation defaultPartRel = defaultPartCtx->partRel;
21236 :
21237 48 : deleteSplitPartitionContext(defaultPartCtx, ti_options);
21238 : /* Keep the lock until commit. */
21239 48 : table_close(defaultPartRel, NoLock);
21240 : }
21241 132 : }
21242 :
21243 : /*
21244 : * createPartitionTable: create table for a new partition with given name
21245 : * (newPartName) like table (modelRel)
21246 : *
21247 : * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name>
21248 : * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)
21249 : *
21250 : * Also, this function sets the new partition access method same as parent
21251 : * table access methods (similarly to CREATE TABLE ... PARTITION OF). It
21252 : * checks that parent and child tables have compatible persistence.
21253 : *
21254 : * Function returns the created relation (locked in AccessExclusiveLock mode).
21255 : */
21256 : static Relation
21257 498 : createPartitionTable(RangeVar *newPartName, Relation modelRel,
21258 : AlterTableUtilityContext *context)
21259 : {
21260 : CreateStmt *createStmt;
21261 : TableLikeClause *tlc;
21262 : PlannedStmt *wrapper;
21263 : Relation newRel;
21264 :
21265 : /* If existing rel is temp, it must belong to this session */
21266 498 : if (modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
21267 24 : !modelRel->rd_islocaltemp)
21268 0 : ereport(ERROR,
21269 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
21270 : errmsg("cannot create as partition of temporary relation of another session")));
21271 :
21272 : /* New partition should have the same persistence as modelRel */
21273 498 : newPartName->relpersistence = modelRel->rd_rel->relpersistence;
21274 :
21275 498 : createStmt = makeNode(CreateStmt);
21276 498 : createStmt->relation = newPartName;
21277 498 : createStmt->tableElts = NIL;
21278 498 : createStmt->inhRelations = NIL;
21279 498 : createStmt->constraints = NIL;
21280 498 : createStmt->options = NIL;
21281 498 : createStmt->oncommit = ONCOMMIT_NOOP;
21282 498 : createStmt->tablespacename = NULL;
21283 498 : createStmt->if_not_exists = false;
21284 498 : createStmt->accessMethod = get_am_name(modelRel->rd_rel->relam);
21285 :
21286 498 : tlc = makeNode(TableLikeClause);
21287 498 : tlc->relation = makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel)),
21288 498 : RelationGetRelationName(modelRel), -1);
21289 :
21290 : /*
21291 : * Indexes will be inherited on "attach new partitions" stage, after data
21292 : * moving.
21293 : */
21294 498 : tlc->options = CREATE_TABLE_LIKE_ALL & ~(CREATE_TABLE_LIKE_INDEXES | CREATE_TABLE_LIKE_IDENTITY);
21295 498 : tlc->relationOid = InvalidOid;
21296 498 : createStmt->tableElts = lappend(createStmt->tableElts, tlc);
21297 :
21298 : /* Need to make a wrapper PlannedStmt. */
21299 498 : wrapper = makeNode(PlannedStmt);
21300 498 : wrapper->commandType = CMD_UTILITY;
21301 498 : wrapper->canSetTag = false;
21302 498 : wrapper->utilityStmt = (Node *) createStmt;
21303 498 : wrapper->stmt_location = context->pstmt->stmt_location;
21304 498 : wrapper->stmt_len = context->pstmt->stmt_len;
21305 :
21306 498 : ProcessUtility(wrapper,
21307 : context->queryString,
21308 : false,
21309 : PROCESS_UTILITY_SUBCOMMAND,
21310 : NULL,
21311 : NULL,
21312 : None_Receiver,
21313 : NULL);
21314 :
21315 : /*
21316 : * Open the new partition with no lock, because we already have
21317 : * AccessExclusiveLock placed there after creation.
21318 : */
21319 498 : newRel = table_openrv(newPartName, NoLock);
21320 :
21321 : /*
21322 : * We intended to create the partition with the same persistence as the
21323 : * parent table, but we still need to recheck because that might be
21324 : * affected by the search_path. If the parent is permanent, so must be
21325 : * all of its partitions.
21326 : */
21327 498 : if (modelRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
21328 474 : newRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
21329 12 : ereport(ERROR,
21330 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
21331 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
21332 : RelationGetRelationName(modelRel))));
21333 :
21334 : /* Permanent rels cannot be partitions belonging to temporary parent */
21335 486 : if (newRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
21336 462 : modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
21337 0 : ereport(ERROR,
21338 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
21339 : errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
21340 : RelationGetRelationName(modelRel))));
21341 :
21342 486 : return newRel;
21343 : }
21344 :
21345 : /*
21346 : * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
21347 : */
21348 : static void
21349 138 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
21350 : PartitionCmd *cmd, AlterTableUtilityContext *context)
21351 : {
21352 : Relation splitRel;
21353 : Oid splitRelOid;
21354 : char relname[NAMEDATALEN];
21355 : Oid namespaceId;
21356 : ListCell *listptr,
21357 : *listptr2;
21358 138 : bool isSameName = false;
21359 : char tmpRelName[NAMEDATALEN];
21360 138 : List *newPartRels = NIL;
21361 : ObjectAddress object;
21362 : Oid defaultPartOid;
21363 :
21364 138 : defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21365 :
21366 : /*
21367 : * We are going to detach and remove this partition: need to use exclusive
21368 : * lock for preventing DML-queries to the partition.
21369 : */
21370 138 : splitRel = table_openrv(cmd->name, AccessExclusiveLock);
21371 :
21372 138 : splitRelOid = RelationGetRelid(splitRel);
21373 :
21374 : /* Check descriptions of new partitions. */
21375 534 : foreach(listptr, cmd->partlist)
21376 : {
21377 : Oid existing_relid;
21378 402 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21379 :
21380 402 : strlcpy(relname, sps->name->relname, NAMEDATALEN);
21381 :
21382 : /*
21383 : * Look up the namespace in which we are supposed to create the
21384 : * partition, check we have permission to create there, lock it
21385 : * against concurrent drop, and mark stmt->relation as
21386 : * RELPERSISTENCE_TEMP if a temporary namespace is selected.
21387 : */
21388 : namespaceId =
21389 402 : RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL);
21390 :
21391 : /*
21392 : * This would fail later on anyway if the relation already exists. But
21393 : * by catching it here we can emit a nicer error message.
21394 : */
21395 402 : existing_relid = get_relname_relid(relname, namespaceId);
21396 402 : if (existing_relid == splitRelOid && !isSameName)
21397 : /* One new partition can have the same name as split partition. */
21398 24 : isSameName = true;
21399 378 : else if (existing_relid != InvalidOid)
21400 6 : ereport(ERROR,
21401 : (errcode(ERRCODE_DUPLICATE_TABLE),
21402 : errmsg("relation \"%s\" already exists", relname)));
21403 : }
21404 :
21405 : /* Detach split partition. */
21406 132 : RemoveInheritance(splitRel, rel, false);
21407 : /* Do the final part of detaching. */
21408 132 : DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
21409 :
21410 : /*
21411 : * If new partition has the same name as split partition then we should
21412 : * rename split partition for reusing name.
21413 : */
21414 132 : if (isSameName)
21415 : {
21416 : /*
21417 : * We must bump the command counter to make the split partition tuple
21418 : * visible for renaming.
21419 : */
21420 24 : CommandCounterIncrement();
21421 : /* Rename partition. */
21422 24 : sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21423 24 : RenameRelationInternal(splitRelOid, tmpRelName, false, false);
21424 :
21425 : /*
21426 : * We must bump the command counter to make the split partition tuple
21427 : * visible after renaming.
21428 : */
21429 24 : CommandCounterIncrement();
21430 : }
21431 :
21432 : /* Create new partitions (like split partition), without indexes. */
21433 528 : foreach(listptr, cmd->partlist)
21434 : {
21435 396 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21436 : Relation newPartRel;
21437 :
21438 396 : newPartRel = createPartitionTable(sps->name, rel, context);
21439 396 : newPartRels = lappend(newPartRels, newPartRel);
21440 : }
21441 :
21442 : /* Copy data from split partition to new partitions. */
21443 132 : moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
21444 : /* Keep the lock until commit. */
21445 132 : table_close(splitRel, NoLock);
21446 :
21447 : /* Attach new partitions to partitioned table. */
21448 528 : forboth(listptr, cmd->partlist, listptr2, newPartRels)
21449 : {
21450 396 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21451 396 : Relation newPartRel = (Relation) lfirst(listptr2);
21452 :
21453 : /*
21454 : * wqueue = NULL: verification for each cloned constraint is not
21455 : * needed.
21456 : */
21457 396 : attachPartitionTable(NULL, rel, newPartRel, sps->bound);
21458 : /* Keep the lock until commit. */
21459 396 : table_close(newPartRel, NoLock);
21460 : }
21461 :
21462 : /* Drop split partition. */
21463 132 : object.classId = RelationRelationId;
21464 132 : object.objectId = splitRelOid;
21465 132 : object.objectSubId = 0;
21466 : /* Probably DROP_CASCADE is not needed. */
21467 132 : performDeletion(&object, DROP_RESTRICT, 0);
21468 132 : }
21469 :
21470 : /*
21471 : * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
21472 : * of the partitioned table (rel) and move rows into the new partition
21473 : * (newPartRel).
21474 : */
21475 : static void
21476 90 : moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
21477 : Relation newPartRel)
21478 : {
21479 : CommandId mycid;
21480 :
21481 : /* The FSM is empty, so don't bother using it. */
21482 90 : int ti_options = TABLE_INSERT_SKIP_FSM;
21483 : ListCell *listptr;
21484 : BulkInsertState bistate; /* state of bulk inserts for partition */
21485 : TupleTableSlot *dstslot;
21486 :
21487 90 : mycid = GetCurrentCommandId(true);
21488 :
21489 : /* Prepare a BulkInsertState for table_tuple_insert. */
21490 90 : bistate = GetBulkInsertState();
21491 :
21492 : /* Create necessary tuple slot. */
21493 90 : dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
21494 : table_slot_callbacks(newPartRel));
21495 90 : ExecStoreAllNullTuple(dstslot);
21496 :
21497 312 : foreach(listptr, mergingPartitionsList)
21498 : {
21499 222 : Relation mergingPartition = (Relation) lfirst(listptr);
21500 : TupleTableSlot *srcslot;
21501 : TupleConversionMap *tuple_map;
21502 : TableScanDesc scan;
21503 : Snapshot snapshot;
21504 :
21505 : /* Create tuple slot for new partition. */
21506 222 : srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
21507 : table_slot_callbacks(mergingPartition));
21508 :
21509 : /*
21510 : * Map computing for moving attributes of merged partition to new
21511 : * partition.
21512 : */
21513 222 : tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
21514 : RelationGetDescr(newPartRel));
21515 :
21516 : /* Scan through the rows. */
21517 222 : snapshot = RegisterSnapshot(GetLatestSnapshot());
21518 222 : scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
21519 :
21520 528 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21521 : {
21522 : TupleTableSlot *insertslot;
21523 :
21524 : /* Extract data from old tuple. */
21525 306 : slot_getallattrs(srcslot);
21526 :
21527 306 : if (tuple_map)
21528 : {
21529 : /* Need to use map to copy attributes. */
21530 30 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
21531 : }
21532 : else
21533 : {
21534 : /* Copy attributes directly. */
21535 276 : insertslot = dstslot;
21536 :
21537 276 : ExecClearTuple(insertslot);
21538 :
21539 276 : memcpy(insertslot->tts_values, srcslot->tts_values,
21540 276 : sizeof(Datum) * srcslot->tts_nvalid);
21541 276 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21542 276 : sizeof(bool) * srcslot->tts_nvalid);
21543 :
21544 276 : ExecStoreVirtualTuple(insertslot);
21545 : }
21546 :
21547 : /* Write the tuple out to the new relation. */
21548 306 : table_tuple_insert(newPartRel, insertslot, mycid,
21549 : ti_options, bistate);
21550 :
21551 306 : CHECK_FOR_INTERRUPTS();
21552 : }
21553 :
21554 222 : table_endscan(scan);
21555 222 : UnregisterSnapshot(snapshot);
21556 :
21557 222 : if (tuple_map)
21558 18 : free_conversion_map(tuple_map);
21559 :
21560 222 : ExecDropSingleTupleTableSlot(srcslot);
21561 : }
21562 :
21563 90 : ExecDropSingleTupleTableSlot(dstslot);
21564 90 : FreeBulkInsertState(bistate);
21565 :
21566 90 : table_finish_bulk_insert(newPartRel, ti_options);
21567 90 : }
21568 :
21569 : /*
21570 : * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
21571 : */
21572 : static void
21573 102 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
21574 : PartitionCmd *cmd, AlterTableUtilityContext *context)
21575 : {
21576 : Relation newPartRel;
21577 : ListCell *listptr;
21578 102 : List *mergingPartitionsList = NIL;
21579 : Oid defaultPartOid;
21580 :
21581 : /*
21582 : * Lock all merged partitions, check them and create list with partitions
21583 : * contexts.
21584 : */
21585 348 : foreach(listptr, cmd->partlist)
21586 : {
21587 246 : RangeVar *name = (RangeVar *) lfirst(listptr);
21588 : Relation mergingPartition;
21589 :
21590 : /*
21591 : * We are going to detach and remove this partition: need to use
21592 : * exclusive lock for preventing DML-queries to the partition.
21593 : */
21594 246 : mergingPartition = table_openrv(name, AccessExclusiveLock);
21595 :
21596 : /*
21597 : * Checking that two partitions have the same name was before, in
21598 : * function transformPartitionCmdForMerge().
21599 : */
21600 246 : if (equal(name, cmd->name))
21601 : {
21602 : /* One new partition can have the same name as merged partition. */
21603 : char tmpRelName[NAMEDATALEN];
21604 :
21605 : /* Generate temporary name. */
21606 12 : sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21607 :
21608 : /*
21609 : * Rename the existing partition with a temporary name, leaving it
21610 : * free for the new partition. We don't need to care about this
21611 : * in the future because we're going to eventually drop the
21612 : * existing partition anyway.
21613 : */
21614 12 : RenameRelationInternal(RelationGetRelid(mergingPartition),
21615 : tmpRelName, false, false);
21616 :
21617 : /*
21618 : * We must bump the command counter to make the new partition
21619 : * tuple visible for rename.
21620 : */
21621 12 : CommandCounterIncrement();
21622 : }
21623 :
21624 : /* Store a next merging partition into the list. */
21625 246 : mergingPartitionsList = lappend(mergingPartitionsList,
21626 : mergingPartition);
21627 : }
21628 :
21629 : /* Detach all merged partitions. */
21630 : defaultPartOid =
21631 102 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21632 348 : foreach(listptr, mergingPartitionsList)
21633 : {
21634 246 : Relation mergingPartition = (Relation) lfirst(listptr);
21635 :
21636 : /* Remove the pg_inherits row first. */
21637 246 : RemoveInheritance(mergingPartition, rel, false);
21638 : /* Do the final part of detaching. */
21639 246 : DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
21640 : }
21641 :
21642 : /* Create table for new partition, use partitioned table as model. */
21643 102 : newPartRel = createPartitionTable(cmd->name, rel, context);
21644 :
21645 : /* Copy data from merged partitions to new partition. */
21646 90 : moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
21647 :
21648 : /* Drop the current partitions before attaching the new one. */
21649 312 : foreach(listptr, mergingPartitionsList)
21650 : {
21651 : ObjectAddress object;
21652 222 : Relation mergingPartition = (Relation) lfirst(listptr);
21653 :
21654 : /* Get relation id before table_close() call. */
21655 222 : object.objectId = RelationGetRelid(mergingPartition);
21656 222 : object.classId = RelationRelationId;
21657 222 : object.objectSubId = 0;
21658 :
21659 : /* Keep the lock until commit. */
21660 222 : table_close(mergingPartition, NoLock);
21661 :
21662 222 : performDeletion(&object, DROP_RESTRICT, 0);
21663 : }
21664 90 : list_free(mergingPartitionsList);
21665 :
21666 : /*
21667 : * Attach a new partition to the partitioned table. wqueue = NULL:
21668 : * verification for each cloned constraint is not needed.
21669 : */
21670 90 : attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
21671 :
21672 : /* Keep the lock until commit. */
21673 90 : table_close(newPartRel, NoLock);
21674 90 : }
|